blob: 4de4a22ff0929ffa3f50948f23bd8c7fbc2dea45 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
Benny Prijono77998ce2007-06-20 10:03:46 +000053 * Called to generate new offer.
54 */
55static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
56 pjmedia_sdp_session **offer);
57
58/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059 * This callback is called when transaction state has changed in INVITE
60 * session. We use this to trap:
61 * - incoming REFER request.
62 * - incoming MESSAGE request.
63 */
64static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
65 pjsip_transaction *tsx,
66 pjsip_event *e);
67
68
Benny Prijonoeebe9af2006-06-13 22:57:13 +000069
70/* Create inactive SDP for call hold. */
71static pj_status_t create_inactive_sdp(pjsua_call *call,
72 pjmedia_sdp_session **p_answer);
73
Benny Prijono7129cc72007-11-05 05:54:25 +000074/* Update SDP version in the offer */
75static void update_sdp_version(pjsua_call *call,
76 pjmedia_sdp_session *sdp)
77{
78 const pjmedia_sdp_session *old_sdp = NULL;
79 pj_status_t status;
80
81 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
82 if (status != PJ_SUCCESS || old_sdp == NULL)
83 return;
84
85 sdp->origin.version = old_sdp->origin.version + 1;
86}
87
88
Benny Prijonod524e822006-09-22 12:48:18 +000089/*
90 * Callback called by event framework when the xfer subscription state
91 * has changed.
92 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000093static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
94static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
95
96/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000097 * Reset call descriptor.
98 */
99static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000100{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 call->index = id;
104 call->inv = NULL;
105 call->user_data = NULL;
106 call->session = NULL;
107 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000108 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109 call->conf_slot = PJSUA_INVALID_ID;
110 call->last_text.ptr = call->last_text_buf_;
111 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000112 call->conn_time.sec = 0;
113 call->conn_time.msec = 0;
114 call->res_time.sec = 0;
115 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000116 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Benny Prijono105217f2006-03-06 16:25:59 +0000117}
118
119
Benny Prijono275fd682006-03-22 11:59:11 +0000120/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000121 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000122 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000124{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125 pjsip_inv_callback inv_cb;
126 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000127 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000129
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 /* Init calls array. */
131 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
132 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000133
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 /* Copy config */
135 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000136
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000137 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000138 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000139 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
140 inv_cb.on_new_session = &pjsua_call_on_forked;
141 inv_cb.on_media_update = &pjsua_call_on_media_update;
142 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000143 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000145
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 /* Initialize invite session module: */
148 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
149 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
150
Benny Prijonoc8141a82006-08-20 09:12:19 +0000151 /* Add "norefersub" in Supported header */
152 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
153 NULL, 1, &str_norefersub);
154
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155 return status;
156}
157
158
159/*
160 * Start call subsystem.
161 */
162pj_status_t pjsua_call_subsys_start(void)
163{
164 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000165 return PJ_SUCCESS;
166}
167
168
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000170 * Get maximum number of calls configured in pjsua.
171 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000172PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000175}
176
177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178/*
179 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000181PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000184}
185
186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187/*
188 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000189 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
191 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000192{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000194
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000195 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
200 if (!pjsua_var.calls[i].inv)
201 continue;
202 ids[c] = i;
203 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000204 }
205
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000209
210 return PJ_SUCCESS;
211}
212
213
Benny Prijono1f7767b2007-10-03 18:28:49 +0000214#define LATE_SDP 0
215
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216/*
217 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000218 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
220 const pj_str_t *dest_uri,
221 unsigned options,
222 void *user_data,
223 const pjsua_msg_data *msg_data,
224 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000225{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000226 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000227 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000228 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000229 pjsua_acc *acc;
230 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000231 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000232 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000233 pjsip_tx_data *tdata;
234 pj_status_t status;
235
Benny Prijono9fc735d2006-05-28 14:58:12 +0000236
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000238 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000239 PJ_EINVAL);
240
Benny Prijono320fa4d2006-12-07 10:09:16 +0000241 /* Check arguments */
242 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
243
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244 PJSUA_LOCK();
245
246 acc = &pjsua_var.acc[acc_id];
247 if (!acc->valid) {
248 pjsua_perror(THIS_FILE, "Unable to make call because account "
249 "is not valid", PJ_EINVALIDOP);
250 PJSUA_UNLOCK();
251 return PJ_EINVALIDOP;
252 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000253
Benny Prijonoa91a0032006-02-26 21:23:45 +0000254 /* Find free call slot. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000255 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000256 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000257 break;
258 }
259
Benny Prijonoa1e69682007-05-11 15:14:34 +0000260 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
262 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000263 return PJ_ETOOMANY;
264 }
265
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000266 call = &pjsua_var.calls[call_id];
267
Benny Prijono320fa4d2006-12-07 10:09:16 +0000268 /* Verify that destination URI is valid before calling
269 * pjsua_acc_create_uac_contact, or otherwise there
270 * a misleading "Invalid Contact URI" error will be printed
271 * when pjsua_acc_create_uac_contact() fails.
272 */
273 if (1) {
274 pj_pool_t *pool;
275 pjsip_uri *uri;
276 pj_str_t dup;
277
278 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
279 if (!pool) {
280 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
281 PJSUA_UNLOCK();
282 return PJ_ENOMEM;
283 }
284
285 pj_strdup_with_null(pool, &dup, dest_uri);
286 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
287 pj_pool_release(pool);
288
289 if (uri == NULL) {
290 pjsua_perror(THIS_FILE, "Unable to make call",
291 PJSIP_EINVALIDREQURI);
292 PJSUA_UNLOCK();
293 return PJSIP_EINVALIDREQURI;
294 }
295 }
296
Benny Prijono093d3022006-09-24 00:07:11 +0000297 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
298 (int)dest_uri->slen, dest_uri->ptr));
299
Benny Prijonoe21e7842006-04-09 16:46:05 +0000300 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000301 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000302
Benny Prijonoe21e7842006-04-09 16:46:05 +0000303 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000304 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000305
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000306 /* Create suitable Contact header */
307 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
308 acc_id, dest_uri);
309 if (status != PJ_SUCCESS) {
310 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
311 PJSUA_UNLOCK();
312 return status;
313 }
314
Benny Prijonoe21e7842006-04-09 16:46:05 +0000315 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000316 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000317 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000318 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000319 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000320 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000321 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000322 return status;
323 }
324
Benny Prijonoc97608e2007-03-23 16:34:20 +0000325 /* Init media channel */
326 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
327 if (status != PJ_SUCCESS) {
328 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
329 goto on_error;
330 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000331
Benny Prijonoc97608e2007-03-23 16:34:20 +0000332 /* Create SDP offer */
Benny Prijono1f7767b2007-10-03 18:28:49 +0000333#if LATE_SDP
334 offer = NULL;
335#else
Benny Prijonoc97608e2007-03-23 16:34:20 +0000336 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000337 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000338 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000339 goto on_error;
340 }
Benny Prijono1f7767b2007-10-03 18:28:49 +0000341#endif
Benny Prijono84126ab2006-02-09 09:30:09 +0000342
343 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000344 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000345 if (acc->cfg.require_100rel)
346 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000347
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000348 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000349 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000350 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000351 goto on_error;
352 }
353
354
355 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000356 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000357 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000358
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000359 dlg->mod_data[pjsua_var.mod.id] = call;
360 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000361
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362 /* Attach user data */
363 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000364
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000365 /* If account is locked to specific transport, then lock dialog
366 * to this transport too.
367 */
368 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
369 pjsip_tpselector tp_sel;
370
371 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
372 pjsip_dlg_set_transport(dlg, &tp_sel);
373 }
374
Benny Prijono84126ab2006-02-09 09:30:09 +0000375 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 if (!pj_list_empty(&acc->route_set))
377 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000378
379
380 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000381 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000382 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000384 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000385
Benny Prijono48ab2b72007-11-08 09:24:30 +0000386 /* Set authentication preference */
387 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000388
389 /* Create initial INVITE: */
390
391 status = pjsip_inv_invite(inv, &tdata);
392 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000393 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
394 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000395 goto on_error;
396 }
397
398
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 /* Add additional headers etc */
400
401 pjsua_process_msg_data( tdata, msg_data);
402
Benny Prijono093d3022006-09-24 00:07:11 +0000403 /* Must increment call counter now */
404 ++pjsua_var.call_cnt;
405
Benny Prijono84126ab2006-02-09 09:30:09 +0000406 /* Send initial INVITE: */
407
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000408 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000409 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000410 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
411 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000412
413 /* Upon failure to send first request, both dialog and invite
414 * session would have been cleared.
415 */
416 inv = NULL;
417 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000418 goto on_error;
419 }
420
Benny Prijono84126ab2006-02-09 09:30:09 +0000421 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000422
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 if (p_call_id)
424 *p_call_id = call_id;
425
426 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000427
428 return PJ_SUCCESS;
429
430
431on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000432 if (inv != NULL) {
433 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000434 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000435 pjsip_dlg_terminate(dlg);
436 }
437
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 if (call_id != -1) {
439 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000440 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000441 }
442
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000443 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000444 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000445}
446
447
Benny Prijono91a6a172007-10-31 08:59:29 +0000448/* Get the NAT type information in remote's SDP */
449static void update_remote_nat_type(pjsua_call *call,
450 const pjmedia_sdp_session *sdp)
451{
452 const pjmedia_sdp_attr *xnat;
453
454 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
455 if (xnat) {
456 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
457 } else {
458 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
459 }
460
461 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
462 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
463}
464
465
Benny Prijonodc39fe82006-05-26 12:17:46 +0000466/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000467 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000469 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000470pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000471{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000472 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000473 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000474 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000475 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
476 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000477 pjsip_tx_data *response = NULL;
478 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000479 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000480 int acc_id;
481 pjsua_call *call;
482 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000483 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000484 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000485
Benny Prijono26ff9062006-02-21 23:47:00 +0000486 /* Don't want to handle anything but INVITE */
487 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
488 return PJ_FALSE;
489
490 /* Don't want to handle anything that's already associated with
491 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000492 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000493 if (dlg || tsx)
494 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000495
Benny Prijono148c9dd2006-09-19 13:37:53 +0000496 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000497
Benny Prijono26ff9062006-02-21 23:47:00 +0000498 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000499 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
500 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000501 break;
502 }
503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
505 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000506 PJSIP_SC_BUSY_HERE, NULL,
507 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000508 PJ_LOG(2,(THIS_FILE,
509 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000510 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000511 return PJ_TRUE;
512 }
513
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000514 /* Clear call descriptor */
515 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000516
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000517 call = &pjsua_var.calls[call_id];
518
519 /* Mark call start time. */
520 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000521
Benny Prijono053f5222006-11-11 16:16:04 +0000522 /* Check INVITE request for Replaces header. If Replaces header is
523 * present, the function will make sure that we can handle the request.
524 */
525 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
526 &response);
527 if (status != PJ_SUCCESS) {
528 /*
529 * Something wrong with the Replaces header.
530 */
531 if (response) {
532 pjsip_response_addr res_addr;
533
534 pjsip_get_response_addr(response->pool, rdata, &res_addr);
535 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
536 NULL, NULL);
537
538 } else {
539
540 /* Respond with 500 (Internal Server Error) */
541 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
542 NULL, NULL);
543 }
544
545 PJSUA_UNLOCK();
546 return PJ_TRUE;
547 }
548
549 /* If this INVITE request contains Replaces header, notify application
550 * about the request so that application can do subsequent checking
551 * if it wants to.
552 */
553 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
554 pjsua_call *replaced_call;
555 int st_code = 200;
556 pj_str_t st_text = { "OK", 2 };
557
558 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000559 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000560
561 /* Notify application */
562 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
563 rdata, &st_code, &st_text);
564
565 /* Must specify final response */
566 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
567
568 /* Check if application rejects this request. */
569 if (st_code >= 300) {
570
571 if (st_text.slen == 2)
572 st_text = *pjsip_get_status_text(st_code);
573
574 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
575 st_code, &st_text, NULL, NULL, NULL);
576 PJSUA_UNLOCK();
577 return PJ_TRUE;
578 }
579 }
580
581
Benny Prijonoc97608e2007-03-23 16:34:20 +0000582 /* Init media channel */
583 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000584 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000585 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000586 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000587 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000588 return PJ_TRUE;
589 }
590
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000591
Benny Prijonoc97608e2007-03-23 16:34:20 +0000592 /* Get media capability from media endpoint: */
593 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
594 if (status != PJ_SUCCESS) {
595 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
596 NULL, NULL);
597 pjsua_media_channel_deinit(call->index);
598 PJSUA_UNLOCK();
599 return PJ_TRUE;
600 }
601
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000602 /*
603 * Get which account is most likely to be associated with this incoming
604 * call. We need the account to find which contact URI to put for
605 * the call.
606 */
607 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000608
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000609 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000610 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000611 if (pjsua_var.acc[acc_id].cfg.require_100rel)
612 options |= PJSIP_INV_REQUIRE_100REL;
613
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000614 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
615 pjsua_var.endpt, &response);
616 if (status != PJ_SUCCESS) {
617
618 /*
619 * No we can't handle the incoming INVITE request.
620 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000621 if (response) {
622 pjsip_response_addr res_addr;
623
624 pjsip_get_response_addr(response->pool, rdata, &res_addr);
625 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
626 NULL, NULL);
627
628 } else {
629
630 /* Respond with 500 (Internal Server Error) */
631 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
632 NULL, NULL);
633 }
634
Benny Prijonoc97608e2007-03-23 16:34:20 +0000635 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000636 PJSUA_UNLOCK();
637 return PJ_TRUE;
638 }
639
640
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000641 /* Get suitable Contact header */
642 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
643 acc_id, rdata);
644 if (status != PJ_SUCCESS) {
645 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
646 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
647 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000648 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000649 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000650 return PJ_TRUE;
651 }
652
Benny Prijono26ff9062006-02-21 23:47:00 +0000653 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000654 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000655 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000656 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000659 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000660 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000661 return PJ_TRUE;
662 }
663
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000664 /* Set credentials */
665 if (pjsua_var.acc[acc_id].cred_cnt) {
666 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
667 pjsua_var.acc[acc_id].cred_cnt,
668 pjsua_var.acc[acc_id].cred);
669 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000670
Benny Prijono48ab2b72007-11-08 09:24:30 +0000671 /* Set preference */
672 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
673 &pjsua_var.acc[acc_id].cfg.auth_pref);
674
Benny Prijono26ff9062006-02-21 23:47:00 +0000675 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000676 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000677 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000678 pjsip_hdr hdr_list;
679 pjsip_warning_hdr *w;
680
681 w = pjsip_warning_hdr_create_from_status(dlg->pool,
682 pjsip_endpt_name(pjsua_var.endpt),
683 status);
684 pj_list_init(&hdr_list);
685 pj_list_push_back(&hdr_list, w);
686
687 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
688
689 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000690 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000691 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000692 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000693 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000694 return PJ_TRUE;
695 }
696
Benny Prijonoea9fd392007-11-06 03:41:40 +0000697 /* Update NAT type of remote endpoint, only when there is SDP in
698 * incoming INVITE!
699 */
700 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
701 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
702 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000703 const pjmedia_sdp_session *remote_sdp;
704
705 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
706 update_remote_nat_type(call, remote_sdp);
707 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000708
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000709 /* Create and attach pjsua_var data to the dialog: */
710 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000711
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000712 dlg->mod_data[pjsua_var.mod.id] = call;
713 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000714
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000715 /* If account is locked to specific transport, then lock dialog
716 * to this transport too.
717 */
718 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
719 pjsip_tpselector tp_sel;
720
721 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
722 pjsip_dlg_set_transport(dlg, &tp_sel);
723 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000724
Benny Prijono64f851e2006-02-23 13:49:28 +0000725 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000726 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000727 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000728 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000729 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000730 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
731 status);
732
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000733 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
734 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000735 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000736 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000737 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000738
739 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000740 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000741 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000742 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000743 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000744 }
745
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000746 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000747
Benny Prijono105217f2006-03-06 16:25:59 +0000748
Benny Prijono053f5222006-11-11 16:16:04 +0000749 /* Check if this request should replace existing call */
750 if (replaced_dlg) {
751 pjsip_inv_session *replaced_inv;
752 struct pjsua_call *replaced_call;
753 pjsip_tx_data *tdata;
754
755 /* Get the invite session in the dialog */
756 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
757
758 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000759 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000760
761 /* Notify application */
762 if (pjsua_var.ua_cfg.cb.on_call_replaced)
763 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
764 call_id);
765
766 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
767 call_id));
768
769 /* Answer the new call with 200 response */
770 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
771 if (status == PJ_SUCCESS)
772 status = pjsip_inv_send_msg(inv, tdata);
773
774 if (status != PJ_SUCCESS)
775 pjsua_perror(THIS_FILE, "Error answering session", status);
776
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000777 /* Note that inv may be invalid if 200/OK has caused error in
778 * starting the media.
779 */
Benny Prijono053f5222006-11-11 16:16:04 +0000780
781 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
782 replaced_call->index));
783
784 /* Disconnect replaced invite session */
785 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
786 &tdata);
787 if (status == PJ_SUCCESS && tdata)
788 status = pjsip_inv_send_msg(replaced_inv, tdata);
789
790 if (status != PJ_SUCCESS)
791 pjsua_perror(THIS_FILE, "Error terminating session", status);
792
793
794 } else {
795
Benny Prijonob5388cf2007-01-04 22:45:08 +0000796 /* Notify application if on_incoming_call() is overriden,
797 * otherwise hangup the call with 480
798 */
799 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000800 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000801 } else {
802 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
803 NULL, NULL);
804 }
Benny Prijono053f5222006-11-11 16:16:04 +0000805 }
806
Benny Prijono8b1889b2006-06-06 18:40:40 +0000807
Benny Prijono26ff9062006-02-21 23:47:00 +0000808 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000809 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000810 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000811}
812
813
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000814
815/*
816 * Check if the specified call has active INVITE session and the INVITE
817 * session has not been disconnected.
818 */
819PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
820{
821 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
822 PJ_EINVAL);
823 return pjsua_var.calls[call_id].inv != NULL &&
824 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
825}
826
827
828/*
829 * Check if call has an active media session.
830 */
831PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
832{
833 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
834 PJ_EINVAL);
835 return pjsua_var.calls[call_id].session != NULL;
836}
837
838
Benny Prijono148c9dd2006-09-19 13:37:53 +0000839/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000840pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000841 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000842 pjsua_call **p_call,
843 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000844{
845 enum { MAX_RETRY=50 };
846 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000847 pjsua_call *call = NULL;
848 pj_bool_t has_pjsua_lock = PJ_FALSE;
849 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000850
851 for (retry=0; retry<MAX_RETRY; ++retry) {
852
853 has_pjsua_lock = PJ_FALSE;
854
855 status = PJSUA_TRY_LOCK();
856 if (status != PJ_SUCCESS) {
857 pj_thread_sleep(retry/10);
858 continue;
859 }
860
861 has_pjsua_lock = PJ_TRUE;
862 call = &pjsua_var.calls[call_id];
863
864 if (call->inv == NULL) {
865 PJSUA_UNLOCK();
866 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
867 return PJSIP_ESESSIONTERMINATED;
868 }
869
870 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
871 if (status != PJ_SUCCESS) {
872 PJSUA_UNLOCK();
873 pj_thread_sleep(retry/10);
874 continue;
875 }
876
877 PJSUA_UNLOCK();
878
879 break;
880 }
881
882 if (status != PJ_SUCCESS) {
883 if (has_pjsua_lock == PJ_FALSE)
884 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
885 "(possibly system has deadlocked) in %s",
886 title));
887 else
888 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
889 "(possibly system has deadlocked) in %s",
890 title));
891 return PJ_ETIMEDOUT;
892 }
893
894 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000895 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000896
897 return PJ_SUCCESS;
898}
899
900
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000901/*
902 * Get the conference port identification associated with the call.
903 */
904PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
905{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000906 pjsua_call *call;
907 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000908 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000909 pj_status_t status;
910
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000911 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
912 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000913
Benny Prijonodc752ca2006-09-22 16:55:42 +0000914 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000915 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000916 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000917
918 port_id = call->conf_slot;
919
Benny Prijonodc752ca2006-09-22 16:55:42 +0000920 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000921
922 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000923}
924
925
Benny Prijono148c9dd2006-09-19 13:37:53 +0000926
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000927/*
928 * Obtain detail information about the specified call.
929 */
930PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
931 pjsua_call_info *info)
932{
933 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000934 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000935 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000936
937 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
938 PJ_EINVAL);
939
Benny Prijonoac623b32006-07-03 15:19:31 +0000940 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000941
Benny Prijonodc752ca2006-09-22 16:55:42 +0000942 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000943 if (status != PJ_SUCCESS) {
944 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000945 }
946
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000947 /* id and role */
948 info->id = call_id;
949 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000950 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000951
952 /* local info */
953 info->local_info.ptr = info->buf_.local_info;
954 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
955 sizeof(info->buf_.local_info));
956
957 /* local contact */
958 info->local_contact.ptr = info->buf_.local_contact;
959 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
960 call->inv->dlg->local.contact->uri,
961 info->local_contact.ptr,
962 sizeof(info->buf_.local_contact));
963
964 /* remote info */
965 info->remote_info.ptr = info->buf_.remote_info;
966 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
967 sizeof(info->buf_.remote_info));
968
969 /* remote contact */
970 if (call->inv->dlg->remote.contact) {
971 int len;
972 info->remote_contact.ptr = info->buf_.remote_contact;
973 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
974 call->inv->dlg->remote.contact->uri,
975 info->remote_contact.ptr,
976 sizeof(info->buf_.remote_contact));
977 if (len < 0) len = 0;
978 info->remote_contact.slen = len;
979 } else {
980 info->remote_contact.slen = 0;
981 }
982
983 /* call id */
984 info->call_id.ptr = info->buf_.call_id;
985 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
986 sizeof(info->buf_.call_id));
987
988 /* state, state_text */
989 info->state = call->inv->state;
990 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
991
992 /* If call is disconnected, set the last_status from the cause code */
993 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
994 /* last_status, last_status_text */
995 info->last_status = call->inv->cause;
996
997 info->last_status_text.ptr = info->buf_.last_status_text;
998 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
999 sizeof(info->buf_.last_status_text));
1000 } else {
1001 /* last_status, last_status_text */
1002 info->last_status = call->last_code;
1003
1004 info->last_status_text.ptr = info->buf_.last_status_text;
1005 pj_strncpy(&info->last_status_text, &call->last_text,
1006 sizeof(info->buf_.last_status_text));
1007 }
1008
1009 /* media status and dir */
1010 info->media_status = call->media_st;
1011 info->media_dir = call->media_dir;
1012
1013
1014 /* conference slot number */
1015 info->conf_slot = call->conf_slot;
1016
1017 /* calculate duration */
1018 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1019
1020 info->total_duration = call->dis_time;
1021 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1022
1023 if (call->conn_time.sec) {
1024 info->connect_duration = call->dis_time;
1025 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1026 }
1027
1028 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1029
1030 pj_gettimeofday(&info->total_duration);
1031 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1032
1033 pj_gettimeofday(&info->connect_duration);
1034 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1035
1036 } else {
1037 pj_gettimeofday(&info->total_duration);
1038 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1039 }
1040
Benny Prijonodc752ca2006-09-22 16:55:42 +00001041 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001042
1043 return PJ_SUCCESS;
1044}
1045
1046
1047/*
1048 * Attach application specific data to the call.
1049 */
1050PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1051 void *user_data)
1052{
1053 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1054 PJ_EINVAL);
1055 pjsua_var.calls[call_id].user_data = user_data;
1056
1057 return PJ_SUCCESS;
1058}
1059
1060
1061/*
1062 * Get user data attached to the call.
1063 */
1064PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1065{
1066 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1067 NULL);
1068 return pjsua_var.calls[call_id].user_data;
1069}
1070
1071
1072/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001073 * Get remote's NAT type.
1074 */
1075PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1076 pj_stun_nat_type *p_type)
1077{
1078 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1079 PJ_EINVAL);
1080 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1081
1082 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1083 return PJ_SUCCESS;
1084}
1085
1086
1087/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001088 * Send response to incoming INVITE request.
1089 */
1090PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1091 unsigned code,
1092 const pj_str_t *reason,
1093 const pjsua_msg_data *msg_data)
1094{
1095 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001096 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001097 pjsip_tx_data *tdata;
1098 pj_status_t status;
1099
1100 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1101 PJ_EINVAL);
1102
Benny Prijonodc752ca2006-09-22 16:55:42 +00001103 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001104 if (status != PJ_SUCCESS)
1105 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001106
Benny Prijono2e507c22006-06-23 15:04:11 +00001107 if (call->res_time.sec == 0)
1108 pj_gettimeofday(&call->res_time);
1109
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001110 if (reason && reason->slen == 0)
1111 reason = NULL;
1112
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 /* Create response message */
1114 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1115 if (status != PJ_SUCCESS) {
1116 pjsua_perror(THIS_FILE, "Error creating response",
1117 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001118 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001119 return status;
1120 }
1121
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001122 /* Call might have been disconnected if application is answering with
1123 * 200/OK and the media failed to start.
1124 */
1125 if (call->inv == NULL) {
1126 pjsip_dlg_dec_lock(dlg);
1127 return PJSIP_ESESSIONTERMINATED;
1128 }
1129
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130 /* Add additional headers etc */
1131 pjsua_process_msg_data( tdata, msg_data);
1132
1133 /* Send the message */
1134 status = pjsip_inv_send_msg(call->inv, tdata);
1135 if (status != PJ_SUCCESS)
1136 pjsua_perror(THIS_FILE, "Error sending response",
1137 status);
1138
Benny Prijonodc752ca2006-09-22 16:55:42 +00001139 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001140
1141 return status;
1142}
1143
1144
1145/*
1146 * Hangup call by using method that is appropriate according to the
1147 * call state.
1148 */
1149PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1150 unsigned code,
1151 const pj_str_t *reason,
1152 const pjsua_msg_data *msg_data)
1153{
1154 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001155 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001156 pj_status_t status;
1157 pjsip_tx_data *tdata;
1158
1159
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1161 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1162 call_id));
1163 }
1164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001165 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1166 PJ_EINVAL);
1167
Benny Prijonodc752ca2006-09-22 16:55:42 +00001168 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001169 if (status != PJ_SUCCESS)
1170 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001171
1172 if (code==0) {
1173 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1174 code = PJSIP_SC_OK;
1175 else if (call->inv->role == PJSIP_ROLE_UAS)
1176 code = PJSIP_SC_DECLINE;
1177 else
1178 code = PJSIP_SC_REQUEST_TERMINATED;
1179 }
1180
1181 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1182 if (status != PJ_SUCCESS) {
1183 pjsua_perror(THIS_FILE,
1184 "Failed to create end session message",
1185 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001186 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001187 return status;
1188 }
1189
1190 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1191 * as p_tdata when INVITE transaction has not been answered
1192 * with any provisional responses.
1193 */
1194 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001195 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001196 return PJ_SUCCESS;
1197 }
1198
1199 /* Add additional headers etc */
1200 pjsua_process_msg_data( tdata, msg_data);
1201
1202 /* Send the message */
1203 status = pjsip_inv_send_msg(call->inv, tdata);
1204 if (status != PJ_SUCCESS) {
1205 pjsua_perror(THIS_FILE,
1206 "Failed to send end session message",
1207 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001208 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209 return status;
1210 }
1211
Benny Prijonodc752ca2006-09-22 16:55:42 +00001212 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001213
1214 return PJ_SUCCESS;
1215}
1216
1217
1218/*
1219 * Put the specified call on hold.
1220 */
1221PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1222 const pjsua_msg_data *msg_data)
1223{
1224 pjmedia_sdp_session *sdp;
1225 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001226 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001227 pjsip_tx_data *tdata;
1228 pj_status_t status;
1229
1230 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1231 PJ_EINVAL);
1232
Benny Prijonodc752ca2006-09-22 16:55:42 +00001233 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001234 if (status != PJ_SUCCESS)
1235 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237
1238 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1239 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001240 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 return PJSIP_ESESSIONSTATE;
1242 }
1243
1244 status = create_inactive_sdp(call, &sdp);
1245 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001246 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 return status;
1248 }
1249
Benny Prijono7129cc72007-11-05 05:54:25 +00001250 update_sdp_version(call, sdp);
1251
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 /* Create re-INVITE with new offer */
1253 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1254 if (status != PJ_SUCCESS) {
1255 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001256 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001257 return status;
1258 }
1259
1260 /* Add additional headers etc */
1261 pjsua_process_msg_data( tdata, msg_data);
1262
1263 /* Send the request */
1264 status = pjsip_inv_send_msg( call->inv, tdata);
1265 if (status != PJ_SUCCESS) {
1266 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001267 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268 return status;
1269 }
1270
Benny Prijonodc752ca2006-09-22 16:55:42 +00001271 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001272
1273 return PJ_SUCCESS;
1274}
1275
1276
1277/*
1278 * Send re-INVITE (to release hold).
1279 */
1280PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1281 pj_bool_t unhold,
1282 const pjsua_msg_data *msg_data)
1283{
1284 pjmedia_sdp_session *sdp;
1285 pjsip_tx_data *tdata;
1286 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001287 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288 pj_status_t status;
1289
1290
1291 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1292 PJ_EINVAL);
1293
Benny Prijonodc752ca2006-09-22 16:55:42 +00001294 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001295 if (status != PJ_SUCCESS)
1296 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001297
1298 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1299 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001300 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001301 return PJSIP_ESESSIONSTATE;
1302 }
1303
Benny Prijono667952e2007-04-02 19:27:54 +00001304 /* Init media channel */
1305 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1306 if (status != PJ_SUCCESS) {
1307 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1308 pjsip_dlg_dec_lock(dlg);
1309 return PJSIP_ESESSIONSTATE;
1310 }
1311
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001312 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001313 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001314 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001315 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316 if (status != PJ_SUCCESS) {
1317 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1318 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001319 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 return status;
1321 }
1322
Benny Prijono7129cc72007-11-05 05:54:25 +00001323 update_sdp_version(call, sdp);
1324
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001325 /* Create re-INVITE with new offer */
1326 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1327 if (status != PJ_SUCCESS) {
1328 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001329 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330 return status;
1331 }
1332
1333 /* Add additional headers etc */
1334 pjsua_process_msg_data( tdata, msg_data);
1335
1336 /* Send the request */
1337 status = pjsip_inv_send_msg( call->inv, tdata);
1338 if (status != PJ_SUCCESS) {
1339 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001340 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341 return status;
1342 }
1343
Benny Prijonodc752ca2006-09-22 16:55:42 +00001344 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345
1346 return PJ_SUCCESS;
1347}
1348
1349
1350/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001351 * Send UPDATE request.
1352 */
1353PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1354 unsigned options,
1355 const pjsua_msg_data *msg_data)
1356{
1357 pjmedia_sdp_session *sdp;
1358 pjsip_tx_data *tdata;
1359 pjsua_call *call;
1360 pjsip_dialog *dlg;
1361 pj_status_t status;
1362
1363 PJ_UNUSED_ARG(options);
1364
1365 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1366 PJ_EINVAL);
1367
1368 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1369 if (status != PJ_SUCCESS)
1370 return status;
1371
1372 /* Init media channel */
1373 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1374 if (status != PJ_SUCCESS) {
1375 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1376 pjsip_dlg_dec_lock(dlg);
1377 return PJSIP_ESESSIONSTATE;
1378 }
1379
1380 /* Create SDP */
1381 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
1382 if (status != PJ_SUCCESS) {
1383 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1384 status);
1385 pjsip_dlg_dec_lock(dlg);
1386 return status;
1387 }
1388
1389 /* Create re-INVITE with new offer */
1390 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1391 if (status != PJ_SUCCESS) {
1392 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1393 pjsip_dlg_dec_lock(dlg);
1394 return status;
1395 }
1396
1397 /* Add additional headers etc */
1398 pjsua_process_msg_data( tdata, msg_data);
1399
1400 /* Send the request */
1401 status = pjsip_inv_send_msg( call->inv, tdata);
1402 if (status != PJ_SUCCESS) {
1403 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1404 pjsip_dlg_dec_lock(dlg);
1405 return status;
1406 }
1407
1408 pjsip_dlg_dec_lock(dlg);
1409
1410 return PJ_SUCCESS;
1411}
1412
1413
1414/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001415 * Initiate call transfer to the specified address.
1416 */
1417PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1418 const pj_str_t *dest,
1419 const pjsua_msg_data *msg_data)
1420{
1421 pjsip_evsub *sub;
1422 pjsip_tx_data *tdata;
1423 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001424 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001425 pjsip_generic_string_hdr *gs_hdr;
1426 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001427 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001428 pj_status_t status;
1429
1430
1431 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1432 PJ_EINVAL);
1433
Benny Prijonodc752ca2006-09-22 16:55:42 +00001434 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001435 if (status != PJ_SUCCESS)
1436 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001437
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438
Benny Prijonod524e822006-09-22 12:48:18 +00001439 /* Create xfer client subscription. */
1440 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001441 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001442
1443 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 if (status != PJ_SUCCESS) {
1445 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001446 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001447 return status;
1448 }
1449
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001450 /* Associate this call with the client subscription */
1451 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1452
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453 /*
1454 * Create REFER request.
1455 */
1456 status = pjsip_xfer_initiate(sub, dest, &tdata);
1457 if (status != PJ_SUCCESS) {
1458 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001459 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001460 return status;
1461 }
1462
Benny Prijono053f5222006-11-11 16:16:04 +00001463 /* Add Referred-By header */
1464 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1465 &dlg->local.info_str);
1466 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1467
1468
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 /* Add additional headers etc */
1470 pjsua_process_msg_data( tdata, msg_data);
1471
1472 /* Send. */
1473 status = pjsip_xfer_send_request(sub, tdata);
1474 if (status != PJ_SUCCESS) {
1475 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001476 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477 return status;
1478 }
1479
1480 /* For simplicity (that's what this program is intended to be!),
1481 * leave the original invite session as it is. More advanced application
1482 * may want to hold the INVITE, or terminate the invite, or whatever.
1483 */
1484
Benny Prijonodc752ca2006-09-22 16:55:42 +00001485 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486
1487 return PJ_SUCCESS;
1488
1489}
1490
1491
1492/*
Benny Prijono053f5222006-11-11 16:16:04 +00001493 * Initiate attended call transfer to the specified address.
1494 */
1495PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1496 pjsua_call_id dest_call_id,
1497 unsigned options,
1498 const pjsua_msg_data *msg_data)
1499{
1500 pjsua_call *dest_call;
1501 pjsip_dialog *dest_dlg;
1502 char str_dest_buf[512];
1503 pj_str_t str_dest;
1504 int len;
1505 pjsip_uri *uri;
1506 pj_status_t status;
1507
1508
1509 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1510 PJ_EINVAL);
1511 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1512 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1513 PJ_EINVAL);
1514
1515 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1516 &dest_call, &dest_dlg);
1517 if (status != PJ_SUCCESS)
1518 return status;
1519
1520 /*
1521 * Create REFER destination URI with Replaces field.
1522 */
1523
1524 /* Make sure we have sufficient buffer's length */
1525 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1526 dest_dlg->call_id->id.slen +
1527 dest_dlg->remote.info->tag.slen +
1528 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001529 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001530
1531 /* Print URI */
1532 str_dest_buf[0] = '<';
1533 str_dest.slen = 1;
1534
Benny Prijonoa1e69682007-05-11 15:14:34 +00001535 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001536 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1537 str_dest_buf+1, sizeof(str_dest_buf)-1);
1538 if (len < 0)
1539 return PJSIP_EURITOOLONG;
1540
1541 str_dest.slen += len;
1542
1543
1544 /* Build the URI */
1545 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1546 sizeof(str_dest_buf) - str_dest.slen,
1547 "?%s"
1548 "Replaces=%.*s"
1549 "%%3Bto-tag%%3D%.*s"
1550 "%%3Bfrom-tag%%3D%.*s>",
1551 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1552 "" : "Require=replaces&"),
1553 (int)dest_dlg->call_id->id.slen,
1554 dest_dlg->call_id->id.ptr,
1555 (int)dest_dlg->remote.info->tag.slen,
1556 dest_dlg->remote.info->tag.ptr,
1557 (int)dest_dlg->local.info->tag.slen,
1558 dest_dlg->local.info->tag.ptr);
1559
1560 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1561 PJSIP_EURITOOLONG);
1562
1563 str_dest.ptr = str_dest_buf;
1564 str_dest.slen += len;
1565
1566 pjsip_dlg_dec_lock(dest_dlg);
1567
1568 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1569}
1570
1571
1572/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 * Send DTMF digits to remote using RFC 2833 payload formats.
1574 */
1575PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1576 const pj_str_t *digits)
1577{
1578 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001579 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001580 pj_status_t status;
1581
1582 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1583 PJ_EINVAL);
1584
Benny Prijonodc752ca2006-09-22 16:55:42 +00001585 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001586 if (status != PJ_SUCCESS)
1587 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001588
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589 if (!call->session) {
1590 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001591 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001592 return PJ_EINVALIDOP;
1593 }
1594
1595 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1596
Benny Prijonodc752ca2006-09-22 16:55:42 +00001597 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598
1599 return status;
1600}
1601
1602
1603/**
1604 * Send instant messaging inside INVITE session.
1605 */
1606PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1607 const pj_str_t *mime_type,
1608 const pj_str_t *content,
1609 const pjsua_msg_data *msg_data,
1610 void *user_data)
1611{
1612 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001613 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001614 const pj_str_t mime_text_plain = pj_str("text/plain");
1615 pjsip_media_type ctype;
1616 pjsua_im_data *im_data;
1617 pjsip_tx_data *tdata;
1618 pj_status_t status;
1619
1620
1621 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1622 PJ_EINVAL);
1623
Benny Prijonodc752ca2006-09-22 16:55:42 +00001624 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001625 if (status != PJ_SUCCESS)
1626 return status;
1627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001628 /* Set default media type if none is specified */
1629 if (mime_type == NULL) {
1630 mime_type = &mime_text_plain;
1631 }
1632
1633 /* Create request message. */
1634 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1635 -1, &tdata);
1636 if (status != PJ_SUCCESS) {
1637 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1638 goto on_return;
1639 }
1640
1641 /* Add accept header. */
1642 pjsip_msg_add_hdr( tdata->msg,
1643 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1644
1645 /* Parse MIME type */
1646 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1647
1648 /* Create "text/plain" message body. */
1649 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1650 &ctype.subtype, content);
1651 if (tdata->msg->body == NULL) {
1652 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1653 pjsip_tx_data_dec_ref(tdata);
1654 goto on_return;
1655 }
1656
1657 /* Add additional headers etc */
1658 pjsua_process_msg_data( tdata, msg_data);
1659
1660 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001661 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662 im_data->acc_id = call->acc_id;
1663 im_data->call_id = call_id;
1664 im_data->to = call->inv->dlg->remote.info_str;
1665 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1666 im_data->user_data = user_data;
1667
1668
1669 /* Send the request. */
1670 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1671 pjsua_var.mod.id, im_data);
1672 if (status != PJ_SUCCESS) {
1673 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1674 goto on_return;
1675 }
1676
1677on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001678 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001679 return status;
1680}
1681
1682
1683/*
1684 * Send IM typing indication inside INVITE session.
1685 */
1686PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1687 pj_bool_t is_typing,
1688 const pjsua_msg_data*msg_data)
1689{
1690 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001691 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001692 pjsip_tx_data *tdata;
1693 pj_status_t status;
1694
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001695 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1696 PJ_EINVAL);
1697
Benny Prijonodc752ca2006-09-22 16:55:42 +00001698 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001699 if (status != PJ_SUCCESS)
1700 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702 /* Create request message. */
1703 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1704 -1, &tdata);
1705 if (status != PJ_SUCCESS) {
1706 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1707 goto on_return;
1708 }
1709
1710 /* Create "application/im-iscomposing+xml" msg body. */
1711 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1712 NULL, NULL, -1);
1713
1714 /* Add additional headers etc */
1715 pjsua_process_msg_data( tdata, msg_data);
1716
1717 /* Send the request. */
1718 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1719 if (status != PJ_SUCCESS) {
1720 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1721 goto on_return;
1722 }
1723
1724on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001725 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001726 return status;
1727}
1728
1729
1730/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001731 * Send arbitrary request.
1732 */
1733PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1734 const pj_str_t *method_str,
1735 const pjsua_msg_data *msg_data)
1736{
1737 pjsua_call *call;
1738 pjsip_dialog *dlg;
1739 pjsip_method method;
1740 pjsip_tx_data *tdata;
1741 pj_status_t status;
1742
1743 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1744 PJ_EINVAL);
1745
1746 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1747 if (status != PJ_SUCCESS)
1748 return status;
1749
1750 /* Init method */
1751 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1752
1753 /* Create request message. */
1754 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1755 if (status != PJ_SUCCESS) {
1756 pjsua_perror(THIS_FILE, "Unable to create request", status);
1757 goto on_return;
1758 }
1759
1760 /* Add additional headers etc */
1761 pjsua_process_msg_data( tdata, msg_data);
1762
1763 /* Send the request. */
1764 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1765 if (status != PJ_SUCCESS) {
1766 pjsua_perror(THIS_FILE, "Unable to send request", status);
1767 goto on_return;
1768 }
1769
1770on_return:
1771 pjsip_dlg_dec_lock(dlg);
1772 return status;
1773}
1774
1775
1776/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001777 * Terminate all calls.
1778 */
1779PJ_DEF(void) pjsua_call_hangup_all(void)
1780{
1781 unsigned i;
1782
1783 PJSUA_LOCK();
1784
1785 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1786 if (pjsua_var.calls[i].inv)
1787 pjsua_call_hangup(i, 0, NULL, NULL);
1788 }
1789
1790 PJSUA_UNLOCK();
1791}
1792
1793
Benny Prijono627cbb42007-09-25 20:48:49 +00001794const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001795{
1796 if (val < 1000) {
1797 pj_ansi_sprintf(buf, "%d", val);
1798 } else if (val < 1000000) {
1799 pj_ansi_sprintf(buf, "%d.%dK",
1800 val / 1000,
1801 (val % 1000) / 100);
1802 } else {
1803 pj_ansi_sprintf(buf, "%d.%02dM",
1804 val / 1000000,
1805 (val % 1000000) / 10000);
1806 }
1807
1808 return buf;
1809}
1810
1811
1812/* Dump media session */
1813static void dump_media_session(const char *indent,
1814 char *buf, unsigned maxlen,
1815 pjmedia_session *session)
1816{
1817 unsigned i;
1818 char *p = buf, *end = buf+maxlen;
1819 int len;
1820 pjmedia_session_info info;
1821
1822 pjmedia_session_get_info(session, &info);
1823
1824 for (i=0; i<info.stream_cnt; ++i) {
1825 pjmedia_rtcp_stat stat;
1826 const char *rem_addr;
1827 int rem_port;
1828 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001829 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001830 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001831 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001832
1833 pjmedia_session_get_stream_stat(session, i, &stat);
1834 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1835 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1836
1837 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1838 dir = "sendonly";
1839 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1840 dir = "recvonly";
1841 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1842 dir = "sendrecv";
1843 else
1844 dir = "inactive";
1845
1846
1847 len = pj_ansi_snprintf(buf, end-p,
1848 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1849 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001850 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 info.stream_info[i].fmt.encoding_name.ptr,
1852 info.stream_info[i].fmt.clock_rate / 1000,
1853 dir,
1854 rem_addr, rem_port);
1855 if (len < 1 || len > end-p) {
1856 *p = '\0';
1857 return;
1858 }
1859
1860 p += len;
1861 *p++ = '\n';
1862 *p = '\0';
1863
1864 if (stat.rx.update_cnt == 0)
1865 strcpy(last_update, "never");
1866 else {
1867 pj_gettimeofday(&now);
1868 PJ_TIME_VAL_SUB(now, stat.rx.update);
1869 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1870 now.sec / 3600,
1871 (now.sec % 3600) / 60,
1872 now.sec % 60,
1873 now.msec);
1874 }
1875
Benny Prijono80019eb2006-08-07 13:22:23 +00001876 pj_gettimeofday(&media_duration);
1877 PJ_TIME_VAL_SUB(media_duration, stat.start);
1878 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1879 media_duration.msec = 1;
1880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001881 len = pj_ansi_snprintf(p, end-p,
1882 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001883 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001884 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1885 "%s (msec) min avg max last\n"
1886 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1887 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1888 indent, info.stream_info[i].fmt.pt,
1889 last_update,
1890 indent,
1891 good_number(packets, stat.rx.pkt),
1892 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001893 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001894 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001895 good_number(avg_ipbps, (stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001896 indent,
1897 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001898 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001899 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001900 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001901 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001902 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001903 indent, indent,
1904 stat.rx.loss_period.min / 1000.0,
1905 stat.rx.loss_period.avg / 1000.0,
1906 stat.rx.loss_period.max / 1000.0,
1907 stat.rx.loss_period.last / 1000.0,
1908 indent,
1909 stat.rx.jitter.min / 1000.0,
1910 stat.rx.jitter.avg / 1000.0,
1911 stat.rx.jitter.max / 1000.0,
1912 stat.rx.jitter.last / 1000.0,
1913 ""
1914 );
1915
1916 if (len < 1 || len > end-p) {
1917 *p = '\0';
1918 return;
1919 }
1920
1921 p += len;
1922 *p++ = '\n';
1923 *p = '\0';
1924
1925 if (stat.tx.update_cnt == 0)
1926 strcpy(last_update, "never");
1927 else {
1928 pj_gettimeofday(&now);
1929 PJ_TIME_VAL_SUB(now, stat.tx.update);
1930 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1931 now.sec / 3600,
1932 (now.sec % 3600) / 60,
1933 now.sec % 60,
1934 now.msec);
1935 }
1936
1937 len = pj_ansi_snprintf(p, end-p,
1938 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001939 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1941 "%s (msec) min avg max last\n"
1942 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1943 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1944 indent,
1945 info.stream_info[i].tx_pt,
1946 info.stream_info[i].param->info.frm_ptime *
1947 info.stream_info[i].param->setting.frm_per_pkt,
1948 last_update,
1949
1950 indent,
1951 good_number(packets, stat.tx.pkt),
1952 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001953 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001954 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001955 good_number(avg_ipbps, (stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001956
1957 indent,
1958 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001959 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001960 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001961 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001962 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001963 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001964
1965 indent, indent,
1966 stat.tx.loss_period.min / 1000.0,
1967 stat.tx.loss_period.avg / 1000.0,
1968 stat.tx.loss_period.max / 1000.0,
1969 stat.tx.loss_period.last / 1000.0,
1970 indent,
1971 stat.tx.jitter.min / 1000.0,
1972 stat.tx.jitter.avg / 1000.0,
1973 stat.tx.jitter.max / 1000.0,
1974 stat.tx.jitter.last / 1000.0,
1975 ""
1976 );
1977
1978 if (len < 1 || len > end-p) {
1979 *p = '\0';
1980 return;
1981 }
1982
1983 p += len;
1984 *p++ = '\n';
1985 *p = '\0';
1986
1987 len = pj_ansi_snprintf(p, end-p,
1988 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1989 indent,
1990 stat.rtt.min / 1000.0,
1991 stat.rtt.avg / 1000.0,
1992 stat.rtt.max / 1000.0,
1993 stat.rtt.last / 1000.0
1994 );
1995 if (len < 1 || len > end-p) {
1996 *p = '\0';
1997 return;
1998 }
1999
2000 p += len;
2001 *p++ = '\n';
2002 *p = '\0';
2003 }
2004}
2005
2006
2007/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002008void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009 int call_id,
2010 char *buf, pj_size_t size)
2011{
2012 int len;
2013 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2014 pjsip_dialog *dlg = inv->dlg;
2015 char userinfo[128];
2016
2017 /* Dump invite sesion info. */
2018
2019 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2020 if (len < 1)
2021 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2022 else
2023 userinfo[len] = '\0';
2024
2025 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2026 title,
2027 pjsip_inv_state_name(inv->state),
2028 userinfo);
2029 if (len < 1 || len >= (int)size) {
2030 pj_ansi_strcpy(buf, "<--uri too long-->");
2031 len = 18;
2032 } else
2033 buf[len] = '\0';
2034}
2035
2036
2037/*
2038 * Dump call and media statistics to string.
2039 */
2040PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2041 pj_bool_t with_media,
2042 char *buffer,
2043 unsigned maxlen,
2044 const char *indent)
2045{
2046 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002047 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048 pj_time_val duration, res_delay, con_delay;
2049 char tmp[128];
2050 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002051 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002052 int len;
2053
2054 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2055 PJ_EINVAL);
2056
Benny Prijonodc752ca2006-09-22 16:55:42 +00002057 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002058 if (status != PJ_SUCCESS)
2059 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002060
2061 *buffer = '\0';
2062 p = buffer;
2063 end = buffer + maxlen;
2064 len = 0;
2065
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002066 print_call(indent, call_id, tmp, sizeof(tmp));
2067
2068 len = pj_ansi_strlen(tmp);
2069 pj_ansi_strcpy(buffer, tmp);
2070
2071 p += len;
2072 *p++ = '\r';
2073 *p++ = '\n';
2074
2075 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002076 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002077 pj_gettimeofday(&duration);
2078 PJ_TIME_VAL_SUB(duration, call->conn_time);
2079 con_delay = call->conn_time;
2080 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2081 } else {
2082 duration.sec = duration.msec = 0;
2083 con_delay.sec = con_delay.msec = 0;
2084 }
2085
2086 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002087 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002088 res_delay = call->res_time;
2089 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2090 } else {
2091 res_delay.sec = res_delay.msec = 0;
2092 }
2093
2094 /* Print duration */
2095 len = pj_ansi_snprintf(p, end-p,
2096 "%s Call time: %02dh:%02dm:%02ds, "
2097 "1st res in %d ms, conn in %dms",
2098 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002099 (int)(duration.sec / 3600),
2100 (int)((duration.sec % 3600)/60),
2101 (int)(duration.sec % 60),
2102 (int)PJ_TIME_VAL_MSEC(res_delay),
2103 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002104
2105 if (len > 0 && len < end-p) {
2106 p += len;
2107 *p++ = '\n';
2108 *p = '\0';
2109 }
2110
2111 /* Dump session statistics */
2112 if (with_media && call->session)
2113 dump_media_session(indent, p, end-p, call->session);
2114
Benny Prijonodc752ca2006-09-22 16:55:42 +00002115 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002116
2117 return PJ_SUCCESS;
2118}
2119
2120
2121/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002122 * This callback receives notification from invite session when the
2123 * session state has changed.
2124 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002125static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2126 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002127{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002129
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002130 PJSUA_LOCK();
2131
Benny Prijonoa1e69682007-05-11 15:14:34 +00002132 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002133
2134 if (!call) {
2135 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002136 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002137 }
2138
Benny Prijonoe21e7842006-04-09 16:46:05 +00002139
2140 /* Get call times */
2141 switch (inv->state) {
2142 case PJSIP_INV_STATE_EARLY:
2143 case PJSIP_INV_STATE_CONNECTING:
2144 if (call->res_time.sec == 0)
2145 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002146 call->last_code = (pjsip_status_code)
2147 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002148 pj_strncpy(&call->last_text,
2149 &e->body.tsx_state.tsx->status_text,
2150 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002151 break;
2152 case PJSIP_INV_STATE_CONFIRMED:
2153 pj_gettimeofday(&call->conn_time);
2154 break;
2155 case PJSIP_INV_STATE_DISCONNECTED:
2156 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002157 if (call->res_time.sec == 0)
2158 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002159 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002160 call->last_code = (pjsip_status_code)
2161 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002162 pj_strncpy(&call->last_text,
2163 &e->body.tsx_state.tsx->status_text,
2164 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002165 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002166 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002167 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002168 call->last_code = (pjsip_status_code)
2169 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002170 pj_strncpy(&call->last_text,
2171 &e->body.tsx_state.tsx->status_text,
2172 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002173 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002174 }
2175
Benny Prijono26ff9062006-02-21 23:47:00 +00002176 /* If this is an outgoing INVITE that was created because of
2177 * REFER/transfer, send NOTIFY to transferer.
2178 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002179 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002180 int st_code = -1;
2181 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2182
2183
Benny Prijonoa91a0032006-02-26 21:23:45 +00002184 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002185 case PJSIP_INV_STATE_NULL:
2186 case PJSIP_INV_STATE_CALLING:
2187 /* Do nothing */
2188 break;
2189
2190 case PJSIP_INV_STATE_EARLY:
2191 case PJSIP_INV_STATE_CONNECTING:
2192 st_code = e->body.tsx_state.tsx->status_code;
2193 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2194 break;
2195
2196 case PJSIP_INV_STATE_CONFIRMED:
2197 /* When state is confirmed, send the final 200/OK and terminate
2198 * subscription.
2199 */
2200 st_code = e->body.tsx_state.tsx->status_code;
2201 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2202 break;
2203
2204 case PJSIP_INV_STATE_DISCONNECTED:
2205 st_code = e->body.tsx_state.tsx->status_code;
2206 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2207 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002208
Benny Prijono8b1889b2006-06-06 18:40:40 +00002209 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002210 /* Nothing to do. Just to keep gcc from complaining about
2211 * unused enums.
2212 */
2213 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002214 }
2215
2216 if (st_code != -1) {
2217 pjsip_tx_data *tdata;
2218 pj_status_t status;
2219
Benny Prijonoa91a0032006-02-26 21:23:45 +00002220 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002221 ev_state, st_code,
2222 NULL, &tdata);
2223 if (status != PJ_SUCCESS) {
2224 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2225 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002226 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002227 if (status != PJ_SUCCESS) {
2228 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2229 }
2230 }
2231 }
2232 }
2233
Benny Prijono84126ab2006-02-09 09:30:09 +00002234
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002235 if (pjsua_var.ua_cfg.cb.on_call_state)
2236 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002237
2238 /* call->inv may be NULL now */
2239
Benny Prijono84126ab2006-02-09 09:30:09 +00002240 /* Destroy media session when invite session is disconnected. */
2241 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002242
Benny Prijonoa91a0032006-02-26 21:23:45 +00002243 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002244
Benny Prijono275fd682006-03-22 11:59:11 +00002245 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002246 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002247
Benny Prijono105217f2006-03-06 16:25:59 +00002248 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002249 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002250 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002251
2252 /* Reset call */
2253 reset_call(call->index);
2254
Benny Prijono84126ab2006-02-09 09:30:09 +00002255 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002256
2257 PJSUA_UNLOCK();
2258}
2259
2260/*
2261 * This callback is called by invite session framework when UAC session
2262 * has forked.
2263 */
2264static void pjsua_call_on_forked( pjsip_inv_session *inv,
2265 pjsip_event *e)
2266{
2267 PJ_UNUSED_ARG(inv);
2268 PJ_UNUSED_ARG(e);
2269
2270 PJ_TODO(HANDLE_FORKED_DIALOG);
2271}
2272
2273
2274/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002275 * Disconnect call upon error.
2276 */
2277static void call_disconnect( pjsip_inv_session *inv,
2278 int code )
2279{
2280 pjsip_tx_data *tdata;
2281 pj_status_t status;
2282
2283 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2284 if (status == PJ_SUCCESS)
2285 pjsip_inv_send_msg(inv, tdata);
2286}
2287
2288/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002289 * Callback to be called when SDP offer/answer negotiation has just completed
2290 * in the session. This function will start/update media if negotiation
2291 * has succeeded.
2292 */
2293static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2294 pj_status_t status)
2295{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002296 pjsua_call *call;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002297 const pjmedia_sdp_session *local_sdp;
2298 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002299
2300 PJSUA_LOCK();
2301
Benny Prijonoa1e69682007-05-11 15:14:34 +00002302 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002303
2304 if (status != PJ_SUCCESS) {
2305
2306 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2307
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002308 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002309 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002310
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002311 /* Disconnect call if we're not in the middle of initializing an
2312 * UAS dialog and if this is not a re-INVITE
2313 */
2314 if (inv->state != PJSIP_INV_STATE_NULL &&
2315 inv->state != PJSIP_INV_STATE_CONFIRMED)
2316 {
2317 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2318 }
2319
2320 PJSUA_UNLOCK();
2321 return;
2322 }
2323
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324
2325 /* Get local and remote SDP */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002326 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2327 if (status != PJ_SUCCESS) {
2328 pjsua_perror(THIS_FILE,
2329 "Unable to retrieve currently active local SDP",
2330 status);
2331 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2332 PJSUA_UNLOCK();
2333 return;
2334 }
2335
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002336 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2337 if (status != PJ_SUCCESS) {
2338 pjsua_perror(THIS_FILE,
2339 "Unable to retrieve currently active remote SDP",
2340 status);
2341 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2342 PJSUA_UNLOCK();
2343 return;
2344 }
2345
Benny Prijono91a6a172007-10-31 08:59:29 +00002346 /* Update remote's NAT type */
2347 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2348 update_remote_nat_type(call, remote_sdp);
2349 }
2350
2351 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002352 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002353 if (status != PJ_SUCCESS) {
2354 pjsua_perror(THIS_FILE, "Unable to create media session",
2355 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002356 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002357 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002358 PJSUA_UNLOCK();
2359 return;
2360 }
2361
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002362
2363 /* Call application callback, if any */
2364 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2365 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2366
2367
2368 PJSUA_UNLOCK();
2369}
2370
2371
2372/*
2373 * Create inactive SDP for call hold.
2374 */
2375static pj_status_t create_inactive_sdp(pjsua_call *call,
2376 pjmedia_sdp_session **p_answer)
2377{
2378 pj_status_t status;
2379 pjmedia_sdp_conn *conn;
2380 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002381 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002382 pjmedia_sdp_session *sdp;
2383
Benny Prijono617c5bc2007-04-02 19:51:21 +00002384 /* Get media socket info */
2385 pjmedia_transport_get_info(call->med_tp, &skinfo);
2386
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002387 /* Create new offer */
2388 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002389 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002390 if (status != PJ_SUCCESS) {
2391 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2392 return status;
2393 }
2394
2395 /* Get SDP media connection line */
2396 conn = sdp->media[0]->conn;
2397 if (!conn)
2398 conn = sdp->conn;
2399
2400 /* Modify address */
2401 conn->addr = pj_str("0.0.0.0");
2402
2403 /* Remove existing directions attributes */
2404 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2405 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2406 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2407 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2408
2409 /* Add inactive attribute */
2410 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2411 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2412
2413 *p_answer = sdp;
2414
2415 return status;
2416}
2417
2418
2419/*
2420 * Called when session received new offer.
2421 */
2422static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2423 const pjmedia_sdp_session *offer)
2424{
2425 const char *remote_state;
2426 pjsua_call *call;
2427 pjmedia_sdp_conn *conn;
2428 pjmedia_sdp_session *answer;
2429 pj_bool_t is_remote_active;
2430 pj_status_t status;
2431
2432 PJSUA_LOCK();
2433
Benny Prijonoa1e69682007-05-11 15:14:34 +00002434 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002435
2436 /*
2437 * See if remote is offering active media (i.e. not on-hold)
2438 */
2439 is_remote_active = PJ_TRUE;
2440
2441 conn = offer->media[0]->conn;
2442 if (!conn)
2443 conn = offer->conn;
2444
2445 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2446 pj_strcmp2(&conn->addr, "0")==0)
2447 {
2448 is_remote_active = PJ_FALSE;
2449
2450 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002451 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2452 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002453 {
2454 is_remote_active = PJ_FALSE;
2455 }
2456
2457 remote_state = (is_remote_active ? "active" : "inactive");
2458
2459 /* Supply candidate answer */
2460 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2461 PJ_LOG(4,(THIS_FILE,
2462 "Call %d: RX new media offer, creating inactive SDP "
2463 "(media in offer is %s)", call->index, remote_state));
2464 status = create_inactive_sdp( call, &answer );
2465 } else {
Benny Prijono667952e2007-04-02 19:27:54 +00002466
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002467 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2468 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002469
2470 /* Init media channel */
Benny Prijonoc9f6ea72007-05-23 07:12:23 +00002471 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono667952e2007-04-02 19:27:54 +00002472 if (status != PJ_SUCCESS) {
2473 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2474 PJSUA_UNLOCK();
2475 return;
2476 }
2477
Benny Prijonoc97608e2007-03-23 16:34:20 +00002478 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002479 }
2480
2481 if (status != PJ_SUCCESS) {
2482 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2483 PJSUA_UNLOCK();
2484 return;
2485 }
2486
2487 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2488 if (status != PJ_SUCCESS) {
2489 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2490 PJSUA_UNLOCK();
2491 return;
2492 }
2493
2494 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002495}
2496
2497
2498/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002499 * Called to generate new offer.
2500 */
2501static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2502 pjmedia_sdp_session **offer)
2503{
2504 pjsua_call *call;
2505 pj_status_t status;
2506
2507 PJSUA_LOCK();
2508
2509 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2510
2511 /* See if we've put call on hold. */
2512 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2513 PJ_LOG(4,(THIS_FILE,
2514 "Call %d: call is on-hold locally, creating inactive SDP ",
2515 call->index));
2516 status = create_inactive_sdp( call, offer );
2517 } else {
2518
2519 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2520 call->index));
2521
2522 /* Init media channel */
2523 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
2524 if (status != PJ_SUCCESS) {
2525 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2526 PJSUA_UNLOCK();
2527 return;
2528 }
2529
2530 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, offer);
2531 }
2532
2533 if (status != PJ_SUCCESS) {
2534 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2535 PJSUA_UNLOCK();
2536 return;
2537 }
2538
Benny Prijono7129cc72007-11-05 05:54:25 +00002539 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002540
2541 PJSUA_UNLOCK();
2542}
2543
2544
2545/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002546 * Callback called by event framework when the xfer subscription state
2547 * has changed.
2548 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002549static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2550{
2551
2552 PJ_UNUSED_ARG(event);
2553
2554 /*
2555 * When subscription is accepted (got 200/OK to REFER), check if
2556 * subscription suppressed.
2557 */
2558 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2559
2560 pjsip_rx_data *rdata;
2561 pjsip_generic_string_hdr *refer_sub;
2562 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2563 pjsua_call *call;
2564
Benny Prijonoa1e69682007-05-11 15:14:34 +00002565 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002566
2567 /* Must be receipt of response message */
2568 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2569 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2570 rdata = event->body.tsx_state.src.rdata;
2571
2572 /* Find Refer-Sub header */
2573 refer_sub = (pjsip_generic_string_hdr*)
2574 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2575 &REFER_SUB, NULL);
2576
2577 /* Check if subscription is suppressed */
2578 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2579 /* Since no subscription is desired, assume that call has been
2580 * transfered successfully.
2581 */
2582 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2583 const pj_str_t ACCEPTED = { "Accepted", 8 };
2584 pj_bool_t cont = PJ_FALSE;
2585 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2586 200,
2587 &ACCEPTED,
2588 PJ_TRUE,
2589 &cont);
2590 }
2591
2592 /* Yes, subscription is suppressed.
2593 * Terminate our subscription now.
2594 */
2595 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2596 "event subcription..."));
2597 pjsip_evsub_terminate(sub, PJ_TRUE);
2598
2599 } else {
2600 /* Notify application about call transfer progress.
2601 * Initially notify with 100/Accepted status.
2602 */
2603 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2604 const pj_str_t ACCEPTED = { "Accepted", 8 };
2605 pj_bool_t cont = PJ_FALSE;
2606 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2607 100,
2608 &ACCEPTED,
2609 PJ_FALSE,
2610 &cont);
2611 }
2612 }
2613 }
2614 /*
2615 * On incoming NOTIFY, notify application about call transfer progress.
2616 */
2617 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2618 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2619 {
2620 pjsua_call *call;
2621 pjsip_msg *msg;
2622 pjsip_msg_body *body;
2623 pjsip_status_line status_line;
2624 pj_bool_t is_last;
2625 pj_bool_t cont;
2626 pj_status_t status;
2627
Benny Prijonoa1e69682007-05-11 15:14:34 +00002628 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002629
2630 /* When subscription is terminated, clear the xfer_sub member of
2631 * the inv_data.
2632 */
2633 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2634 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2635 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2636
2637 }
2638
2639 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2640 /* Application is not interested with call progress status */
2641 return;
2642 }
2643
2644 /* This better be a NOTIFY request */
2645 if (event->type == PJSIP_EVENT_TSX_STATE &&
2646 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2647 {
2648 pjsip_rx_data *rdata;
2649
2650 rdata = event->body.tsx_state.src.rdata;
2651
2652 /* Check if there's body */
2653 msg = rdata->msg_info.msg;
2654 body = msg->body;
2655 if (!body) {
2656 PJ_LOG(4,(THIS_FILE,
2657 "Warning: received NOTIFY without message body"));
2658 return;
2659 }
2660
2661 /* Check for appropriate content */
2662 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2663 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2664 {
2665 PJ_LOG(4,(THIS_FILE,
2666 "Warning: received NOTIFY with non message/sipfrag "
2667 "content"));
2668 return;
2669 }
2670
2671 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002672 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002673 &status_line);
2674 if (status != PJ_SUCCESS) {
2675 PJ_LOG(4,(THIS_FILE,
2676 "Warning: received NOTIFY with invalid "
2677 "message/sipfrag content"));
2678 return;
2679 }
2680
2681 } else {
2682 status_line.code = 500;
2683 status_line.reason = *pjsip_get_status_text(500);
2684 }
2685
2686 /* Notify application */
2687 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2688 cont = !is_last;
2689 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2690 status_line.code,
2691 &status_line.reason,
2692 is_last, &cont);
2693
2694 if (!cont) {
2695 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2696 }
2697 }
2698}
2699
2700
2701/*
2702 * Callback called by event framework when the xfer subscription state
2703 * has changed.
2704 */
2705static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002706{
2707
2708 PJ_UNUSED_ARG(event);
2709
2710 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002711 * When subscription is terminated, clear the xfer_sub member of
2712 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002713 */
2714 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002715 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002716
Benny Prijonoa1e69682007-05-11 15:14:34 +00002717 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002718 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002719 return;
2720
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002721 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002722 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002723
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002724 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002725 }
2726}
2727
2728
2729/*
2730 * Follow transfer (REFER) request.
2731 */
2732static void on_call_transfered( pjsip_inv_session *inv,
2733 pjsip_rx_data *rdata )
2734{
2735 pj_status_t status;
2736 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002737 pjsua_call *existing_call;
2738 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002739 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002740 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002741 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002742 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002743 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002744 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002745 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002746 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002747 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002748 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002749 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002750 pjsip_evsub *sub;
2751
Benny Prijonoa1e69682007-05-11 15:14:34 +00002752 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002753
Benny Prijono26ff9062006-02-21 23:47:00 +00002754 /* Find the Refer-To header */
2755 refer_to = (pjsip_generic_string_hdr*)
2756 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2757
2758 if (refer_to == NULL) {
2759 /* Invalid Request.
2760 * No Refer-To header!
2761 */
2762 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002763 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002764 return;
2765 }
2766
Benny Prijonoc8141a82006-08-20 09:12:19 +00002767 /* Find optional Refer-Sub header */
2768 refer_sub = (pjsip_generic_string_hdr*)
2769 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2770
2771 if (refer_sub) {
2772 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2773 no_refer_sub = PJ_TRUE;
2774 }
2775
Benny Prijono053f5222006-11-11 16:16:04 +00002776 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2777 * request.
2778 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002779 ref_by_hdr = (pjsip_hdr*)
2780 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002781 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002782
Benny Prijono9fc735d2006-05-28 14:58:12 +00002783 /* Notify callback */
2784 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002785 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2786 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2787 &refer_to->hvalue,
2788 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002789
2790 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002791 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002792 if (code >= 300) {
2793 /* Application rejects call transfer request */
2794 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2795 return;
2796 }
2797
Benny Prijono26ff9062006-02-21 23:47:00 +00002798 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2799 (int)inv->dlg->remote.info_str.slen,
2800 inv->dlg->remote.info_str.ptr,
2801 (int)refer_to->hvalue.slen,
2802 refer_to->hvalue.ptr));
2803
Benny Prijonoc8141a82006-08-20 09:12:19 +00002804 if (no_refer_sub) {
2805 /*
2806 * Always answer with 200.
2807 */
2808 pjsip_tx_data *tdata;
2809 const pj_str_t str_false = { "false", 5};
2810 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002811
Benny Prijonoc8141a82006-08-20 09:12:19 +00002812 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2813 if (status != PJ_SUCCESS) {
2814 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2815 status);
2816 return;
2817 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002818
Benny Prijonoc8141a82006-08-20 09:12:19 +00002819 /* Add Refer-Sub header */
2820 hdr = (pjsip_hdr*)
2821 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2822 &str_false);
2823 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002824
Benny Prijono26ff9062006-02-21 23:47:00 +00002825
Benny Prijonoc8141a82006-08-20 09:12:19 +00002826 /* Send answer */
2827 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2828 tdata);
2829 if (status != PJ_SUCCESS) {
2830 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2831 status);
2832 return;
2833 }
2834
2835 /* Don't have subscription */
2836 sub = NULL;
2837
2838 } else {
2839 struct pjsip_evsub_user xfer_cb;
2840 pjsip_hdr hdr_list;
2841
2842 /* Init callback */
2843 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002844 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002845
2846 /* Init additional header list to be sent with REFER response */
2847 pj_list_init(&hdr_list);
2848
2849 /* Create transferee event subscription */
2850 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2851 if (status != PJ_SUCCESS) {
2852 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2853 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2854 return;
2855 }
2856
2857 /* If there's Refer-Sub header and the value is "true", send back
2858 * Refer-Sub in the response with value "true" too.
2859 */
2860 if (refer_sub) {
2861 const pj_str_t str_true = { "true", 4 };
2862 pjsip_hdr *hdr;
2863
2864 hdr = (pjsip_hdr*)
2865 pjsip_generic_string_hdr_create(inv->dlg->pool,
2866 &str_refer_sub,
2867 &str_true);
2868 pj_list_push_back(&hdr_list, hdr);
2869
2870 }
2871
2872 /* Accept the REFER request, send 200 (OK). */
2873 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2874
2875 /* Create initial NOTIFY request */
2876 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2877 100, NULL, &tdata);
2878 if (status != PJ_SUCCESS) {
2879 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2880 status);
2881 return;
2882 }
2883
2884 /* Send initial NOTIFY request */
2885 status = pjsip_xfer_send_request( sub, tdata);
2886 if (status != PJ_SUCCESS) {
2887 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2888 return;
2889 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002890 }
2891
2892 /* We're cheating here.
2893 * We need to get a null terminated string from a pj_str_t.
2894 * So grab the pointer from the hvalue and NULL terminate it, knowing
2895 * that the NULL position will be occupied by a newline.
2896 */
2897 uri = refer_to->hvalue.ptr;
2898 uri[refer_to->hvalue.slen] = '\0';
2899
Benny Prijono053f5222006-11-11 16:16:04 +00002900 /* Init msg_data */
2901 pjsua_msg_data_init(&msg_data);
2902
2903 /* If Referred-By header is present in the REFER request, copy this
2904 * to the outgoing INVITE request.
2905 */
2906 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002907 pjsip_hdr *dup = (pjsip_hdr*)
2908 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00002909 pj_list_push_back(&msg_data.hdr_list, dup);
2910 }
2911
Benny Prijono26ff9062006-02-21 23:47:00 +00002912 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002913 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002914 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002915 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002917 if (status != PJ_SUCCESS) {
2918
Benny Prijonoc8141a82006-08-20 09:12:19 +00002919 /* Notify xferer about the error (if we have subscription) */
2920 if (sub) {
2921 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2922 500, NULL, &tdata);
2923 if (status != PJ_SUCCESS) {
2924 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2925 status);
2926 return;
2927 }
2928 status = pjsip_xfer_send_request(sub, tdata);
2929 if (status != PJ_SUCCESS) {
2930 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2931 status);
2932 return;
2933 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002934 }
2935 return;
2936 }
2937
Benny Prijonoc8141a82006-08-20 09:12:19 +00002938 if (sub) {
2939 /* Put the server subscription in inv_data.
2940 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2941 * reported back to the server subscription.
2942 */
2943 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002944
Benny Prijonoc8141a82006-08-20 09:12:19 +00002945 /* Put the invite_data in the subscription. */
2946 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2947 &pjsua_var.calls[new_call]);
2948 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002949}
2950
2951
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002952
Benny Prijono26ff9062006-02-21 23:47:00 +00002953/*
2954 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002955 * session. We use this to trap:
2956 * - incoming REFER request.
2957 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002958 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002959static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2960 pjsip_transaction *tsx,
2961 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002962{
Benny Prijonoa1e69682007-05-11 15:14:34 +00002963 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002964
2965 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002966
Benny Prijonofeb69f42007-10-05 09:12:26 +00002967 /* Notify application callback first */
2968 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
2969 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
2970 }
2971
Benny Prijono26ff9062006-02-21 23:47:00 +00002972 if (tsx->role==PJSIP_ROLE_UAS &&
2973 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00002974 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002975 {
2976 /*
2977 * Incoming REFER request.
2978 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002979 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002980
Benny Prijono26ff9062006-02-21 23:47:00 +00002981 }
Benny Prijonob0808372006-03-02 21:18:58 +00002982 else if (tsx->role==PJSIP_ROLE_UAS &&
2983 tsx->state==PJSIP_TSX_STATE_TRYING &&
2984 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2985 {
2986 /*
2987 * Incoming MESSAGE request!
2988 */
2989 pjsip_rx_data *rdata;
2990 pjsip_msg *msg;
2991 pjsip_accept_hdr *accept_hdr;
2992 pj_status_t status;
2993
2994 rdata = e->body.tsx_state.src.rdata;
2995 msg = rdata->msg_info.msg;
2996
2997 /* Request MUST have message body, with Content-Type equal to
2998 * "text/plain".
2999 */
3000 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3001
3002 pjsip_hdr hdr_list;
3003
3004 pj_list_init(&hdr_list);
3005 pj_list_push_back(&hdr_list, accept_hdr);
3006
3007 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3008 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003009 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003010 return;
3011 }
3012
3013 /* Respond with 200 first, so that remote doesn't retransmit in case
3014 * the UI takes too long to process the message.
3015 */
3016 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3017
3018 /* Process MESSAGE request */
3019 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3020 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003021
Benny Prijonob0808372006-03-02 21:18:58 +00003022 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003023 else if (tsx->role == PJSIP_ROLE_UAC &&
3024 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003025 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003026 /* Handle outgoing pager status */
3027 if (tsx->status_code >= 200) {
3028 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003029
Benny Prijonoa1e69682007-05-11 15:14:34 +00003030 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003031 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003032
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003033 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3034 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3035 &im_data->to,
3036 &im_data->body,
3037 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003038 (pjsip_status_code)
3039 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003040 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003041 }
Benny Prijonofccab712006-02-22 22:23:22 +00003042 }
Benny Prijono834aee32006-02-19 01:38:06 +00003043 }
Benny Prijono834aee32006-02-19 01:38:06 +00003044
Benny Prijono26ff9062006-02-21 23:47:00 +00003045
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003046 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003047}