blob: 02a78b937f06c970f86234ade7dc597c8a1ad5d9 [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,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000633 &on_make_call_med_tp_complete);
634 if (status == PJ_SUCCESS) {
635 status = on_make_call_med_tp_complete(call->index, NULL);
636 if (status != PJ_SUCCESS)
637 goto on_error;
638 } else if (status != PJ_EPENDING) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000639 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
640 goto on_error;
641 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000642
Benny Prijono84126ab2006-02-09 09:30:09 +0000643 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000644
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000645 if (p_call_id)
646 *p_call_id = call_id;
647
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000648 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000649 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000650
Benny Prijonob90fd382011-09-18 14:59:56 +0000651 pj_log_pop_indent();
652
Benny Prijono84126ab2006-02-09 09:30:09 +0000653 return PJ_SUCCESS;
654
655
656on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000657 if (dlg) {
Sauw Ming73ecfe82011-09-21 10:20:01 +0000658 pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000659 /* This may destroy the dialog */
660 pjsip_dlg_dec_lock(dlg);
661 }
662
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000663 if (call_id != -1) {
664 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000665 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000666 }
667
Benny Prijonob90fd382011-09-18 14:59:56 +0000668 if (tmp_pool)
669 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000670 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000671
672 pj_log_pop_indent();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000673 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000674}
675
676
Benny Prijono91a6a172007-10-31 08:59:29 +0000677/* Get the NAT type information in remote's SDP */
678static void update_remote_nat_type(pjsua_call *call,
679 const pjmedia_sdp_session *sdp)
680{
681 const pjmedia_sdp_attr *xnat;
682
683 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
684 if (xnat) {
685 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
686 } else {
687 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
688 }
689
690 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
691 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
692}
693
694
Sauw Mingec765352011-10-03 02:04:36 +0000695/* Incoming call callback when media transport creation is completed. */
696static pj_status_t
697on_incoming_call_med_tp_complete(pjsua_call_id call_id,
698 const pjsua_med_tp_state_info *info)
699{
700 pjsua_call *call = &pjsua_var.calls[call_id];
701 const pjmedia_sdp_session *offer=NULL;
702 pjmedia_sdp_session *answer;
703 pjsip_tx_data *response = NULL;
704 unsigned options = 0;
705 int sip_err_code = (info? info->sip_err_code: 0);
706 pj_status_t status = (info? info->status: PJ_SUCCESS);
707
708 PJSUA_LOCK();
709
710 if (status != PJ_SUCCESS) {
711 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
712 goto on_return;
713 }
Sauw Ming903154f2011-10-03 08:22:48 +0000714
715 /* pjsua_media_channel_deinit() has been called. */
716 if (call->async_call.med_ch_deinit) {
717 pjsua_media_channel_deinit(call->index);
718 call->med_ch_cb = NULL;
719 PJSUA_UNLOCK();
720 return PJ_SUCCESS;
721 }
722
Sauw Mingec765352011-10-03 02:04:36 +0000723 /* Get remote SDP offer (if any). */
724 if (call->inv->neg)
725 pjmedia_sdp_neg_get_neg_remote(call->inv->neg, &offer);
726
727 status = pjsua_media_channel_create_sdp(call_id,
728 call->async_call.dlg->pool,
729 offer, &answer, &sip_err_code);
730 if (status != PJ_SUCCESS) {
731 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
732 goto on_return;
733 }
734
735 status = pjsip_inv_set_local_sdp(call->inv, answer);
736 if (status != PJ_SUCCESS) {
737 pjsua_perror(THIS_FILE, "Error setting local SDP", status);
738 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
739 goto on_return;
740 }
741
742 /* Verify that we can handle the request. */
743 status = pjsip_inv_verify_request3(NULL,
744 call->inv->pool_prov, &options, offer,
745 answer, NULL, pjsua_var.endpt, &response);
746 if (status != PJ_SUCCESS) {
747 /*
748 * No we can't handle the incoming INVITE request.
749 */
750 goto on_return;
751 }
752
753on_return:
754 if (status != PJ_SUCCESS) {
755 pjsip_tx_data *tdata;
756 pj_status_t status_;
757
758 status_ = pjsip_inv_end_session(call->inv, sip_err_code, NULL, &tdata);
759 if (status_ == PJ_SUCCESS && tdata)
760 status_ = pjsip_inv_send_msg(call->inv, tdata);
761
762 pjsua_media_channel_deinit(call->index);
763 }
764
765 /* Set the callback to NULL to indicate that the async operation
766 * has completed.
767 */
768 call->med_ch_cb = NULL;
769
770 if (status == PJ_SUCCESS &&
771 !pj_list_empty(&call->async_call.call_var.inc_call.answers))
772 {
773 struct call_answer *answer, *next;
774
775 answer = call->async_call.call_var.inc_call.answers.next;
776 while (answer != &call->async_call.call_var.inc_call.answers) {
777 next = answer->next;
778 pjsua_call_answer(call_id, answer->code, answer->reason,
779 answer->msg_data);
780 pj_list_erase(answer);
781 answer = next;
782 }
783 }
784
785 PJSUA_UNLOCK();
786 return status;
787}
788
789
Benny Prijonodc39fe82006-05-26 12:17:46 +0000790/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000791 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000792 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000793 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000794pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000795{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000796 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000797 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000798 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000799 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
800 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000801 pjsip_tx_data *response = NULL;
802 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000803 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000804 int acc_id;
805 pjsua_call *call;
806 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000807 int sip_err_code;
Sauw Mingec765352011-10-03 02:04:36 +0000808 pjmedia_sdp_session *offer=NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000809 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000810
Benny Prijono26ff9062006-02-21 23:47:00 +0000811 /* Don't want to handle anything but INVITE */
812 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
813 return PJ_FALSE;
814
815 /* Don't want to handle anything that's already associated with
816 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000817 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000818 if (dlg || tsx)
819 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000820
Benny Prijono384dab42009-10-14 01:58:04 +0000821 /* Don't want to accept the call if shutdown is in progress */
822 if (pjsua_var.thread_quit_flag) {
823 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
824 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
825 NULL, NULL);
826 return PJ_TRUE;
827 }
828
Benny Prijonob90fd382011-09-18 14:59:56 +0000829 PJ_LOG(4,(THIS_FILE, "Incoming %s", rdata->msg_info.info));
830 pj_log_push_indent();
831
Benny Prijono148c9dd2006-09-19 13:37:53 +0000832 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000833
Benny Prijono26ff9062006-02-21 23:47:00 +0000834 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000835 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000836
Benny Prijono5773cd62008-01-19 13:01:42 +0000837 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000838 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000839 PJSIP_SC_BUSY_HERE, NULL,
840 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000841 PJ_LOG(2,(THIS_FILE,
842 "Unable to accept incoming call (too many calls)"));
Benny Prijonob90fd382011-09-18 14:59:56 +0000843 goto on_return;
Benny Prijono84126ab2006-02-09 09:30:09 +0000844 }
845
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000846 /* Clear call descriptor */
847 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000848
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000849 call = &pjsua_var.calls[call_id];
850
851 /* Mark call start time. */
852 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000853
Benny Prijono053f5222006-11-11 16:16:04 +0000854 /* Check INVITE request for Replaces header. If Replaces header is
855 * present, the function will make sure that we can handle the request.
856 */
857 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
858 &response);
859 if (status != PJ_SUCCESS) {
860 /*
861 * Something wrong with the Replaces header.
862 */
863 if (response) {
864 pjsip_response_addr res_addr;
865
866 pjsip_get_response_addr(response->pool, rdata, &res_addr);
867 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
868 NULL, NULL);
869
870 } else {
871
872 /* Respond with 500 (Internal Server Error) */
873 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
874 NULL, NULL);
875 }
876
Benny Prijonob90fd382011-09-18 14:59:56 +0000877 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000878 }
879
880 /* If this INVITE request contains Replaces header, notify application
881 * about the request so that application can do subsequent checking
882 * if it wants to.
883 */
884 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
885 pjsua_call *replaced_call;
886 int st_code = 200;
887 pj_str_t st_text = { "OK", 2 };
888
889 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000890 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000891
892 /* Notify application */
893 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
894 rdata, &st_code, &st_text);
895
896 /* Must specify final response */
897 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
898
899 /* Check if application rejects this request. */
900 if (st_code >= 300) {
901
902 if (st_text.slen == 2)
903 st_text = *pjsip_get_status_text(st_code);
904
905 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
906 st_code, &st_text, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000907 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000908 }
909 }
910
Benny Prijonod8179652008-01-23 20:39:07 +0000911 /*
912 * Get which account is most likely to be associated with this incoming
913 * call. We need the account to find which contact URI to put for
914 * the call.
915 */
916 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000917 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000918
Benny Prijonodb844a42008-02-02 17:07:18 +0000919 /* Get call's secure level */
920 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
921 call->secure_level = 2;
922 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
923 call->secure_level = 1;
924 else
925 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000926
Benny Prijonod8179652008-01-23 20:39:07 +0000927 /* Parse SDP from incoming request */
928 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000929 pjsip_rdata_sdp_info *sdp_info;
930
931 sdp_info = pjsip_rdata_get_sdp_info(rdata);
932 offer = sdp_info->sdp;
933
934 status = sdp_info->sdp_err;
935 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
936 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000937
Benny Prijonod8179652008-01-23 20:39:07 +0000938 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000939 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000940 pjsip_hdr hdr_list;
941 pjsip_warning_hdr *w;
942
943 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000944 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000945
946 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
947 pjsip_endpt_name(pjsua_var.endpt),
948 status);
949 pj_list_init(&hdr_list);
950 pj_list_push_back(&hdr_list, w);
951
Benny Prijono224b4e22008-06-19 14:10:28 +0000952 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000953 &reason, &hdr_list, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000954 goto on_return;
Benny Prijonod8179652008-01-23 20:39:07 +0000955 }
Benny Prijono617b8602008-04-07 10:10:31 +0000956
957 /* Do quick checks on SDP before passing it to transports. More elabore
958 * checks will be done in pjsip_inv_verify_request2() below.
959 */
960 if (offer->media_count==0) {
961 const pj_str_t reason = pj_str("Missing media in SDP");
962 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
963 NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000964 goto on_return;
Benny Prijono617b8602008-04-07 10:10:31 +0000965 }
966
Benny Prijonod8179652008-01-23 20:39:07 +0000967 } else {
968 offer = NULL;
969 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000970
971 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000972 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000973 options |= PJSIP_INV_SUPPORT_TIMER;
Sauw Minge7dbbc82011-10-24 09:28:13 +0000974 if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY)
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000975 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000976 if (pjsua_var.media_cfg.enable_ice)
977 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000978 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
979 options |= PJSIP_INV_REQUIRE_TIMER;
980 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
981 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000982
Sauw Mingec765352011-10-03 02:04:36 +0000983 status = pjsip_inv_verify_request2(rdata, &options, offer, NULL, NULL,
Benny Prijonod8179652008-01-23 20:39:07 +0000984 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000985 if (status != PJ_SUCCESS) {
986
987 /*
988 * No we can't handle the incoming INVITE request.
989 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000990 if (response) {
991 pjsip_response_addr res_addr;
992
993 pjsip_get_response_addr(response->pool, rdata, &res_addr);
994 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
995 NULL, NULL);
996
997 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000998 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000999 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
1000 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001001 }
1002
Benny Prijonob90fd382011-09-18 14:59:56 +00001003 goto on_return;
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001004 }
1005
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001006 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001007 if (pjsua_var.acc[acc_id].contact.slen) {
1008 contact = pjsua_var.acc[acc_id].contact;
1009 } else {
1010 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
1011 acc_id, rdata);
1012 if (status != PJ_SUCCESS) {
1013 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1014 status);
1015 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
1016 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001017 goto on_return;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001018 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001019 }
1020
Benny Prijono26ff9062006-02-21 23:47:00 +00001021 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +00001022 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001023 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +00001024 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +00001026 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001027 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001028 }
1029
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001030 /* Set credentials */
1031 if (pjsua_var.acc[acc_id].cred_cnt) {
1032 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
1033 pjsua_var.acc[acc_id].cred_cnt,
1034 pjsua_var.acc[acc_id].cred);
1035 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001036
Benny Prijono48ab2b72007-11-08 09:24:30 +00001037 /* Set preference */
1038 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
1039 &pjsua_var.acc[acc_id].cfg.auth_pref);
1040
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +00001041 /* Disable Session Timers if not prefered and the incoming INVITE request
1042 * did not require it.
1043 */
1044 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
1045 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
1046 {
1047 options &= ~(PJSIP_INV_SUPPORT_TIMER);
1048 }
1049
Sauw Minge7dbbc82011-10-24 09:28:13 +00001050 /* If 100rel is optional and UAC supports it, use it. */
1051 if ((options & PJSIP_INV_REQUIRE_100REL)==0 &&
1052 pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_OPTIONAL)
1053 {
1054 const pj_str_t token = { "100rel", 6};
1055 pjsip_dialog_cap_status cap_status;
1056
1057 cap_status = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_SUPPORTED, NULL,
1058 &token);
1059 if (cap_status == PJSIP_DIALOG_CAP_SUPPORTED)
1060 options |= PJSIP_INV_REQUIRE_100REL;
1061 }
1062
Benny Prijono26ff9062006-02-21 23:47:00 +00001063 /* Create invite session: */
Sauw Mingec765352011-10-03 02:04:36 +00001064 status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +00001065 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +00001066 pjsip_hdr hdr_list;
1067 pjsip_warning_hdr *w;
1068
1069 w = pjsip_warning_hdr_create_from_status(dlg->pool,
1070 pjsip_endpt_name(pjsua_var.endpt),
1071 status);
1072 pj_list_init(&hdr_list);
1073 pj_list_push_back(&hdr_list, w);
1074
1075 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
1076
1077 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +00001078 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001079 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001080 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001081 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001082 }
1083
Sauw Ming903154f2011-10-03 08:22:48 +00001084 /* If account is locked to specific transport, then lock dialog
1085 * to this transport too.
1086 */
1087 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1088 pjsip_tpselector tp_sel;
1089
1090 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1091 pjsip_dlg_set_transport(dlg, &tp_sel);
1092 }
1093
Sauw Mingec765352011-10-03 02:04:36 +00001094 /* Create and attach pjsua_var data to the dialog: */
1095 call->inv = inv;
1096 dlg->mod_data[pjsua_var.mod.id] = call;
1097 inv->mod_data[pjsua_var.mod.id] = call;
1098
1099 /* Store variables required for the callback after the async
1100 * media transport creation is completed.
1101 */
1102 call->async_call.dlg = dlg;
1103 pj_list_init(&call->async_call.call_var.inc_call.answers);
1104
1105 /* Init media channel */
1106 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
1107 call->secure_level,
1108 rdata->tp_info.pool,
1109 offer,
1110 &sip_err_code, PJ_TRUE,
Sauw Mingec765352011-10-03 02:04:36 +00001111 &on_incoming_call_med_tp_complete);
1112 if (status == PJ_SUCCESS) {
1113 status = on_incoming_call_med_tp_complete(call_id, NULL);
1114 if (status != PJ_SUCCESS) {
1115 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1116 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1117 goto on_return;
1118 }
1119 } else if (status != PJ_EPENDING) {
1120 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1121 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1122 goto on_return;
1123 }
1124
1125 /* Create answer */
1126/*
1127 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
1128 offer, &answer, &sip_err_code);
1129 if (status != PJ_SUCCESS) {
1130 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
1131 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
1132 sip_err_code, NULL, NULL, NULL, NULL);
1133 goto on_return;
1134 }
1135*/
1136
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001137 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +00001138 status = pjsip_timer_init_session(inv,
1139 &pjsua_var.acc[acc_id].cfg.timer_setting);
1140 if (status != PJ_SUCCESS) {
1141 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
1142 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
1143 NULL, &response);
1144 if (status == PJ_SUCCESS && response)
1145 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001146
Nanang Izzuddin65add622009-08-11 16:26:20 +00001147 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001148
Benny Prijonob90fd382011-09-18 14:59:56 +00001149 goto on_return;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001150 }
1151
Benny Prijonoea9fd392007-11-06 03:41:40 +00001152 /* Update NAT type of remote endpoint, only when there is SDP in
1153 * incoming INVITE!
1154 */
Sauw Mingec765352011-10-03 02:04:36 +00001155 if (pjsua_var.ua_cfg.nat_type_in_sdp && inv->neg &&
Benny Prijonoea9fd392007-11-06 03:41:40 +00001156 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
1157 {
Benny Prijono91a6a172007-10-31 08:59:29 +00001158 const pjmedia_sdp_session *remote_sdp;
1159
1160 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
1161 update_remote_nat_type(call, remote_sdp);
1162 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001163
Benny Prijono2285e7e2008-12-17 14:28:18 +00001164 /* Must answer with some response to initial INVITE. We'll do this before
1165 * attaching the call to the invite session/dialog, so that the application
1166 * will not get notification about this event (on another scenario, it is
1167 * also possible that inv_send_msg() fails and causes the invite session to
1168 * be disconnected. If we have the call attached at this time, this will
1169 * cause the disconnection callback to be called before on_incoming_call()
1170 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +00001171 */
Benny Prijono64f851e2006-02-23 13:49:28 +00001172 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001173 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +00001174 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001175 if (response == NULL) {
1176 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1177 status);
1178 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1179 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1180 } else {
1181 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001182 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001183 PJ_FALSE);
1184 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001185 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001186 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001187
1188 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001189 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001190 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001191 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001192 goto on_return;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001193 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001194 }
1195
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001196 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001197
Benny Prijono053f5222006-11-11 16:16:04 +00001198 /* Check if this request should replace existing call */
1199 if (replaced_dlg) {
1200 pjsip_inv_session *replaced_inv;
1201 struct pjsua_call *replaced_call;
1202 pjsip_tx_data *tdata;
1203
1204 /* Get the invite session in the dialog */
1205 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1206
1207 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001208 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001209
1210 /* Notify application */
1211 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1212 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1213 call_id);
1214
1215 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1216 call_id));
1217
1218 /* Answer the new call with 200 response */
1219 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1220 if (status == PJ_SUCCESS)
1221 status = pjsip_inv_send_msg(inv, tdata);
1222
1223 if (status != PJ_SUCCESS)
1224 pjsua_perror(THIS_FILE, "Error answering session", status);
1225
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001226 /* Note that inv may be invalid if 200/OK has caused error in
1227 * starting the media.
1228 */
Benny Prijono053f5222006-11-11 16:16:04 +00001229
1230 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1231 replaced_call->index));
1232
1233 /* Disconnect replaced invite session */
1234 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1235 &tdata);
1236 if (status == PJ_SUCCESS && tdata)
1237 status = pjsip_inv_send_msg(replaced_inv, tdata);
1238
1239 if (status != PJ_SUCCESS)
1240 pjsua_perror(THIS_FILE, "Error terminating session", status);
1241
1242
1243 } else {
1244
Benny Prijonob5388cf2007-01-04 22:45:08 +00001245 /* Notify application if on_incoming_call() is overriden,
1246 * otherwise hangup the call with 480
1247 */
1248 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001249 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001250 } else {
1251 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1252 NULL, NULL);
1253 }
Benny Prijono053f5222006-11-11 16:16:04 +00001254 }
1255
Benny Prijono8b1889b2006-06-06 18:40:40 +00001256
Benny Prijono26ff9062006-02-21 23:47:00 +00001257 /* This INVITE request has been handled. */
Benny Prijonob90fd382011-09-18 14:59:56 +00001258on_return:
1259 pj_log_pop_indent();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001260 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001261 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001262}
1263
1264
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001265
1266/*
1267 * Check if the specified call has active INVITE session and the INVITE
1268 * session has not been disconnected.
1269 */
1270PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1271{
1272 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1273 PJ_EINVAL);
1274 return pjsua_var.calls[call_id].inv != NULL &&
1275 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1276}
1277
1278
1279/*
1280 * Check if call has an active media session.
1281 */
1282PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1283{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001284 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1286 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001287 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonocf986c42008-09-02 11:25:07 +00001288}
1289
1290
Benny Prijono148c9dd2006-09-19 13:37:53 +00001291/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001292pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001293 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001294 pjsua_call **p_call,
1295 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001296{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001297 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001298 pjsua_call *call = NULL;
1299 pj_bool_t has_pjsua_lock = PJ_FALSE;
1300 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001301 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001302
Sauw Ming844c1c92010-09-07 05:12:02 +00001303 pj_gettimeofday(&time_start);
Sauw Minge7dbbc82011-10-24 09:28:13 +00001304 timeout.sec = 0;
Sauw Ming844c1c92010-09-07 05:12:02 +00001305 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1306 pj_time_val_normalize(&timeout);
1307
1308 for (retry=0; ; ++retry) {
1309
1310 if (retry % 10 == 9) {
1311 pj_time_val dtime;
1312
1313 pj_gettimeofday(&dtime);
1314 PJ_TIME_VAL_SUB(dtime, time_start);
1315 if (!PJ_TIME_VAL_LT(dtime, timeout))
1316 break;
1317 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001318
1319 has_pjsua_lock = PJ_FALSE;
1320
1321 status = PJSUA_TRY_LOCK();
1322 if (status != PJ_SUCCESS) {
1323 pj_thread_sleep(retry/10);
1324 continue;
1325 }
1326
1327 has_pjsua_lock = PJ_TRUE;
1328 call = &pjsua_var.calls[call_id];
1329
1330 if (call->inv == NULL) {
1331 PJSUA_UNLOCK();
1332 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1333 return PJSIP_ESESSIONTERMINATED;
1334 }
1335
1336 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1337 if (status != PJ_SUCCESS) {
1338 PJSUA_UNLOCK();
1339 pj_thread_sleep(retry/10);
1340 continue;
1341 }
1342
1343 PJSUA_UNLOCK();
1344
1345 break;
1346 }
1347
1348 if (status != PJ_SUCCESS) {
1349 if (has_pjsua_lock == PJ_FALSE)
1350 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1351 "(possibly system has deadlocked) in %s",
1352 title));
1353 else
1354 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1355 "(possibly system has deadlocked) in %s",
1356 title));
1357 return PJ_ETIMEDOUT;
1358 }
1359
1360 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001361 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001362
1363 return PJ_SUCCESS;
1364}
1365
1366
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367/*
1368 * Get the conference port identification associated with the call.
1369 */
1370PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1371{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001372 pjsua_call *call;
Sauw Minge7dbbc82011-10-24 09:28:13 +00001373 pjsua_conf_port_id port_id = PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001374
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1376 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001377
Sauw Minge7dbbc82011-10-24 09:28:13 +00001378 /* Use PJSUA_LOCK() instead of acquire_call():
1379 * https://trac.pjsip.org/repos/ticket/1371
1380 */
1381 PJSUA_LOCK();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001382
Sauw Minge7dbbc82011-10-24 09:28:13 +00001383 if (!pjsua_call_is_active(call_id))
1384 goto on_return;
1385
1386 call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001387 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001388
Sauw Minge7dbbc82011-10-24 09:28:13 +00001389on_return:
1390 PJSUA_UNLOCK();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001391
1392 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001393}
1394
1395
Benny Prijono148c9dd2006-09-19 13:37:53 +00001396
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001397/*
1398 * Obtain detail information about the specified call.
1399 */
1400PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1401 pjsua_call_info *info)
1402{
1403 pjsua_call *call;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001404 unsigned mi;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405
1406 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1407 PJ_EINVAL);
1408
Benny Prijonoac623b32006-07-03 15:19:31 +00001409 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410
Sauw Minge7dbbc82011-10-24 09:28:13 +00001411 /* Use PJSUA_LOCK() instead of acquire_call():
1412 * https://trac.pjsip.org/repos/ticket/1371
1413 */
1414 PJSUA_LOCK();
1415
1416 call = &pjsua_var.calls[call_id];
1417
1418 if (!call->inv) {
1419 PJSUA_UNLOCK();
1420 return PJSIP_ESESSIONTERMINATED;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001421 }
1422
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001423 /* id and role */
1424 info->id = call_id;
1425 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001426 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001427
1428 /* local info */
1429 info->local_info.ptr = info->buf_.local_info;
1430 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1431 sizeof(info->buf_.local_info));
1432
1433 /* local contact */
1434 info->local_contact.ptr = info->buf_.local_contact;
1435 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1436 call->inv->dlg->local.contact->uri,
1437 info->local_contact.ptr,
1438 sizeof(info->buf_.local_contact));
1439
1440 /* remote info */
1441 info->remote_info.ptr = info->buf_.remote_info;
1442 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1443 sizeof(info->buf_.remote_info));
1444
1445 /* remote contact */
1446 if (call->inv->dlg->remote.contact) {
1447 int len;
1448 info->remote_contact.ptr = info->buf_.remote_contact;
1449 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1450 call->inv->dlg->remote.contact->uri,
1451 info->remote_contact.ptr,
1452 sizeof(info->buf_.remote_contact));
1453 if (len < 0) len = 0;
1454 info->remote_contact.slen = len;
1455 } else {
1456 info->remote_contact.slen = 0;
1457 }
1458
1459 /* call id */
1460 info->call_id.ptr = info->buf_.call_id;
1461 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1462 sizeof(info->buf_.call_id));
1463
1464 /* state, state_text */
1465 info->state = call->inv->state;
1466 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1467
1468 /* If call is disconnected, set the last_status from the cause code */
1469 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1470 /* last_status, last_status_text */
1471 info->last_status = call->inv->cause;
1472
1473 info->last_status_text.ptr = info->buf_.last_status_text;
1474 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1475 sizeof(info->buf_.last_status_text));
1476 } else {
1477 /* last_status, last_status_text */
1478 info->last_status = call->last_code;
1479
1480 info->last_status_text.ptr = info->buf_.last_status_text;
1481 pj_strncpy(&info->last_status_text, &call->last_text,
1482 sizeof(info->buf_.last_status_text));
1483 }
1484
Benny Prijono0bc99a92011-03-17 04:34:43 +00001485 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001486 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001487 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001488 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001489 {
1490 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001491
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001492 info->media[info->media_cnt].index = mi;
1493 info->media[info->media_cnt].status = call_med->state;
1494 info->media[info->media_cnt].dir = call_med->dir;
1495 info->media[info->media_cnt].type = call_med->type;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001497 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001498 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001499 call_med->strm.a.conf_slot;
1500 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001501 pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV;
1502
1503 info->media[info->media_cnt].stream.vid.win_in =
1504 call_med->strm.v.rdr_win_id;
1505
1506 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddind93c68a2011-07-19 08:40:20 +00001507 cap_dev = call_med->strm.v.cap_dev;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001508 }
1509 info->media[info->media_cnt].stream.vid.cap_dev = cap_dev;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001510 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001511 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001512 }
1513 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001514 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001515
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001516 if (call->audio_idx != -1) {
1517 info->media_status = call->media[call->audio_idx].state;
1518 info->media_dir = call->media[call->audio_idx].dir;
1519 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001520 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001521
1522 /* calculate duration */
1523 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1524
1525 info->total_duration = call->dis_time;
1526 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1527
1528 if (call->conn_time.sec) {
1529 info->connect_duration = call->dis_time;
1530 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1531 }
1532
1533 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1534
1535 pj_gettimeofday(&info->total_duration);
1536 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1537
1538 pj_gettimeofday(&info->connect_duration);
1539 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1540
1541 } else {
1542 pj_gettimeofday(&info->total_duration);
1543 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1544 }
1545
Sauw Minge7dbbc82011-10-24 09:28:13 +00001546 PJSUA_UNLOCK();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001547
1548 return PJ_SUCCESS;
1549}
1550
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001551/*
1552 * Check if call remote peer support the specified capability.
1553 */
1554PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1555 pjsua_call_id call_id,
1556 int htype,
1557 const pj_str_t *hname,
1558 const pj_str_t *token)
1559{
1560 pjsua_call *call;
1561 pjsip_dialog *dlg;
1562 pj_status_t status;
1563 pjsip_dialog_cap_status cap_status;
1564
1565 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1566 if (status != PJ_SUCCESS)
1567 return PJSIP_DIALOG_CAP_UNKNOWN;
1568
1569 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1570
1571 pjsip_dlg_dec_lock(dlg);
1572
1573 return cap_status;
1574}
1575
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001576
1577/*
1578 * Attach application specific data to the call.
1579 */
1580PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1581 void *user_data)
1582{
1583 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1584 PJ_EINVAL);
1585 pjsua_var.calls[call_id].user_data = user_data;
1586
1587 return PJ_SUCCESS;
1588}
1589
1590
1591/*
1592 * Get user data attached to the call.
1593 */
1594PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1595{
1596 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1597 NULL);
1598 return pjsua_var.calls[call_id].user_data;
1599}
1600
1601
1602/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001603 * Get remote's NAT type.
1604 */
1605PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1606 pj_stun_nat_type *p_type)
1607{
1608 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1609 PJ_EINVAL);
1610 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1611
1612 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1613 return PJ_SUCCESS;
1614}
1615
1616
1617/*
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001618 * Get media stream info for the specified media index.
1619 */
1620PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
1621 unsigned med_idx,
1622 pjsua_stream_info *psi)
1623{
1624 pjsua_call *call;
1625 pjsua_call_media *call_med;
1626 pj_status_t status;
1627
1628 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1629 PJ_EINVAL);
1630 PJ_ASSERT_RETURN(psi, PJ_EINVAL);
1631
1632 PJSUA_LOCK();
1633
1634 call = &pjsua_var.calls[call_id];
1635
1636 if (med_idx >= call->med_cnt) {
1637 PJSUA_UNLOCK();
1638 return PJ_EINVAL;
1639 }
1640
1641 call_med = &call->media[med_idx];
1642 psi->type = call_med->type;
1643 switch (call_med->type) {
1644 case PJMEDIA_TYPE_AUDIO:
1645 status = pjmedia_stream_get_info(call_med->strm.a.stream,
1646 &psi->info.aud);
1647 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001648#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001649 case PJMEDIA_TYPE_VIDEO:
1650 status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
1651 &psi->info.vid);
1652 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001653#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001654 default:
1655 status = PJMEDIA_EINVALIMEDIATYPE;
1656 break;
1657 }
1658
1659 PJSUA_UNLOCK();
1660 return status;
1661}
1662
1663
1664/*
1665 * Get media stream statistic for the specified media index.
1666 */
1667PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
1668 unsigned med_idx,
1669 pjsua_stream_stat *stat)
1670{
1671 pjsua_call *call;
1672 pjsua_call_media *call_med;
1673 pj_status_t status;
1674
1675 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1676 PJ_EINVAL);
1677 PJ_ASSERT_RETURN(stat, PJ_EINVAL);
1678
1679 PJSUA_LOCK();
1680
1681 call = &pjsua_var.calls[call_id];
1682
1683 if (med_idx >= call->med_cnt) {
1684 PJSUA_UNLOCK();
1685 return PJ_EINVAL;
1686 }
1687
1688 call_med = &call->media[med_idx];
1689 switch (call_med->type) {
1690 case PJMEDIA_TYPE_AUDIO:
1691 status = pjmedia_stream_get_stat(call_med->strm.a.stream,
1692 &stat->rtcp);
1693 if (status == PJ_SUCCESS)
1694 status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
1695 &stat->jbuf);
1696 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001697#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001698 case PJMEDIA_TYPE_VIDEO:
1699 status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
1700 &stat->rtcp);
1701 if (status == PJ_SUCCESS)
1702 status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
1703 &stat->jbuf);
1704 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001705#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001706 default:
1707 status = PJMEDIA_EINVALIMEDIATYPE;
1708 break;
1709 }
1710
1711 PJSUA_UNLOCK();
1712 return status;
1713}
1714
1715
1716/*
1717 * Get media transport info for the specified media index.
1718 */
Benny Prijonoe212bc12011-08-15 09:38:42 +00001719PJ_DEF(pj_status_t)
1720pjsua_call_get_med_transport_info(pjsua_call_id call_id,
1721 unsigned med_idx,
1722 pjmedia_transport_info *t)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001723{
1724 pjsua_call *call;
1725 pjsua_call_media *call_med;
1726 pj_status_t status;
1727
1728 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1729 PJ_EINVAL);
1730 PJ_ASSERT_RETURN(t, PJ_EINVAL);
1731
1732 PJSUA_LOCK();
1733
1734 call = &pjsua_var.calls[call_id];
1735
1736 if (med_idx >= call->med_cnt) {
1737 PJSUA_UNLOCK();
1738 return PJ_EINVAL;
1739 }
1740
1741 call_med = &call->media[med_idx];
1742
1743 pjmedia_transport_info_init(t);
1744 status = pjmedia_transport_get_info(call_med->tp, t);
1745
1746 PJSUA_UNLOCK();
1747 return status;
1748}
1749
1750
1751/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752 * Send response to incoming INVITE request.
1753 */
1754PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1755 unsigned code,
1756 const pj_str_t *reason,
1757 const pjsua_msg_data *msg_data)
1758{
1759 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001760 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761 pjsip_tx_data *tdata;
1762 pj_status_t status;
1763
1764 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1765 PJ_EINVAL);
1766
Benny Prijonob90fd382011-09-18 14:59:56 +00001767 PJ_LOG(4,(THIS_FILE, "Answering call %d: code=%d", call_id, code));
1768 pj_log_push_indent();
1769
Benny Prijonodc752ca2006-09-22 16:55:42 +00001770 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001771 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001772 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001773
Sauw Mingec765352011-10-03 02:04:36 +00001774 PJSUA_LOCK();
1775 /* If media transport creation is not yet completed, we will answer
1776 * the call in the media transport creation callback instead.
1777 */
1778 if (call->med_ch_cb) {
1779 struct call_answer *answer;
1780
1781 PJ_LOG(4,(THIS_FILE, "Pending answering call %d upon completion "
1782 "of media transport", call_id));
1783
1784 answer = PJ_POOL_ZALLOC_T(call->inv->pool_prov, struct call_answer);
1785 answer->code = code;
1786 if (reason) {
1787 pj_strdup(call->inv->pool_prov, answer->reason, reason);
1788 }
1789 if (msg_data) {
1790 answer->msg_data = pjsua_msg_data_clone(call->inv->pool_prov,
1791 msg_data);
1792 }
1793 pj_list_push_back(&call->async_call.call_var.inc_call.answers,
1794 answer);
1795
1796 PJSUA_UNLOCK();
1797 if (dlg) pjsip_dlg_dec_lock(dlg);
1798 pj_log_pop_indent();
1799 return status;
1800 }
1801 PJSUA_UNLOCK();
1802
Benny Prijono2e507c22006-06-23 15:04:11 +00001803 if (call->res_time.sec == 0)
1804 pj_gettimeofday(&call->res_time);
1805
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001806 if (reason && reason->slen == 0)
1807 reason = NULL;
1808
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001809 /* Create response message */
1810 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1811 if (status != PJ_SUCCESS) {
1812 pjsua_perror(THIS_FILE, "Error creating response",
1813 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001814 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001815 }
1816
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001817 /* Call might have been disconnected if application is answering with
1818 * 200/OK and the media failed to start.
1819 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001820 if (call->inv == NULL)
1821 goto on_return;
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001822
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001823 /* Add additional headers etc */
1824 pjsua_process_msg_data( tdata, msg_data);
1825
1826 /* Send the message */
1827 status = pjsip_inv_send_msg(call->inv, tdata);
1828 if (status != PJ_SUCCESS)
1829 pjsua_perror(THIS_FILE, "Error sending response",
1830 status);
1831
Benny Prijonob90fd382011-09-18 14:59:56 +00001832on_return:
1833 if (dlg) pjsip_dlg_dec_lock(dlg);
1834 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835 return status;
1836}
1837
1838
1839/*
1840 * Hangup call by using method that is appropriate according to the
1841 * call state.
1842 */
1843PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1844 unsigned code,
1845 const pj_str_t *reason,
1846 const pjsua_msg_data *msg_data)
1847{
1848 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001849 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 pj_status_t status;
1851 pjsip_tx_data *tdata;
1852
1853
Benny Prijono148c9dd2006-09-19 13:37:53 +00001854 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1855 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1856 call_id));
1857 }
1858
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001859 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1860 PJ_EINVAL);
1861
Benny Prijonob90fd382011-09-18 14:59:56 +00001862 PJ_LOG(4,(THIS_FILE, "Call %d hanging up: code=%d..", call_id, code));
1863 pj_log_push_indent();
1864
Benny Prijonodc752ca2006-09-22 16:55:42 +00001865 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001866 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001867 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001868
1869 if (code==0) {
1870 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1871 code = PJSIP_SC_OK;
1872 else if (call->inv->role == PJSIP_ROLE_UAS)
1873 code = PJSIP_SC_DECLINE;
1874 else
1875 code = PJSIP_SC_REQUEST_TERMINATED;
1876 }
1877
1878 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1879 if (status != PJ_SUCCESS) {
1880 pjsua_perror(THIS_FILE,
1881 "Failed to create end session message",
1882 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001883 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001884 }
1885
1886 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1887 * as p_tdata when INVITE transaction has not been answered
1888 * with any provisional responses.
1889 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001890 if (tdata == NULL)
1891 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001892
1893 /* Add additional headers etc */
1894 pjsua_process_msg_data( tdata, msg_data);
1895
1896 /* Send the message */
1897 status = pjsip_inv_send_msg(call->inv, tdata);
1898 if (status != PJ_SUCCESS) {
1899 pjsua_perror(THIS_FILE,
1900 "Failed to send end session message",
1901 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001902 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001903 }
1904
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001905 /* Stop lock codec timer, if it is active */
1906 if (call->lock_codec.reinv_timer.id) {
1907 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1908 &call->lock_codec.reinv_timer);
1909 call->lock_codec.reinv_timer.id = PJ_FALSE;
1910 }
1911
Benny Prijonob90fd382011-09-18 14:59:56 +00001912on_return:
1913 if (dlg) pjsip_dlg_dec_lock(dlg);
1914 pj_log_pop_indent();
1915 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001916}
1917
1918
1919/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001920 * Accept or reject redirection.
1921 */
1922PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1923 pjsip_redirect_op cmd)
1924{
1925 pjsua_call *call;
1926 pjsip_dialog *dlg;
1927 pj_status_t status;
1928
1929 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1930 PJ_EINVAL);
1931
1932 status = acquire_call("pjsua_call_process_redirect()", call_id,
1933 &call, &dlg);
1934 if (status != PJ_SUCCESS)
1935 return status;
1936
1937 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1938
1939 pjsip_dlg_dec_lock(dlg);
1940
1941 return status;
1942}
1943
1944
1945/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001946 * Put the specified call on hold.
1947 */
1948PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1949 const pjsua_msg_data *msg_data)
1950{
1951 pjmedia_sdp_session *sdp;
1952 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001953 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001954 pjsip_tx_data *tdata;
1955 pj_status_t status;
1956
1957 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1958 PJ_EINVAL);
1959
Benny Prijonob90fd382011-09-18 14:59:56 +00001960 PJ_LOG(4,(THIS_FILE, "Putting call %d on hold", call_id));
1961 pj_log_push_indent();
1962
Benny Prijonodc752ca2006-09-22 16:55:42 +00001963 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001964 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001965 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001966
1967 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1968 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00001969 status = PJSIP_ESESSIONSTATE;
1970 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971 }
1972
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001973 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonob90fd382011-09-18 14:59:56 +00001974 if (status != PJ_SUCCESS)
1975 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001976
1977 /* Create re-INVITE with new offer */
1978 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1979 if (status != PJ_SUCCESS) {
1980 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001981 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001982 }
1983
1984 /* Add additional headers etc */
1985 pjsua_process_msg_data( tdata, msg_data);
1986
Sauw Minge7dbbc82011-10-24 09:28:13 +00001987 /* Record the tx_data to keep track the operation */
1988 call->hold_msg = (void*) tdata;
1989
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001990 /* Send the request */
1991 status = pjsip_inv_send_msg( call->inv, tdata);
1992 if (status != PJ_SUCCESS) {
1993 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Sauw Minge7dbbc82011-10-24 09:28:13 +00001994 call->hold_msg = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00001995 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001996 }
1997
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001998 /* Set flag that local put the call on hold */
1999 call->local_hold = PJ_TRUE;
2000
Benny Prijonob90fd382011-09-18 14:59:56 +00002001on_return:
2002 if (dlg) pjsip_dlg_dec_lock(dlg);
2003 pj_log_pop_indent();
2004 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002005}
2006
2007
2008/*
2009 * Send re-INVITE (to release hold).
2010 */
2011PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
Benny Prijonodec3a372011-03-16 03:52:20 +00002012 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002013 const pjsua_msg_data *msg_data)
2014{
2015 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00002016 pj_str_t *new_contact = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002017 pjsip_tx_data *tdata;
2018 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002019 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002020 pj_status_t status;
2021
2022
2023 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2024 PJ_EINVAL);
2025
Benny Prijonob90fd382011-09-18 14:59:56 +00002026 PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
2027 pj_log_push_indent();
2028
Benny Prijonodc752ca2006-09-22 16:55:42 +00002029 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002030 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002031 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002032
2033 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
2034 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002035 status = PJSIP_ESESSIONSTATE;
2036 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037 }
2038
2039 /* Create SDP */
Benny Prijonodec3a372011-03-16 03:52:20 +00002040 if (call->local_hold && (options & PJSUA_CALL_UNHOLD)==0) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002041 status = create_sdp_of_call_hold(call, &sdp);
2042 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00002043 status = pjsua_media_channel_create_sdp(call->index,
2044 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002045 NULL, &sdp, NULL);
2046 call->local_hold = PJ_FALSE;
2047 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048 if (status != PJ_SUCCESS) {
2049 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2050 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002051 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002052 }
2053
Benny Prijonodec3a372011-03-16 03:52:20 +00002054 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
2055 pjsua_acc_is_valid(call->acc_id))
2056 {
2057 new_contact = &pjsua_var.acc[call->acc_id].contact;
2058 }
2059
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002060 /* Create re-INVITE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002061 status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002062 if (status != PJ_SUCCESS) {
2063 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002064 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 }
2066
2067 /* Add additional headers etc */
2068 pjsua_process_msg_data( tdata, msg_data);
2069
2070 /* Send the request */
2071 status = pjsip_inv_send_msg( call->inv, tdata);
2072 if (status != PJ_SUCCESS) {
2073 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002074 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002075 }
2076
Benny Prijonob90fd382011-09-18 14:59:56 +00002077on_return:
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00002078 if (dlg) pjsip_dlg_dec_lock(dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002079 pj_log_pop_indent();
2080 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002081}
2082
2083
2084/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002085 * Send UPDATE request.
2086 */
2087PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
2088 unsigned options,
2089 const pjsua_msg_data *msg_data)
2090{
2091 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00002092 pj_str_t *new_contact = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002093 pjsip_tx_data *tdata;
2094 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002095 pjsip_dialog *dlg = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002096 pj_status_t status;
2097
2098 PJ_UNUSED_ARG(options);
2099
2100 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2101 PJ_EINVAL);
2102
Benny Prijonob90fd382011-09-18 14:59:56 +00002103 PJ_LOG(4,(THIS_FILE, "Sending UPDATE on call %d", call_id));
2104 pj_log_push_indent();
2105
Benny Prijonoc08682e2007-10-04 06:17:58 +00002106 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
2107 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002108 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002109
Benny Prijonoc08682e2007-10-04 06:17:58 +00002110 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00002111 status = pjsua_media_channel_create_sdp(call->index,
2112 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002113 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002114 if (status != PJ_SUCCESS) {
2115 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2116 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002117 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002118 }
2119
Benny Prijonodec3a372011-03-16 03:52:20 +00002120 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
2121 pjsua_acc_is_valid(call->acc_id))
2122 {
2123 new_contact = &pjsua_var.acc[call->acc_id].contact;
2124 }
2125
Benny Prijono224b4e22008-06-19 14:10:28 +00002126 /* Create UPDATE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002127 status = pjsip_inv_update(call->inv, new_contact, sdp, &tdata);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002128 if (status != PJ_SUCCESS) {
2129 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002130 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002131 }
2132
2133 /* Add additional headers etc */
2134 pjsua_process_msg_data( tdata, msg_data);
2135
2136 /* Send the request */
2137 status = pjsip_inv_send_msg( call->inv, tdata);
2138 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002139 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002140 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002141 }
2142
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002143 call->local_hold = PJ_FALSE;
2144
Benny Prijonob90fd382011-09-18 14:59:56 +00002145on_return:
2146 if (dlg) pjsip_dlg_dec_lock(dlg);
2147 pj_log_pop_indent();
2148 return status;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002149}
2150
2151
2152/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002153 * Initiate call transfer to the specified address.
2154 */
2155PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
2156 const pj_str_t *dest,
2157 const pjsua_msg_data *msg_data)
2158{
2159 pjsip_evsub *sub;
2160 pjsip_tx_data *tdata;
2161 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002162 pjsip_dialog *dlg = NULL;
Benny Prijono053f5222006-11-11 16:16:04 +00002163 pjsip_generic_string_hdr *gs_hdr;
2164 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00002165 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002166 pj_status_t status;
2167
2168
Benny Prijonob90fd382011-09-18 14:59:56 +00002169 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls &&
2170 dest, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171
Benny Prijonob90fd382011-09-18 14:59:56 +00002172 PJ_LOG(4,(THIS_FILE, "Transfering call %d to %.*s", call_id,
2173 (int)dest->slen, dest->ptr));
2174 pj_log_push_indent();
2175
Benny Prijonodc752ca2006-09-22 16:55:42 +00002176 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002177 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002178 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002179
Benny Prijonod524e822006-09-22 12:48:18 +00002180 /* Create xfer client subscription. */
2181 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002182 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00002183
2184 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 if (status != PJ_SUCCESS) {
2186 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002187 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002188 }
2189
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002190 /* Associate this call with the client subscription */
2191 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
2192
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002193 /*
2194 * Create REFER request.
2195 */
2196 status = pjsip_xfer_initiate(sub, dest, &tdata);
2197 if (status != PJ_SUCCESS) {
2198 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002199 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002200 }
2201
Benny Prijono053f5222006-11-11 16:16:04 +00002202 /* Add Referred-By header */
2203 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
2204 &dlg->local.info_str);
2205 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
2206
2207
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002208 /* Add additional headers etc */
2209 pjsua_process_msg_data( tdata, msg_data);
2210
2211 /* Send. */
2212 status = pjsip_xfer_send_request(sub, tdata);
2213 if (status != PJ_SUCCESS) {
2214 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002215 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 }
2217
2218 /* For simplicity (that's what this program is intended to be!),
2219 * leave the original invite session as it is. More advanced application
2220 * may want to hold the INVITE, or terminate the invite, or whatever.
2221 */
Benny Prijonob90fd382011-09-18 14:59:56 +00002222on_return:
2223 if (dlg) pjsip_dlg_dec_lock(dlg);
2224 pj_log_pop_indent();
2225 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226
2227}
2228
2229
2230/*
Benny Prijono053f5222006-11-11 16:16:04 +00002231 * Initiate attended call transfer to the specified address.
2232 */
2233PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
2234 pjsua_call_id dest_call_id,
2235 unsigned options,
2236 const pjsua_msg_data *msg_data)
2237{
2238 pjsua_call *dest_call;
2239 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00002240 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00002241 pj_str_t str_dest;
2242 int len;
2243 pjsip_uri *uri;
2244 pj_status_t status;
2245
2246
2247 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2248 PJ_EINVAL);
2249 PJ_ASSERT_RETURN(dest_call_id>=0 &&
2250 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
2251 PJ_EINVAL);
2252
Benny Prijonob90fd382011-09-18 14:59:56 +00002253 PJ_LOG(4,(THIS_FILE, "Transfering call %d replacing with call %d",
2254 call_id, dest_call_id));
2255 pj_log_push_indent();
2256
Benny Prijono053f5222006-11-11 16:16:04 +00002257 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
2258 &dest_call, &dest_dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002259 if (status != PJ_SUCCESS) {
2260 pj_log_pop_indent();
Benny Prijono053f5222006-11-11 16:16:04 +00002261 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00002262 }
Benny Prijono053f5222006-11-11 16:16:04 +00002263
2264 /*
2265 * Create REFER destination URI with Replaces field.
2266 */
2267
2268 /* Make sure we have sufficient buffer's length */
Benny Prijonob90fd382011-09-18 14:59:56 +00002269 PJ_ASSERT_ON_FAIL(dest_dlg->remote.info_str.slen +
Benny Prijono053f5222006-11-11 16:16:04 +00002270 dest_dlg->call_id->id.slen +
2271 dest_dlg->remote.info->tag.slen +
2272 dest_dlg->local.info->tag.slen + 32
Benny Prijonob90fd382011-09-18 14:59:56 +00002273 < (long)sizeof(str_dest_buf),
2274 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002275
2276 /* Print URI */
2277 str_dest_buf[0] = '<';
2278 str_dest.slen = 1;
2279
Benny Prijonoa1e69682007-05-11 15:14:34 +00002280 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00002281 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
2282 str_dest_buf+1, sizeof(str_dest_buf)-1);
Benny Prijonob90fd382011-09-18 14:59:56 +00002283 if (len < 0) {
2284 status = PJSIP_EURITOOLONG;
2285 goto on_error;
2286 }
Benny Prijono053f5222006-11-11 16:16:04 +00002287
2288 str_dest.slen += len;
2289
2290
2291 /* Build the URI */
2292 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
2293 sizeof(str_dest_buf) - str_dest.slen,
2294 "?%s"
2295 "Replaces=%.*s"
2296 "%%3Bto-tag%%3D%.*s"
2297 "%%3Bfrom-tag%%3D%.*s>",
2298 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
2299 "" : "Require=replaces&"),
2300 (int)dest_dlg->call_id->id.slen,
2301 dest_dlg->call_id->id.ptr,
2302 (int)dest_dlg->remote.info->tag.slen,
2303 dest_dlg->remote.info->tag.ptr,
2304 (int)dest_dlg->local.info->tag.slen,
2305 dest_dlg->local.info->tag.ptr);
2306
Benny Prijonob90fd382011-09-18 14:59:56 +00002307 PJ_ASSERT_ON_FAIL(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
2308 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002309
2310 str_dest.ptr = str_dest_buf;
2311 str_dest.slen += len;
2312
2313 pjsip_dlg_dec_lock(dest_dlg);
2314
Benny Prijonob90fd382011-09-18 14:59:56 +00002315 status = pjsua_call_xfer(call_id, &str_dest, msg_data);
2316
2317 pj_log_pop_indent();
2318 return status;
2319
2320on_error:
2321 if (dest_dlg) pjsip_dlg_dec_lock(dest_dlg);
2322 pj_log_pop_indent();
2323 return status;
Benny Prijono053f5222006-11-11 16:16:04 +00002324}
2325
2326
2327/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002328 * Send DTMF digits to remote using RFC 2833 payload formats.
2329 */
2330PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
2331 const pj_str_t *digits)
2332{
2333 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002334 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002335 pj_status_t status;
2336
2337 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2338 PJ_EINVAL);
2339
Benny Prijonob90fd382011-09-18 14:59:56 +00002340 PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s",
2341 call_id, (int)digits->slen, digits->ptr));
2342 pj_log_push_indent();
2343
Benny Prijonodc752ca2006-09-22 16:55:42 +00002344 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002345 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002346 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002347
Benny Prijono0bc99a92011-03-17 04:34:43 +00002348 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002349 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002350 status = PJ_EINVALIDOP;
2351 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002352 }
2353
Benny Prijono0bc99a92011-03-17 04:34:43 +00002354 status = pjmedia_stream_dial_dtmf(
2355 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002356
Benny Prijonob90fd382011-09-18 14:59:56 +00002357on_return:
2358 if (dlg) pjsip_dlg_dec_lock(dlg);
2359 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002360 return status;
2361}
2362
2363
2364/**
2365 * Send instant messaging inside INVITE session.
2366 */
2367PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
2368 const pj_str_t *mime_type,
2369 const pj_str_t *content,
2370 const pjsua_msg_data *msg_data,
2371 void *user_data)
2372{
2373 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002374 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002375 const pj_str_t mime_text_plain = pj_str("text/plain");
2376 pjsip_media_type ctype;
2377 pjsua_im_data *im_data;
2378 pjsip_tx_data *tdata;
2379 pj_status_t status;
2380
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002381 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2382 PJ_EINVAL);
2383
Benny Prijonob90fd382011-09-18 14:59:56 +00002384 PJ_LOG(4,(THIS_FILE, "Call %d sending %d bytes MESSAGE..",
2385 call_id, (int)content->slen));
2386 pj_log_push_indent();
2387
Benny Prijonodc752ca2006-09-22 16:55:42 +00002388 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002389 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002390 goto on_return;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002392 /* Set default media type if none is specified */
2393 if (mime_type == NULL) {
2394 mime_type = &mime_text_plain;
2395 }
2396
2397 /* Create request message. */
2398 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2399 -1, &tdata);
2400 if (status != PJ_SUCCESS) {
2401 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2402 goto on_return;
2403 }
2404
2405 /* Add accept header. */
2406 pjsip_msg_add_hdr( tdata->msg,
2407 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2408
2409 /* Parse MIME type */
2410 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2411
2412 /* Create "text/plain" message body. */
2413 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2414 &ctype.subtype, content);
2415 if (tdata->msg->body == NULL) {
2416 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2417 pjsip_tx_data_dec_ref(tdata);
2418 goto on_return;
2419 }
2420
2421 /* Add additional headers etc */
2422 pjsua_process_msg_data( tdata, msg_data);
2423
2424 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002425 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002426 im_data->acc_id = call->acc_id;
2427 im_data->call_id = call_id;
2428 im_data->to = call->inv->dlg->remote.info_str;
2429 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2430 im_data->user_data = user_data;
2431
2432
2433 /* Send the request. */
2434 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2435 pjsua_var.mod.id, im_data);
2436 if (status != PJ_SUCCESS) {
2437 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2438 goto on_return;
2439 }
2440
2441on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002442 if (dlg) pjsip_dlg_dec_lock(dlg);
2443 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002444 return status;
2445}
2446
2447
2448/*
2449 * Send IM typing indication inside INVITE session.
2450 */
2451PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2452 pj_bool_t is_typing,
2453 const pjsua_msg_data*msg_data)
2454{
2455 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002456 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002457 pjsip_tx_data *tdata;
2458 pj_status_t status;
2459
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002460 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2461 PJ_EINVAL);
2462
Benny Prijonob90fd382011-09-18 14:59:56 +00002463 PJ_LOG(4,(THIS_FILE, "Call %d sending typing indication..",
2464 call_id));
2465 pj_log_push_indent();
2466
Benny Prijonodc752ca2006-09-22 16:55:42 +00002467 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002468 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002469 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002470
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002471 /* Create request message. */
2472 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2473 -1, &tdata);
2474 if (status != PJ_SUCCESS) {
2475 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2476 goto on_return;
2477 }
2478
2479 /* Create "application/im-iscomposing+xml" msg body. */
2480 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2481 NULL, NULL, -1);
2482
2483 /* Add additional headers etc */
2484 pjsua_process_msg_data( tdata, msg_data);
2485
2486 /* Send the request. */
2487 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2488 if (status != PJ_SUCCESS) {
2489 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2490 goto on_return;
2491 }
2492
2493on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002494 if (dlg) pjsip_dlg_dec_lock(dlg);
2495 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002496 return status;
2497}
2498
2499
2500/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002501 * Send arbitrary request.
2502 */
2503PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2504 const pj_str_t *method_str,
2505 const pjsua_msg_data *msg_data)
2506{
2507 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002508 pjsip_dialog *dlg = NULL;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002509 pjsip_method method;
2510 pjsip_tx_data *tdata;
2511 pj_status_t status;
2512
2513 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2514 PJ_EINVAL);
2515
Benny Prijonob90fd382011-09-18 14:59:56 +00002516 PJ_LOG(4,(THIS_FILE, "Call %d sending %.*s request..",
2517 call_id, (int)method_str->slen, method_str->ptr));
2518 pj_log_push_indent();
2519
Benny Prijonofeb69f42007-10-05 09:12:26 +00002520 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2521 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002522 goto on_return;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002523
2524 /* Init method */
2525 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2526
2527 /* Create request message. */
2528 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2529 if (status != PJ_SUCCESS) {
2530 pjsua_perror(THIS_FILE, "Unable to create request", status);
2531 goto on_return;
2532 }
2533
2534 /* Add additional headers etc */
2535 pjsua_process_msg_data( tdata, msg_data);
2536
2537 /* Send the request. */
2538 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2539 if (status != PJ_SUCCESS) {
2540 pjsua_perror(THIS_FILE, "Unable to send request", status);
2541 goto on_return;
2542 }
2543
2544on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002545 if (dlg) pjsip_dlg_dec_lock(dlg);
2546 pj_log_pop_indent();
Benny Prijonofeb69f42007-10-05 09:12:26 +00002547 return status;
2548}
2549
2550
2551/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002552 * Terminate all calls.
2553 */
2554PJ_DEF(void) pjsua_call_hangup_all(void)
2555{
2556 unsigned i;
2557
Benny Prijonob90fd382011-09-18 14:59:56 +00002558 PJ_LOG(4,(THIS_FILE, "Hangup all calls.."));
2559 pj_log_push_indent();
2560
Sauw Minge7dbbc82011-10-24 09:28:13 +00002561 // This may deadlock, see https://trac.pjsip.org/repos/ticket/1305
2562 //PJSUA_LOCK();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002563
2564 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2565 if (pjsua_var.calls[i].inv)
2566 pjsua_call_hangup(i, 0, NULL, NULL);
2567 }
2568
Sauw Minge7dbbc82011-10-24 09:28:13 +00002569 //PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002570 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002571}
2572
2573
Benny Prijono1e601552010-10-20 05:31:08 +00002574/* Proto */
2575static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002576
Benny Prijono1e601552010-10-20 05:31:08 +00002577/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002578static void reinv_timer_cb(pj_timer_heap_t *th,
2579 pj_timer_entry *entry)
2580{
2581 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2582 pjsip_dialog *dlg;
2583 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002584 pj_status_t status;
2585
2586 PJ_UNUSED_ARG(th);
2587
2588 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2589
2590 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2591 if (status != PJ_SUCCESS)
2592 return;
2593
Benny Prijono1e601552010-10-20 05:31:08 +00002594 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002595
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002596 pjsip_dlg_dec_lock(dlg);
2597}
2598
2599
2600/* Check if the specified format can be skipped in counting codecs */
2601static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2602 const pj_str_t *fmt)
2603{
Benny Prijono1e601552010-10-20 05:31:08 +00002604 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002605 unsigned pt;
2606
2607 pt = pj_strtoul(fmt);
2608
2609 /* Check for comfort noise */
2610 if (pt == PJMEDIA_RTP_PT_CN)
2611 return PJ_TRUE;
2612
2613 /* Dynamic PT, check the format name */
2614 if (pt >= 96) {
2615 pjmedia_sdp_attr *a;
2616 pjmedia_sdp_rtpmap rtpmap;
2617
2618 /* Get the format name */
2619 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2620 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2621 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002622 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002623 return PJ_TRUE;
2624 } else {
2625 /* Invalid SDP, should not reach here */
2626 pj_assert(!"SDP should have been validated!");
2627 return PJ_TRUE;
2628 }
2629 }
2630
2631 return PJ_FALSE;
2632}
2633
2634
Benny Prijono1e601552010-10-20 05:31:08 +00002635/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2636 * out of several codecs presented by callee in his answer.
2637 */
2638static pj_status_t perform_lock_codec(pjsua_call *call)
2639{
2640 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2641 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002642 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002643 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002644 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002645 pjsip_tx_data *tdata;
2646 pj_status_t status;
2647
2648 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2649 PJ_EINVALIDOP);
2650
2651 /* Verify if another SDP negotiation is in progress, e.g: session timer
2652 * or another re-INVITE.
2653 */
2654 if (call->inv==NULL || call->inv->neg==NULL ||
2655 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2656 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002657 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002658 }
2659
Benny Prijono02493272010-11-17 09:15:04 +00002660 /* Don't do this if call is disconnecting! */
2661 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2662 call->inv->cause >= 200)
2663 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002664 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002665 }
2666
Benny Prijono1e601552010-10-20 05:31:08 +00002667 /* Verify if another SDP negotiation has been completed by comparing
2668 * the SDP version.
2669 */
2670 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2671 if (status != PJ_SUCCESS)
2672 return status;
2673 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2674 return PJMEDIA_SDP_EINVER;
2675
2676 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2677
2678 /* Update the new offer so it contains only a codec. Note that formats
2679 * order in the offer should have been matched to the answer, so we can
2680 * just directly update the offer without looking-up the answer.
2681 */
2682 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002683
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002684 for (i = 0; i < call->med_cnt; ++i) {
2685 unsigned j = 0, codec_cnt = 0;
2686 const pjmedia_sdp_media *ref_m;
2687 pjmedia_sdp_media *m;
2688 pjsua_call_media *call_med = &call->media[i];
2689
2690 /* Verify if media is deactivated */
2691 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2692 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2693 call_med->dir == PJMEDIA_DIR_NONE)
2694 {
Benny Prijono1e601552010-10-20 05:31:08 +00002695 continue;
2696 }
2697
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002698 ref_m = local_sdp->media[i];
2699 m = new_sdp->media[i];
2700
2701 /* Verify that media must be active. */
2702 pj_assert(ref_m->desc.port);
2703
2704 while (j < m->desc.fmt_count) {
2705 pjmedia_sdp_attr *a;
2706 pj_str_t *fmt = &m->desc.fmt[j];
2707
2708 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2709 ++j;
2710 continue;
2711 }
2712
2713 /* Remove format */
2714 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2715 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2716 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2717 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2718 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2719 m->desc.fmt_count, j);
2720 --m->desc.fmt_count;
2721 }
2722
2723 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002724 }
2725
Nanang Izzuddinec919002010-11-25 09:27:06 +00002726 /* Last check if SDP trully needs to be updated. It is possible that OA
2727 * negotiations have completed and SDP has changed but we didn't
2728 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002729 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002730 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002731 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002732
2733 /* Send UPDATE or re-INVITE */
2734 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2735 PJSIP_H_ALLOW,
2736 NULL, &STR_UPDATE) ==
2737 PJSIP_DIALOG_CAP_SUPPORTED;
2738 if (rem_can_update) {
2739 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2740 } else {
2741 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2742 }
2743
2744 if (status==PJ_EINVALIDOP &&
2745 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2746 {
2747 /* Ups, let's reschedule again */
2748 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2749 pj_time_val_normalize(&delay);
2750 call->lock_codec.reinv_timer.id = PJ_TRUE;
2751 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2752 &call->lock_codec.reinv_timer, &delay);
2753 return status;
2754 } else if (status != PJ_SUCCESS) {
2755 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2756 status);
2757 return status;
2758 }
2759
2760 /* Send the UPDATE/re-INVITE request */
2761 status = pjsip_inv_send_msg(call->inv, tdata);
2762 if (status != PJ_SUCCESS) {
2763 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2764 status);
2765 return status;
2766 }
2767
2768 return status;
2769}
2770
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002771/* Check if remote answerer has given us more than one codecs. If so,
2772 * create another offer with one codec only to lock down the codec.
2773 */
2774static pj_status_t lock_codec(pjsua_call *call)
2775{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002776 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002777 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002778 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002779 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002780 unsigned i;
2781 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002782 pj_status_t status;
2783
Nanang Izzuddinec919002010-11-25 09:27:06 +00002784 /* Stop lock codec timer, if it is active */
2785 if (call->lock_codec.reinv_timer.id) {
2786 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2787 &call->lock_codec.reinv_timer);
2788 call->lock_codec.reinv_timer.id = PJ_FALSE;
2789 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002790
Nanang Izzuddinec919002010-11-25 09:27:06 +00002791 /* Skip this if we are the answerer */
2792 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2793 return PJ_SUCCESS;
2794 }
2795
Nanang Izzuddinec919002010-11-25 09:27:06 +00002796 /* Delay this when the SDP negotiation done in call state EARLY and
2797 * remote does not support UPDATE method.
2798 */
2799 if (inv->state == PJSIP_INV_STATE_EARLY &&
2800 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2801 PJSIP_DIALOG_CAP_SUPPORTED)
2802 {
2803 call->lock_codec.pending = PJ_TRUE;
2804 return PJ_SUCCESS;
2805 }
2806
2807 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002808 if (status != PJ_SUCCESS)
2809 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002810 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002811 if (status != PJ_SUCCESS)
2812 return status;
2813
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002814 /* Find multiple codecs answer in all media */
2815 for (i = 0; i < call->med_cnt; ++i) {
2816 pjsua_call_media *call_med = &call->media[i];
2817 const pjmedia_sdp_media *rem_m, *loc_m;
2818 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002819
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002820 /* Skip this if the media is inactive or error */
2821 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2822 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2823 call_med->dir == PJMEDIA_DIR_NONE)
2824 {
2825 continue;
2826 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002827
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002828 /* Remote may answer with less media lines. */
2829 if (i >= remote_sdp->media_count)
2830 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002831
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002832 rem_m = remote_sdp->media[i];
2833 loc_m = local_sdp->media[i];
2834
2835 /* Verify that media must be active. */
2836 pj_assert(loc_m->desc.port && rem_m->desc.port);
2837
2838 /* Count the formats in the answer. */
2839 if (rem_m->desc.fmt_count==1) {
2840 codec_cnt = 1;
2841 } else {
2842 unsigned j;
2843 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
2844 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
2845 ++codec_cnt;
2846 }
2847 }
2848
2849 if (codec_cnt > 1) {
2850 has_mult_fmt = PJ_TRUE;
2851 break;
2852 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002853 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002854
2855 /* Each media in the answer already contains single codec. */
2856 if (!has_mult_fmt) {
2857 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002858 return PJ_SUCCESS;
2859 }
2860
Nanang Izzuddinec919002010-11-25 09:27:06 +00002861 /* Remote keeps answering with multiple codecs, let's just give up
2862 * locking codec to avoid infinite retry loop.
2863 */
2864 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
2865 return PJ_SUCCESS;
2866
Benny Prijonob90fd382011-09-18 14:59:56 +00002867 PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002868 "updating media session to use only one codec.."));
2869
Benny Prijono1e601552010-10-20 05:31:08 +00002870 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002871
Benny Prijono1e601552010-10-20 05:31:08 +00002872 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
2873 * See: https://trac.pjsip.org/repos/ticket/1149
2874 */
2875 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
2876 (void*)(pj_size_t)call->index,
2877 &reinv_timer_cb);
2878 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2879 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002880
2881 return PJ_SUCCESS;
2882}
2883
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002884/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002885 * This callback receives notification from invite session when the
2886 * session state has changed.
2887 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002888static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2889 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002890{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002891 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002892
Benny Prijonob90fd382011-09-18 14:59:56 +00002893 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002894 PJSUA_LOCK();
2895
Benny Prijonoa1e69682007-05-11 15:14:34 +00002896 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002897
2898 if (!call) {
2899 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002900 pj_log_pop_indent();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002901 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002902 }
2903
Benny Prijonoe21e7842006-04-09 16:46:05 +00002904
2905 /* Get call times */
2906 switch (inv->state) {
2907 case PJSIP_INV_STATE_EARLY:
2908 case PJSIP_INV_STATE_CONNECTING:
2909 if (call->res_time.sec == 0)
2910 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002911 call->last_code = (pjsip_status_code)
2912 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002913 pj_strncpy(&call->last_text,
2914 &e->body.tsx_state.tsx->status_text,
2915 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002916 break;
2917 case PJSIP_INV_STATE_CONFIRMED:
2918 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002919
Nanang Izzuddinec919002010-11-25 09:27:06 +00002920 /* See if lock codec was pended as media update was done in the
2921 * EARLY state and remote does not support UPDATE.
2922 */
2923 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002924 pj_status_t status;
2925 status = lock_codec(call);
2926 if (status != PJ_SUCCESS) {
2927 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00002928 }
2929 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002930 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002931 break;
2932 case PJSIP_INV_STATE_DISCONNECTED:
2933 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002934 if (call->res_time.sec == 0)
2935 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002936 if (e->type == PJSIP_EVENT_TSX_STATE &&
2937 e->body.tsx_state.tsx->status_code > call->last_code)
2938 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002939 call->last_code = (pjsip_status_code)
2940 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002941 pj_strncpy(&call->last_text,
2942 &e->body.tsx_state.tsx->status_text,
2943 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002944 } else {
2945 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2946 pj_strncpy(&call->last_text,
2947 pjsip_get_status_text(call->last_code),
2948 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002949 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002950
2951 /* Stop lock codec timer, if it is active */
2952 if (call->lock_codec.reinv_timer.id) {
2953 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2954 &call->lock_codec.reinv_timer);
2955 call->lock_codec.reinv_timer.id = PJ_FALSE;
2956 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002957 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002958 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002959 call->last_code = (pjsip_status_code)
2960 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002961 pj_strncpy(&call->last_text,
2962 &e->body.tsx_state.tsx->status_text,
2963 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002964 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002965 }
2966
Benny Prijono26ff9062006-02-21 23:47:00 +00002967 /* If this is an outgoing INVITE that was created because of
2968 * REFER/transfer, send NOTIFY to transferer.
2969 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002970 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002971 int st_code = -1;
2972 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2973
2974
Benny Prijonoa91a0032006-02-26 21:23:45 +00002975 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002976 case PJSIP_INV_STATE_NULL:
2977 case PJSIP_INV_STATE_CALLING:
2978 /* Do nothing */
2979 break;
2980
2981 case PJSIP_INV_STATE_EARLY:
2982 case PJSIP_INV_STATE_CONNECTING:
2983 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002984 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2985 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2986 else
2987 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002988 break;
2989
Benny Prijono140beae2009-10-11 05:06:43 +00002990 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002991#if 0
2992/* We don't need this, as we've terminated the subscription in
2993 * CONNECTING state.
2994 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002995 /* When state is confirmed, send the final 200/OK and terminate
2996 * subscription.
2997 */
2998 st_code = e->body.tsx_state.tsx->status_code;
2999 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003000#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003001 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003002
3003 case PJSIP_INV_STATE_DISCONNECTED:
3004 st_code = e->body.tsx_state.tsx->status_code;
3005 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3006 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003007
Benny Prijono8b1889b2006-06-06 18:40:40 +00003008 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003009 /* Nothing to do. Just to keep gcc from complaining about
3010 * unused enums.
3011 */
3012 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003013 }
3014
3015 if (st_code != -1) {
3016 pjsip_tx_data *tdata;
3017 pj_status_t status;
3018
Benny Prijonoa91a0032006-02-26 21:23:45 +00003019 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003020 ev_state, st_code,
3021 NULL, &tdata);
3022 if (status != PJ_SUCCESS) {
3023 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3024 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003025 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003026 if (status != PJ_SUCCESS) {
3027 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3028 }
3029 }
3030 }
3031 }
3032
Benny Prijono84126ab2006-02-09 09:30:09 +00003033
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003034 if (pjsua_var.ua_cfg.cb.on_call_state)
3035 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003036
3037 /* call->inv may be NULL now */
3038
Benny Prijono84126ab2006-02-09 09:30:09 +00003039 /* Destroy media session when invite session is disconnected. */
3040 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003041
Benny Prijonoa91a0032006-02-26 21:23:45 +00003042 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003043
Benny Prijono275fd682006-03-22 11:59:11 +00003044 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003045 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003046
Benny Prijono105217f2006-03-06 16:25:59 +00003047 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003048 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003049 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003050
3051 /* Reset call */
3052 reset_call(call->index);
3053
Benny Prijono84126ab2006-02-09 09:30:09 +00003054 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003055
3056 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003057 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003058}
3059
3060/*
3061 * This callback is called by invite session framework when UAC session
3062 * has forked.
3063 */
3064static void pjsua_call_on_forked( pjsip_inv_session *inv,
3065 pjsip_event *e)
3066{
3067 PJ_UNUSED_ARG(inv);
3068 PJ_UNUSED_ARG(e);
3069
3070 PJ_TODO(HANDLE_FORKED_DIALOG);
3071}
3072
3073
3074/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003075 * Callback from UA layer when forked dialog response is received.
3076 */
3077pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3078{
3079 if (dlg->uac_has_2xx &&
3080 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3081 pjsip_rdata_get_tsx(res) == NULL &&
3082 res->msg_info.msg->line.status.code/100 == 2)
3083 {
3084 pjsip_dialog *forked_dlg;
3085 pjsip_tx_data *bye;
3086 pj_status_t status;
3087
3088 /* Create forked dialog */
3089 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3090 if (status != PJ_SUCCESS)
3091 return NULL;
3092
3093 pjsip_dlg_inc_lock(forked_dlg);
3094
3095 /* Disconnect the call */
3096 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3097 -1, &bye);
3098 if (status == PJ_SUCCESS) {
3099 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3100 }
3101
3102 pjsip_dlg_dec_lock(forked_dlg);
3103
3104 if (status != PJ_SUCCESS) {
3105 return NULL;
3106 }
3107
3108 return forked_dlg;
3109
3110 } else {
3111 return dlg;
3112 }
3113}
3114
3115/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003116 * Disconnect call upon error.
3117 */
3118static void call_disconnect( pjsip_inv_session *inv,
3119 int code )
3120{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003121 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003122 pjsip_tx_data *tdata;
3123 pj_status_t status;
3124
Benny Prijono59b3aed2008-01-15 16:54:54 +00003125 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3126
Benny Prijonoa38ada02006-07-02 14:22:35 +00003127 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003128 if (status != PJ_SUCCESS)
3129 return;
3130
3131 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003132#if DISABLED_FOR_TICKET_1185
3133 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00003134 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3135 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003136 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003137 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003138
Benny Prijono734fc2d2008-03-17 16:05:35 +00003139 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003140 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003141 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003142 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003143 if (status == PJ_SUCCESS) {
3144 pjsip_create_sdp_body(tdata->pool, local_sdp,
3145 &tdata->msg->body);
3146 }
3147 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00003148#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00003149
3150 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003151}
3152
3153/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003154 * Callback to be called when SDP offer/answer negotiation has just completed
3155 * in the session. This function will start/update media if negotiation
3156 * has succeeded.
3157 */
3158static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3159 pj_status_t status)
3160{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003161 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003162 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003163 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003164 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003165
Benny Prijonob90fd382011-09-18 14:59:56 +00003166 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003167 PJSUA_LOCK();
3168
Benny Prijonoa1e69682007-05-11 15:14:34 +00003169 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003170
3171 if (status != PJ_SUCCESS) {
3172
3173 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3174
Benny Prijono2331d202008-06-26 15:46:52 +00003175 /* Do not deinitialize media since this may be a re-INVITE or
3176 * UPDATE (which in this case the media should not get affected
3177 * by the failed re-INVITE/UPDATE). The media will be shutdown
3178 * when call is disconnected anyway.
3179 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003180 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003181 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003182
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003183 /* Disconnect call if we're not in the middle of initializing an
3184 * UAS dialog and if this is not a re-INVITE
3185 */
3186 if (inv->state != PJSIP_INV_STATE_NULL &&
3187 inv->state != PJSIP_INV_STATE_CONFIRMED)
3188 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003189 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003190 }
3191
Benny Prijonob90fd382011-09-18 14:59:56 +00003192 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003193 }
3194
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003195
3196 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003197 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003198 if (status != PJ_SUCCESS) {
3199 pjsua_perror(THIS_FILE,
3200 "Unable to retrieve currently active local SDP",
3201 status);
3202 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003203 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003204 }
3205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003206 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3207 if (status != PJ_SUCCESS) {
3208 pjsua_perror(THIS_FILE,
3209 "Unable to retrieve currently active remote SDP",
3210 status);
3211 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003212 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003213 }
3214
Benny Prijono91a6a172007-10-31 08:59:29 +00003215 /* Update remote's NAT type */
3216 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3217 update_remote_nat_type(call, remote_sdp);
3218 }
3219
3220 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003221 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003222 if (status != PJ_SUCCESS) {
3223 pjsua_perror(THIS_FILE, "Unable to create media session",
3224 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003225 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003226 /* No need to deinitialize; media will be shutdown when call
3227 * state is disconnected anyway.
3228 */
3229 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonob90fd382011-09-18 14:59:56 +00003230 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003231 }
3232
Nanang Izzuddinec919002010-11-25 09:27:06 +00003233 /* Ticket #476: make sure only one codec is specified in the answer. */
3234 status = lock_codec(call);
3235 if (status != PJ_SUCCESS) {
3236 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003237 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003238
3239 /* Call application callback, if any */
3240 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3241 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3242
Benny Prijonob90fd382011-09-18 14:59:56 +00003243on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003244 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003245 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003246}
3247
3248
Benny Prijonodd63b992010-10-01 02:03:42 +00003249/* Modify SDP for call hold. */
3250static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3251 pj_pool_t *pool,
3252 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003253{
Benny Prijono316f02a2011-04-07 07:53:25 +00003254 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003255
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003256 /* Call-hold is done by set the media direction to 'sendonly'
3257 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3258 * 'inactive' (PJMEDIA_DIR_NONE).
3259 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3260 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003261 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003262 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003263 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003264 /* https://trac.pjsip.org/repos/ticket/1142:
3265 * configuration to use c=0.0.0.0 for call hold.
3266 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003267
Benny Prijono316f02a2011-04-07 07:53:25 +00003268 for (mi=0; mi<sdp->media_count; ++mi) {
3269 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003270
Benny Prijono316f02a2011-04-07 07:53:25 +00003271 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3272 pjmedia_sdp_conn *conn;
3273 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003274
Benny Prijono316f02a2011-04-07 07:53:25 +00003275 /* Get SDP media connection line */
3276 conn = m->conn;
3277 if (!conn)
3278 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003279
Benny Prijono316f02a2011-04-07 07:53:25 +00003280 /* Modify address */
3281 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003282
Benny Prijono316f02a2011-04-07 07:53:25 +00003283 /* Remove existing directions attributes */
3284 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3285 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3286 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3287 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003288
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003289 /* Add inactive attribute */
3290 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003291 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003292
3293
3294 } else {
3295 pjmedia_sdp_attr *attr;
3296
3297 /* Remove existing directions attributes */
3298 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3299 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3300 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3301 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3302
3303 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3304 /* Add sendonly attribute */
3305 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3306 pjmedia_sdp_media_add_attr(m, attr);
3307 } else {
3308 /* Add inactive attribute */
3309 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3310 pjmedia_sdp_media_add_attr(m, attr);
3311 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003312 }
3313 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003314
Benny Prijonodd63b992010-10-01 02:03:42 +00003315 return PJ_SUCCESS;
3316}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003317
Benny Prijonodd63b992010-10-01 02:03:42 +00003318/* Create SDP for call hold. */
3319static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3320 pjmedia_sdp_session **p_sdp)
3321{
3322 pj_status_t status;
3323 pj_pool_t *pool;
3324 pjmedia_sdp_session *sdp;
3325
3326 /* Use call's provisional pool */
3327 pool = call->inv->pool_prov;
3328
3329 /* Create new offer */
3330 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3331 NULL);
3332 if (status != PJ_SUCCESS) {
3333 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3334 return status;
3335 }
3336
3337 status = modify_sdp_of_call_hold(call, pool, sdp);
3338 if (status != PJ_SUCCESS)
3339 return status;
3340
3341 *p_sdp = sdp;
3342
3343 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003344}
3345
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003346/*
3347 * Called when session received new offer.
3348 */
3349static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3350 const pjmedia_sdp_session *offer)
3351{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003352 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003353 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003354 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003355 pj_status_t status;
3356
3357 PJSUA_LOCK();
3358
Benny Prijonoa1e69682007-05-11 15:14:34 +00003359 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003360
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003361 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003362 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3363 call->index));
Benny Prijonob90fd382011-09-18 14:59:56 +00003364 pj_log_push_indent();
Benny Prijono667952e2007-04-02 19:27:54 +00003365
Benny Prijono40d62b62009-08-12 17:53:47 +00003366 status = pjsua_media_channel_create_sdp(call->index,
3367 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003368 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003369 if (status != PJ_SUCCESS) {
3370 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003371 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003372 }
3373
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003374 /* Validate media count in the generated answer */
3375 pj_assert(answer->media_count == offer->media_count);
3376
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003377 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003378 for (i = 0; i < answer->media_count; ++i) {
3379 pjmedia_sdp_conn *conn;
3380
3381 conn = offer->media[i]->conn;
3382 if (!conn)
3383 conn = offer->conn;
3384
3385 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3386 pj_strcmp2(&conn->addr, "0")==0)
3387 {
3388 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3389
3390 /* Modify answer address */
3391 if (a_conn) {
3392 a_conn->addr = pj_str("0.0.0.0");
3393 } else if (answer->conn == NULL ||
3394 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3395 {
3396 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3397 pjmedia_sdp_conn);
3398 a_conn->net_type = pj_str("IN");
3399 a_conn->addr_type = pj_str("IP4");
3400 a_conn->addr = pj_str("0.0.0.0");
3401 answer->media[i]->conn = a_conn;
3402 }
3403 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003404 }
3405
3406 /* Check if call is on-hold */
3407 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003408 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003409 }
3410
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003411 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3412 if (status != PJ_SUCCESS) {
3413 pjsua_perror(THIS_FILE, "Unable to set answer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003414 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003415 }
3416
Benny Prijonob90fd382011-09-18 14:59:56 +00003417on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003418 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003419 pj_log_pop_indent();
Benny Prijono84126ab2006-02-09 09:30:09 +00003420}
3421
3422
3423/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003424 * Called to generate new offer.
3425 */
3426static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3427 pjmedia_sdp_session **offer)
3428{
3429 pjsua_call *call;
3430 pj_status_t status;
3431
Benny Prijonob90fd382011-09-18 14:59:56 +00003432 pj_log_push_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003433 PJSUA_LOCK();
3434
3435 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3436
3437 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003438 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003439 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003440 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003441 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003442 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003443 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003444 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3445 call->index));
3446
Benny Prijono40d62b62009-08-12 17:53:47 +00003447 status = pjsua_media_channel_create_sdp(call->index,
3448 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003449 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003450 }
3451
3452 if (status != PJ_SUCCESS) {
3453 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003454 goto on_return;
Benny Prijono77998ce2007-06-20 10:03:46 +00003455 }
3456
Benny Prijonob90fd382011-09-18 14:59:56 +00003457on_return:
Benny Prijono77998ce2007-06-20 10:03:46 +00003458 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003459 pj_log_pop_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003460}
3461
3462
3463/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003464 * Callback called by event framework when the xfer subscription state
3465 * has changed.
3466 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003467static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3468{
3469
3470 PJ_UNUSED_ARG(event);
3471
Benny Prijonob90fd382011-09-18 14:59:56 +00003472 pj_log_push_indent();
3473
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003474 /*
3475 * When subscription is accepted (got 200/OK to REFER), check if
3476 * subscription suppressed.
3477 */
3478 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3479
3480 pjsip_rx_data *rdata;
3481 pjsip_generic_string_hdr *refer_sub;
3482 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3483 pjsua_call *call;
3484
Benny Prijonoa1e69682007-05-11 15:14:34 +00003485 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003486
3487 /* Must be receipt of response message */
3488 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3489 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3490 rdata = event->body.tsx_state.src.rdata;
3491
3492 /* Find Refer-Sub header */
3493 refer_sub = (pjsip_generic_string_hdr*)
3494 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3495 &REFER_SUB, NULL);
3496
3497 /* Check if subscription is suppressed */
3498 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3499 /* Since no subscription is desired, assume that call has been
3500 * transfered successfully.
3501 */
3502 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3503 const pj_str_t ACCEPTED = { "Accepted", 8 };
3504 pj_bool_t cont = PJ_FALSE;
3505 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3506 200,
3507 &ACCEPTED,
3508 PJ_TRUE,
3509 &cont);
3510 }
3511
3512 /* Yes, subscription is suppressed.
3513 * Terminate our subscription now.
3514 */
3515 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3516 "event subcription..."));
3517 pjsip_evsub_terminate(sub, PJ_TRUE);
3518
3519 } else {
3520 /* Notify application about call transfer progress.
3521 * Initially notify with 100/Accepted status.
3522 */
3523 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3524 const pj_str_t ACCEPTED = { "Accepted", 8 };
3525 pj_bool_t cont = PJ_FALSE;
3526 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3527 100,
3528 &ACCEPTED,
3529 PJ_FALSE,
3530 &cont);
3531 }
3532 }
3533 }
3534 /*
3535 * On incoming NOTIFY, notify application about call transfer progress.
3536 */
3537 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3538 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3539 {
3540 pjsua_call *call;
3541 pjsip_msg *msg;
3542 pjsip_msg_body *body;
3543 pjsip_status_line status_line;
3544 pj_bool_t is_last;
3545 pj_bool_t cont;
3546 pj_status_t status;
3547
Benny Prijonoa1e69682007-05-11 15:14:34 +00003548 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003549
3550 /* When subscription is terminated, clear the xfer_sub member of
3551 * the inv_data.
3552 */
3553 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3554 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3555 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3556
3557 }
3558
3559 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3560 /* Application is not interested with call progress status */
Benny Prijonob90fd382011-09-18 14:59:56 +00003561 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003562 }
3563
3564 /* This better be a NOTIFY request */
3565 if (event->type == PJSIP_EVENT_TSX_STATE &&
3566 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3567 {
3568 pjsip_rx_data *rdata;
3569
3570 rdata = event->body.tsx_state.src.rdata;
3571
3572 /* Check if there's body */
3573 msg = rdata->msg_info.msg;
3574 body = msg->body;
3575 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003576 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003577 "Warning: received NOTIFY without message body"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003578 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003579 }
3580
3581 /* Check for appropriate content */
3582 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3583 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3584 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003585 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003586 "Warning: received NOTIFY with non message/sipfrag "
3587 "content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003588 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003589 }
3590
3591 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003592 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003593 &status_line);
3594 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003595 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003596 "Warning: received NOTIFY with invalid "
3597 "message/sipfrag content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003598 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003599 }
3600
3601 } else {
3602 status_line.code = 500;
3603 status_line.reason = *pjsip_get_status_text(500);
3604 }
3605
3606 /* Notify application */
3607 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3608 cont = !is_last;
3609 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3610 status_line.code,
3611 &status_line.reason,
3612 is_last, &cont);
3613
3614 if (!cont) {
3615 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3616 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003617
3618 /* If the call transfer has completed but the subscription is
3619 * not terminated, terminate it now.
3620 */
3621 if (status_line.code/100 == 2 && !is_last) {
3622 pjsip_tx_data *tdata;
3623
3624 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3625 0, &tdata);
3626 if (status == PJ_SUCCESS)
3627 status = pjsip_evsub_send_request(sub, tdata);
3628 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003629 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003630
3631on_return:
3632 pj_log_pop_indent();
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003633}
3634
3635
3636/*
3637 * Callback called by event framework when the xfer subscription state
3638 * has changed.
3639 */
3640static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003641{
Benny Prijono26ff9062006-02-21 23:47:00 +00003642 PJ_UNUSED_ARG(event);
3643
Benny Prijonob90fd382011-09-18 14:59:56 +00003644 pj_log_push_indent();
3645
Benny Prijono26ff9062006-02-21 23:47:00 +00003646 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003647 * When subscription is terminated, clear the xfer_sub member of
3648 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003649 */
3650 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003651 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003652
Benny Prijonoa1e69682007-05-11 15:14:34 +00003653 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003654 if (!call)
Benny Prijonob90fd382011-09-18 14:59:56 +00003655 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003656
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003657 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003658 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003659
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003660 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003661 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003662
3663on_return:
3664 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003665}
3666
3667
3668/*
3669 * Follow transfer (REFER) request.
3670 */
3671static void on_call_transfered( pjsip_inv_session *inv,
3672 pjsip_rx_data *rdata )
3673{
3674 pj_status_t status;
3675 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003676 pjsua_call *existing_call;
3677 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003678 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003679 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003680 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003681 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003682 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003683 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003684 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003685 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003686 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003687 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003688 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003689 pjsip_evsub *sub;
3690
Benny Prijonob90fd382011-09-18 14:59:56 +00003691 pj_log_push_indent();
3692
Benny Prijonoa1e69682007-05-11 15:14:34 +00003693 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003694
Benny Prijono26ff9062006-02-21 23:47:00 +00003695 /* Find the Refer-To header */
3696 refer_to = (pjsip_generic_string_hdr*)
3697 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3698
3699 if (refer_to == NULL) {
3700 /* Invalid Request.
3701 * No Refer-To header!
3702 */
3703 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003704 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003705 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003706 }
3707
Benny Prijonoc8141a82006-08-20 09:12:19 +00003708 /* Find optional Refer-Sub header */
3709 refer_sub = (pjsip_generic_string_hdr*)
3710 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3711
3712 if (refer_sub) {
3713 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3714 no_refer_sub = PJ_TRUE;
3715 }
3716
Benny Prijono053f5222006-11-11 16:16:04 +00003717 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3718 * request.
3719 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003720 ref_by_hdr = (pjsip_hdr*)
3721 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003722 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003723
Benny Prijono9fc735d2006-05-28 14:58:12 +00003724 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003725 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003726 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3727 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3728 &refer_to->hvalue,
3729 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003730
3731 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003732 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003733 if (code >= 300) {
3734 /* Application rejects call transfer request */
3735 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003736 goto on_return;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003737 }
3738
Benny Prijono26ff9062006-02-21 23:47:00 +00003739 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3740 (int)inv->dlg->remote.info_str.slen,
3741 inv->dlg->remote.info_str.ptr,
3742 (int)refer_to->hvalue.slen,
3743 refer_to->hvalue.ptr));
3744
Benny Prijonoc8141a82006-08-20 09:12:19 +00003745 if (no_refer_sub) {
3746 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003747 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003748 */
3749 pjsip_tx_data *tdata;
3750 const pj_str_t str_false = { "false", 5};
3751 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003752
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003753 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3754 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003755 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003756 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003757 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003758 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003759 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003760
Benny Prijonoc8141a82006-08-20 09:12:19 +00003761 /* Add Refer-Sub header */
3762 hdr = (pjsip_hdr*)
3763 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3764 &str_false);
3765 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003766
Benny Prijono26ff9062006-02-21 23:47:00 +00003767
Benny Prijonoc8141a82006-08-20 09:12:19 +00003768 /* Send answer */
3769 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3770 tdata);
3771 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003772 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003773 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003774 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003775 }
3776
3777 /* Don't have subscription */
3778 sub = NULL;
3779
3780 } else {
3781 struct pjsip_evsub_user xfer_cb;
3782 pjsip_hdr hdr_list;
3783
3784 /* Init callback */
3785 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003786 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003787
3788 /* Init additional header list to be sent with REFER response */
3789 pj_list_init(&hdr_list);
3790
3791 /* Create transferee event subscription */
3792 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3793 if (status != PJ_SUCCESS) {
3794 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3795 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003796 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003797 }
3798
3799 /* If there's Refer-Sub header and the value is "true", send back
3800 * Refer-Sub in the response with value "true" too.
3801 */
3802 if (refer_sub) {
3803 const pj_str_t str_true = { "true", 4 };
3804 pjsip_hdr *hdr;
3805
3806 hdr = (pjsip_hdr*)
3807 pjsip_generic_string_hdr_create(inv->dlg->pool,
3808 &str_refer_sub,
3809 &str_true);
3810 pj_list_push_back(&hdr_list, hdr);
3811
3812 }
3813
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003814 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003815 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3816
3817 /* Create initial NOTIFY request */
3818 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3819 100, NULL, &tdata);
3820 if (status != PJ_SUCCESS) {
3821 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3822 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003823 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003824 }
3825
3826 /* Send initial NOTIFY request */
3827 status = pjsip_xfer_send_request( sub, tdata);
3828 if (status != PJ_SUCCESS) {
3829 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003830 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003831 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003832 }
3833
3834 /* We're cheating here.
3835 * We need to get a null terminated string from a pj_str_t.
3836 * So grab the pointer from the hvalue and NULL terminate it, knowing
3837 * that the NULL position will be occupied by a newline.
3838 */
3839 uri = refer_to->hvalue.ptr;
3840 uri[refer_to->hvalue.slen] = '\0';
3841
Benny Prijono053f5222006-11-11 16:16:04 +00003842 /* Init msg_data */
3843 pjsua_msg_data_init(&msg_data);
3844
3845 /* If Referred-By header is present in the REFER request, copy this
3846 * to the outgoing INVITE request.
3847 */
3848 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003849 pjsip_hdr *dup = (pjsip_hdr*)
3850 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003851 pj_list_push_back(&msg_data.hdr_list, dup);
3852 }
3853
Benny Prijono26ff9062006-02-21 23:47:00 +00003854 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003855 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003856 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003857 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003858 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003859 if (status != PJ_SUCCESS) {
3860
Benny Prijonoc8141a82006-08-20 09:12:19 +00003861 /* Notify xferer about the error (if we have subscription) */
3862 if (sub) {
3863 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3864 500, NULL, &tdata);
3865 if (status != PJ_SUCCESS) {
3866 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3867 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003868 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003869 }
3870 status = pjsip_xfer_send_request(sub, tdata);
3871 if (status != PJ_SUCCESS) {
3872 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3873 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003874 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003875 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003876 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003877 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003878 }
3879
Benny Prijonoc8141a82006-08-20 09:12:19 +00003880 if (sub) {
3881 /* Put the server subscription in inv_data.
3882 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3883 * reported back to the server subscription.
3884 */
3885 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003886
Benny Prijonoc8141a82006-08-20 09:12:19 +00003887 /* Put the invite_data in the subscription. */
3888 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3889 &pjsua_var.calls[new_call]);
3890 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003891
3892on_return:
3893 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003894}
3895
3896
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003897
Benny Prijono26ff9062006-02-21 23:47:00 +00003898/*
3899 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003900 * session. We use this to trap:
3901 * - incoming REFER request.
3902 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003903 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003904static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3905 pjsip_transaction *tsx,
3906 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003907{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003908 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003909
Benny Prijonob90fd382011-09-18 14:59:56 +00003910 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003911 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003912
Benny Prijono2285e7e2008-12-17 14:28:18 +00003913 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3914
Benny Prijonob90fd382011-09-18 14:59:56 +00003915 if (call == NULL)
3916 goto on_return;
Benny Prijono2285e7e2008-12-17 14:28:18 +00003917
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003918 if (call->inv == NULL) {
3919 /* Shouldn't happen. It happens only when we don't terminate the
3920 * server subscription caused by REFER after the call has been
3921 * transfered (and this call has been disconnected), and we
3922 * receive another REFER for this call.
3923 */
Benny Prijonob90fd382011-09-18 14:59:56 +00003924 goto on_return;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003925 }
3926
Benny Prijonofeb69f42007-10-05 09:12:26 +00003927 /* Notify application callback first */
3928 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3929 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3930 }
3931
Benny Prijono26ff9062006-02-21 23:47:00 +00003932 if (tsx->role==PJSIP_ROLE_UAS &&
3933 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003934 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003935 {
3936 /*
3937 * Incoming REFER request.
3938 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003939 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003940
Benny Prijono26ff9062006-02-21 23:47:00 +00003941 }
Benny Prijonob0808372006-03-02 21:18:58 +00003942 else if (tsx->role==PJSIP_ROLE_UAS &&
3943 tsx->state==PJSIP_TSX_STATE_TRYING &&
3944 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3945 {
3946 /*
3947 * Incoming MESSAGE request!
3948 */
3949 pjsip_rx_data *rdata;
3950 pjsip_msg *msg;
3951 pjsip_accept_hdr *accept_hdr;
3952 pj_status_t status;
3953
3954 rdata = e->body.tsx_state.src.rdata;
3955 msg = rdata->msg_info.msg;
3956
3957 /* Request MUST have message body, with Content-Type equal to
3958 * "text/plain".
3959 */
3960 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3961
3962 pjsip_hdr hdr_list;
3963
3964 pj_list_init(&hdr_list);
3965 pj_list_push_back(&hdr_list, accept_hdr);
3966
3967 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3968 NULL, &hdr_list, NULL );
Benny Prijonob90fd382011-09-18 14:59:56 +00003969 goto on_return;
Benny Prijonob0808372006-03-02 21:18:58 +00003970 }
3971
3972 /* Respond with 200 first, so that remote doesn't retransmit in case
3973 * the UI takes too long to process the message.
3974 */
3975 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3976
3977 /* Process MESSAGE request */
3978 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3979 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003980
Benny Prijonob0808372006-03-02 21:18:58 +00003981 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003982 else if (tsx->role == PJSIP_ROLE_UAC &&
3983 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003984 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003985 /* Handle outgoing pager status */
3986 if (tsx->status_code >= 200) {
3987 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003988
Benny Prijonoa1e69682007-05-11 15:14:34 +00003989 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003990 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003991
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003992 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3993 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3994 &im_data->to,
3995 &im_data->body,
3996 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003997 (pjsip_status_code)
3998 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003999 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004000 }
Benny Prijonofccab712006-02-22 22:23:22 +00004001 }
Sauw Minge7dbbc82011-10-24 09:28:13 +00004002 } else if (tsx->role == PJSIP_ROLE_UAC &&
4003 tsx->last_tx == (pjsip_tx_data*)call->hold_msg &&
4004 tsx->state >= PJSIP_TSX_STATE_COMPLETED)
4005 {
4006 /* Monitor the status of call hold request */
4007 call->hold_msg = NULL;
4008 if (tsx->status_code/100 != 2) {
4009 /* Outgoing call hold failed */
4010 call->local_hold = PJ_FALSE;
4011 PJ_LOG(3,(THIS_FILE, "Error putting call %d on hold (reason=%d)",
4012 call->index, tsx->status_code));
4013 }
Benny Prijono834aee32006-02-19 01:38:06 +00004014 }
Benny Prijono834aee32006-02-19 01:38:06 +00004015
Benny Prijonob90fd382011-09-18 14:59:56 +00004016on_return:
Sauw Minge7dbbc82011-10-24 09:28:13 +00004017
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004018 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00004019 pj_log_pop_indent();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004020}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004021
4022
4023/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004024static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4025 const pjsip_uri *target,
4026 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004027{
4028 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004029 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004030
Benny Prijonob90fd382011-09-18 14:59:56 +00004031 pj_log_push_indent();
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004032 PJSUA_LOCK();
4033
4034 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004035 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4036 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004037 } else {
4038 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4039 "(callback not implemented by application). Disconnecting "
4040 "call.",
4041 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004042 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004043 }
4044
4045 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00004046 pj_log_pop_indent();
Benny Prijono08a48b82008-11-27 12:42:07 +00004047
4048 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004049}
4050