blob: 64aeac4dc3ec0d768b47eb9f8e025649cbf11246 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_call.c"
25
26
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000027/* Retry interval of sending re-INVITE for locking a codec when remote
28 * SDP answer contains multiple codec, in milliseconds.
29 */
30#define LOCK_CODEC_RETRY_INTERVAL 200
31
Benny Prijono1e601552010-10-20 05:31:08 +000032/*
33 * Max UPDATE/re-INVITE retry to lock codec
34 */
35#define LOCK_CODEC_MAX_RETRY 5
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000036
Benny Prijonoeebe9af2006-06-13 22:57:13 +000037/* This callback receives notification from invite session when the
38 * session state has changed.
39 */
40static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
41 pjsip_event *e);
42
43/* This callback is called by invite session framework when UAC session
44 * has forked.
45 */
46static void pjsua_call_on_forked( pjsip_inv_session *inv,
47 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000048
49/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050 * Callback to be called when SDP offer/answer negotiation has just completed
51 * in the session. This function will start/update media if negotiation
52 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000053 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054static void pjsua_call_on_media_update(pjsip_inv_session *inv,
55 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000056
57/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000058 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000059 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
61 const pjmedia_sdp_session *offer);
62
63/*
Benny Prijono77998ce2007-06-20 10:03:46 +000064 * Called to generate new offer.
65 */
66static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
67 pjmedia_sdp_session **offer);
68
69/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000070 * This callback is called when transaction state has changed in INVITE
71 * session. We use this to trap:
72 * - incoming REFER request.
73 * - incoming MESSAGE request.
74 */
75static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
76 pjsip_transaction *tsx,
77 pjsip_event *e);
78
Benny Prijono5e51a4e2008-11-27 00:06:46 +000079/*
80 * Redirection handler.
81 */
Benny Prijono08a48b82008-11-27 12:42:07 +000082static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
83 const pjsip_uri *target,
84 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000085
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086
Nanang Izzuddin99d69522008-08-04 15:01:38 +000087/* Create SDP for call hold. */
88static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
Benny Prijonodd63b992010-10-01 02:03:42 +000089 pjmedia_sdp_session **p_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000090
Benny Prijonod524e822006-09-22 12:48:18 +000091/*
92 * Callback called by event framework when the xfer subscription state
93 * has changed.
94 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000095static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
96static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
97
98/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 * Reset call descriptor.
100 */
101static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000102{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000104 unsigned i;
Benny Prijono105217f2006-03-06 16:25:59 +0000105
Benny Prijono0bc99a92011-03-17 04:34:43 +0000106 pj_bzero(call, sizeof(*call));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107 call->index = id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->last_text.ptr = call->last_text_buf_;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000109 for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {
110 pjsua_call_media *call_med = &call->media[i];
111 call_med->ssrc = pj_rand();
112 call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000113 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
114 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000115 call_med->call = call;
116 call_med->idx = i;
117 call_med->tp_auto_del = PJ_TRUE;
118 }
Benny Prijono105217f2006-03-06 16:25:59 +0000119}
120
121
Benny Prijono275fd682006-03-22 11:59:11 +0000122/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000124 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000126{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 pjsip_inv_callback inv_cb;
128 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000129 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000131
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000132 /* Init calls array. */
133 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
134 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 /* Copy config */
137 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000138
Benny Prijono1cd713b2009-11-11 00:33:00 +0000139 /* Verify settings */
140 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
141 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
142 }
143
Benny Prijono91d06b62008-09-20 12:16:56 +0000144 /* Check the route URI's and force loose route if required */
145 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
146 status = normalize_route_uri(pjsua_var.pool,
147 &pjsua_var.ua_cfg.outbound_proxy[i]);
148 if (status != PJ_SUCCESS)
149 return status;
150 }
151
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000153 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
155 inv_cb.on_new_session = &pjsua_call_on_forked;
156 inv_cb.on_media_update = &pjsua_call_on_media_update;
157 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000158 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000160 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 /* Initialize invite session module: */
163 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
164 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
165
Benny Prijonoc8141a82006-08-20 09:12:19 +0000166 /* Add "norefersub" in Supported header */
167 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
168 NULL, 1, &str_norefersub);
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 return status;
171}
172
173
174/*
175 * Start call subsystem.
176 */
177pj_status_t pjsua_call_subsys_start(void)
178{
179 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000180 return PJ_SUCCESS;
181}
182
183
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185 * Get maximum number of calls configured in pjsua.
186 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000187PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190}
191
192
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193/*
194 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000196PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199}
200
201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202/*
203 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000204 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
206 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000209
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
215 if (!pjsua_var.calls[i].inv)
216 continue;
217 ids[c] = i;
218 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219 }
220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000222
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000223 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000224
225 return PJ_SUCCESS;
226}
227
228
Benny Prijono5773cd62008-01-19 13:01:42 +0000229/* Allocate one call id */
230static pjsua_call_id alloc_call_id(void)
231{
232 pjsua_call_id cid;
233
234#if 1
235 /* New algorithm: round-robin */
236 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
237 pjsua_var.next_call_id < 0)
238 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000239 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000240 }
241
242 for (cid=pjsua_var.next_call_id;
243 cid<(int)pjsua_var.ua_cfg.max_calls;
244 ++cid)
245 {
246 if (pjsua_var.calls[cid].inv == NULL) {
247 ++pjsua_var.next_call_id;
248 return cid;
249 }
250 }
251
252 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
253 if (pjsua_var.calls[cid].inv == NULL) {
254 ++pjsua_var.next_call_id;
255 return cid;
256 }
257 }
258
259#else
260 /* Old algorithm */
261 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
262 if (pjsua_var.calls[cid].inv == NULL)
263 return cid;
264 }
265#endif
266
267 return PJSUA_INVALID_ID;
268}
269
Benny Prijonod8179652008-01-23 20:39:07 +0000270/* Get signaling secure level.
271 * Return:
272 * 0: if signaling is not secure
273 * 1: if TLS transport is used for immediate hop
274 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000275 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000276static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000277{
278 const pj_str_t tls = pj_str(";transport=tls");
279 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000280 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000281
282 if (pj_stristr(dst_uri, &sips))
283 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000284
285 if (!pj_list_empty(&acc->route_set)) {
286 pjsip_route_hdr *r = acc->route_set.next;
287 pjsip_uri *uri = r->name_addr.uri;
288 pjsip_sip_uri *sip_uri;
289
290 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
291 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
292 return 1;
293
294 } else {
295 if (pj_stristr(dst_uri, &tls))
296 return 1;
297 }
298
Benny Prijonod8179652008-01-23 20:39:07 +0000299 return 0;
300}
301
Benny Prijono224b4e22008-06-19 14:10:28 +0000302/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000303static int call_get_secure_level(pjsua_call *call)
304{
305 if (call->inv->dlg->secure)
306 return 2;
307
308 if (!pj_list_empty(&call->inv->dlg->route_set)) {
309 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
310 pjsip_uri *uri = r->name_addr.uri;
311 pjsip_sip_uri *sip_uri;
312
313 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
314 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
315 return 1;
316
317 } else {
318 pjsip_sip_uri *sip_uri;
319
320 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
321 return 2;
322 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
323 return 0;
324
325 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
326 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
327 return 1;
328 }
329
330 return 0;
331}
Benny Prijono224b4e22008-06-19 14:10:28 +0000332*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000333
Sauw Mingec765352011-10-03 02:04:36 +0000334/* Outgoing call callback when media transport creation is completed. */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000335static pj_status_t
336on_make_call_med_tp_complete(pjsua_call_id call_id,
337 const pjsua_med_tp_state_info *info)
338{
339 pjmedia_sdp_session *offer;
340 pjsip_inv_session *inv = NULL;
341 pjsua_call *call = &pjsua_var.calls[call_id];
342 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
343 pjsip_dialog *dlg = call->async_call.dlg;
344 unsigned options = call->async_call.call_var.out_call.options;
345 pjsip_tx_data *tdata;
346 pj_status_t status = (info? info->status: PJ_SUCCESS);
347
348 PJSUA_LOCK();
349
350 /* Increment the dialog's lock otherwise when invite session creation
351 * fails the dialog will be destroyed prematurely.
352 */
353 pjsip_dlg_inc_lock(dlg);
354
355 if (status != PJ_SUCCESS) {
356 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
357 goto on_error;
358 }
359
Sauw Ming903154f2011-10-03 08:22:48 +0000360 /* pjsua_media_channel_deinit() has been called. */
361 if (call->async_call.med_ch_deinit)
362 goto on_error;
363
Sauw Ming73ecfe82011-09-21 10:20:01 +0000364 /* Create offer */
365 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
366 &offer, NULL);
367 if (status != PJ_SUCCESS) {
368 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
369 goto on_error;
370 }
371
372 /* Create the INVITE session: */
373 options |= PJSIP_INV_SUPPORT_100REL;
374 if (acc->cfg.require_100rel)
375 options |= PJSIP_INV_REQUIRE_100REL;
376 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
377 options |= PJSIP_INV_SUPPORT_TIMER;
378 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
379 options |= PJSIP_INV_REQUIRE_TIMER;
380 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
381 options |= PJSIP_INV_ALWAYS_USE_TIMER;
382 }
383
384 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
385 if (status != PJ_SUCCESS) {
386 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
387 goto on_error;
388 }
389
390 /* Init Session Timers */
391 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
392 if (status != PJ_SUCCESS) {
393 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
394 goto on_error;
395 }
396
397 /* Create and associate our data in the session. */
398 call->inv = inv;
399
400 dlg->mod_data[pjsua_var.mod.id] = call;
401 inv->mod_data[pjsua_var.mod.id] = call;
402
403 /* If account is locked to specific transport, then lock dialog
404 * to this transport too.
405 */
406 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
407 pjsip_tpselector tp_sel;
408
409 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
410 pjsip_dlg_set_transport(dlg, &tp_sel);
411 }
412
413 /* Set dialog Route-Set: */
414 if (!pj_list_empty(&acc->route_set))
415 pjsip_dlg_set_route_set(dlg, &acc->route_set);
416
417
418 /* Set credentials: */
419 if (acc->cred_cnt) {
420 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
421 acc->cred_cnt, acc->cred);
422 }
423
424 /* Set authentication preference */
425 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
426
427 /* Create initial INVITE: */
428
429 status = pjsip_inv_invite(inv, &tdata);
430 if (status != PJ_SUCCESS) {
431 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
432 status);
433 goto on_error;
434 }
435
436
437 /* Add additional headers etc */
438
439 pjsua_process_msg_data( tdata,
440 call->async_call.call_var.out_call.msg_data);
441
442 /* Must increment call counter now */
443 ++pjsua_var.call_cnt;
444
445 /* Send initial INVITE: */
446
447 status = pjsip_inv_send_msg(inv, tdata);
448 if (status != PJ_SUCCESS) {
449 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
450 status);
451
452 /* Upon failure to send first request, the invite
453 * session would have been cleared.
454 */
455 inv = NULL;
456 goto on_error;
457 }
458
459 /* Done. */
460
461 pjsip_dlg_dec_lock(dlg);
462 PJSUA_UNLOCK();
463
464 return PJ_SUCCESS;
465
466on_error:
467 if (dlg) {
468 /* This may destroy the dialog */
469 pjsip_dlg_dec_lock(dlg);
470 }
471
472 if (inv != NULL) {
473 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
474 }
475
476 if (call_id != -1) {
477 reset_call(call_id);
478 pjsua_media_channel_deinit(call_id);
479 }
480
481 PJSUA_UNLOCK();
482 return status;
483}
484
Benny Prijonodb844a42008-02-02 17:07:18 +0000485
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486/*
487 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000488 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
490 const pj_str_t *dest_uri,
491 unsigned options,
492 void *user_data,
493 const pjsua_msg_data *msg_data,
494 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000495{
Benny Prijonob90fd382011-09-18 14:59:56 +0000496 pj_pool_t *tmp_pool = NULL;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000497 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000498 pjsua_acc *acc;
499 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000500 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000501 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000502 pj_status_t status;
503
Benny Prijono9fc735d2006-05-28 14:58:12 +0000504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000506 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000507 PJ_EINVAL);
508
Benny Prijono320fa4d2006-12-07 10:09:16 +0000509 /* Check arguments */
510 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
511
Benny Prijonob90fd382011-09-18 14:59:56 +0000512 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
513 (int)dest_uri->slen, dest_uri->ptr));
514
515 pj_log_push_indent();
516
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000517 PJSUA_LOCK();
518
Benny Prijonof798e502009-03-09 13:08:16 +0000519 /* Create sound port if none is instantiated, to check if sound device
520 * can be used. But only do this with the conference bridge, as with
521 * audio switchboard (i.e. APS-Direct), we can only open the sound
522 * device once the correct format has been known
523 */
524 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
525 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000526 {
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000527 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
Benny Prijonob90fd382011-09-18 14:59:56 +0000528 if (status != PJ_SUCCESS)
529 goto on_error;
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000530 }
531
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000532 acc = &pjsua_var.acc[acc_id];
533 if (!acc->valid) {
534 pjsua_perror(THIS_FILE, "Unable to make call because account "
535 "is not valid", PJ_EINVALIDOP);
Benny Prijonob90fd382011-09-18 14:59:56 +0000536 status = PJ_EINVALIDOP;
537 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000539
Benny Prijonoa91a0032006-02-26 21:23:45 +0000540 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000541 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000542
Benny Prijono5773cd62008-01-19 13:01:42 +0000543 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000544 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonob90fd382011-09-18 14:59:56 +0000545 status = PJ_ETOOMANY;
546 goto on_error;
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000547 }
548
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000549 call = &pjsua_var.calls[call_id];
550
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000551 /* Associate session with account */
552 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000553 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000554
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000555 /* Create temporary pool */
556 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
557
Benny Prijono320fa4d2006-12-07 10:09:16 +0000558 /* Verify that destination URI is valid before calling
559 * pjsua_acc_create_uac_contact, or otherwise there
560 * a misleading "Invalid Contact URI" error will be printed
561 * when pjsua_acc_create_uac_contact() fails.
562 */
563 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000564 pjsip_uri *uri;
565 pj_str_t dup;
566
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000567 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
568 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000569
570 if (uri == NULL) {
571 pjsua_perror(THIS_FILE, "Unable to make call",
572 PJSIP_EINVALIDREQURI);
Benny Prijonob90fd382011-09-18 14:59:56 +0000573 status = PJSIP_EINVALIDREQURI;
574 goto on_error;
Benny Prijono320fa4d2006-12-07 10:09:16 +0000575 }
576 }
577
Benny Prijonoe21e7842006-04-09 16:46:05 +0000578 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000580
Benny Prijonoe21e7842006-04-09 16:46:05 +0000581 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000582 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000583
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000584 /* Create suitable Contact header unless a Contact header has been
585 * set in the account.
586 */
587 if (acc->contact.slen) {
588 contact = acc->contact;
589 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000590 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000591 acc_id, dest_uri);
592 if (status != PJ_SUCCESS) {
593 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
594 status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000595 goto on_error;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000596 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000597 }
598
Benny Prijonoe21e7842006-04-09 16:46:05 +0000599 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000600 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000601 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000602 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000603 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000604 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000605 goto on_error;
Benny Prijono84126ab2006-02-09 09:30:09 +0000606 }
607
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000608 /* Increment the dialog's lock otherwise when invite session creation
609 * fails the dialog will be destroyed prematurely.
610 */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000611// pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000612
Benny Prijonodb844a42008-02-02 17:07:18 +0000613 /* Calculate call's secure level */
614 call->secure_level = get_secure_level(acc_id, dest_uri);
615
Sauw Ming73ecfe82011-09-21 10:20:01 +0000616 /* Attach user data */
617 call->user_data = user_data;
618
Sauw Mingec765352011-10-03 02:04:36 +0000619 /* Store variables required for the callback after the async
620 * media transport creation is completed.
621 */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000622 call->async_call.call_var.out_call.options = options;
Sauw Ming848742f2011-09-28 04:20:30 +0000623 if (msg_data) {
624 call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
625 dlg->pool, msg_data);
626 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000627 call->async_call.dlg = dlg;
628
Benny Prijonoc97608e2007-03-23 16:34:20 +0000629 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000630 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000631 call->secure_level, dlg->pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000632 NULL, NULL, PJ_TRUE,
633 (pjsua_med_tp_state_cb)
634 &on_make_call_med_tp_complete);
635 if (status == PJ_SUCCESS) {
636 status = on_make_call_med_tp_complete(call->index, NULL);
637 if (status != PJ_SUCCESS)
638 goto on_error;
639 } else if (status != PJ_EPENDING) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000640 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
641 goto on_error;
642 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000643
Benny Prijono84126ab2006-02-09 09:30:09 +0000644 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000645
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000646 if (p_call_id)
647 *p_call_id = call_id;
648
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000649 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000650 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000651
Benny Prijonob90fd382011-09-18 14:59:56 +0000652 pj_log_pop_indent();
653
Benny Prijono84126ab2006-02-09 09:30:09 +0000654 return PJ_SUCCESS;
655
656
657on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000658 if (dlg) {
Sauw Ming73ecfe82011-09-21 10:20:01 +0000659 pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000660 /* This may destroy the dialog */
661 pjsip_dlg_dec_lock(dlg);
662 }
663
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000664 if (call_id != -1) {
665 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000666 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000667 }
668
Benny Prijonob90fd382011-09-18 14:59:56 +0000669 if (tmp_pool)
670 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000671 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000672
673 pj_log_pop_indent();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000674 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000675}
676
677
Benny Prijono91a6a172007-10-31 08:59:29 +0000678/* Get the NAT type information in remote's SDP */
679static void update_remote_nat_type(pjsua_call *call,
680 const pjmedia_sdp_session *sdp)
681{
682 const pjmedia_sdp_attr *xnat;
683
684 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
685 if (xnat) {
686 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
687 } else {
688 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
689 }
690
691 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
692 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
693}
694
695
Sauw Mingec765352011-10-03 02:04:36 +0000696/* Incoming call callback when media transport creation is completed. */
697static pj_status_t
698on_incoming_call_med_tp_complete(pjsua_call_id call_id,
699 const pjsua_med_tp_state_info *info)
700{
701 pjsua_call *call = &pjsua_var.calls[call_id];
702 const pjmedia_sdp_session *offer=NULL;
703 pjmedia_sdp_session *answer;
704 pjsip_tx_data *response = NULL;
705 unsigned options = 0;
706 int sip_err_code = (info? info->sip_err_code: 0);
707 pj_status_t status = (info? info->status: PJ_SUCCESS);
708
709 PJSUA_LOCK();
710
711 if (status != PJ_SUCCESS) {
712 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
713 goto on_return;
714 }
Sauw Ming903154f2011-10-03 08:22:48 +0000715
716 /* pjsua_media_channel_deinit() has been called. */
717 if (call->async_call.med_ch_deinit) {
718 pjsua_media_channel_deinit(call->index);
719 call->med_ch_cb = NULL;
720 PJSUA_UNLOCK();
721 return PJ_SUCCESS;
722 }
723
Sauw Mingec765352011-10-03 02:04:36 +0000724 /* Get remote SDP offer (if any). */
725 if (call->inv->neg)
726 pjmedia_sdp_neg_get_neg_remote(call->inv->neg, &offer);
727
728 status = pjsua_media_channel_create_sdp(call_id,
729 call->async_call.dlg->pool,
730 offer, &answer, &sip_err_code);
731 if (status != PJ_SUCCESS) {
732 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
733 goto on_return;
734 }
735
736 status = pjsip_inv_set_local_sdp(call->inv, answer);
737 if (status != PJ_SUCCESS) {
738 pjsua_perror(THIS_FILE, "Error setting local SDP", status);
739 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
740 goto on_return;
741 }
742
743 /* Verify that we can handle the request. */
744 status = pjsip_inv_verify_request3(NULL,
745 call->inv->pool_prov, &options, offer,
746 answer, NULL, pjsua_var.endpt, &response);
747 if (status != PJ_SUCCESS) {
748 /*
749 * No we can't handle the incoming INVITE request.
750 */
751 goto on_return;
752 }
753
754on_return:
755 if (status != PJ_SUCCESS) {
756 pjsip_tx_data *tdata;
757 pj_status_t status_;
758
759 status_ = pjsip_inv_end_session(call->inv, sip_err_code, NULL, &tdata);
760 if (status_ == PJ_SUCCESS && tdata)
761 status_ = pjsip_inv_send_msg(call->inv, tdata);
762
763 pjsua_media_channel_deinit(call->index);
764 }
765
766 /* Set the callback to NULL to indicate that the async operation
767 * has completed.
768 */
769 call->med_ch_cb = NULL;
770
771 if (status == PJ_SUCCESS &&
772 !pj_list_empty(&call->async_call.call_var.inc_call.answers))
773 {
774 struct call_answer *answer, *next;
775
776 answer = call->async_call.call_var.inc_call.answers.next;
777 while (answer != &call->async_call.call_var.inc_call.answers) {
778 next = answer->next;
779 pjsua_call_answer(call_id, answer->code, answer->reason,
780 answer->msg_data);
781 pj_list_erase(answer);
782 answer = next;
783 }
784 }
785
786 PJSUA_UNLOCK();
787 return status;
788}
789
790
Benny Prijonodc39fe82006-05-26 12:17:46 +0000791/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000792 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000793 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000794 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000795pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000796{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000797 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000798 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000799 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000800 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
801 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000802 pjsip_tx_data *response = NULL;
803 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000804 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000805 int acc_id;
806 pjsua_call *call;
807 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000808 int sip_err_code;
Sauw Mingec765352011-10-03 02:04:36 +0000809 pjmedia_sdp_session *offer=NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000810 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000811
Benny Prijono26ff9062006-02-21 23:47:00 +0000812 /* Don't want to handle anything but INVITE */
813 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
814 return PJ_FALSE;
815
816 /* Don't want to handle anything that's already associated with
817 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000818 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000819 if (dlg || tsx)
820 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000821
Benny Prijono384dab42009-10-14 01:58:04 +0000822 /* Don't want to accept the call if shutdown is in progress */
823 if (pjsua_var.thread_quit_flag) {
824 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
825 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
826 NULL, NULL);
827 return PJ_TRUE;
828 }
829
Benny Prijonob90fd382011-09-18 14:59:56 +0000830 PJ_LOG(4,(THIS_FILE, "Incoming %s", rdata->msg_info.info));
831 pj_log_push_indent();
832
Benny Prijono148c9dd2006-09-19 13:37:53 +0000833 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000834
Benny Prijono26ff9062006-02-21 23:47:00 +0000835 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000836 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000837
Benny Prijono5773cd62008-01-19 13:01:42 +0000838 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000839 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000840 PJSIP_SC_BUSY_HERE, NULL,
841 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000842 PJ_LOG(2,(THIS_FILE,
843 "Unable to accept incoming call (too many calls)"));
Benny Prijonob90fd382011-09-18 14:59:56 +0000844 goto on_return;
Benny Prijono84126ab2006-02-09 09:30:09 +0000845 }
846
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000847 /* Clear call descriptor */
848 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000849
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000850 call = &pjsua_var.calls[call_id];
851
852 /* Mark call start time. */
853 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000854
Benny Prijono053f5222006-11-11 16:16:04 +0000855 /* Check INVITE request for Replaces header. If Replaces header is
856 * present, the function will make sure that we can handle the request.
857 */
858 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
859 &response);
860 if (status != PJ_SUCCESS) {
861 /*
862 * Something wrong with the Replaces header.
863 */
864 if (response) {
865 pjsip_response_addr res_addr;
866
867 pjsip_get_response_addr(response->pool, rdata, &res_addr);
868 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
869 NULL, NULL);
870
871 } else {
872
873 /* Respond with 500 (Internal Server Error) */
874 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
875 NULL, NULL);
876 }
877
Benny Prijonob90fd382011-09-18 14:59:56 +0000878 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000879 }
880
881 /* If this INVITE request contains Replaces header, notify application
882 * about the request so that application can do subsequent checking
883 * if it wants to.
884 */
885 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
886 pjsua_call *replaced_call;
887 int st_code = 200;
888 pj_str_t st_text = { "OK", 2 };
889
890 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000891 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000892
893 /* Notify application */
894 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
895 rdata, &st_code, &st_text);
896
897 /* Must specify final response */
898 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
899
900 /* Check if application rejects this request. */
901 if (st_code >= 300) {
902
903 if (st_text.slen == 2)
904 st_text = *pjsip_get_status_text(st_code);
905
906 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
907 st_code, &st_text, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000908 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000909 }
910 }
911
Benny Prijonod8179652008-01-23 20:39:07 +0000912 /*
913 * Get which account is most likely to be associated with this incoming
914 * call. We need the account to find which contact URI to put for
915 * the call.
916 */
917 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000918 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000919
Benny Prijonodb844a42008-02-02 17:07:18 +0000920 /* Get call's secure level */
921 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
922 call->secure_level = 2;
923 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
924 call->secure_level = 1;
925 else
926 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000927
Benny Prijonod8179652008-01-23 20:39:07 +0000928 /* Parse SDP from incoming request */
929 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000930 pjsip_rdata_sdp_info *sdp_info;
931
932 sdp_info = pjsip_rdata_get_sdp_info(rdata);
933 offer = sdp_info->sdp;
934
935 status = sdp_info->sdp_err;
936 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
937 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000938
Benny Prijonod8179652008-01-23 20:39:07 +0000939 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000940 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000941 pjsip_hdr hdr_list;
942 pjsip_warning_hdr *w;
943
944 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000945 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000946
947 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
948 pjsip_endpt_name(pjsua_var.endpt),
949 status);
950 pj_list_init(&hdr_list);
951 pj_list_push_back(&hdr_list, w);
952
Benny Prijono224b4e22008-06-19 14:10:28 +0000953 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000954 &reason, &hdr_list, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000955 goto on_return;
Benny Prijonod8179652008-01-23 20:39:07 +0000956 }
Benny Prijono617b8602008-04-07 10:10:31 +0000957
958 /* Do quick checks on SDP before passing it to transports. More elabore
959 * checks will be done in pjsip_inv_verify_request2() below.
960 */
961 if (offer->media_count==0) {
962 const pj_str_t reason = pj_str("Missing media in SDP");
963 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
964 NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000965 goto on_return;
Benny Prijono617b8602008-04-07 10:10:31 +0000966 }
967
Benny Prijonod8179652008-01-23 20:39:07 +0000968 } else {
969 offer = NULL;
970 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000971
972 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000973 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000974 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000975 if (pjsua_var.acc[acc_id].cfg.require_100rel)
976 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000977 if (pjsua_var.media_cfg.enable_ice)
978 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000979 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
980 options |= PJSIP_INV_REQUIRE_TIMER;
981 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
982 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000983
Sauw Mingec765352011-10-03 02:04:36 +0000984 status = pjsip_inv_verify_request2(rdata, &options, offer, NULL, NULL,
Benny Prijonod8179652008-01-23 20:39:07 +0000985 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000986 if (status != PJ_SUCCESS) {
987
988 /*
989 * No we can't handle the incoming INVITE request.
990 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000991 if (response) {
992 pjsip_response_addr res_addr;
993
994 pjsip_get_response_addr(response->pool, rdata, &res_addr);
995 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
996 NULL, NULL);
997
998 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000999 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +00001000 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
1001 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001002 }
1003
Benny Prijonob90fd382011-09-18 14:59:56 +00001004 goto on_return;
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001005 }
1006
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001007 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001008 if (pjsua_var.acc[acc_id].contact.slen) {
1009 contact = pjsua_var.acc[acc_id].contact;
1010 } else {
1011 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
1012 acc_id, rdata);
1013 if (status != PJ_SUCCESS) {
1014 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1015 status);
1016 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
1017 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001018 goto on_return;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001019 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001020 }
1021
Benny Prijono26ff9062006-02-21 23:47:00 +00001022 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +00001023 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001024 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +00001025 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001026 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +00001027 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001028 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001029 }
1030
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001031 /* Set credentials */
1032 if (pjsua_var.acc[acc_id].cred_cnt) {
1033 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
1034 pjsua_var.acc[acc_id].cred_cnt,
1035 pjsua_var.acc[acc_id].cred);
1036 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001037
Benny Prijono48ab2b72007-11-08 09:24:30 +00001038 /* Set preference */
1039 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
1040 &pjsua_var.acc[acc_id].cfg.auth_pref);
1041
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +00001042 /* Disable Session Timers if not prefered and the incoming INVITE request
1043 * did not require it.
1044 */
1045 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
1046 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
1047 {
1048 options &= ~(PJSIP_INV_SUPPORT_TIMER);
1049 }
1050
Benny Prijono26ff9062006-02-21 23:47:00 +00001051 /* Create invite session: */
Sauw Mingec765352011-10-03 02:04:36 +00001052 status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +00001053 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +00001054 pjsip_hdr hdr_list;
1055 pjsip_warning_hdr *w;
1056
1057 w = pjsip_warning_hdr_create_from_status(dlg->pool,
1058 pjsip_endpt_name(pjsua_var.endpt),
1059 status);
1060 pj_list_init(&hdr_list);
1061 pj_list_push_back(&hdr_list, w);
1062
1063 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
1064
1065 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +00001066 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001067 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001068 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001069 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001070 }
1071
Sauw Ming903154f2011-10-03 08:22:48 +00001072 /* If account is locked to specific transport, then lock dialog
1073 * to this transport too.
1074 */
1075 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1076 pjsip_tpselector tp_sel;
1077
1078 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1079 pjsip_dlg_set_transport(dlg, &tp_sel);
1080 }
1081
Sauw Mingec765352011-10-03 02:04:36 +00001082 /* Create and attach pjsua_var data to the dialog: */
1083 call->inv = inv;
1084 dlg->mod_data[pjsua_var.mod.id] = call;
1085 inv->mod_data[pjsua_var.mod.id] = call;
1086
1087 /* Store variables required for the callback after the async
1088 * media transport creation is completed.
1089 */
1090 call->async_call.dlg = dlg;
1091 pj_list_init(&call->async_call.call_var.inc_call.answers);
1092
1093 /* Init media channel */
1094 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
1095 call->secure_level,
1096 rdata->tp_info.pool,
1097 offer,
1098 &sip_err_code, PJ_TRUE,
1099 (pjsua_med_tp_state_cb)
1100 &on_incoming_call_med_tp_complete);
1101 if (status == PJ_SUCCESS) {
1102 status = on_incoming_call_med_tp_complete(call_id, NULL);
1103 if (status != PJ_SUCCESS) {
1104 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1105 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1106 goto on_return;
1107 }
1108 } else if (status != PJ_EPENDING) {
1109 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1110 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1111 goto on_return;
1112 }
1113
1114 /* Create answer */
1115/*
1116 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
1117 offer, &answer, &sip_err_code);
1118 if (status != PJ_SUCCESS) {
1119 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
1120 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
1121 sip_err_code, NULL, NULL, NULL, NULL);
1122 goto on_return;
1123 }
1124*/
1125
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001126 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +00001127 status = pjsip_timer_init_session(inv,
1128 &pjsua_var.acc[acc_id].cfg.timer_setting);
1129 if (status != PJ_SUCCESS) {
1130 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
1131 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
1132 NULL, &response);
1133 if (status == PJ_SUCCESS && response)
1134 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001135
Nanang Izzuddin65add622009-08-11 16:26:20 +00001136 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001137
Benny Prijonob90fd382011-09-18 14:59:56 +00001138 goto on_return;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001139 }
1140
Benny Prijonoea9fd392007-11-06 03:41:40 +00001141 /* Update NAT type of remote endpoint, only when there is SDP in
1142 * incoming INVITE!
1143 */
Sauw Mingec765352011-10-03 02:04:36 +00001144 if (pjsua_var.ua_cfg.nat_type_in_sdp && inv->neg &&
Benny Prijonoea9fd392007-11-06 03:41:40 +00001145 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
1146 {
Benny Prijono91a6a172007-10-31 08:59:29 +00001147 const pjmedia_sdp_session *remote_sdp;
1148
1149 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
1150 update_remote_nat_type(call, remote_sdp);
1151 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001152
Benny Prijono2285e7e2008-12-17 14:28:18 +00001153 /* Must answer with some response to initial INVITE. We'll do this before
1154 * attaching the call to the invite session/dialog, so that the application
1155 * will not get notification about this event (on another scenario, it is
1156 * also possible that inv_send_msg() fails and causes the invite session to
1157 * be disconnected. If we have the call attached at this time, this will
1158 * cause the disconnection callback to be called before on_incoming_call()
1159 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +00001160 */
Benny Prijono64f851e2006-02-23 13:49:28 +00001161 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +00001163 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001164 if (response == NULL) {
1165 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1166 status);
1167 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1168 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1169 } else {
1170 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001171 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001172 PJ_FALSE);
1173 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001174 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001175 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001176
1177 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001178 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001179 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001180 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001181 goto on_return;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001182 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001183 }
1184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001186
Benny Prijono053f5222006-11-11 16:16:04 +00001187 /* Check if this request should replace existing call */
1188 if (replaced_dlg) {
1189 pjsip_inv_session *replaced_inv;
1190 struct pjsua_call *replaced_call;
1191 pjsip_tx_data *tdata;
1192
1193 /* Get the invite session in the dialog */
1194 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1195
1196 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001197 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001198
1199 /* Notify application */
1200 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1201 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1202 call_id);
1203
1204 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1205 call_id));
1206
1207 /* Answer the new call with 200 response */
1208 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1209 if (status == PJ_SUCCESS)
1210 status = pjsip_inv_send_msg(inv, tdata);
1211
1212 if (status != PJ_SUCCESS)
1213 pjsua_perror(THIS_FILE, "Error answering session", status);
1214
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001215 /* Note that inv may be invalid if 200/OK has caused error in
1216 * starting the media.
1217 */
Benny Prijono053f5222006-11-11 16:16:04 +00001218
1219 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1220 replaced_call->index));
1221
1222 /* Disconnect replaced invite session */
1223 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1224 &tdata);
1225 if (status == PJ_SUCCESS && tdata)
1226 status = pjsip_inv_send_msg(replaced_inv, tdata);
1227
1228 if (status != PJ_SUCCESS)
1229 pjsua_perror(THIS_FILE, "Error terminating session", status);
1230
1231
1232 } else {
1233
Benny Prijonob5388cf2007-01-04 22:45:08 +00001234 /* Notify application if on_incoming_call() is overriden,
1235 * otherwise hangup the call with 480
1236 */
1237 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001238 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001239 } else {
1240 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1241 NULL, NULL);
1242 }
Benny Prijono053f5222006-11-11 16:16:04 +00001243 }
1244
Benny Prijono8b1889b2006-06-06 18:40:40 +00001245
Benny Prijono26ff9062006-02-21 23:47:00 +00001246 /* This INVITE request has been handled. */
Benny Prijonob90fd382011-09-18 14:59:56 +00001247on_return:
1248 pj_log_pop_indent();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001249 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001250 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001251}
1252
1253
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001254
1255/*
1256 * Check if the specified call has active INVITE session and the INVITE
1257 * session has not been disconnected.
1258 */
1259PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1260{
1261 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1262 PJ_EINVAL);
1263 return pjsua_var.calls[call_id].inv != NULL &&
1264 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1265}
1266
1267
1268/*
1269 * Check if call has an active media session.
1270 */
1271PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1272{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001273 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1275 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001276 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonocf986c42008-09-02 11:25:07 +00001277}
1278
1279
Benny Prijono148c9dd2006-09-19 13:37:53 +00001280/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001281pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001282 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001283 pjsua_call **p_call,
1284 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001285{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001286 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001287 pjsua_call *call = NULL;
1288 pj_bool_t has_pjsua_lock = PJ_FALSE;
1289 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001290 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001291
Sauw Ming844c1c92010-09-07 05:12:02 +00001292 pj_gettimeofday(&time_start);
1293 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1294 pj_time_val_normalize(&timeout);
1295
1296 for (retry=0; ; ++retry) {
1297
1298 if (retry % 10 == 9) {
1299 pj_time_val dtime;
1300
1301 pj_gettimeofday(&dtime);
1302 PJ_TIME_VAL_SUB(dtime, time_start);
1303 if (!PJ_TIME_VAL_LT(dtime, timeout))
1304 break;
1305 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001306
1307 has_pjsua_lock = PJ_FALSE;
1308
1309 status = PJSUA_TRY_LOCK();
1310 if (status != PJ_SUCCESS) {
1311 pj_thread_sleep(retry/10);
1312 continue;
1313 }
1314
1315 has_pjsua_lock = PJ_TRUE;
1316 call = &pjsua_var.calls[call_id];
1317
1318 if (call->inv == NULL) {
1319 PJSUA_UNLOCK();
1320 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1321 return PJSIP_ESESSIONTERMINATED;
1322 }
1323
1324 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1325 if (status != PJ_SUCCESS) {
1326 PJSUA_UNLOCK();
1327 pj_thread_sleep(retry/10);
1328 continue;
1329 }
1330
1331 PJSUA_UNLOCK();
1332
1333 break;
1334 }
1335
1336 if (status != PJ_SUCCESS) {
1337 if (has_pjsua_lock == PJ_FALSE)
1338 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1339 "(possibly system has deadlocked) in %s",
1340 title));
1341 else
1342 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1343 "(possibly system has deadlocked) in %s",
1344 title));
1345 return PJ_ETIMEDOUT;
1346 }
1347
1348 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001349 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001350
1351 return PJ_SUCCESS;
1352}
1353
1354
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355/*
1356 * Get the conference port identification associated with the call.
1357 */
1358PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1359{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001360 pjsua_call *call;
1361 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001362 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001363 pj_status_t status;
1364
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1366 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001367
Benny Prijonodc752ca2006-09-22 16:55:42 +00001368 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001369 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001370 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001371
Benny Prijono0bc99a92011-03-17 04:34:43 +00001372 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001373
Benny Prijonodc752ca2006-09-22 16:55:42 +00001374 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001375
1376 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377}
1378
1379
Benny Prijono148c9dd2006-09-19 13:37:53 +00001380
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381/*
1382 * Obtain detail information about the specified call.
1383 */
1384PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1385 pjsua_call_info *info)
1386{
1387 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001388 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001389 unsigned mi;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001390 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391
1392 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1393 PJ_EINVAL);
1394
Benny Prijonoac623b32006-07-03 15:19:31 +00001395 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396
Benny Prijonodc752ca2006-09-22 16:55:42 +00001397 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001398 if (status != PJ_SUCCESS) {
1399 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 }
1401
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402 /* id and role */
1403 info->id = call_id;
1404 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001405 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406
1407 /* local info */
1408 info->local_info.ptr = info->buf_.local_info;
1409 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1410 sizeof(info->buf_.local_info));
1411
1412 /* local contact */
1413 info->local_contact.ptr = info->buf_.local_contact;
1414 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1415 call->inv->dlg->local.contact->uri,
1416 info->local_contact.ptr,
1417 sizeof(info->buf_.local_contact));
1418
1419 /* remote info */
1420 info->remote_info.ptr = info->buf_.remote_info;
1421 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1422 sizeof(info->buf_.remote_info));
1423
1424 /* remote contact */
1425 if (call->inv->dlg->remote.contact) {
1426 int len;
1427 info->remote_contact.ptr = info->buf_.remote_contact;
1428 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1429 call->inv->dlg->remote.contact->uri,
1430 info->remote_contact.ptr,
1431 sizeof(info->buf_.remote_contact));
1432 if (len < 0) len = 0;
1433 info->remote_contact.slen = len;
1434 } else {
1435 info->remote_contact.slen = 0;
1436 }
1437
1438 /* call id */
1439 info->call_id.ptr = info->buf_.call_id;
1440 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1441 sizeof(info->buf_.call_id));
1442
1443 /* state, state_text */
1444 info->state = call->inv->state;
1445 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1446
1447 /* If call is disconnected, set the last_status from the cause code */
1448 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1449 /* last_status, last_status_text */
1450 info->last_status = call->inv->cause;
1451
1452 info->last_status_text.ptr = info->buf_.last_status_text;
1453 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1454 sizeof(info->buf_.last_status_text));
1455 } else {
1456 /* last_status, last_status_text */
1457 info->last_status = call->last_code;
1458
1459 info->last_status_text.ptr = info->buf_.last_status_text;
1460 pj_strncpy(&info->last_status_text, &call->last_text,
1461 sizeof(info->buf_.last_status_text));
1462 }
1463
Benny Prijono0bc99a92011-03-17 04:34:43 +00001464 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001465 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001466 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001467 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001468 {
1469 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001471 info->media[info->media_cnt].index = mi;
1472 info->media[info->media_cnt].status = call_med->state;
1473 info->media[info->media_cnt].dir = call_med->dir;
1474 info->media[info->media_cnt].type = call_med->type;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001475
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001476 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001477 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001478 call_med->strm.a.conf_slot;
1479 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001480 pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV;
1481
1482 info->media[info->media_cnt].stream.vid.win_in =
1483 call_med->strm.v.rdr_win_id;
1484
1485 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddind93c68a2011-07-19 08:40:20 +00001486 cap_dev = call_med->strm.v.cap_dev;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001487 }
1488 info->media[info->media_cnt].stream.vid.cap_dev = cap_dev;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001489 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001490 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001491 }
1492 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001493 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001495 if (call->audio_idx != -1) {
1496 info->media_status = call->media[call->audio_idx].state;
1497 info->media_dir = call->media[call->audio_idx].dir;
1498 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001499 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500
1501 /* calculate duration */
1502 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1503
1504 info->total_duration = call->dis_time;
1505 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1506
1507 if (call->conn_time.sec) {
1508 info->connect_duration = call->dis_time;
1509 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1510 }
1511
1512 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1513
1514 pj_gettimeofday(&info->total_duration);
1515 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1516
1517 pj_gettimeofday(&info->connect_duration);
1518 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1519
1520 } else {
1521 pj_gettimeofday(&info->total_duration);
1522 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1523 }
1524
Benny Prijonodc752ca2006-09-22 16:55:42 +00001525 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001526
1527 return PJ_SUCCESS;
1528}
1529
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001530/*
1531 * Check if call remote peer support the specified capability.
1532 */
1533PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1534 pjsua_call_id call_id,
1535 int htype,
1536 const pj_str_t *hname,
1537 const pj_str_t *token)
1538{
1539 pjsua_call *call;
1540 pjsip_dialog *dlg;
1541 pj_status_t status;
1542 pjsip_dialog_cap_status cap_status;
1543
1544 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1545 if (status != PJ_SUCCESS)
1546 return PJSIP_DIALOG_CAP_UNKNOWN;
1547
1548 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1549
1550 pjsip_dlg_dec_lock(dlg);
1551
1552 return cap_status;
1553}
1554
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555
1556/*
1557 * Attach application specific data to the call.
1558 */
1559PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1560 void *user_data)
1561{
1562 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1563 PJ_EINVAL);
1564 pjsua_var.calls[call_id].user_data = user_data;
1565
1566 return PJ_SUCCESS;
1567}
1568
1569
1570/*
1571 * Get user data attached to the call.
1572 */
1573PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1574{
1575 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1576 NULL);
1577 return pjsua_var.calls[call_id].user_data;
1578}
1579
1580
1581/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001582 * Get remote's NAT type.
1583 */
1584PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1585 pj_stun_nat_type *p_type)
1586{
1587 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1588 PJ_EINVAL);
1589 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1590
1591 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1592 return PJ_SUCCESS;
1593}
1594
1595
1596/*
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001597 * Get media stream info for the specified media index.
1598 */
1599PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
1600 unsigned med_idx,
1601 pjsua_stream_info *psi)
1602{
1603 pjsua_call *call;
1604 pjsua_call_media *call_med;
1605 pj_status_t status;
1606
1607 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1608 PJ_EINVAL);
1609 PJ_ASSERT_RETURN(psi, PJ_EINVAL);
1610
1611 PJSUA_LOCK();
1612
1613 call = &pjsua_var.calls[call_id];
1614
1615 if (med_idx >= call->med_cnt) {
1616 PJSUA_UNLOCK();
1617 return PJ_EINVAL;
1618 }
1619
1620 call_med = &call->media[med_idx];
1621 psi->type = call_med->type;
1622 switch (call_med->type) {
1623 case PJMEDIA_TYPE_AUDIO:
1624 status = pjmedia_stream_get_info(call_med->strm.a.stream,
1625 &psi->info.aud);
1626 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001627#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001628 case PJMEDIA_TYPE_VIDEO:
1629 status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
1630 &psi->info.vid);
1631 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001632#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001633 default:
1634 status = PJMEDIA_EINVALIMEDIATYPE;
1635 break;
1636 }
1637
1638 PJSUA_UNLOCK();
1639 return status;
1640}
1641
1642
1643/*
1644 * Get media stream statistic for the specified media index.
1645 */
1646PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
1647 unsigned med_idx,
1648 pjsua_stream_stat *stat)
1649{
1650 pjsua_call *call;
1651 pjsua_call_media *call_med;
1652 pj_status_t status;
1653
1654 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1655 PJ_EINVAL);
1656 PJ_ASSERT_RETURN(stat, PJ_EINVAL);
1657
1658 PJSUA_LOCK();
1659
1660 call = &pjsua_var.calls[call_id];
1661
1662 if (med_idx >= call->med_cnt) {
1663 PJSUA_UNLOCK();
1664 return PJ_EINVAL;
1665 }
1666
1667 call_med = &call->media[med_idx];
1668 switch (call_med->type) {
1669 case PJMEDIA_TYPE_AUDIO:
1670 status = pjmedia_stream_get_stat(call_med->strm.a.stream,
1671 &stat->rtcp);
1672 if (status == PJ_SUCCESS)
1673 status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
1674 &stat->jbuf);
1675 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001676#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001677 case PJMEDIA_TYPE_VIDEO:
1678 status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
1679 &stat->rtcp);
1680 if (status == PJ_SUCCESS)
1681 status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
1682 &stat->jbuf);
1683 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001684#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001685 default:
1686 status = PJMEDIA_EINVALIMEDIATYPE;
1687 break;
1688 }
1689
1690 PJSUA_UNLOCK();
1691 return status;
1692}
1693
1694
1695/*
1696 * Get media transport info for the specified media index.
1697 */
Benny Prijonoe212bc12011-08-15 09:38:42 +00001698PJ_DEF(pj_status_t)
1699pjsua_call_get_med_transport_info(pjsua_call_id call_id,
1700 unsigned med_idx,
1701 pjmedia_transport_info *t)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001702{
1703 pjsua_call *call;
1704 pjsua_call_media *call_med;
1705 pj_status_t status;
1706
1707 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1708 PJ_EINVAL);
1709 PJ_ASSERT_RETURN(t, PJ_EINVAL);
1710
1711 PJSUA_LOCK();
1712
1713 call = &pjsua_var.calls[call_id];
1714
1715 if (med_idx >= call->med_cnt) {
1716 PJSUA_UNLOCK();
1717 return PJ_EINVAL;
1718 }
1719
1720 call_med = &call->media[med_idx];
1721
1722 pjmedia_transport_info_init(t);
1723 status = pjmedia_transport_get_info(call_med->tp, t);
1724
1725 PJSUA_UNLOCK();
1726 return status;
1727}
1728
1729
1730/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001731 * Send response to incoming INVITE request.
1732 */
1733PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1734 unsigned code,
1735 const pj_str_t *reason,
1736 const pjsua_msg_data *msg_data)
1737{
1738 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001739 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001740 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
Benny Prijonob90fd382011-09-18 14:59:56 +00001746 PJ_LOG(4,(THIS_FILE, "Answering call %d: code=%d", call_id, code));
1747 pj_log_push_indent();
1748
Benny Prijonodc752ca2006-09-22 16:55:42 +00001749 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001750 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001751 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752
Sauw Mingec765352011-10-03 02:04:36 +00001753 PJSUA_LOCK();
1754 /* If media transport creation is not yet completed, we will answer
1755 * the call in the media transport creation callback instead.
1756 */
1757 if (call->med_ch_cb) {
1758 struct call_answer *answer;
1759
1760 PJ_LOG(4,(THIS_FILE, "Pending answering call %d upon completion "
1761 "of media transport", call_id));
1762
1763 answer = PJ_POOL_ZALLOC_T(call->inv->pool_prov, struct call_answer);
1764 answer->code = code;
1765 if (reason) {
1766 pj_strdup(call->inv->pool_prov, answer->reason, reason);
1767 }
1768 if (msg_data) {
1769 answer->msg_data = pjsua_msg_data_clone(call->inv->pool_prov,
1770 msg_data);
1771 }
1772 pj_list_push_back(&call->async_call.call_var.inc_call.answers,
1773 answer);
1774
1775 PJSUA_UNLOCK();
1776 if (dlg) pjsip_dlg_dec_lock(dlg);
1777 pj_log_pop_indent();
1778 return status;
1779 }
1780 PJSUA_UNLOCK();
1781
Benny Prijono2e507c22006-06-23 15:04:11 +00001782 if (call->res_time.sec == 0)
1783 pj_gettimeofday(&call->res_time);
1784
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001785 if (reason && reason->slen == 0)
1786 reason = NULL;
1787
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001788 /* Create response message */
1789 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1790 if (status != PJ_SUCCESS) {
1791 pjsua_perror(THIS_FILE, "Error creating response",
1792 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001793 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001794 }
1795
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001796 /* Call might have been disconnected if application is answering with
1797 * 200/OK and the media failed to start.
1798 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001799 if (call->inv == NULL)
1800 goto on_return;
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001801
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001802 /* Add additional headers etc */
1803 pjsua_process_msg_data( tdata, msg_data);
1804
1805 /* Send the message */
1806 status = pjsip_inv_send_msg(call->inv, tdata);
1807 if (status != PJ_SUCCESS)
1808 pjsua_perror(THIS_FILE, "Error sending response",
1809 status);
1810
Benny Prijonob90fd382011-09-18 14:59:56 +00001811on_return:
1812 if (dlg) pjsip_dlg_dec_lock(dlg);
1813 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001814 return status;
1815}
1816
1817
1818/*
1819 * Hangup call by using method that is appropriate according to the
1820 * call state.
1821 */
1822PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1823 unsigned code,
1824 const pj_str_t *reason,
1825 const pjsua_msg_data *msg_data)
1826{
1827 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001828 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001829 pj_status_t status;
1830 pjsip_tx_data *tdata;
1831
1832
Benny Prijono148c9dd2006-09-19 13:37:53 +00001833 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1834 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1835 call_id));
1836 }
1837
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001838 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1839 PJ_EINVAL);
1840
Benny Prijonob90fd382011-09-18 14:59:56 +00001841 PJ_LOG(4,(THIS_FILE, "Call %d hanging up: code=%d..", call_id, code));
1842 pj_log_push_indent();
1843
Benny Prijonodc752ca2006-09-22 16:55:42 +00001844 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001845 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001846 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001847
1848 if (code==0) {
1849 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1850 code = PJSIP_SC_OK;
1851 else if (call->inv->role == PJSIP_ROLE_UAS)
1852 code = PJSIP_SC_DECLINE;
1853 else
1854 code = PJSIP_SC_REQUEST_TERMINATED;
1855 }
1856
1857 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1858 if (status != PJ_SUCCESS) {
1859 pjsua_perror(THIS_FILE,
1860 "Failed to create end session message",
1861 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001862 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001863 }
1864
1865 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1866 * as p_tdata when INVITE transaction has not been answered
1867 * with any provisional responses.
1868 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001869 if (tdata == NULL)
1870 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001871
1872 /* Add additional headers etc */
1873 pjsua_process_msg_data( tdata, msg_data);
1874
1875 /* Send the message */
1876 status = pjsip_inv_send_msg(call->inv, tdata);
1877 if (status != PJ_SUCCESS) {
1878 pjsua_perror(THIS_FILE,
1879 "Failed to send end session message",
1880 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001881 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882 }
1883
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001884 /* Stop lock codec timer, if it is active */
1885 if (call->lock_codec.reinv_timer.id) {
1886 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1887 &call->lock_codec.reinv_timer);
1888 call->lock_codec.reinv_timer.id = PJ_FALSE;
1889 }
1890
Benny Prijonob90fd382011-09-18 14:59:56 +00001891on_return:
1892 if (dlg) pjsip_dlg_dec_lock(dlg);
1893 pj_log_pop_indent();
1894 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001895}
1896
1897
1898/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001899 * Accept or reject redirection.
1900 */
1901PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1902 pjsip_redirect_op cmd)
1903{
1904 pjsua_call *call;
1905 pjsip_dialog *dlg;
1906 pj_status_t status;
1907
1908 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1909 PJ_EINVAL);
1910
1911 status = acquire_call("pjsua_call_process_redirect()", call_id,
1912 &call, &dlg);
1913 if (status != PJ_SUCCESS)
1914 return status;
1915
1916 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1917
1918 pjsip_dlg_dec_lock(dlg);
1919
1920 return status;
1921}
1922
1923
1924/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001925 * Put the specified call on hold.
1926 */
1927PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1928 const pjsua_msg_data *msg_data)
1929{
1930 pjmedia_sdp_session *sdp;
1931 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001932 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001933 pjsip_tx_data *tdata;
1934 pj_status_t status;
1935
1936 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1937 PJ_EINVAL);
1938
Benny Prijonob90fd382011-09-18 14:59:56 +00001939 PJ_LOG(4,(THIS_FILE, "Putting call %d on hold", call_id));
1940 pj_log_push_indent();
1941
Benny Prijonodc752ca2006-09-22 16:55:42 +00001942 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001943 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001944 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001945
1946 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1947 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00001948 status = PJSIP_ESESSIONSTATE;
1949 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001950 }
1951
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001952 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonob90fd382011-09-18 14:59:56 +00001953 if (status != PJ_SUCCESS)
1954 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001955
1956 /* Create re-INVITE with new offer */
1957 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1958 if (status != PJ_SUCCESS) {
1959 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001960 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961 }
1962
1963 /* Add additional headers etc */
1964 pjsua_process_msg_data( tdata, msg_data);
1965
1966 /* Send the request */
1967 status = pjsip_inv_send_msg( call->inv, tdata);
1968 if (status != PJ_SUCCESS) {
1969 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001970 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971 }
1972
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001973 /* Set flag that local put the call on hold */
1974 call->local_hold = PJ_TRUE;
1975
Benny Prijonob90fd382011-09-18 14:59:56 +00001976on_return:
1977 if (dlg) pjsip_dlg_dec_lock(dlg);
1978 pj_log_pop_indent();
1979 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001980}
1981
1982
1983/*
1984 * Send re-INVITE (to release hold).
1985 */
1986PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
Benny Prijonodec3a372011-03-16 03:52:20 +00001987 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001988 const pjsua_msg_data *msg_data)
1989{
1990 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00001991 pj_str_t *new_contact = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001992 pjsip_tx_data *tdata;
1993 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001994 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001995 pj_status_t status;
1996
1997
1998 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1999 PJ_EINVAL);
2000
Benny Prijonob90fd382011-09-18 14:59:56 +00002001 PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
2002 pj_log_push_indent();
2003
Benny Prijonodc752ca2006-09-22 16:55:42 +00002004 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002005 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002006 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002007
2008 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
2009 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002010 status = PJSIP_ESESSIONSTATE;
2011 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002012 }
2013
2014 /* Create SDP */
Benny Prijonodec3a372011-03-16 03:52:20 +00002015 if (call->local_hold && (options & PJSUA_CALL_UNHOLD)==0) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002016 status = create_sdp_of_call_hold(call, &sdp);
2017 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00002018 status = pjsua_media_channel_create_sdp(call->index,
2019 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002020 NULL, &sdp, NULL);
2021 call->local_hold = PJ_FALSE;
2022 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002023 if (status != PJ_SUCCESS) {
2024 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2025 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002026 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002027 }
2028
Benny Prijonodec3a372011-03-16 03:52:20 +00002029 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
2030 pjsua_acc_is_valid(call->acc_id))
2031 {
2032 new_contact = &pjsua_var.acc[call->acc_id].contact;
2033 }
2034
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002035 /* Create re-INVITE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002036 status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037 if (status != PJ_SUCCESS) {
2038 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002039 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002040 }
2041
2042 /* Add additional headers etc */
2043 pjsua_process_msg_data( tdata, msg_data);
2044
2045 /* Send the request */
2046 status = pjsip_inv_send_msg( call->inv, tdata);
2047 if (status != PJ_SUCCESS) {
2048 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002049 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002050 }
2051
Benny Prijonob90fd382011-09-18 14:59:56 +00002052on_return:
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00002053 if (dlg) pjsip_dlg_dec_lock(dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002054 pj_log_pop_indent();
2055 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002056}
2057
2058
2059/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002060 * Send UPDATE request.
2061 */
2062PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
2063 unsigned options,
2064 const pjsua_msg_data *msg_data)
2065{
2066 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00002067 pj_str_t *new_contact = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002068 pjsip_tx_data *tdata;
2069 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002070 pjsip_dialog *dlg = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002071 pj_status_t status;
2072
2073 PJ_UNUSED_ARG(options);
2074
2075 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2076 PJ_EINVAL);
2077
Benny Prijonob90fd382011-09-18 14:59:56 +00002078 PJ_LOG(4,(THIS_FILE, "Sending UPDATE on call %d", call_id));
2079 pj_log_push_indent();
2080
Benny Prijonoc08682e2007-10-04 06:17:58 +00002081 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
2082 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002083 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002084
Benny Prijonoc08682e2007-10-04 06:17:58 +00002085 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00002086 status = pjsua_media_channel_create_sdp(call->index,
2087 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002088 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002089 if (status != PJ_SUCCESS) {
2090 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2091 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002092 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002093 }
2094
Benny Prijonodec3a372011-03-16 03:52:20 +00002095 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
2096 pjsua_acc_is_valid(call->acc_id))
2097 {
2098 new_contact = &pjsua_var.acc[call->acc_id].contact;
2099 }
2100
Benny Prijono224b4e22008-06-19 14:10:28 +00002101 /* Create UPDATE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002102 status = pjsip_inv_update(call->inv, new_contact, sdp, &tdata);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002103 if (status != PJ_SUCCESS) {
2104 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002105 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002106 }
2107
2108 /* Add additional headers etc */
2109 pjsua_process_msg_data( tdata, msg_data);
2110
2111 /* Send the request */
2112 status = pjsip_inv_send_msg( call->inv, tdata);
2113 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002114 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002115 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002116 }
2117
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002118 call->local_hold = PJ_FALSE;
2119
Benny Prijonob90fd382011-09-18 14:59:56 +00002120on_return:
2121 if (dlg) pjsip_dlg_dec_lock(dlg);
2122 pj_log_pop_indent();
2123 return status;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002124}
2125
2126
2127/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128 * Initiate call transfer to the specified address.
2129 */
2130PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
2131 const pj_str_t *dest,
2132 const pjsua_msg_data *msg_data)
2133{
2134 pjsip_evsub *sub;
2135 pjsip_tx_data *tdata;
2136 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002137 pjsip_dialog *dlg = NULL;
Benny Prijono053f5222006-11-11 16:16:04 +00002138 pjsip_generic_string_hdr *gs_hdr;
2139 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00002140 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141 pj_status_t status;
2142
2143
Benny Prijonob90fd382011-09-18 14:59:56 +00002144 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls &&
2145 dest, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002146
Benny Prijonob90fd382011-09-18 14:59:56 +00002147 PJ_LOG(4,(THIS_FILE, "Transfering call %d to %.*s", call_id,
2148 (int)dest->slen, dest->ptr));
2149 pj_log_push_indent();
2150
Benny Prijonodc752ca2006-09-22 16:55:42 +00002151 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002152 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002153 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002154
Benny Prijonod524e822006-09-22 12:48:18 +00002155 /* Create xfer client subscription. */
2156 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002157 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00002158
2159 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002160 if (status != PJ_SUCCESS) {
2161 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002162 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163 }
2164
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002165 /* Associate this call with the client subscription */
2166 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
2167
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002168 /*
2169 * Create REFER request.
2170 */
2171 status = pjsip_xfer_initiate(sub, dest, &tdata);
2172 if (status != PJ_SUCCESS) {
2173 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002174 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002175 }
2176
Benny Prijono053f5222006-11-11 16:16:04 +00002177 /* Add Referred-By header */
2178 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
2179 &dlg->local.info_str);
2180 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
2181
2182
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 /* Add additional headers etc */
2184 pjsua_process_msg_data( tdata, msg_data);
2185
2186 /* Send. */
2187 status = pjsip_xfer_send_request(sub, tdata);
2188 if (status != PJ_SUCCESS) {
2189 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002190 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191 }
2192
2193 /* For simplicity (that's what this program is intended to be!),
2194 * leave the original invite session as it is. More advanced application
2195 * may want to hold the INVITE, or terminate the invite, or whatever.
2196 */
Benny Prijonob90fd382011-09-18 14:59:56 +00002197on_return:
2198 if (dlg) pjsip_dlg_dec_lock(dlg);
2199 pj_log_pop_indent();
2200 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002201
2202}
2203
2204
2205/*
Benny Prijono053f5222006-11-11 16:16:04 +00002206 * Initiate attended call transfer to the specified address.
2207 */
2208PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
2209 pjsua_call_id dest_call_id,
2210 unsigned options,
2211 const pjsua_msg_data *msg_data)
2212{
2213 pjsua_call *dest_call;
2214 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00002215 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00002216 pj_str_t str_dest;
2217 int len;
2218 pjsip_uri *uri;
2219 pj_status_t status;
2220
2221
2222 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2223 PJ_EINVAL);
2224 PJ_ASSERT_RETURN(dest_call_id>=0 &&
2225 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
2226 PJ_EINVAL);
2227
Benny Prijonob90fd382011-09-18 14:59:56 +00002228 PJ_LOG(4,(THIS_FILE, "Transfering call %d replacing with call %d",
2229 call_id, dest_call_id));
2230 pj_log_push_indent();
2231
Benny Prijono053f5222006-11-11 16:16:04 +00002232 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
2233 &dest_call, &dest_dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002234 if (status != PJ_SUCCESS) {
2235 pj_log_pop_indent();
Benny Prijono053f5222006-11-11 16:16:04 +00002236 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00002237 }
Benny Prijono053f5222006-11-11 16:16:04 +00002238
2239 /*
2240 * Create REFER destination URI with Replaces field.
2241 */
2242
2243 /* Make sure we have sufficient buffer's length */
Benny Prijonob90fd382011-09-18 14:59:56 +00002244 PJ_ASSERT_ON_FAIL(dest_dlg->remote.info_str.slen +
Benny Prijono053f5222006-11-11 16:16:04 +00002245 dest_dlg->call_id->id.slen +
2246 dest_dlg->remote.info->tag.slen +
2247 dest_dlg->local.info->tag.slen + 32
Benny Prijonob90fd382011-09-18 14:59:56 +00002248 < (long)sizeof(str_dest_buf),
2249 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002250
2251 /* Print URI */
2252 str_dest_buf[0] = '<';
2253 str_dest.slen = 1;
2254
Benny Prijonoa1e69682007-05-11 15:14:34 +00002255 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00002256 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
2257 str_dest_buf+1, sizeof(str_dest_buf)-1);
Benny Prijonob90fd382011-09-18 14:59:56 +00002258 if (len < 0) {
2259 status = PJSIP_EURITOOLONG;
2260 goto on_error;
2261 }
Benny Prijono053f5222006-11-11 16:16:04 +00002262
2263 str_dest.slen += len;
2264
2265
2266 /* Build the URI */
2267 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
2268 sizeof(str_dest_buf) - str_dest.slen,
2269 "?%s"
2270 "Replaces=%.*s"
2271 "%%3Bto-tag%%3D%.*s"
2272 "%%3Bfrom-tag%%3D%.*s>",
2273 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
2274 "" : "Require=replaces&"),
2275 (int)dest_dlg->call_id->id.slen,
2276 dest_dlg->call_id->id.ptr,
2277 (int)dest_dlg->remote.info->tag.slen,
2278 dest_dlg->remote.info->tag.ptr,
2279 (int)dest_dlg->local.info->tag.slen,
2280 dest_dlg->local.info->tag.ptr);
2281
Benny Prijonob90fd382011-09-18 14:59:56 +00002282 PJ_ASSERT_ON_FAIL(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
2283 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002284
2285 str_dest.ptr = str_dest_buf;
2286 str_dest.slen += len;
2287
2288 pjsip_dlg_dec_lock(dest_dlg);
2289
Benny Prijonob90fd382011-09-18 14:59:56 +00002290 status = pjsua_call_xfer(call_id, &str_dest, msg_data);
2291
2292 pj_log_pop_indent();
2293 return status;
2294
2295on_error:
2296 if (dest_dlg) pjsip_dlg_dec_lock(dest_dlg);
2297 pj_log_pop_indent();
2298 return status;
Benny Prijono053f5222006-11-11 16:16:04 +00002299}
2300
2301
2302/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002303 * Send DTMF digits to remote using RFC 2833 payload formats.
2304 */
2305PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
2306 const pj_str_t *digits)
2307{
2308 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002309 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002310 pj_status_t status;
2311
2312 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2313 PJ_EINVAL);
2314
Benny Prijonob90fd382011-09-18 14:59:56 +00002315 PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s",
2316 call_id, (int)digits->slen, digits->ptr));
2317 pj_log_push_indent();
2318
Benny Prijonodc752ca2006-09-22 16:55:42 +00002319 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002320 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002321 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002322
Benny Prijono0bc99a92011-03-17 04:34:43 +00002323 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002325 status = PJ_EINVALIDOP;
2326 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002327 }
2328
Benny Prijono0bc99a92011-03-17 04:34:43 +00002329 status = pjmedia_stream_dial_dtmf(
2330 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002331
Benny Prijonob90fd382011-09-18 14:59:56 +00002332on_return:
2333 if (dlg) pjsip_dlg_dec_lock(dlg);
2334 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002335 return status;
2336}
2337
2338
2339/**
2340 * Send instant messaging inside INVITE session.
2341 */
2342PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
2343 const pj_str_t *mime_type,
2344 const pj_str_t *content,
2345 const pjsua_msg_data *msg_data,
2346 void *user_data)
2347{
2348 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002349 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002350 const pj_str_t mime_text_plain = pj_str("text/plain");
2351 pjsip_media_type ctype;
2352 pjsua_im_data *im_data;
2353 pjsip_tx_data *tdata;
2354 pj_status_t status;
2355
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002356 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2357 PJ_EINVAL);
2358
Benny Prijonob90fd382011-09-18 14:59:56 +00002359 PJ_LOG(4,(THIS_FILE, "Call %d sending %d bytes MESSAGE..",
2360 call_id, (int)content->slen));
2361 pj_log_push_indent();
2362
Benny Prijonodc752ca2006-09-22 16:55:42 +00002363 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002364 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002365 goto on_return;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002366
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002367 /* Set default media type if none is specified */
2368 if (mime_type == NULL) {
2369 mime_type = &mime_text_plain;
2370 }
2371
2372 /* Create request message. */
2373 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2374 -1, &tdata);
2375 if (status != PJ_SUCCESS) {
2376 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2377 goto on_return;
2378 }
2379
2380 /* Add accept header. */
2381 pjsip_msg_add_hdr( tdata->msg,
2382 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2383
2384 /* Parse MIME type */
2385 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2386
2387 /* Create "text/plain" message body. */
2388 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2389 &ctype.subtype, content);
2390 if (tdata->msg->body == NULL) {
2391 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2392 pjsip_tx_data_dec_ref(tdata);
2393 goto on_return;
2394 }
2395
2396 /* Add additional headers etc */
2397 pjsua_process_msg_data( tdata, msg_data);
2398
2399 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002400 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002401 im_data->acc_id = call->acc_id;
2402 im_data->call_id = call_id;
2403 im_data->to = call->inv->dlg->remote.info_str;
2404 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2405 im_data->user_data = user_data;
2406
2407
2408 /* Send the request. */
2409 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2410 pjsua_var.mod.id, im_data);
2411 if (status != PJ_SUCCESS) {
2412 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2413 goto on_return;
2414 }
2415
2416on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002417 if (dlg) pjsip_dlg_dec_lock(dlg);
2418 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002419 return status;
2420}
2421
2422
2423/*
2424 * Send IM typing indication inside INVITE session.
2425 */
2426PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2427 pj_bool_t is_typing,
2428 const pjsua_msg_data*msg_data)
2429{
2430 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002431 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002432 pjsip_tx_data *tdata;
2433 pj_status_t status;
2434
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002435 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2436 PJ_EINVAL);
2437
Benny Prijonob90fd382011-09-18 14:59:56 +00002438 PJ_LOG(4,(THIS_FILE, "Call %d sending typing indication..",
2439 call_id));
2440 pj_log_push_indent();
2441
Benny Prijonodc752ca2006-09-22 16:55:42 +00002442 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002443 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002444 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002445
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002446 /* Create request message. */
2447 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2448 -1, &tdata);
2449 if (status != PJ_SUCCESS) {
2450 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2451 goto on_return;
2452 }
2453
2454 /* Create "application/im-iscomposing+xml" msg body. */
2455 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2456 NULL, NULL, -1);
2457
2458 /* Add additional headers etc */
2459 pjsua_process_msg_data( tdata, msg_data);
2460
2461 /* Send the request. */
2462 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2463 if (status != PJ_SUCCESS) {
2464 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2465 goto on_return;
2466 }
2467
2468on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002469 if (dlg) pjsip_dlg_dec_lock(dlg);
2470 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002471 return status;
2472}
2473
2474
2475/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002476 * Send arbitrary request.
2477 */
2478PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2479 const pj_str_t *method_str,
2480 const pjsua_msg_data *msg_data)
2481{
2482 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002483 pjsip_dialog *dlg = NULL;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002484 pjsip_method method;
2485 pjsip_tx_data *tdata;
2486 pj_status_t status;
2487
2488 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2489 PJ_EINVAL);
2490
Benny Prijonob90fd382011-09-18 14:59:56 +00002491 PJ_LOG(4,(THIS_FILE, "Call %d sending %.*s request..",
2492 call_id, (int)method_str->slen, method_str->ptr));
2493 pj_log_push_indent();
2494
Benny Prijonofeb69f42007-10-05 09:12:26 +00002495 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2496 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002497 goto on_return;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002498
2499 /* Init method */
2500 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2501
2502 /* Create request message. */
2503 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2504 if (status != PJ_SUCCESS) {
2505 pjsua_perror(THIS_FILE, "Unable to create request", status);
2506 goto on_return;
2507 }
2508
2509 /* Add additional headers etc */
2510 pjsua_process_msg_data( tdata, msg_data);
2511
2512 /* Send the request. */
2513 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2514 if (status != PJ_SUCCESS) {
2515 pjsua_perror(THIS_FILE, "Unable to send request", status);
2516 goto on_return;
2517 }
2518
2519on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002520 if (dlg) pjsip_dlg_dec_lock(dlg);
2521 pj_log_pop_indent();
Benny Prijonofeb69f42007-10-05 09:12:26 +00002522 return status;
2523}
2524
2525
2526/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002527 * Terminate all calls.
2528 */
2529PJ_DEF(void) pjsua_call_hangup_all(void)
2530{
2531 unsigned i;
2532
Benny Prijonob90fd382011-09-18 14:59:56 +00002533 PJ_LOG(4,(THIS_FILE, "Hangup all calls.."));
2534 pj_log_push_indent();
2535
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002536 PJSUA_LOCK();
2537
2538 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2539 if (pjsua_var.calls[i].inv)
2540 pjsua_call_hangup(i, 0, NULL, NULL);
2541 }
2542
2543 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002544 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002545}
2546
2547
Benny Prijono1e601552010-10-20 05:31:08 +00002548/* Proto */
2549static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002550
Benny Prijono1e601552010-10-20 05:31:08 +00002551/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002552static void reinv_timer_cb(pj_timer_heap_t *th,
2553 pj_timer_entry *entry)
2554{
2555 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2556 pjsip_dialog *dlg;
2557 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002558 pj_status_t status;
2559
2560 PJ_UNUSED_ARG(th);
2561
2562 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2563
2564 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2565 if (status != PJ_SUCCESS)
2566 return;
2567
Benny Prijono1e601552010-10-20 05:31:08 +00002568 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002569
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002570 pjsip_dlg_dec_lock(dlg);
2571}
2572
2573
2574/* Check if the specified format can be skipped in counting codecs */
2575static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2576 const pj_str_t *fmt)
2577{
Benny Prijono1e601552010-10-20 05:31:08 +00002578 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002579 unsigned pt;
2580
2581 pt = pj_strtoul(fmt);
2582
2583 /* Check for comfort noise */
2584 if (pt == PJMEDIA_RTP_PT_CN)
2585 return PJ_TRUE;
2586
2587 /* Dynamic PT, check the format name */
2588 if (pt >= 96) {
2589 pjmedia_sdp_attr *a;
2590 pjmedia_sdp_rtpmap rtpmap;
2591
2592 /* Get the format name */
2593 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2594 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2595 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002596 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002597 return PJ_TRUE;
2598 } else {
2599 /* Invalid SDP, should not reach here */
2600 pj_assert(!"SDP should have been validated!");
2601 return PJ_TRUE;
2602 }
2603 }
2604
2605 return PJ_FALSE;
2606}
2607
2608
Benny Prijono1e601552010-10-20 05:31:08 +00002609/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2610 * out of several codecs presented by callee in his answer.
2611 */
2612static pj_status_t perform_lock_codec(pjsua_call *call)
2613{
2614 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2615 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002616 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002617 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002618 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002619 pjsip_tx_data *tdata;
2620 pj_status_t status;
2621
2622 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2623 PJ_EINVALIDOP);
2624
2625 /* Verify if another SDP negotiation is in progress, e.g: session timer
2626 * or another re-INVITE.
2627 */
2628 if (call->inv==NULL || call->inv->neg==NULL ||
2629 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2630 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002631 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002632 }
2633
Benny Prijono02493272010-11-17 09:15:04 +00002634 /* Don't do this if call is disconnecting! */
2635 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2636 call->inv->cause >= 200)
2637 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002638 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002639 }
2640
Benny Prijono1e601552010-10-20 05:31:08 +00002641 /* Verify if another SDP negotiation has been completed by comparing
2642 * the SDP version.
2643 */
2644 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2645 if (status != PJ_SUCCESS)
2646 return status;
2647 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2648 return PJMEDIA_SDP_EINVER;
2649
2650 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2651
2652 /* Update the new offer so it contains only a codec. Note that formats
2653 * order in the offer should have been matched to the answer, so we can
2654 * just directly update the offer without looking-up the answer.
2655 */
2656 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002657
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002658 for (i = 0; i < call->med_cnt; ++i) {
2659 unsigned j = 0, codec_cnt = 0;
2660 const pjmedia_sdp_media *ref_m;
2661 pjmedia_sdp_media *m;
2662 pjsua_call_media *call_med = &call->media[i];
2663
2664 /* Verify if media is deactivated */
2665 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2666 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2667 call_med->dir == PJMEDIA_DIR_NONE)
2668 {
Benny Prijono1e601552010-10-20 05:31:08 +00002669 continue;
2670 }
2671
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002672 ref_m = local_sdp->media[i];
2673 m = new_sdp->media[i];
2674
2675 /* Verify that media must be active. */
2676 pj_assert(ref_m->desc.port);
2677
2678 while (j < m->desc.fmt_count) {
2679 pjmedia_sdp_attr *a;
2680 pj_str_t *fmt = &m->desc.fmt[j];
2681
2682 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2683 ++j;
2684 continue;
2685 }
2686
2687 /* Remove format */
2688 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2689 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2690 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2691 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2692 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2693 m->desc.fmt_count, j);
2694 --m->desc.fmt_count;
2695 }
2696
2697 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002698 }
2699
Nanang Izzuddinec919002010-11-25 09:27:06 +00002700 /* Last check if SDP trully needs to be updated. It is possible that OA
2701 * negotiations have completed and SDP has changed but we didn't
2702 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002703 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002704 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002705 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002706
2707 /* Send UPDATE or re-INVITE */
2708 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2709 PJSIP_H_ALLOW,
2710 NULL, &STR_UPDATE) ==
2711 PJSIP_DIALOG_CAP_SUPPORTED;
2712 if (rem_can_update) {
2713 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2714 } else {
2715 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2716 }
2717
2718 if (status==PJ_EINVALIDOP &&
2719 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2720 {
2721 /* Ups, let's reschedule again */
2722 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2723 pj_time_val_normalize(&delay);
2724 call->lock_codec.reinv_timer.id = PJ_TRUE;
2725 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2726 &call->lock_codec.reinv_timer, &delay);
2727 return status;
2728 } else if (status != PJ_SUCCESS) {
2729 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2730 status);
2731 return status;
2732 }
2733
2734 /* Send the UPDATE/re-INVITE request */
2735 status = pjsip_inv_send_msg(call->inv, tdata);
2736 if (status != PJ_SUCCESS) {
2737 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2738 status);
2739 return status;
2740 }
2741
2742 return status;
2743}
2744
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002745/* Check if remote answerer has given us more than one codecs. If so,
2746 * create another offer with one codec only to lock down the codec.
2747 */
2748static pj_status_t lock_codec(pjsua_call *call)
2749{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002750 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002751 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002752 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002753 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002754 unsigned i;
2755 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002756 pj_status_t status;
2757
Nanang Izzuddinec919002010-11-25 09:27:06 +00002758 /* Stop lock codec timer, if it is active */
2759 if (call->lock_codec.reinv_timer.id) {
2760 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2761 &call->lock_codec.reinv_timer);
2762 call->lock_codec.reinv_timer.id = PJ_FALSE;
2763 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002764
Nanang Izzuddinec919002010-11-25 09:27:06 +00002765 /* Skip this if we are the answerer */
2766 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2767 return PJ_SUCCESS;
2768 }
2769
Nanang Izzuddinec919002010-11-25 09:27:06 +00002770 /* Delay this when the SDP negotiation done in call state EARLY and
2771 * remote does not support UPDATE method.
2772 */
2773 if (inv->state == PJSIP_INV_STATE_EARLY &&
2774 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2775 PJSIP_DIALOG_CAP_SUPPORTED)
2776 {
2777 call->lock_codec.pending = PJ_TRUE;
2778 return PJ_SUCCESS;
2779 }
2780
2781 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002782 if (status != PJ_SUCCESS)
2783 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002784 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002785 if (status != PJ_SUCCESS)
2786 return status;
2787
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002788 /* Find multiple codecs answer in all media */
2789 for (i = 0; i < call->med_cnt; ++i) {
2790 pjsua_call_media *call_med = &call->media[i];
2791 const pjmedia_sdp_media *rem_m, *loc_m;
2792 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002793
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002794 /* Skip this if the media is inactive or error */
2795 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2796 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2797 call_med->dir == PJMEDIA_DIR_NONE)
2798 {
2799 continue;
2800 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002801
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002802 /* Remote may answer with less media lines. */
2803 if (i >= remote_sdp->media_count)
2804 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002805
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002806 rem_m = remote_sdp->media[i];
2807 loc_m = local_sdp->media[i];
2808
2809 /* Verify that media must be active. */
2810 pj_assert(loc_m->desc.port && rem_m->desc.port);
2811
2812 /* Count the formats in the answer. */
2813 if (rem_m->desc.fmt_count==1) {
2814 codec_cnt = 1;
2815 } else {
2816 unsigned j;
2817 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
2818 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
2819 ++codec_cnt;
2820 }
2821 }
2822
2823 if (codec_cnt > 1) {
2824 has_mult_fmt = PJ_TRUE;
2825 break;
2826 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002827 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002828
2829 /* Each media in the answer already contains single codec. */
2830 if (!has_mult_fmt) {
2831 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002832 return PJ_SUCCESS;
2833 }
2834
Nanang Izzuddinec919002010-11-25 09:27:06 +00002835 /* Remote keeps answering with multiple codecs, let's just give up
2836 * locking codec to avoid infinite retry loop.
2837 */
2838 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
2839 return PJ_SUCCESS;
2840
Benny Prijonob90fd382011-09-18 14:59:56 +00002841 PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002842 "updating media session to use only one codec.."));
2843
Benny Prijono1e601552010-10-20 05:31:08 +00002844 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002845
Benny Prijono1e601552010-10-20 05:31:08 +00002846 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
2847 * See: https://trac.pjsip.org/repos/ticket/1149
2848 */
2849 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
2850 (void*)(pj_size_t)call->index,
2851 &reinv_timer_cb);
2852 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2853 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002854
2855 return PJ_SUCCESS;
2856}
2857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002858/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002859 * This callback receives notification from invite session when the
2860 * session state has changed.
2861 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002862static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2863 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002864{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002865 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002866
Benny Prijonob90fd382011-09-18 14:59:56 +00002867 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002868 PJSUA_LOCK();
2869
Benny Prijonoa1e69682007-05-11 15:14:34 +00002870 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002871
2872 if (!call) {
2873 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002874 pj_log_pop_indent();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002875 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002876 }
2877
Benny Prijonoe21e7842006-04-09 16:46:05 +00002878
2879 /* Get call times */
2880 switch (inv->state) {
2881 case PJSIP_INV_STATE_EARLY:
2882 case PJSIP_INV_STATE_CONNECTING:
2883 if (call->res_time.sec == 0)
2884 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002885 call->last_code = (pjsip_status_code)
2886 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887 pj_strncpy(&call->last_text,
2888 &e->body.tsx_state.tsx->status_text,
2889 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002890 break;
2891 case PJSIP_INV_STATE_CONFIRMED:
2892 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002893
Nanang Izzuddinec919002010-11-25 09:27:06 +00002894 /* See if lock codec was pended as media update was done in the
2895 * EARLY state and remote does not support UPDATE.
2896 */
2897 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002898 pj_status_t status;
2899 status = lock_codec(call);
2900 if (status != PJ_SUCCESS) {
2901 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00002902 }
2903 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002904 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002905 break;
2906 case PJSIP_INV_STATE_DISCONNECTED:
2907 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002908 if (call->res_time.sec == 0)
2909 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002910 if (e->type == PJSIP_EVENT_TSX_STATE &&
2911 e->body.tsx_state.tsx->status_code > call->last_code)
2912 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002913 call->last_code = (pjsip_status_code)
2914 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002915 pj_strncpy(&call->last_text,
2916 &e->body.tsx_state.tsx->status_text,
2917 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002918 } else {
2919 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2920 pj_strncpy(&call->last_text,
2921 pjsip_get_status_text(call->last_code),
2922 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002923 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002924
2925 /* Stop lock codec timer, if it is active */
2926 if (call->lock_codec.reinv_timer.id) {
2927 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2928 &call->lock_codec.reinv_timer);
2929 call->lock_codec.reinv_timer.id = PJ_FALSE;
2930 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002931 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002932 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002933 call->last_code = (pjsip_status_code)
2934 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002935 pj_strncpy(&call->last_text,
2936 &e->body.tsx_state.tsx->status_text,
2937 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002938 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002939 }
2940
Benny Prijono26ff9062006-02-21 23:47:00 +00002941 /* If this is an outgoing INVITE that was created because of
2942 * REFER/transfer, send NOTIFY to transferer.
2943 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002944 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002945 int st_code = -1;
2946 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2947
2948
Benny Prijonoa91a0032006-02-26 21:23:45 +00002949 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002950 case PJSIP_INV_STATE_NULL:
2951 case PJSIP_INV_STATE_CALLING:
2952 /* Do nothing */
2953 break;
2954
2955 case PJSIP_INV_STATE_EARLY:
2956 case PJSIP_INV_STATE_CONNECTING:
2957 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002958 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2959 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2960 else
2961 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002962 break;
2963
Benny Prijono140beae2009-10-11 05:06:43 +00002964 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002965#if 0
2966/* We don't need this, as we've terminated the subscription in
2967 * CONNECTING state.
2968 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002969 /* When state is confirmed, send the final 200/OK and terminate
2970 * subscription.
2971 */
2972 st_code = e->body.tsx_state.tsx->status_code;
2973 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002974#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002975 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002976
2977 case PJSIP_INV_STATE_DISCONNECTED:
2978 st_code = e->body.tsx_state.tsx->status_code;
2979 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2980 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002981
Benny Prijono8b1889b2006-06-06 18:40:40 +00002982 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002983 /* Nothing to do. Just to keep gcc from complaining about
2984 * unused enums.
2985 */
2986 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002987 }
2988
2989 if (st_code != -1) {
2990 pjsip_tx_data *tdata;
2991 pj_status_t status;
2992
Benny Prijonoa91a0032006-02-26 21:23:45 +00002993 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002994 ev_state, st_code,
2995 NULL, &tdata);
2996 if (status != PJ_SUCCESS) {
2997 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2998 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002999 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003000 if (status != PJ_SUCCESS) {
3001 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3002 }
3003 }
3004 }
3005 }
3006
Benny Prijono84126ab2006-02-09 09:30:09 +00003007
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003008 if (pjsua_var.ua_cfg.cb.on_call_state)
3009 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003010
3011 /* call->inv may be NULL now */
3012
Benny Prijono84126ab2006-02-09 09:30:09 +00003013 /* Destroy media session when invite session is disconnected. */
3014 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003015
Benny Prijonoa91a0032006-02-26 21:23:45 +00003016 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003017
Benny Prijono275fd682006-03-22 11:59:11 +00003018 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003019 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003020
Benny Prijono105217f2006-03-06 16:25:59 +00003021 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003022 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003023 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003024
3025 /* Reset call */
3026 reset_call(call->index);
3027
Benny Prijono84126ab2006-02-09 09:30:09 +00003028 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003029
3030 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003031 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003032}
3033
3034/*
3035 * This callback is called by invite session framework when UAC session
3036 * has forked.
3037 */
3038static void pjsua_call_on_forked( pjsip_inv_session *inv,
3039 pjsip_event *e)
3040{
3041 PJ_UNUSED_ARG(inv);
3042 PJ_UNUSED_ARG(e);
3043
3044 PJ_TODO(HANDLE_FORKED_DIALOG);
3045}
3046
3047
3048/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003049 * Callback from UA layer when forked dialog response is received.
3050 */
3051pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3052{
3053 if (dlg->uac_has_2xx &&
3054 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3055 pjsip_rdata_get_tsx(res) == NULL &&
3056 res->msg_info.msg->line.status.code/100 == 2)
3057 {
3058 pjsip_dialog *forked_dlg;
3059 pjsip_tx_data *bye;
3060 pj_status_t status;
3061
3062 /* Create forked dialog */
3063 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3064 if (status != PJ_SUCCESS)
3065 return NULL;
3066
3067 pjsip_dlg_inc_lock(forked_dlg);
3068
3069 /* Disconnect the call */
3070 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3071 -1, &bye);
3072 if (status == PJ_SUCCESS) {
3073 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3074 }
3075
3076 pjsip_dlg_dec_lock(forked_dlg);
3077
3078 if (status != PJ_SUCCESS) {
3079 return NULL;
3080 }
3081
3082 return forked_dlg;
3083
3084 } else {
3085 return dlg;
3086 }
3087}
3088
3089/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003090 * Disconnect call upon error.
3091 */
3092static void call_disconnect( pjsip_inv_session *inv,
3093 int code )
3094{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003095 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003096 pjsip_tx_data *tdata;
3097 pj_status_t status;
3098
Benny Prijono59b3aed2008-01-15 16:54:54 +00003099 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3100
Benny Prijonoa38ada02006-07-02 14:22:35 +00003101 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003102 if (status != PJ_SUCCESS)
3103 return;
3104
3105 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003106#if DISABLED_FOR_TICKET_1185
3107 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00003108 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3109 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003110 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003111 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003112
Benny Prijono734fc2d2008-03-17 16:05:35 +00003113 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003114 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003115 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003116 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003117 if (status == PJ_SUCCESS) {
3118 pjsip_create_sdp_body(tdata->pool, local_sdp,
3119 &tdata->msg->body);
3120 }
3121 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00003122#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00003123
3124 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003125}
3126
3127/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003128 * Callback to be called when SDP offer/answer negotiation has just completed
3129 * in the session. This function will start/update media if negotiation
3130 * has succeeded.
3131 */
3132static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3133 pj_status_t status)
3134{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003135 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003136 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003137 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003138 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003139
Benny Prijonob90fd382011-09-18 14:59:56 +00003140 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003141 PJSUA_LOCK();
3142
Benny Prijonoa1e69682007-05-11 15:14:34 +00003143 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003144
3145 if (status != PJ_SUCCESS) {
3146
3147 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3148
Benny Prijono2331d202008-06-26 15:46:52 +00003149 /* Do not deinitialize media since this may be a re-INVITE or
3150 * UPDATE (which in this case the media should not get affected
3151 * by the failed re-INVITE/UPDATE). The media will be shutdown
3152 * when call is disconnected anyway.
3153 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003154 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003155 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003156
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003157 /* Disconnect call if we're not in the middle of initializing an
3158 * UAS dialog and if this is not a re-INVITE
3159 */
3160 if (inv->state != PJSIP_INV_STATE_NULL &&
3161 inv->state != PJSIP_INV_STATE_CONFIRMED)
3162 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003163 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003164 }
3165
Benny Prijonob90fd382011-09-18 14:59:56 +00003166 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003167 }
3168
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003169
3170 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003171 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003172 if (status != PJ_SUCCESS) {
3173 pjsua_perror(THIS_FILE,
3174 "Unable to retrieve currently active local SDP",
3175 status);
3176 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003177 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003178 }
3179
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003180 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3181 if (status != PJ_SUCCESS) {
3182 pjsua_perror(THIS_FILE,
3183 "Unable to retrieve currently active remote SDP",
3184 status);
3185 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003186 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003187 }
3188
Benny Prijono91a6a172007-10-31 08:59:29 +00003189 /* Update remote's NAT type */
3190 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3191 update_remote_nat_type(call, remote_sdp);
3192 }
3193
3194 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003195 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003196 if (status != PJ_SUCCESS) {
3197 pjsua_perror(THIS_FILE, "Unable to create media session",
3198 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003199 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003200 /* No need to deinitialize; media will be shutdown when call
3201 * state is disconnected anyway.
3202 */
3203 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonob90fd382011-09-18 14:59:56 +00003204 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003205 }
3206
Nanang Izzuddinec919002010-11-25 09:27:06 +00003207 /* Ticket #476: make sure only one codec is specified in the answer. */
3208 status = lock_codec(call);
3209 if (status != PJ_SUCCESS) {
3210 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003211 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003212
3213 /* Call application callback, if any */
3214 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3215 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3216
Benny Prijonob90fd382011-09-18 14:59:56 +00003217on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003218 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003219 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003220}
3221
3222
Benny Prijonodd63b992010-10-01 02:03:42 +00003223/* Modify SDP for call hold. */
3224static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3225 pj_pool_t *pool,
3226 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003227{
Benny Prijono316f02a2011-04-07 07:53:25 +00003228 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003229
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003230 /* Call-hold is done by set the media direction to 'sendonly'
3231 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3232 * 'inactive' (PJMEDIA_DIR_NONE).
3233 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3234 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003235 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003236 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003237 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003238 /* https://trac.pjsip.org/repos/ticket/1142:
3239 * configuration to use c=0.0.0.0 for call hold.
3240 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003241
Benny Prijono316f02a2011-04-07 07:53:25 +00003242 for (mi=0; mi<sdp->media_count; ++mi) {
3243 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003244
Benny Prijono316f02a2011-04-07 07:53:25 +00003245 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3246 pjmedia_sdp_conn *conn;
3247 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003248
Benny Prijono316f02a2011-04-07 07:53:25 +00003249 /* Get SDP media connection line */
3250 conn = m->conn;
3251 if (!conn)
3252 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003253
Benny Prijono316f02a2011-04-07 07:53:25 +00003254 /* Modify address */
3255 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003256
Benny Prijono316f02a2011-04-07 07:53:25 +00003257 /* Remove existing directions attributes */
3258 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3259 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3260 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3261 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003262
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003263 /* Add inactive attribute */
3264 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003265 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003266
3267
3268 } else {
3269 pjmedia_sdp_attr *attr;
3270
3271 /* Remove existing directions attributes */
3272 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3273 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3274 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3275 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3276
3277 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3278 /* Add sendonly attribute */
3279 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3280 pjmedia_sdp_media_add_attr(m, attr);
3281 } else {
3282 /* Add inactive attribute */
3283 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3284 pjmedia_sdp_media_add_attr(m, attr);
3285 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003286 }
3287 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003288
Benny Prijonodd63b992010-10-01 02:03:42 +00003289 return PJ_SUCCESS;
3290}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003291
Benny Prijonodd63b992010-10-01 02:03:42 +00003292/* Create SDP for call hold. */
3293static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3294 pjmedia_sdp_session **p_sdp)
3295{
3296 pj_status_t status;
3297 pj_pool_t *pool;
3298 pjmedia_sdp_session *sdp;
3299
3300 /* Use call's provisional pool */
3301 pool = call->inv->pool_prov;
3302
3303 /* Create new offer */
3304 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3305 NULL);
3306 if (status != PJ_SUCCESS) {
3307 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3308 return status;
3309 }
3310
3311 status = modify_sdp_of_call_hold(call, pool, sdp);
3312 if (status != PJ_SUCCESS)
3313 return status;
3314
3315 *p_sdp = sdp;
3316
3317 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003318}
3319
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003320/*
3321 * Called when session received new offer.
3322 */
3323static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3324 const pjmedia_sdp_session *offer)
3325{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003326 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003327 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003328 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003329 pj_status_t status;
3330
3331 PJSUA_LOCK();
3332
Benny Prijonoa1e69682007-05-11 15:14:34 +00003333 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003334
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003335 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003336 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3337 call->index));
Benny Prijonob90fd382011-09-18 14:59:56 +00003338 pj_log_push_indent();
Benny Prijono667952e2007-04-02 19:27:54 +00003339
Benny Prijono40d62b62009-08-12 17:53:47 +00003340 status = pjsua_media_channel_create_sdp(call->index,
3341 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003342 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003343 if (status != PJ_SUCCESS) {
3344 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003345 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003346 }
3347
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003348 /* Validate media count in the generated answer */
3349 pj_assert(answer->media_count == offer->media_count);
3350
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003351 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003352 for (i = 0; i < answer->media_count; ++i) {
3353 pjmedia_sdp_conn *conn;
3354
3355 conn = offer->media[i]->conn;
3356 if (!conn)
3357 conn = offer->conn;
3358
3359 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3360 pj_strcmp2(&conn->addr, "0")==0)
3361 {
3362 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3363
3364 /* Modify answer address */
3365 if (a_conn) {
3366 a_conn->addr = pj_str("0.0.0.0");
3367 } else if (answer->conn == NULL ||
3368 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3369 {
3370 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3371 pjmedia_sdp_conn);
3372 a_conn->net_type = pj_str("IN");
3373 a_conn->addr_type = pj_str("IP4");
3374 a_conn->addr = pj_str("0.0.0.0");
3375 answer->media[i]->conn = a_conn;
3376 }
3377 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003378 }
3379
3380 /* Check if call is on-hold */
3381 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003382 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003383 }
3384
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003385 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3386 if (status != PJ_SUCCESS) {
3387 pjsua_perror(THIS_FILE, "Unable to set answer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003388 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003389 }
3390
Benny Prijonob90fd382011-09-18 14:59:56 +00003391on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003392 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003393 pj_log_pop_indent();
Benny Prijono84126ab2006-02-09 09:30:09 +00003394}
3395
3396
3397/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003398 * Called to generate new offer.
3399 */
3400static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3401 pjmedia_sdp_session **offer)
3402{
3403 pjsua_call *call;
3404 pj_status_t status;
3405
Benny Prijonob90fd382011-09-18 14:59:56 +00003406 pj_log_push_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003407 PJSUA_LOCK();
3408
3409 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3410
3411 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003412 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003413 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003414 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003415 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003416 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003417 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003418 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3419 call->index));
3420
Benny Prijono40d62b62009-08-12 17:53:47 +00003421 status = pjsua_media_channel_create_sdp(call->index,
3422 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003423 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003424 }
3425
3426 if (status != PJ_SUCCESS) {
3427 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003428 goto on_return;
Benny Prijono77998ce2007-06-20 10:03:46 +00003429 }
3430
Benny Prijonob90fd382011-09-18 14:59:56 +00003431on_return:
Benny Prijono77998ce2007-06-20 10:03:46 +00003432 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003433 pj_log_pop_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003434}
3435
3436
3437/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003438 * Callback called by event framework when the xfer subscription state
3439 * has changed.
3440 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003441static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3442{
3443
3444 PJ_UNUSED_ARG(event);
3445
Benny Prijonob90fd382011-09-18 14:59:56 +00003446 pj_log_push_indent();
3447
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003448 /*
3449 * When subscription is accepted (got 200/OK to REFER), check if
3450 * subscription suppressed.
3451 */
3452 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3453
3454 pjsip_rx_data *rdata;
3455 pjsip_generic_string_hdr *refer_sub;
3456 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3457 pjsua_call *call;
3458
Benny Prijonoa1e69682007-05-11 15:14:34 +00003459 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003460
3461 /* Must be receipt of response message */
3462 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3463 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3464 rdata = event->body.tsx_state.src.rdata;
3465
3466 /* Find Refer-Sub header */
3467 refer_sub = (pjsip_generic_string_hdr*)
3468 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3469 &REFER_SUB, NULL);
3470
3471 /* Check if subscription is suppressed */
3472 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3473 /* Since no subscription is desired, assume that call has been
3474 * transfered successfully.
3475 */
3476 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3477 const pj_str_t ACCEPTED = { "Accepted", 8 };
3478 pj_bool_t cont = PJ_FALSE;
3479 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3480 200,
3481 &ACCEPTED,
3482 PJ_TRUE,
3483 &cont);
3484 }
3485
3486 /* Yes, subscription is suppressed.
3487 * Terminate our subscription now.
3488 */
3489 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3490 "event subcription..."));
3491 pjsip_evsub_terminate(sub, PJ_TRUE);
3492
3493 } else {
3494 /* Notify application about call transfer progress.
3495 * Initially notify with 100/Accepted status.
3496 */
3497 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3498 const pj_str_t ACCEPTED = { "Accepted", 8 };
3499 pj_bool_t cont = PJ_FALSE;
3500 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3501 100,
3502 &ACCEPTED,
3503 PJ_FALSE,
3504 &cont);
3505 }
3506 }
3507 }
3508 /*
3509 * On incoming NOTIFY, notify application about call transfer progress.
3510 */
3511 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3512 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3513 {
3514 pjsua_call *call;
3515 pjsip_msg *msg;
3516 pjsip_msg_body *body;
3517 pjsip_status_line status_line;
3518 pj_bool_t is_last;
3519 pj_bool_t cont;
3520 pj_status_t status;
3521
Benny Prijonoa1e69682007-05-11 15:14:34 +00003522 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003523
3524 /* When subscription is terminated, clear the xfer_sub member of
3525 * the inv_data.
3526 */
3527 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3528 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3529 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3530
3531 }
3532
3533 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3534 /* Application is not interested with call progress status */
Benny Prijonob90fd382011-09-18 14:59:56 +00003535 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003536 }
3537
3538 /* This better be a NOTIFY request */
3539 if (event->type == PJSIP_EVENT_TSX_STATE &&
3540 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3541 {
3542 pjsip_rx_data *rdata;
3543
3544 rdata = event->body.tsx_state.src.rdata;
3545
3546 /* Check if there's body */
3547 msg = rdata->msg_info.msg;
3548 body = msg->body;
3549 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003550 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003551 "Warning: received NOTIFY without message body"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003552 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003553 }
3554
3555 /* Check for appropriate content */
3556 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3557 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3558 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003559 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003560 "Warning: received NOTIFY with non message/sipfrag "
3561 "content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003562 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003563 }
3564
3565 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003566 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003567 &status_line);
3568 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003569 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003570 "Warning: received NOTIFY with invalid "
3571 "message/sipfrag content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003572 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003573 }
3574
3575 } else {
3576 status_line.code = 500;
3577 status_line.reason = *pjsip_get_status_text(500);
3578 }
3579
3580 /* Notify application */
3581 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3582 cont = !is_last;
3583 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3584 status_line.code,
3585 &status_line.reason,
3586 is_last, &cont);
3587
3588 if (!cont) {
3589 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3590 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003591
3592 /* If the call transfer has completed but the subscription is
3593 * not terminated, terminate it now.
3594 */
3595 if (status_line.code/100 == 2 && !is_last) {
3596 pjsip_tx_data *tdata;
3597
3598 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3599 0, &tdata);
3600 if (status == PJ_SUCCESS)
3601 status = pjsip_evsub_send_request(sub, tdata);
3602 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003603 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003604
3605on_return:
3606 pj_log_pop_indent();
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003607}
3608
3609
3610/*
3611 * Callback called by event framework when the xfer subscription state
3612 * has changed.
3613 */
3614static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003615{
Benny Prijono26ff9062006-02-21 23:47:00 +00003616 PJ_UNUSED_ARG(event);
3617
Benny Prijonob90fd382011-09-18 14:59:56 +00003618 pj_log_push_indent();
3619
Benny Prijono26ff9062006-02-21 23:47:00 +00003620 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003621 * When subscription is terminated, clear the xfer_sub member of
3622 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003623 */
3624 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003625 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003626
Benny Prijonoa1e69682007-05-11 15:14:34 +00003627 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003628 if (!call)
Benny Prijonob90fd382011-09-18 14:59:56 +00003629 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003630
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003631 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003632 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003633
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003634 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003635 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003636
3637on_return:
3638 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003639}
3640
3641
3642/*
3643 * Follow transfer (REFER) request.
3644 */
3645static void on_call_transfered( pjsip_inv_session *inv,
3646 pjsip_rx_data *rdata )
3647{
3648 pj_status_t status;
3649 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003650 pjsua_call *existing_call;
3651 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003652 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003653 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003654 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003655 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003656 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003657 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003658 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003659 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003660 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003661 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003662 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003663 pjsip_evsub *sub;
3664
Benny Prijonob90fd382011-09-18 14:59:56 +00003665 pj_log_push_indent();
3666
Benny Prijonoa1e69682007-05-11 15:14:34 +00003667 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003668
Benny Prijono26ff9062006-02-21 23:47:00 +00003669 /* Find the Refer-To header */
3670 refer_to = (pjsip_generic_string_hdr*)
3671 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3672
3673 if (refer_to == NULL) {
3674 /* Invalid Request.
3675 * No Refer-To header!
3676 */
3677 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003678 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003679 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003680 }
3681
Benny Prijonoc8141a82006-08-20 09:12:19 +00003682 /* Find optional Refer-Sub header */
3683 refer_sub = (pjsip_generic_string_hdr*)
3684 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3685
3686 if (refer_sub) {
3687 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3688 no_refer_sub = PJ_TRUE;
3689 }
3690
Benny Prijono053f5222006-11-11 16:16:04 +00003691 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3692 * request.
3693 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003694 ref_by_hdr = (pjsip_hdr*)
3695 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003696 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003697
Benny Prijono9fc735d2006-05-28 14:58:12 +00003698 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003699 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003700 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3701 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3702 &refer_to->hvalue,
3703 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003704
3705 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003706 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003707 if (code >= 300) {
3708 /* Application rejects call transfer request */
3709 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003710 goto on_return;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003711 }
3712
Benny Prijono26ff9062006-02-21 23:47:00 +00003713 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3714 (int)inv->dlg->remote.info_str.slen,
3715 inv->dlg->remote.info_str.ptr,
3716 (int)refer_to->hvalue.slen,
3717 refer_to->hvalue.ptr));
3718
Benny Prijonoc8141a82006-08-20 09:12:19 +00003719 if (no_refer_sub) {
3720 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003721 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003722 */
3723 pjsip_tx_data *tdata;
3724 const pj_str_t str_false = { "false", 5};
3725 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003726
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003727 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3728 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003729 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003730 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003731 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003732 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003733 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003734
Benny Prijonoc8141a82006-08-20 09:12:19 +00003735 /* Add Refer-Sub header */
3736 hdr = (pjsip_hdr*)
3737 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3738 &str_false);
3739 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003740
Benny Prijono26ff9062006-02-21 23:47:00 +00003741
Benny Prijonoc8141a82006-08-20 09:12:19 +00003742 /* Send answer */
3743 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3744 tdata);
3745 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003746 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003747 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003748 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003749 }
3750
3751 /* Don't have subscription */
3752 sub = NULL;
3753
3754 } else {
3755 struct pjsip_evsub_user xfer_cb;
3756 pjsip_hdr hdr_list;
3757
3758 /* Init callback */
3759 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003760 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003761
3762 /* Init additional header list to be sent with REFER response */
3763 pj_list_init(&hdr_list);
3764
3765 /* Create transferee event subscription */
3766 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3767 if (status != PJ_SUCCESS) {
3768 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3769 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003770 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003771 }
3772
3773 /* If there's Refer-Sub header and the value is "true", send back
3774 * Refer-Sub in the response with value "true" too.
3775 */
3776 if (refer_sub) {
3777 const pj_str_t str_true = { "true", 4 };
3778 pjsip_hdr *hdr;
3779
3780 hdr = (pjsip_hdr*)
3781 pjsip_generic_string_hdr_create(inv->dlg->pool,
3782 &str_refer_sub,
3783 &str_true);
3784 pj_list_push_back(&hdr_list, hdr);
3785
3786 }
3787
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003788 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003789 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3790
3791 /* Create initial NOTIFY request */
3792 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3793 100, NULL, &tdata);
3794 if (status != PJ_SUCCESS) {
3795 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3796 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003797 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003798 }
3799
3800 /* Send initial NOTIFY request */
3801 status = pjsip_xfer_send_request( sub, tdata);
3802 if (status != PJ_SUCCESS) {
3803 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003804 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003805 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003806 }
3807
3808 /* We're cheating here.
3809 * We need to get a null terminated string from a pj_str_t.
3810 * So grab the pointer from the hvalue and NULL terminate it, knowing
3811 * that the NULL position will be occupied by a newline.
3812 */
3813 uri = refer_to->hvalue.ptr;
3814 uri[refer_to->hvalue.slen] = '\0';
3815
Benny Prijono053f5222006-11-11 16:16:04 +00003816 /* Init msg_data */
3817 pjsua_msg_data_init(&msg_data);
3818
3819 /* If Referred-By header is present in the REFER request, copy this
3820 * to the outgoing INVITE request.
3821 */
3822 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003823 pjsip_hdr *dup = (pjsip_hdr*)
3824 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003825 pj_list_push_back(&msg_data.hdr_list, dup);
3826 }
3827
Benny Prijono26ff9062006-02-21 23:47:00 +00003828 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003829 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003830 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003831 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003832 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003833 if (status != PJ_SUCCESS) {
3834
Benny Prijonoc8141a82006-08-20 09:12:19 +00003835 /* Notify xferer about the error (if we have subscription) */
3836 if (sub) {
3837 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3838 500, NULL, &tdata);
3839 if (status != PJ_SUCCESS) {
3840 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3841 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003842 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003843 }
3844 status = pjsip_xfer_send_request(sub, tdata);
3845 if (status != PJ_SUCCESS) {
3846 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3847 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003848 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003849 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003850 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003851 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003852 }
3853
Benny Prijonoc8141a82006-08-20 09:12:19 +00003854 if (sub) {
3855 /* Put the server subscription in inv_data.
3856 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3857 * reported back to the server subscription.
3858 */
3859 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003860
Benny Prijonoc8141a82006-08-20 09:12:19 +00003861 /* Put the invite_data in the subscription. */
3862 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3863 &pjsua_var.calls[new_call]);
3864 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003865
3866on_return:
3867 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003868}
3869
3870
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003871
Benny Prijono26ff9062006-02-21 23:47:00 +00003872/*
3873 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003874 * session. We use this to trap:
3875 * - incoming REFER request.
3876 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003877 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003878static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3879 pjsip_transaction *tsx,
3880 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003881{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003882 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003883
Benny Prijonob90fd382011-09-18 14:59:56 +00003884 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003885 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003886
Benny Prijono2285e7e2008-12-17 14:28:18 +00003887 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3888
Benny Prijonob90fd382011-09-18 14:59:56 +00003889 if (call == NULL)
3890 goto on_return;
Benny Prijono2285e7e2008-12-17 14:28:18 +00003891
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003892 if (call->inv == NULL) {
3893 /* Shouldn't happen. It happens only when we don't terminate the
3894 * server subscription caused by REFER after the call has been
3895 * transfered (and this call has been disconnected), and we
3896 * receive another REFER for this call.
3897 */
Benny Prijonob90fd382011-09-18 14:59:56 +00003898 goto on_return;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003899 }
3900
Benny Prijonofeb69f42007-10-05 09:12:26 +00003901 /* Notify application callback first */
3902 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3903 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3904 }
3905
Benny Prijono26ff9062006-02-21 23:47:00 +00003906 if (tsx->role==PJSIP_ROLE_UAS &&
3907 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003908 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003909 {
3910 /*
3911 * Incoming REFER request.
3912 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003913 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003914
Benny Prijono26ff9062006-02-21 23:47:00 +00003915 }
Benny Prijonob0808372006-03-02 21:18:58 +00003916 else if (tsx->role==PJSIP_ROLE_UAS &&
3917 tsx->state==PJSIP_TSX_STATE_TRYING &&
3918 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3919 {
3920 /*
3921 * Incoming MESSAGE request!
3922 */
3923 pjsip_rx_data *rdata;
3924 pjsip_msg *msg;
3925 pjsip_accept_hdr *accept_hdr;
3926 pj_status_t status;
3927
3928 rdata = e->body.tsx_state.src.rdata;
3929 msg = rdata->msg_info.msg;
3930
3931 /* Request MUST have message body, with Content-Type equal to
3932 * "text/plain".
3933 */
3934 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3935
3936 pjsip_hdr hdr_list;
3937
3938 pj_list_init(&hdr_list);
3939 pj_list_push_back(&hdr_list, accept_hdr);
3940
3941 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3942 NULL, &hdr_list, NULL );
Benny Prijonob90fd382011-09-18 14:59:56 +00003943 goto on_return;
Benny Prijonob0808372006-03-02 21:18:58 +00003944 }
3945
3946 /* Respond with 200 first, so that remote doesn't retransmit in case
3947 * the UI takes too long to process the message.
3948 */
3949 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3950
3951 /* Process MESSAGE request */
3952 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3953 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003954
Benny Prijonob0808372006-03-02 21:18:58 +00003955 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003956 else if (tsx->role == PJSIP_ROLE_UAC &&
3957 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003958 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003959 /* Handle outgoing pager status */
3960 if (tsx->status_code >= 200) {
3961 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003962
Benny Prijonoa1e69682007-05-11 15:14:34 +00003963 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003964 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003965
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003966 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3967 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3968 &im_data->to,
3969 &im_data->body,
3970 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003971 (pjsip_status_code)
3972 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003973 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003974 }
Benny Prijonofccab712006-02-22 22:23:22 +00003975 }
Benny Prijono834aee32006-02-19 01:38:06 +00003976 }
Benny Prijono834aee32006-02-19 01:38:06 +00003977
Benny Prijonob90fd382011-09-18 14:59:56 +00003978on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003979 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003980 pj_log_pop_indent();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003981}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003982
3983
3984/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003985static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3986 const pjsip_uri *target,
3987 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003988{
3989 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003990 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003991
Benny Prijonob90fd382011-09-18 14:59:56 +00003992 pj_log_push_indent();
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003993 PJSUA_LOCK();
3994
3995 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003996 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3997 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003998 } else {
3999 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4000 "(callback not implemented by application). Disconnecting "
4001 "call.",
4002 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004003 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004004 }
4005
4006 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00004007 pj_log_pop_indent();
Benny Prijono08a48b82008-11-27 12:42:07 +00004008
4009 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004010}
4011