blob: d3c655f1933db71841704f0041ba017f8ac35c17 [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 Ming73ecfe82011-09-21 10:20:01 +0000334static pj_status_t
335on_make_call_med_tp_complete(pjsua_call_id call_id,
336 const pjsua_med_tp_state_info *info)
337{
338 pjmedia_sdp_session *offer;
339 pjsip_inv_session *inv = NULL;
340 pjsua_call *call = &pjsua_var.calls[call_id];
341 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
342 pjsip_dialog *dlg = call->async_call.dlg;
343 unsigned options = call->async_call.call_var.out_call.options;
344 pjsip_tx_data *tdata;
345 pj_status_t status = (info? info->status: PJ_SUCCESS);
346
347 PJSUA_LOCK();
348
349 /* Increment the dialog's lock otherwise when invite session creation
350 * fails the dialog will be destroyed prematurely.
351 */
352 pjsip_dlg_inc_lock(dlg);
353
354 if (status != PJ_SUCCESS) {
355 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
356 goto on_error;
357 }
358
359 /* Create offer */
360 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
361 &offer, NULL);
362 if (status != PJ_SUCCESS) {
363 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
364 goto on_error;
365 }
366
367 /* Create the INVITE session: */
368 options |= PJSIP_INV_SUPPORT_100REL;
369 if (acc->cfg.require_100rel)
370 options |= PJSIP_INV_REQUIRE_100REL;
371 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
372 options |= PJSIP_INV_SUPPORT_TIMER;
373 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
374 options |= PJSIP_INV_REQUIRE_TIMER;
375 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
376 options |= PJSIP_INV_ALWAYS_USE_TIMER;
377 }
378
379 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
380 if (status != PJ_SUCCESS) {
381 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
382 goto on_error;
383 }
384
385 /* Init Session Timers */
386 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
387 if (status != PJ_SUCCESS) {
388 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
389 goto on_error;
390 }
391
392 /* Create and associate our data in the session. */
393 call->inv = inv;
394
395 dlg->mod_data[pjsua_var.mod.id] = call;
396 inv->mod_data[pjsua_var.mod.id] = call;
397
398 /* If account is locked to specific transport, then lock dialog
399 * to this transport too.
400 */
401 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
402 pjsip_tpselector tp_sel;
403
404 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
405 pjsip_dlg_set_transport(dlg, &tp_sel);
406 }
407
408 /* Set dialog Route-Set: */
409 if (!pj_list_empty(&acc->route_set))
410 pjsip_dlg_set_route_set(dlg, &acc->route_set);
411
412
413 /* Set credentials: */
414 if (acc->cred_cnt) {
415 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
416 acc->cred_cnt, acc->cred);
417 }
418
419 /* Set authentication preference */
420 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
421
422 /* Create initial INVITE: */
423
424 status = pjsip_inv_invite(inv, &tdata);
425 if (status != PJ_SUCCESS) {
426 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
427 status);
428 goto on_error;
429 }
430
431
432 /* Add additional headers etc */
433
434 pjsua_process_msg_data( tdata,
435 call->async_call.call_var.out_call.msg_data);
436
437 /* Must increment call counter now */
438 ++pjsua_var.call_cnt;
439
440 /* Send initial INVITE: */
441
442 status = pjsip_inv_send_msg(inv, tdata);
443 if (status != PJ_SUCCESS) {
444 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
445 status);
446
447 /* Upon failure to send first request, the invite
448 * session would have been cleared.
449 */
450 inv = NULL;
451 goto on_error;
452 }
453
454 /* Done. */
455
456 pjsip_dlg_dec_lock(dlg);
457 PJSUA_UNLOCK();
458
459 return PJ_SUCCESS;
460
461on_error:
462 if (dlg) {
463 /* This may destroy the dialog */
464 pjsip_dlg_dec_lock(dlg);
465 }
466
467 if (inv != NULL) {
468 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
469 }
470
471 if (call_id != -1) {
472 reset_call(call_id);
473 pjsua_media_channel_deinit(call_id);
474 }
475
476 PJSUA_UNLOCK();
477 return status;
478}
479
Benny Prijonodb844a42008-02-02 17:07:18 +0000480
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481/*
482 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000483 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
485 const pj_str_t *dest_uri,
486 unsigned options,
487 void *user_data,
488 const pjsua_msg_data *msg_data,
489 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000490{
Benny Prijonob90fd382011-09-18 14:59:56 +0000491 pj_pool_t *tmp_pool = NULL;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000492 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000493 pjsua_acc *acc;
494 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000495 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000496 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000497 pj_status_t status;
498
Benny Prijono9fc735d2006-05-28 14:58:12 +0000499
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000501 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000502 PJ_EINVAL);
503
Benny Prijono320fa4d2006-12-07 10:09:16 +0000504 /* Check arguments */
505 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
506
Benny Prijonob90fd382011-09-18 14:59:56 +0000507 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
508 (int)dest_uri->slen, dest_uri->ptr));
509
510 pj_log_push_indent();
511
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000512 PJSUA_LOCK();
513
Benny Prijonof798e502009-03-09 13:08:16 +0000514 /* Create sound port if none is instantiated, to check if sound device
515 * can be used. But only do this with the conference bridge, as with
516 * audio switchboard (i.e. APS-Direct), we can only open the sound
517 * device once the correct format has been known
518 */
519 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
520 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000521 {
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000522 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
Benny Prijonob90fd382011-09-18 14:59:56 +0000523 if (status != PJ_SUCCESS)
524 goto on_error;
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000525 }
526
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000527 acc = &pjsua_var.acc[acc_id];
528 if (!acc->valid) {
529 pjsua_perror(THIS_FILE, "Unable to make call because account "
530 "is not valid", PJ_EINVALIDOP);
Benny Prijonob90fd382011-09-18 14:59:56 +0000531 status = PJ_EINVALIDOP;
532 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000533 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000534
Benny Prijonoa91a0032006-02-26 21:23:45 +0000535 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000536 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000537
Benny Prijono5773cd62008-01-19 13:01:42 +0000538 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000539 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonob90fd382011-09-18 14:59:56 +0000540 status = PJ_ETOOMANY;
541 goto on_error;
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000542 }
543
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 call = &pjsua_var.calls[call_id];
545
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000546 /* Associate session with account */
547 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000548 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000549
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000550 /* Create temporary pool */
551 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
552
Benny Prijono320fa4d2006-12-07 10:09:16 +0000553 /* Verify that destination URI is valid before calling
554 * pjsua_acc_create_uac_contact, or otherwise there
555 * a misleading "Invalid Contact URI" error will be printed
556 * when pjsua_acc_create_uac_contact() fails.
557 */
558 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000559 pjsip_uri *uri;
560 pj_str_t dup;
561
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000562 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
563 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000564
565 if (uri == NULL) {
566 pjsua_perror(THIS_FILE, "Unable to make call",
567 PJSIP_EINVALIDREQURI);
Benny Prijonob90fd382011-09-18 14:59:56 +0000568 status = PJSIP_EINVALIDREQURI;
569 goto on_error;
Benny Prijono320fa4d2006-12-07 10:09:16 +0000570 }
571 }
572
Benny Prijonoe21e7842006-04-09 16:46:05 +0000573 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000575
Benny Prijonoe21e7842006-04-09 16:46:05 +0000576 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000577 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000578
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000579 /* Create suitable Contact header unless a Contact header has been
580 * set in the account.
581 */
582 if (acc->contact.slen) {
583 contact = acc->contact;
584 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000585 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000586 acc_id, dest_uri);
587 if (status != PJ_SUCCESS) {
588 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
589 status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000590 goto on_error;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000591 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000592 }
593
Benny Prijonoe21e7842006-04-09 16:46:05 +0000594 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000595 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000596 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000597 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000598 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000599 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000600 goto on_error;
Benny Prijono84126ab2006-02-09 09:30:09 +0000601 }
602
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000603 /* Increment the dialog's lock otherwise when invite session creation
604 * fails the dialog will be destroyed prematurely.
605 */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000606// pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000607
Benny Prijonodb844a42008-02-02 17:07:18 +0000608 /* Calculate call's secure level */
609 call->secure_level = get_secure_level(acc_id, dest_uri);
610
Sauw Ming73ecfe82011-09-21 10:20:01 +0000611 /* Attach user data */
612 call->user_data = user_data;
613
614 call->async_call.call_var.out_call.options = options;
615 call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
616 dlg->pool, msg_data);
617 call->async_call.dlg = dlg;
618
Benny Prijonoc97608e2007-03-23 16:34:20 +0000619 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000620 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000621 call->secure_level, dlg->pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000622 NULL, NULL, PJ_TRUE,
623 (pjsua_med_tp_state_cb)
624 &on_make_call_med_tp_complete);
625 if (status == PJ_SUCCESS) {
626 status = on_make_call_med_tp_complete(call->index, NULL);
627 if (status != PJ_SUCCESS)
628 goto on_error;
629 } else if (status != PJ_EPENDING) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000630 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
631 goto on_error;
632 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000633
Benny Prijono84126ab2006-02-09 09:30:09 +0000634 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000635
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000636 if (p_call_id)
637 *p_call_id = call_id;
638
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000639 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000640 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000641
Benny Prijonob90fd382011-09-18 14:59:56 +0000642 pj_log_pop_indent();
643
Benny Prijono84126ab2006-02-09 09:30:09 +0000644 return PJ_SUCCESS;
645
646
647on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000648 if (dlg) {
Sauw Ming73ecfe82011-09-21 10:20:01 +0000649 pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000650 /* This may destroy the dialog */
651 pjsip_dlg_dec_lock(dlg);
652 }
653
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 if (call_id != -1) {
655 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000656 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000657 }
658
Benny Prijonob90fd382011-09-18 14:59:56 +0000659 if (tmp_pool)
660 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000661 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000662
663 pj_log_pop_indent();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000664 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000665}
666
667
Benny Prijono91a6a172007-10-31 08:59:29 +0000668/* Get the NAT type information in remote's SDP */
669static void update_remote_nat_type(pjsua_call *call,
670 const pjmedia_sdp_session *sdp)
671{
672 const pjmedia_sdp_attr *xnat;
673
674 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
675 if (xnat) {
676 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
677 } else {
678 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
679 }
680
681 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
682 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
683}
684
685
Benny Prijonodc39fe82006-05-26 12:17:46 +0000686/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000687 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000689 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000690pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000691{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000692 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000693 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000694 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000695 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
696 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000697 pjsip_tx_data *response = NULL;
698 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000699 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000700 int acc_id;
701 pjsua_call *call;
702 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000703 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000704 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000705 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000706
Benny Prijono26ff9062006-02-21 23:47:00 +0000707 /* Don't want to handle anything but INVITE */
708 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
709 return PJ_FALSE;
710
711 /* Don't want to handle anything that's already associated with
712 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000713 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000714 if (dlg || tsx)
715 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000716
Benny Prijono384dab42009-10-14 01:58:04 +0000717 /* Don't want to accept the call if shutdown is in progress */
718 if (pjsua_var.thread_quit_flag) {
719 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
720 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
721 NULL, NULL);
722 return PJ_TRUE;
723 }
724
Benny Prijonob90fd382011-09-18 14:59:56 +0000725 PJ_LOG(4,(THIS_FILE, "Incoming %s", rdata->msg_info.info));
726 pj_log_push_indent();
727
Benny Prijono148c9dd2006-09-19 13:37:53 +0000728 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000729
Benny Prijono26ff9062006-02-21 23:47:00 +0000730 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000731 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000732
Benny Prijono5773cd62008-01-19 13:01:42 +0000733 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000734 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000735 PJSIP_SC_BUSY_HERE, NULL,
736 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000737 PJ_LOG(2,(THIS_FILE,
738 "Unable to accept incoming call (too many calls)"));
Benny Prijonob90fd382011-09-18 14:59:56 +0000739 goto on_return;
Benny Prijono84126ab2006-02-09 09:30:09 +0000740 }
741
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000742 /* Clear call descriptor */
743 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000744
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000745 call = &pjsua_var.calls[call_id];
746
747 /* Mark call start time. */
748 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000749
Benny Prijono053f5222006-11-11 16:16:04 +0000750 /* Check INVITE request for Replaces header. If Replaces header is
751 * present, the function will make sure that we can handle the request.
752 */
753 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
754 &response);
755 if (status != PJ_SUCCESS) {
756 /*
757 * Something wrong with the Replaces header.
758 */
759 if (response) {
760 pjsip_response_addr res_addr;
761
762 pjsip_get_response_addr(response->pool, rdata, &res_addr);
763 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
764 NULL, NULL);
765
766 } else {
767
768 /* Respond with 500 (Internal Server Error) */
769 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
770 NULL, NULL);
771 }
772
Benny Prijonob90fd382011-09-18 14:59:56 +0000773 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000774 }
775
776 /* If this INVITE request contains Replaces header, notify application
777 * about the request so that application can do subsequent checking
778 * if it wants to.
779 */
780 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
781 pjsua_call *replaced_call;
782 int st_code = 200;
783 pj_str_t st_text = { "OK", 2 };
784
785 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000786 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000787
788 /* Notify application */
789 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
790 rdata, &st_code, &st_text);
791
792 /* Must specify final response */
793 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
794
795 /* Check if application rejects this request. */
796 if (st_code >= 300) {
797
798 if (st_text.slen == 2)
799 st_text = *pjsip_get_status_text(st_code);
800
801 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
802 st_code, &st_text, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000803 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000804 }
805 }
806
Benny Prijonod8179652008-01-23 20:39:07 +0000807 /*
808 * Get which account is most likely to be associated with this incoming
809 * call. We need the account to find which contact URI to put for
810 * the call.
811 */
812 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000813 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000814
Benny Prijonodb844a42008-02-02 17:07:18 +0000815 /* Get call's secure level */
816 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
817 call->secure_level = 2;
818 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
819 call->secure_level = 1;
820 else
821 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000822
Benny Prijonod8179652008-01-23 20:39:07 +0000823 /* Parse SDP from incoming request */
824 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000825 pjsip_rdata_sdp_info *sdp_info;
826
827 sdp_info = pjsip_rdata_get_sdp_info(rdata);
828 offer = sdp_info->sdp;
829
830 status = sdp_info->sdp_err;
831 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
832 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000833
Benny Prijonod8179652008-01-23 20:39:07 +0000834 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000835 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000836 pjsip_hdr hdr_list;
837 pjsip_warning_hdr *w;
838
839 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000840 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000841
842 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
843 pjsip_endpt_name(pjsua_var.endpt),
844 status);
845 pj_list_init(&hdr_list);
846 pj_list_push_back(&hdr_list, w);
847
Benny Prijono224b4e22008-06-19 14:10:28 +0000848 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000849 &reason, &hdr_list, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000850 goto on_return;
Benny Prijonod8179652008-01-23 20:39:07 +0000851 }
Benny Prijono617b8602008-04-07 10:10:31 +0000852
853 /* Do quick checks on SDP before passing it to transports. More elabore
854 * checks will be done in pjsip_inv_verify_request2() below.
855 */
856 if (offer->media_count==0) {
857 const pj_str_t reason = pj_str("Missing media in SDP");
858 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
859 NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000860 goto on_return;
Benny Prijono617b8602008-04-07 10:10:31 +0000861 }
862
Benny Prijonod8179652008-01-23 20:39:07 +0000863 } else {
864 offer = NULL;
865 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000866
Benny Prijono224b4e22008-06-19 14:10:28 +0000867 /* Init media channel */
868 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
869 call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000870 rdata->tp_info.pool,
871 offer,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000872 &sip_err_code, PJ_FALSE,
873 NULL);
Benny Prijono224b4e22008-06-19 14:10:28 +0000874 if (status != PJ_SUCCESS) {
875 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
876 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
877 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000878 goto on_return;
Benny Prijono224b4e22008-06-19 14:10:28 +0000879 }
880
881 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000882 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000883 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000884 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000885 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000886 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
887 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000888 goto on_return;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000889 }
890
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000891 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000892 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000893 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000894 if (pjsua_var.acc[acc_id].cfg.require_100rel)
895 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000896 if (pjsua_var.media_cfg.enable_ice)
897 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000898 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
899 options |= PJSIP_INV_REQUIRE_TIMER;
900 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
901 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000902
Benny Prijonod8179652008-01-23 20:39:07 +0000903 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
904 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000905 if (status != PJ_SUCCESS) {
906
907 /*
908 * No we can't handle the incoming INVITE request.
909 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000910 if (response) {
911 pjsip_response_addr res_addr;
912
913 pjsip_get_response_addr(response->pool, rdata, &res_addr);
914 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
915 NULL, NULL);
916
917 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000918 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000919 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
920 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000921 }
922
Benny Prijonoc97608e2007-03-23 16:34:20 +0000923 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +0000924 goto on_return;
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000925 }
926
927
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000928 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000929 if (pjsua_var.acc[acc_id].contact.slen) {
930 contact = pjsua_var.acc[acc_id].contact;
931 } else {
932 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
933 acc_id, rdata);
934 if (status != PJ_SUCCESS) {
935 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
936 status);
937 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
938 NULL, NULL);
939 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +0000940 goto on_return;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000941 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000942 }
943
Benny Prijono26ff9062006-02-21 23:47:00 +0000944 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000945 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000946 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000947 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000948 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000949 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000950 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +0000951 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +0000952 }
953
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000954 /* Set credentials */
955 if (pjsua_var.acc[acc_id].cred_cnt) {
956 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
957 pjsua_var.acc[acc_id].cred_cnt,
958 pjsua_var.acc[acc_id].cred);
959 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000960
Benny Prijono48ab2b72007-11-08 09:24:30 +0000961 /* Set preference */
962 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
963 &pjsua_var.acc[acc_id].cfg.auth_pref);
964
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000965 /* Disable Session Timers if not prefered and the incoming INVITE request
966 * did not require it.
967 */
968 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
969 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
970 {
971 options &= ~(PJSIP_INV_SUPPORT_TIMER);
972 }
973
Benny Prijono26ff9062006-02-21 23:47:00 +0000974 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000975 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000976 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000977 pjsip_hdr hdr_list;
978 pjsip_warning_hdr *w;
979
980 w = pjsip_warning_hdr_create_from_status(dlg->pool,
981 pjsip_endpt_name(pjsua_var.endpt),
982 status);
983 pj_list_init(&hdr_list);
984 pj_list_push_back(&hdr_list, w);
985
986 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
987
988 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000989 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000990 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000991 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +0000992 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +0000993 }
994
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000995 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000996 status = pjsip_timer_init_session(inv,
997 &pjsua_var.acc[acc_id].cfg.timer_setting);
998 if (status != PJ_SUCCESS) {
999 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
1000 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
1001 NULL, &response);
1002 if (status == PJ_SUCCESS && response)
1003 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001004
Nanang Izzuddin65add622009-08-11 16:26:20 +00001005 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001006
Benny Prijonob90fd382011-09-18 14:59:56 +00001007 goto on_return;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001008 }
1009
Benny Prijonoea9fd392007-11-06 03:41:40 +00001010 /* Update NAT type of remote endpoint, only when there is SDP in
1011 * incoming INVITE!
1012 */
1013 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
1014 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
1015 {
Benny Prijono91a6a172007-10-31 08:59:29 +00001016 const pjmedia_sdp_session *remote_sdp;
1017
1018 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
1019 update_remote_nat_type(call, remote_sdp);
1020 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001021
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001022 /* If account is locked to specific transport, then lock dialog
1023 * to this transport too.
1024 */
1025 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1026 pjsip_tpselector tp_sel;
1027
1028 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1029 pjsip_dlg_set_transport(dlg, &tp_sel);
1030 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001031
Benny Prijono2285e7e2008-12-17 14:28:18 +00001032 /* Must answer with some response to initial INVITE. We'll do this before
1033 * attaching the call to the invite session/dialog, so that the application
1034 * will not get notification about this event (on another scenario, it is
1035 * also possible that inv_send_msg() fails and causes the invite session to
1036 * be disconnected. If we have the call attached at this time, this will
1037 * cause the disconnection callback to be called before on_incoming_call()
1038 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +00001039 */
Benny Prijono64f851e2006-02-23 13:49:28 +00001040 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001041 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +00001042 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001043 if (response == NULL) {
1044 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1045 status);
1046 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1047 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1048 } else {
1049 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001050 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001051 PJ_FALSE);
1052 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001053 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001054 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001055
1056 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001057 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001058 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001059 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001060 goto on_return;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001061 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001062 }
1063
Benny Prijono2285e7e2008-12-17 14:28:18 +00001064 /* Create and attach pjsua_var data to the dialog: */
1065 call->inv = inv;
1066
1067 dlg->mod_data[pjsua_var.mod.id] = call;
1068 inv->mod_data[pjsua_var.mod.id] = call;
1069
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001070 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001071
Benny Prijono053f5222006-11-11 16:16:04 +00001072 /* Check if this request should replace existing call */
1073 if (replaced_dlg) {
1074 pjsip_inv_session *replaced_inv;
1075 struct pjsua_call *replaced_call;
1076 pjsip_tx_data *tdata;
1077
1078 /* Get the invite session in the dialog */
1079 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1080
1081 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001082 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001083
1084 /* Notify application */
1085 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1086 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1087 call_id);
1088
1089 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1090 call_id));
1091
1092 /* Answer the new call with 200 response */
1093 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1094 if (status == PJ_SUCCESS)
1095 status = pjsip_inv_send_msg(inv, tdata);
1096
1097 if (status != PJ_SUCCESS)
1098 pjsua_perror(THIS_FILE, "Error answering session", status);
1099
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001100 /* Note that inv may be invalid if 200/OK has caused error in
1101 * starting the media.
1102 */
Benny Prijono053f5222006-11-11 16:16:04 +00001103
1104 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1105 replaced_call->index));
1106
1107 /* Disconnect replaced invite session */
1108 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1109 &tdata);
1110 if (status == PJ_SUCCESS && tdata)
1111 status = pjsip_inv_send_msg(replaced_inv, tdata);
1112
1113 if (status != PJ_SUCCESS)
1114 pjsua_perror(THIS_FILE, "Error terminating session", status);
1115
1116
1117 } else {
1118
Benny Prijonob5388cf2007-01-04 22:45:08 +00001119 /* Notify application if on_incoming_call() is overriden,
1120 * otherwise hangup the call with 480
1121 */
1122 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001123 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001124 } else {
1125 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1126 NULL, NULL);
1127 }
Benny Prijono053f5222006-11-11 16:16:04 +00001128 }
1129
Benny Prijono8b1889b2006-06-06 18:40:40 +00001130
Benny Prijono26ff9062006-02-21 23:47:00 +00001131 /* This INVITE request has been handled. */
Benny Prijonob90fd382011-09-18 14:59:56 +00001132on_return:
1133 pj_log_pop_indent();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001134 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001135 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001136}
1137
1138
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001139
1140/*
1141 * Check if the specified call has active INVITE session and the INVITE
1142 * session has not been disconnected.
1143 */
1144PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1145{
1146 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1147 PJ_EINVAL);
1148 return pjsua_var.calls[call_id].inv != NULL &&
1149 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1150}
1151
1152
1153/*
1154 * Check if call has an active media session.
1155 */
1156PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1157{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001158 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1160 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001161 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonocf986c42008-09-02 11:25:07 +00001162}
1163
1164
Benny Prijono148c9dd2006-09-19 13:37:53 +00001165/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001166pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001167 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001168 pjsua_call **p_call,
1169 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001170{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001171 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001172 pjsua_call *call = NULL;
1173 pj_bool_t has_pjsua_lock = PJ_FALSE;
1174 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001175 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001176
Sauw Ming844c1c92010-09-07 05:12:02 +00001177 pj_gettimeofday(&time_start);
1178 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1179 pj_time_val_normalize(&timeout);
1180
1181 for (retry=0; ; ++retry) {
1182
1183 if (retry % 10 == 9) {
1184 pj_time_val dtime;
1185
1186 pj_gettimeofday(&dtime);
1187 PJ_TIME_VAL_SUB(dtime, time_start);
1188 if (!PJ_TIME_VAL_LT(dtime, timeout))
1189 break;
1190 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001191
1192 has_pjsua_lock = PJ_FALSE;
1193
1194 status = PJSUA_TRY_LOCK();
1195 if (status != PJ_SUCCESS) {
1196 pj_thread_sleep(retry/10);
1197 continue;
1198 }
1199
1200 has_pjsua_lock = PJ_TRUE;
1201 call = &pjsua_var.calls[call_id];
1202
1203 if (call->inv == NULL) {
1204 PJSUA_UNLOCK();
1205 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1206 return PJSIP_ESESSIONTERMINATED;
1207 }
1208
1209 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1210 if (status != PJ_SUCCESS) {
1211 PJSUA_UNLOCK();
1212 pj_thread_sleep(retry/10);
1213 continue;
1214 }
1215
1216 PJSUA_UNLOCK();
1217
1218 break;
1219 }
1220
1221 if (status != PJ_SUCCESS) {
1222 if (has_pjsua_lock == PJ_FALSE)
1223 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1224 "(possibly system has deadlocked) in %s",
1225 title));
1226 else
1227 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1228 "(possibly system has deadlocked) in %s",
1229 title));
1230 return PJ_ETIMEDOUT;
1231 }
1232
1233 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001234 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001235
1236 return PJ_SUCCESS;
1237}
1238
1239
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240/*
1241 * Get the conference port identification associated with the call.
1242 */
1243PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1244{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001245 pjsua_call *call;
1246 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001247 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001248 pj_status_t status;
1249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1251 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001252
Benny Prijonodc752ca2006-09-22 16:55:42 +00001253 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001254 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001255 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001256
Benny Prijono0bc99a92011-03-17 04:34:43 +00001257 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001258
Benny Prijonodc752ca2006-09-22 16:55:42 +00001259 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001260
1261 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262}
1263
1264
Benny Prijono148c9dd2006-09-19 13:37:53 +00001265
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266/*
1267 * Obtain detail information about the specified call.
1268 */
1269PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1270 pjsua_call_info *info)
1271{
1272 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001273 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001274 unsigned mi;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001275 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001276
1277 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1278 PJ_EINVAL);
1279
Benny Prijonoac623b32006-07-03 15:19:31 +00001280 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001281
Benny Prijonodc752ca2006-09-22 16:55:42 +00001282 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001283 if (status != PJ_SUCCESS) {
1284 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285 }
1286
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001287 /* id and role */
1288 info->id = call_id;
1289 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001290 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291
1292 /* local info */
1293 info->local_info.ptr = info->buf_.local_info;
1294 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1295 sizeof(info->buf_.local_info));
1296
1297 /* local contact */
1298 info->local_contact.ptr = info->buf_.local_contact;
1299 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1300 call->inv->dlg->local.contact->uri,
1301 info->local_contact.ptr,
1302 sizeof(info->buf_.local_contact));
1303
1304 /* remote info */
1305 info->remote_info.ptr = info->buf_.remote_info;
1306 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1307 sizeof(info->buf_.remote_info));
1308
1309 /* remote contact */
1310 if (call->inv->dlg->remote.contact) {
1311 int len;
1312 info->remote_contact.ptr = info->buf_.remote_contact;
1313 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1314 call->inv->dlg->remote.contact->uri,
1315 info->remote_contact.ptr,
1316 sizeof(info->buf_.remote_contact));
1317 if (len < 0) len = 0;
1318 info->remote_contact.slen = len;
1319 } else {
1320 info->remote_contact.slen = 0;
1321 }
1322
1323 /* call id */
1324 info->call_id.ptr = info->buf_.call_id;
1325 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1326 sizeof(info->buf_.call_id));
1327
1328 /* state, state_text */
1329 info->state = call->inv->state;
1330 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1331
1332 /* If call is disconnected, set the last_status from the cause code */
1333 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1334 /* last_status, last_status_text */
1335 info->last_status = call->inv->cause;
1336
1337 info->last_status_text.ptr = info->buf_.last_status_text;
1338 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1339 sizeof(info->buf_.last_status_text));
1340 } else {
1341 /* last_status, last_status_text */
1342 info->last_status = call->last_code;
1343
1344 info->last_status_text.ptr = info->buf_.last_status_text;
1345 pj_strncpy(&info->last_status_text, &call->last_text,
1346 sizeof(info->buf_.last_status_text));
1347 }
1348
Benny Prijono0bc99a92011-03-17 04:34:43 +00001349 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001350 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001351 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001352 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001353 {
1354 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001356 info->media[info->media_cnt].index = mi;
1357 info->media[info->media_cnt].status = call_med->state;
1358 info->media[info->media_cnt].dir = call_med->dir;
1359 info->media[info->media_cnt].type = call_med->type;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001361 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001362 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001363 call_med->strm.a.conf_slot;
1364 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001365 pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV;
1366
1367 info->media[info->media_cnt].stream.vid.win_in =
1368 call_med->strm.v.rdr_win_id;
1369
1370 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddind93c68a2011-07-19 08:40:20 +00001371 cap_dev = call_med->strm.v.cap_dev;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001372 }
1373 info->media[info->media_cnt].stream.vid.cap_dev = cap_dev;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001374 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001375 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001376 }
1377 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001378 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001379
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001380 if (call->audio_idx != -1) {
1381 info->media_status = call->media[call->audio_idx].state;
1382 info->media_dir = call->media[call->audio_idx].dir;
1383 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001384 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385
1386 /* calculate duration */
1387 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1388
1389 info->total_duration = call->dis_time;
1390 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1391
1392 if (call->conn_time.sec) {
1393 info->connect_duration = call->dis_time;
1394 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1395 }
1396
1397 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1398
1399 pj_gettimeofday(&info->total_duration);
1400 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1401
1402 pj_gettimeofday(&info->connect_duration);
1403 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1404
1405 } else {
1406 pj_gettimeofday(&info->total_duration);
1407 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1408 }
1409
Benny Prijonodc752ca2006-09-22 16:55:42 +00001410 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001411
1412 return PJ_SUCCESS;
1413}
1414
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001415/*
1416 * Check if call remote peer support the specified capability.
1417 */
1418PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1419 pjsua_call_id call_id,
1420 int htype,
1421 const pj_str_t *hname,
1422 const pj_str_t *token)
1423{
1424 pjsua_call *call;
1425 pjsip_dialog *dlg;
1426 pj_status_t status;
1427 pjsip_dialog_cap_status cap_status;
1428
1429 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1430 if (status != PJ_SUCCESS)
1431 return PJSIP_DIALOG_CAP_UNKNOWN;
1432
1433 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1434
1435 pjsip_dlg_dec_lock(dlg);
1436
1437 return cap_status;
1438}
1439
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440
1441/*
1442 * Attach application specific data to the call.
1443 */
1444PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1445 void *user_data)
1446{
1447 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1448 PJ_EINVAL);
1449 pjsua_var.calls[call_id].user_data = user_data;
1450
1451 return PJ_SUCCESS;
1452}
1453
1454
1455/*
1456 * Get user data attached to the call.
1457 */
1458PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1459{
1460 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1461 NULL);
1462 return pjsua_var.calls[call_id].user_data;
1463}
1464
1465
1466/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001467 * Get remote's NAT type.
1468 */
1469PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1470 pj_stun_nat_type *p_type)
1471{
1472 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1473 PJ_EINVAL);
1474 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1475
1476 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1477 return PJ_SUCCESS;
1478}
1479
1480
1481/*
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001482 * Get media stream info for the specified media index.
1483 */
1484PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
1485 unsigned med_idx,
1486 pjsua_stream_info *psi)
1487{
1488 pjsua_call *call;
1489 pjsua_call_media *call_med;
1490 pj_status_t status;
1491
1492 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1493 PJ_EINVAL);
1494 PJ_ASSERT_RETURN(psi, PJ_EINVAL);
1495
1496 PJSUA_LOCK();
1497
1498 call = &pjsua_var.calls[call_id];
1499
1500 if (med_idx >= call->med_cnt) {
1501 PJSUA_UNLOCK();
1502 return PJ_EINVAL;
1503 }
1504
1505 call_med = &call->media[med_idx];
1506 psi->type = call_med->type;
1507 switch (call_med->type) {
1508 case PJMEDIA_TYPE_AUDIO:
1509 status = pjmedia_stream_get_info(call_med->strm.a.stream,
1510 &psi->info.aud);
1511 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001512#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001513 case PJMEDIA_TYPE_VIDEO:
1514 status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
1515 &psi->info.vid);
1516 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001517#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001518 default:
1519 status = PJMEDIA_EINVALIMEDIATYPE;
1520 break;
1521 }
1522
1523 PJSUA_UNLOCK();
1524 return status;
1525}
1526
1527
1528/*
1529 * Get media stream statistic for the specified media index.
1530 */
1531PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
1532 unsigned med_idx,
1533 pjsua_stream_stat *stat)
1534{
1535 pjsua_call *call;
1536 pjsua_call_media *call_med;
1537 pj_status_t status;
1538
1539 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1540 PJ_EINVAL);
1541 PJ_ASSERT_RETURN(stat, PJ_EINVAL);
1542
1543 PJSUA_LOCK();
1544
1545 call = &pjsua_var.calls[call_id];
1546
1547 if (med_idx >= call->med_cnt) {
1548 PJSUA_UNLOCK();
1549 return PJ_EINVAL;
1550 }
1551
1552 call_med = &call->media[med_idx];
1553 switch (call_med->type) {
1554 case PJMEDIA_TYPE_AUDIO:
1555 status = pjmedia_stream_get_stat(call_med->strm.a.stream,
1556 &stat->rtcp);
1557 if (status == PJ_SUCCESS)
1558 status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
1559 &stat->jbuf);
1560 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001561#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001562 case PJMEDIA_TYPE_VIDEO:
1563 status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
1564 &stat->rtcp);
1565 if (status == PJ_SUCCESS)
1566 status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
1567 &stat->jbuf);
1568 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001569#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001570 default:
1571 status = PJMEDIA_EINVALIMEDIATYPE;
1572 break;
1573 }
1574
1575 PJSUA_UNLOCK();
1576 return status;
1577}
1578
1579
1580/*
1581 * Get media transport info for the specified media index.
1582 */
Benny Prijonoe212bc12011-08-15 09:38:42 +00001583PJ_DEF(pj_status_t)
1584pjsua_call_get_med_transport_info(pjsua_call_id call_id,
1585 unsigned med_idx,
1586 pjmedia_transport_info *t)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001587{
1588 pjsua_call *call;
1589 pjsua_call_media *call_med;
1590 pj_status_t status;
1591
1592 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1593 PJ_EINVAL);
1594 PJ_ASSERT_RETURN(t, PJ_EINVAL);
1595
1596 PJSUA_LOCK();
1597
1598 call = &pjsua_var.calls[call_id];
1599
1600 if (med_idx >= call->med_cnt) {
1601 PJSUA_UNLOCK();
1602 return PJ_EINVAL;
1603 }
1604
1605 call_med = &call->media[med_idx];
1606
1607 pjmedia_transport_info_init(t);
1608 status = pjmedia_transport_get_info(call_med->tp, t);
1609
1610 PJSUA_UNLOCK();
1611 return status;
1612}
1613
1614
1615/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 * Send response to incoming INVITE request.
1617 */
1618PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1619 unsigned code,
1620 const pj_str_t *reason,
1621 const pjsua_msg_data *msg_data)
1622{
1623 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001624 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 pjsip_tx_data *tdata;
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
Benny Prijonob90fd382011-09-18 14:59:56 +00001631 PJ_LOG(4,(THIS_FILE, "Answering call %d: code=%d", call_id, code));
1632 pj_log_push_indent();
1633
Benny Prijonodc752ca2006-09-22 16:55:42 +00001634 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001635 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001636 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001637
Benny Prijono2e507c22006-06-23 15:04:11 +00001638 if (call->res_time.sec == 0)
1639 pj_gettimeofday(&call->res_time);
1640
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001641 if (reason && reason->slen == 0)
1642 reason = NULL;
1643
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644 /* Create response message */
1645 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1646 if (status != PJ_SUCCESS) {
1647 pjsua_perror(THIS_FILE, "Error creating response",
1648 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001649 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001650 }
1651
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001652 /* Call might have been disconnected if application is answering with
1653 * 200/OK and the media failed to start.
1654 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001655 if (call->inv == NULL)
1656 goto on_return;
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001657
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001658 /* Add additional headers etc */
1659 pjsua_process_msg_data( tdata, msg_data);
1660
1661 /* Send the message */
1662 status = pjsip_inv_send_msg(call->inv, tdata);
1663 if (status != PJ_SUCCESS)
1664 pjsua_perror(THIS_FILE, "Error sending response",
1665 status);
1666
Benny Prijonob90fd382011-09-18 14:59:56 +00001667on_return:
1668 if (dlg) pjsip_dlg_dec_lock(dlg);
1669 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670 return status;
1671}
1672
1673
1674/*
1675 * Hangup call by using method that is appropriate according to the
1676 * call state.
1677 */
1678PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1679 unsigned code,
1680 const pj_str_t *reason,
1681 const pjsua_msg_data *msg_data)
1682{
1683 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001684 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685 pj_status_t status;
1686 pjsip_tx_data *tdata;
1687
1688
Benny Prijono148c9dd2006-09-19 13:37:53 +00001689 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1690 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1691 call_id));
1692 }
1693
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1695 PJ_EINVAL);
1696
Benny Prijonob90fd382011-09-18 14:59:56 +00001697 PJ_LOG(4,(THIS_FILE, "Call %d hanging up: code=%d..", call_id, code));
1698 pj_log_push_indent();
1699
Benny Prijonodc752ca2006-09-22 16:55:42 +00001700 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001701 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001702 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703
1704 if (code==0) {
1705 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1706 code = PJSIP_SC_OK;
1707 else if (call->inv->role == PJSIP_ROLE_UAS)
1708 code = PJSIP_SC_DECLINE;
1709 else
1710 code = PJSIP_SC_REQUEST_TERMINATED;
1711 }
1712
1713 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1714 if (status != PJ_SUCCESS) {
1715 pjsua_perror(THIS_FILE,
1716 "Failed to create end session message",
1717 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001718 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001719 }
1720
1721 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1722 * as p_tdata when INVITE transaction has not been answered
1723 * with any provisional responses.
1724 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001725 if (tdata == NULL)
1726 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727
1728 /* Add additional headers etc */
1729 pjsua_process_msg_data( tdata, msg_data);
1730
1731 /* Send the message */
1732 status = pjsip_inv_send_msg(call->inv, tdata);
1733 if (status != PJ_SUCCESS) {
1734 pjsua_perror(THIS_FILE,
1735 "Failed to send end session message",
1736 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001737 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001738 }
1739
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001740 /* Stop lock codec timer, if it is active */
1741 if (call->lock_codec.reinv_timer.id) {
1742 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1743 &call->lock_codec.reinv_timer);
1744 call->lock_codec.reinv_timer.id = PJ_FALSE;
1745 }
1746
Benny Prijonob90fd382011-09-18 14:59:56 +00001747on_return:
1748 if (dlg) pjsip_dlg_dec_lock(dlg);
1749 pj_log_pop_indent();
1750 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001751}
1752
1753
1754/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001755 * Accept or reject redirection.
1756 */
1757PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1758 pjsip_redirect_op cmd)
1759{
1760 pjsua_call *call;
1761 pjsip_dialog *dlg;
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
1767 status = acquire_call("pjsua_call_process_redirect()", call_id,
1768 &call, &dlg);
1769 if (status != PJ_SUCCESS)
1770 return status;
1771
1772 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1773
1774 pjsip_dlg_dec_lock(dlg);
1775
1776 return status;
1777}
1778
1779
1780/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781 * Put the specified call on hold.
1782 */
1783PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1784 const pjsua_msg_data *msg_data)
1785{
1786 pjmedia_sdp_session *sdp;
1787 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001788 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001789 pjsip_tx_data *tdata;
1790 pj_status_t status;
1791
1792 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1793 PJ_EINVAL);
1794
Benny Prijonob90fd382011-09-18 14:59:56 +00001795 PJ_LOG(4,(THIS_FILE, "Putting call %d on hold", call_id));
1796 pj_log_push_indent();
1797
Benny Prijonodc752ca2006-09-22 16:55:42 +00001798 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001799 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001800 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001801
1802 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1803 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00001804 status = PJSIP_ESESSIONSTATE;
1805 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806 }
1807
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001808 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonob90fd382011-09-18 14:59:56 +00001809 if (status != PJ_SUCCESS)
1810 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811
1812 /* Create re-INVITE with new offer */
1813 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1814 if (status != PJ_SUCCESS) {
1815 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001816 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001817 }
1818
1819 /* Add additional headers etc */
1820 pjsua_process_msg_data( tdata, msg_data);
1821
1822 /* Send the request */
1823 status = pjsip_inv_send_msg( call->inv, tdata);
1824 if (status != PJ_SUCCESS) {
1825 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001826 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001827 }
1828
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001829 /* Set flag that local put the call on hold */
1830 call->local_hold = PJ_TRUE;
1831
Benny Prijonob90fd382011-09-18 14:59:56 +00001832on_return:
1833 if (dlg) pjsip_dlg_dec_lock(dlg);
1834 pj_log_pop_indent();
1835 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836}
1837
1838
1839/*
1840 * Send re-INVITE (to release hold).
1841 */
1842PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
Benny Prijonodec3a372011-03-16 03:52:20 +00001843 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001844 const pjsua_msg_data *msg_data)
1845{
1846 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00001847 pj_str_t *new_contact = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001848 pjsip_tx_data *tdata;
1849 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001850 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 pj_status_t status;
1852
1853
1854 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1855 PJ_EINVAL);
1856
Benny Prijonob90fd382011-09-18 14:59:56 +00001857 PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
1858 pj_log_push_indent();
1859
Benny Prijonodc752ca2006-09-22 16:55:42 +00001860 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001861 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001862 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001863
1864 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1865 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00001866 status = PJSIP_ESESSIONSTATE;
1867 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001868 }
1869
1870 /* Create SDP */
Benny Prijonodec3a372011-03-16 03:52:20 +00001871 if (call->local_hold && (options & PJSUA_CALL_UNHOLD)==0) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001872 status = create_sdp_of_call_hold(call, &sdp);
1873 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001874 status = pjsua_media_channel_create_sdp(call->index,
1875 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001876 NULL, &sdp, NULL);
1877 call->local_hold = PJ_FALSE;
1878 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001879 if (status != PJ_SUCCESS) {
1880 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1881 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001882 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001883 }
1884
Benny Prijonodec3a372011-03-16 03:52:20 +00001885 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
1886 pjsua_acc_is_valid(call->acc_id))
1887 {
1888 new_contact = &pjsua_var.acc[call->acc_id].contact;
1889 }
1890
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001891 /* Create re-INVITE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00001892 status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001893 if (status != PJ_SUCCESS) {
1894 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001895 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001896 }
1897
1898 /* Add additional headers etc */
1899 pjsua_process_msg_data( tdata, msg_data);
1900
1901 /* Send the request */
1902 status = pjsip_inv_send_msg( call->inv, tdata);
1903 if (status != PJ_SUCCESS) {
1904 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001905 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001906 }
1907
Benny Prijonob90fd382011-09-18 14:59:56 +00001908on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001909 pjsip_dlg_dec_lock(dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00001910 pj_log_pop_indent();
1911 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001912}
1913
1914
1915/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001916 * Send UPDATE request.
1917 */
1918PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1919 unsigned options,
1920 const pjsua_msg_data *msg_data)
1921{
1922 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00001923 pj_str_t *new_contact = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001924 pjsip_tx_data *tdata;
1925 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001926 pjsip_dialog *dlg = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001927 pj_status_t status;
1928
1929 PJ_UNUSED_ARG(options);
1930
1931 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1932 PJ_EINVAL);
1933
Benny Prijonob90fd382011-09-18 14:59:56 +00001934 PJ_LOG(4,(THIS_FILE, "Sending UPDATE on call %d", call_id));
1935 pj_log_push_indent();
1936
Benny Prijonoc08682e2007-10-04 06:17:58 +00001937 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1938 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001939 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001940
Benny Prijonoc08682e2007-10-04 06:17:58 +00001941 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001942 status = pjsua_media_channel_create_sdp(call->index,
1943 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001944 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001945 if (status != PJ_SUCCESS) {
1946 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1947 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001948 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001949 }
1950
Benny Prijonodec3a372011-03-16 03:52:20 +00001951 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
1952 pjsua_acc_is_valid(call->acc_id))
1953 {
1954 new_contact = &pjsua_var.acc[call->acc_id].contact;
1955 }
1956
Benny Prijono224b4e22008-06-19 14:10:28 +00001957 /* Create UPDATE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00001958 status = pjsip_inv_update(call->inv, new_contact, sdp, &tdata);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001959 if (status != PJ_SUCCESS) {
1960 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001961 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001962 }
1963
1964 /* Add additional headers etc */
1965 pjsua_process_msg_data( tdata, msg_data);
1966
1967 /* Send the request */
1968 status = pjsip_inv_send_msg( call->inv, tdata);
1969 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001970 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001971 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001972 }
1973
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001974 call->local_hold = PJ_FALSE;
1975
Benny Prijonob90fd382011-09-18 14:59:56 +00001976on_return:
1977 if (dlg) pjsip_dlg_dec_lock(dlg);
1978 pj_log_pop_indent();
1979 return status;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001980}
1981
1982
1983/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001984 * Initiate call transfer to the specified address.
1985 */
1986PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1987 const pj_str_t *dest,
1988 const pjsua_msg_data *msg_data)
1989{
1990 pjsip_evsub *sub;
1991 pjsip_tx_data *tdata;
1992 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001993 pjsip_dialog *dlg = NULL;
Benny Prijono053f5222006-11-11 16:16:04 +00001994 pjsip_generic_string_hdr *gs_hdr;
1995 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001996 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001997 pj_status_t status;
1998
1999
Benny Prijonob90fd382011-09-18 14:59:56 +00002000 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls &&
2001 dest, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002002
Benny Prijonob90fd382011-09-18 14:59:56 +00002003 PJ_LOG(4,(THIS_FILE, "Transfering call %d to %.*s", call_id,
2004 (int)dest->slen, dest->ptr));
2005 pj_log_push_indent();
2006
Benny Prijonodc752ca2006-09-22 16:55:42 +00002007 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002008 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002009 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002010
Benny Prijonod524e822006-09-22 12:48:18 +00002011 /* Create xfer client subscription. */
2012 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002013 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00002014
2015 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002016 if (status != PJ_SUCCESS) {
2017 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002018 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002019 }
2020
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002021 /* Associate this call with the client subscription */
2022 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
2023
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024 /*
2025 * Create REFER request.
2026 */
2027 status = pjsip_xfer_initiate(sub, dest, &tdata);
2028 if (status != PJ_SUCCESS) {
2029 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002030 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002031 }
2032
Benny Prijono053f5222006-11-11 16:16:04 +00002033 /* Add Referred-By header */
2034 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
2035 &dlg->local.info_str);
2036 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
2037
2038
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002039 /* Add additional headers etc */
2040 pjsua_process_msg_data( tdata, msg_data);
2041
2042 /* Send. */
2043 status = pjsip_xfer_send_request(sub, tdata);
2044 if (status != PJ_SUCCESS) {
2045 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002046 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002047 }
2048
2049 /* For simplicity (that's what this program is intended to be!),
2050 * leave the original invite session as it is. More advanced application
2051 * may want to hold the INVITE, or terminate the invite, or whatever.
2052 */
Benny Prijonob90fd382011-09-18 14:59:56 +00002053on_return:
2054 if (dlg) pjsip_dlg_dec_lock(dlg);
2055 pj_log_pop_indent();
2056 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002057
2058}
2059
2060
2061/*
Benny Prijono053f5222006-11-11 16:16:04 +00002062 * Initiate attended call transfer to the specified address.
2063 */
2064PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
2065 pjsua_call_id dest_call_id,
2066 unsigned options,
2067 const pjsua_msg_data *msg_data)
2068{
2069 pjsua_call *dest_call;
2070 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00002071 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00002072 pj_str_t str_dest;
2073 int len;
2074 pjsip_uri *uri;
2075 pj_status_t status;
2076
2077
2078 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2079 PJ_EINVAL);
2080 PJ_ASSERT_RETURN(dest_call_id>=0 &&
2081 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
2082 PJ_EINVAL);
2083
Benny Prijonob90fd382011-09-18 14:59:56 +00002084 PJ_LOG(4,(THIS_FILE, "Transfering call %d replacing with call %d",
2085 call_id, dest_call_id));
2086 pj_log_push_indent();
2087
Benny Prijono053f5222006-11-11 16:16:04 +00002088 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
2089 &dest_call, &dest_dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002090 if (status != PJ_SUCCESS) {
2091 pj_log_pop_indent();
Benny Prijono053f5222006-11-11 16:16:04 +00002092 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00002093 }
Benny Prijono053f5222006-11-11 16:16:04 +00002094
2095 /*
2096 * Create REFER destination URI with Replaces field.
2097 */
2098
2099 /* Make sure we have sufficient buffer's length */
Benny Prijonob90fd382011-09-18 14:59:56 +00002100 PJ_ASSERT_ON_FAIL(dest_dlg->remote.info_str.slen +
Benny Prijono053f5222006-11-11 16:16:04 +00002101 dest_dlg->call_id->id.slen +
2102 dest_dlg->remote.info->tag.slen +
2103 dest_dlg->local.info->tag.slen + 32
Benny Prijonob90fd382011-09-18 14:59:56 +00002104 < (long)sizeof(str_dest_buf),
2105 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002106
2107 /* Print URI */
2108 str_dest_buf[0] = '<';
2109 str_dest.slen = 1;
2110
Benny Prijonoa1e69682007-05-11 15:14:34 +00002111 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00002112 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
2113 str_dest_buf+1, sizeof(str_dest_buf)-1);
Benny Prijonob90fd382011-09-18 14:59:56 +00002114 if (len < 0) {
2115 status = PJSIP_EURITOOLONG;
2116 goto on_error;
2117 }
Benny Prijono053f5222006-11-11 16:16:04 +00002118
2119 str_dest.slen += len;
2120
2121
2122 /* Build the URI */
2123 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
2124 sizeof(str_dest_buf) - str_dest.slen,
2125 "?%s"
2126 "Replaces=%.*s"
2127 "%%3Bto-tag%%3D%.*s"
2128 "%%3Bfrom-tag%%3D%.*s>",
2129 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
2130 "" : "Require=replaces&"),
2131 (int)dest_dlg->call_id->id.slen,
2132 dest_dlg->call_id->id.ptr,
2133 (int)dest_dlg->remote.info->tag.slen,
2134 dest_dlg->remote.info->tag.ptr,
2135 (int)dest_dlg->local.info->tag.slen,
2136 dest_dlg->local.info->tag.ptr);
2137
Benny Prijonob90fd382011-09-18 14:59:56 +00002138 PJ_ASSERT_ON_FAIL(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
2139 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002140
2141 str_dest.ptr = str_dest_buf;
2142 str_dest.slen += len;
2143
2144 pjsip_dlg_dec_lock(dest_dlg);
2145
Benny Prijonob90fd382011-09-18 14:59:56 +00002146 status = pjsua_call_xfer(call_id, &str_dest, msg_data);
2147
2148 pj_log_pop_indent();
2149 return status;
2150
2151on_error:
2152 if (dest_dlg) pjsip_dlg_dec_lock(dest_dlg);
2153 pj_log_pop_indent();
2154 return status;
Benny Prijono053f5222006-11-11 16:16:04 +00002155}
2156
2157
2158/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002159 * Send DTMF digits to remote using RFC 2833 payload formats.
2160 */
2161PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
2162 const pj_str_t *digits)
2163{
2164 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002165 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002166 pj_status_t status;
2167
2168 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2169 PJ_EINVAL);
2170
Benny Prijonob90fd382011-09-18 14:59:56 +00002171 PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s",
2172 call_id, (int)digits->slen, digits->ptr));
2173 pj_log_push_indent();
2174
Benny Prijonodc752ca2006-09-22 16:55:42 +00002175 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002176 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002177 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002178
Benny Prijono0bc99a92011-03-17 04:34:43 +00002179 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002180 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002181 status = PJ_EINVALIDOP;
2182 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 }
2184
Benny Prijono0bc99a92011-03-17 04:34:43 +00002185 status = pjmedia_stream_dial_dtmf(
2186 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002187
Benny Prijonob90fd382011-09-18 14:59:56 +00002188on_return:
2189 if (dlg) pjsip_dlg_dec_lock(dlg);
2190 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191 return status;
2192}
2193
2194
2195/**
2196 * Send instant messaging inside INVITE session.
2197 */
2198PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
2199 const pj_str_t *mime_type,
2200 const pj_str_t *content,
2201 const pjsua_msg_data *msg_data,
2202 void *user_data)
2203{
2204 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002205 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002206 const pj_str_t mime_text_plain = pj_str("text/plain");
2207 pjsip_media_type ctype;
2208 pjsua_im_data *im_data;
2209 pjsip_tx_data *tdata;
2210 pj_status_t status;
2211
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2213 PJ_EINVAL);
2214
Benny Prijonob90fd382011-09-18 14:59:56 +00002215 PJ_LOG(4,(THIS_FILE, "Call %d sending %d bytes MESSAGE..",
2216 call_id, (int)content->slen));
2217 pj_log_push_indent();
2218
Benny Prijonodc752ca2006-09-22 16:55:42 +00002219 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002220 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002221 goto on_return;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002222
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002223 /* Set default media type if none is specified */
2224 if (mime_type == NULL) {
2225 mime_type = &mime_text_plain;
2226 }
2227
2228 /* Create request message. */
2229 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2230 -1, &tdata);
2231 if (status != PJ_SUCCESS) {
2232 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2233 goto on_return;
2234 }
2235
2236 /* Add accept header. */
2237 pjsip_msg_add_hdr( tdata->msg,
2238 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2239
2240 /* Parse MIME type */
2241 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2242
2243 /* Create "text/plain" message body. */
2244 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2245 &ctype.subtype, content);
2246 if (tdata->msg->body == NULL) {
2247 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2248 pjsip_tx_data_dec_ref(tdata);
2249 goto on_return;
2250 }
2251
2252 /* Add additional headers etc */
2253 pjsua_process_msg_data( tdata, msg_data);
2254
2255 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002256 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002257 im_data->acc_id = call->acc_id;
2258 im_data->call_id = call_id;
2259 im_data->to = call->inv->dlg->remote.info_str;
2260 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2261 im_data->user_data = user_data;
2262
2263
2264 /* Send the request. */
2265 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2266 pjsua_var.mod.id, im_data);
2267 if (status != PJ_SUCCESS) {
2268 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2269 goto on_return;
2270 }
2271
2272on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002273 if (dlg) pjsip_dlg_dec_lock(dlg);
2274 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002275 return status;
2276}
2277
2278
2279/*
2280 * Send IM typing indication inside INVITE session.
2281 */
2282PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2283 pj_bool_t is_typing,
2284 const pjsua_msg_data*msg_data)
2285{
2286 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002287 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002288 pjsip_tx_data *tdata;
2289 pj_status_t status;
2290
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002291 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2292 PJ_EINVAL);
2293
Benny Prijonob90fd382011-09-18 14:59:56 +00002294 PJ_LOG(4,(THIS_FILE, "Call %d sending typing indication..",
2295 call_id));
2296 pj_log_push_indent();
2297
Benny Prijonodc752ca2006-09-22 16:55:42 +00002298 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002299 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002300 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002301
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002302 /* Create request message. */
2303 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2304 -1, &tdata);
2305 if (status != PJ_SUCCESS) {
2306 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2307 goto on_return;
2308 }
2309
2310 /* Create "application/im-iscomposing+xml" msg body. */
2311 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2312 NULL, NULL, -1);
2313
2314 /* Add additional headers etc */
2315 pjsua_process_msg_data( tdata, msg_data);
2316
2317 /* Send the request. */
2318 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2319 if (status != PJ_SUCCESS) {
2320 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2321 goto on_return;
2322 }
2323
2324on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002325 if (dlg) pjsip_dlg_dec_lock(dlg);
2326 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002327 return status;
2328}
2329
2330
2331/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002332 * Send arbitrary request.
2333 */
2334PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2335 const pj_str_t *method_str,
2336 const pjsua_msg_data *msg_data)
2337{
2338 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002339 pjsip_dialog *dlg = NULL;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002340 pjsip_method method;
2341 pjsip_tx_data *tdata;
2342 pj_status_t status;
2343
2344 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2345 PJ_EINVAL);
2346
Benny Prijonob90fd382011-09-18 14:59:56 +00002347 PJ_LOG(4,(THIS_FILE, "Call %d sending %.*s request..",
2348 call_id, (int)method_str->slen, method_str->ptr));
2349 pj_log_push_indent();
2350
Benny Prijonofeb69f42007-10-05 09:12:26 +00002351 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2352 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002353 goto on_return;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002354
2355 /* Init method */
2356 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2357
2358 /* Create request message. */
2359 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2360 if (status != PJ_SUCCESS) {
2361 pjsua_perror(THIS_FILE, "Unable to create request", status);
2362 goto on_return;
2363 }
2364
2365 /* Add additional headers etc */
2366 pjsua_process_msg_data( tdata, msg_data);
2367
2368 /* Send the request. */
2369 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2370 if (status != PJ_SUCCESS) {
2371 pjsua_perror(THIS_FILE, "Unable to send request", status);
2372 goto on_return;
2373 }
2374
2375on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002376 if (dlg) pjsip_dlg_dec_lock(dlg);
2377 pj_log_pop_indent();
Benny Prijonofeb69f42007-10-05 09:12:26 +00002378 return status;
2379}
2380
2381
2382/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002383 * Terminate all calls.
2384 */
2385PJ_DEF(void) pjsua_call_hangup_all(void)
2386{
2387 unsigned i;
2388
Benny Prijonob90fd382011-09-18 14:59:56 +00002389 PJ_LOG(4,(THIS_FILE, "Hangup all calls.."));
2390 pj_log_push_indent();
2391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002392 PJSUA_LOCK();
2393
2394 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2395 if (pjsua_var.calls[i].inv)
2396 pjsua_call_hangup(i, 0, NULL, NULL);
2397 }
2398
2399 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002400 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002401}
2402
2403
Benny Prijono1e601552010-10-20 05:31:08 +00002404/* Proto */
2405static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002406
Benny Prijono1e601552010-10-20 05:31:08 +00002407/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002408static void reinv_timer_cb(pj_timer_heap_t *th,
2409 pj_timer_entry *entry)
2410{
2411 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2412 pjsip_dialog *dlg;
2413 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002414 pj_status_t status;
2415
2416 PJ_UNUSED_ARG(th);
2417
2418 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2419
2420 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2421 if (status != PJ_SUCCESS)
2422 return;
2423
Benny Prijono1e601552010-10-20 05:31:08 +00002424 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002425
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002426 pjsip_dlg_dec_lock(dlg);
2427}
2428
2429
2430/* Check if the specified format can be skipped in counting codecs */
2431static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2432 const pj_str_t *fmt)
2433{
Benny Prijono1e601552010-10-20 05:31:08 +00002434 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002435 unsigned pt;
2436
2437 pt = pj_strtoul(fmt);
2438
2439 /* Check for comfort noise */
2440 if (pt == PJMEDIA_RTP_PT_CN)
2441 return PJ_TRUE;
2442
2443 /* Dynamic PT, check the format name */
2444 if (pt >= 96) {
2445 pjmedia_sdp_attr *a;
2446 pjmedia_sdp_rtpmap rtpmap;
2447
2448 /* Get the format name */
2449 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2450 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2451 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002452 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002453 return PJ_TRUE;
2454 } else {
2455 /* Invalid SDP, should not reach here */
2456 pj_assert(!"SDP should have been validated!");
2457 return PJ_TRUE;
2458 }
2459 }
2460
2461 return PJ_FALSE;
2462}
2463
2464
Benny Prijono1e601552010-10-20 05:31:08 +00002465/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2466 * out of several codecs presented by callee in his answer.
2467 */
2468static pj_status_t perform_lock_codec(pjsua_call *call)
2469{
2470 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2471 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002472 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002473 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002474 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002475 pjsip_tx_data *tdata;
2476 pj_status_t status;
2477
2478 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2479 PJ_EINVALIDOP);
2480
2481 /* Verify if another SDP negotiation is in progress, e.g: session timer
2482 * or another re-INVITE.
2483 */
2484 if (call->inv==NULL || call->inv->neg==NULL ||
2485 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2486 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002487 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002488 }
2489
Benny Prijono02493272010-11-17 09:15:04 +00002490 /* Don't do this if call is disconnecting! */
2491 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2492 call->inv->cause >= 200)
2493 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002494 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002495 }
2496
Benny Prijono1e601552010-10-20 05:31:08 +00002497 /* Verify if another SDP negotiation has been completed by comparing
2498 * the SDP version.
2499 */
2500 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2501 if (status != PJ_SUCCESS)
2502 return status;
2503 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2504 return PJMEDIA_SDP_EINVER;
2505
2506 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2507
2508 /* Update the new offer so it contains only a codec. Note that formats
2509 * order in the offer should have been matched to the answer, so we can
2510 * just directly update the offer without looking-up the answer.
2511 */
2512 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002513
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002514 for (i = 0; i < call->med_cnt; ++i) {
2515 unsigned j = 0, codec_cnt = 0;
2516 const pjmedia_sdp_media *ref_m;
2517 pjmedia_sdp_media *m;
2518 pjsua_call_media *call_med = &call->media[i];
2519
2520 /* Verify if media is deactivated */
2521 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2522 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2523 call_med->dir == PJMEDIA_DIR_NONE)
2524 {
Benny Prijono1e601552010-10-20 05:31:08 +00002525 continue;
2526 }
2527
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002528 ref_m = local_sdp->media[i];
2529 m = new_sdp->media[i];
2530
2531 /* Verify that media must be active. */
2532 pj_assert(ref_m->desc.port);
2533
2534 while (j < m->desc.fmt_count) {
2535 pjmedia_sdp_attr *a;
2536 pj_str_t *fmt = &m->desc.fmt[j];
2537
2538 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2539 ++j;
2540 continue;
2541 }
2542
2543 /* Remove format */
2544 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2545 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2546 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2547 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2548 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2549 m->desc.fmt_count, j);
2550 --m->desc.fmt_count;
2551 }
2552
2553 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002554 }
2555
Nanang Izzuddinec919002010-11-25 09:27:06 +00002556 /* Last check if SDP trully needs to be updated. It is possible that OA
2557 * negotiations have completed and SDP has changed but we didn't
2558 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002559 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002560 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002561 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002562
2563 /* Send UPDATE or re-INVITE */
2564 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2565 PJSIP_H_ALLOW,
2566 NULL, &STR_UPDATE) ==
2567 PJSIP_DIALOG_CAP_SUPPORTED;
2568 if (rem_can_update) {
2569 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2570 } else {
2571 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2572 }
2573
2574 if (status==PJ_EINVALIDOP &&
2575 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2576 {
2577 /* Ups, let's reschedule again */
2578 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2579 pj_time_val_normalize(&delay);
2580 call->lock_codec.reinv_timer.id = PJ_TRUE;
2581 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2582 &call->lock_codec.reinv_timer, &delay);
2583 return status;
2584 } else if (status != PJ_SUCCESS) {
2585 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2586 status);
2587 return status;
2588 }
2589
2590 /* Send the UPDATE/re-INVITE request */
2591 status = pjsip_inv_send_msg(call->inv, tdata);
2592 if (status != PJ_SUCCESS) {
2593 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2594 status);
2595 return status;
2596 }
2597
2598 return status;
2599}
2600
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002601/* Check if remote answerer has given us more than one codecs. If so,
2602 * create another offer with one codec only to lock down the codec.
2603 */
2604static pj_status_t lock_codec(pjsua_call *call)
2605{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002606 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002607 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002608 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002609 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002610 unsigned i;
2611 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002612 pj_status_t status;
2613
Nanang Izzuddinec919002010-11-25 09:27:06 +00002614 /* Stop lock codec timer, if it is active */
2615 if (call->lock_codec.reinv_timer.id) {
2616 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2617 &call->lock_codec.reinv_timer);
2618 call->lock_codec.reinv_timer.id = PJ_FALSE;
2619 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002620
Nanang Izzuddinec919002010-11-25 09:27:06 +00002621 /* Skip this if we are the answerer */
2622 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2623 return PJ_SUCCESS;
2624 }
2625
Nanang Izzuddinec919002010-11-25 09:27:06 +00002626 /* Delay this when the SDP negotiation done in call state EARLY and
2627 * remote does not support UPDATE method.
2628 */
2629 if (inv->state == PJSIP_INV_STATE_EARLY &&
2630 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2631 PJSIP_DIALOG_CAP_SUPPORTED)
2632 {
2633 call->lock_codec.pending = PJ_TRUE;
2634 return PJ_SUCCESS;
2635 }
2636
2637 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002638 if (status != PJ_SUCCESS)
2639 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002640 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002641 if (status != PJ_SUCCESS)
2642 return status;
2643
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002644 /* Find multiple codecs answer in all media */
2645 for (i = 0; i < call->med_cnt; ++i) {
2646 pjsua_call_media *call_med = &call->media[i];
2647 const pjmedia_sdp_media *rem_m, *loc_m;
2648 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002649
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002650 /* Skip this if the media is inactive or error */
2651 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2652 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2653 call_med->dir == PJMEDIA_DIR_NONE)
2654 {
2655 continue;
2656 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002657
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002658 /* Remote may answer with less media lines. */
2659 if (i >= remote_sdp->media_count)
2660 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002661
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002662 rem_m = remote_sdp->media[i];
2663 loc_m = local_sdp->media[i];
2664
2665 /* Verify that media must be active. */
2666 pj_assert(loc_m->desc.port && rem_m->desc.port);
2667
2668 /* Count the formats in the answer. */
2669 if (rem_m->desc.fmt_count==1) {
2670 codec_cnt = 1;
2671 } else {
2672 unsigned j;
2673 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
2674 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
2675 ++codec_cnt;
2676 }
2677 }
2678
2679 if (codec_cnt > 1) {
2680 has_mult_fmt = PJ_TRUE;
2681 break;
2682 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002683 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002684
2685 /* Each media in the answer already contains single codec. */
2686 if (!has_mult_fmt) {
2687 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002688 return PJ_SUCCESS;
2689 }
2690
Nanang Izzuddinec919002010-11-25 09:27:06 +00002691 /* Remote keeps answering with multiple codecs, let's just give up
2692 * locking codec to avoid infinite retry loop.
2693 */
2694 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
2695 return PJ_SUCCESS;
2696
Benny Prijonob90fd382011-09-18 14:59:56 +00002697 PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002698 "updating media session to use only one codec.."));
2699
Benny Prijono1e601552010-10-20 05:31:08 +00002700 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002701
Benny Prijono1e601552010-10-20 05:31:08 +00002702 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
2703 * See: https://trac.pjsip.org/repos/ticket/1149
2704 */
2705 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
2706 (void*)(pj_size_t)call->index,
2707 &reinv_timer_cb);
2708 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2709 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002710
2711 return PJ_SUCCESS;
2712}
2713
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002714/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002715 * This callback receives notification from invite session when the
2716 * session state has changed.
2717 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002718static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2719 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002720{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002721 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002722
Benny Prijonob90fd382011-09-18 14:59:56 +00002723 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002724 PJSUA_LOCK();
2725
Benny Prijonoa1e69682007-05-11 15:14:34 +00002726 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002727
2728 if (!call) {
2729 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002730 pj_log_pop_indent();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002731 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002732 }
2733
Benny Prijonoe21e7842006-04-09 16:46:05 +00002734
2735 /* Get call times */
2736 switch (inv->state) {
2737 case PJSIP_INV_STATE_EARLY:
2738 case PJSIP_INV_STATE_CONNECTING:
2739 if (call->res_time.sec == 0)
2740 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002741 call->last_code = (pjsip_status_code)
2742 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002743 pj_strncpy(&call->last_text,
2744 &e->body.tsx_state.tsx->status_text,
2745 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002746 break;
2747 case PJSIP_INV_STATE_CONFIRMED:
2748 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002749
Nanang Izzuddinec919002010-11-25 09:27:06 +00002750 /* See if lock codec was pended as media update was done in the
2751 * EARLY state and remote does not support UPDATE.
2752 */
2753 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002754 pj_status_t status;
2755 status = lock_codec(call);
2756 if (status != PJ_SUCCESS) {
2757 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00002758 }
2759 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002760 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002761 break;
2762 case PJSIP_INV_STATE_DISCONNECTED:
2763 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002764 if (call->res_time.sec == 0)
2765 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002766 if (e->type == PJSIP_EVENT_TSX_STATE &&
2767 e->body.tsx_state.tsx->status_code > call->last_code)
2768 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002769 call->last_code = (pjsip_status_code)
2770 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002771 pj_strncpy(&call->last_text,
2772 &e->body.tsx_state.tsx->status_text,
2773 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002774 } else {
2775 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2776 pj_strncpy(&call->last_text,
2777 pjsip_get_status_text(call->last_code),
2778 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002779 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002780
2781 /* Stop lock codec timer, if it is active */
2782 if (call->lock_codec.reinv_timer.id) {
2783 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2784 &call->lock_codec.reinv_timer);
2785 call->lock_codec.reinv_timer.id = PJ_FALSE;
2786 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002787 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002788 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002789 call->last_code = (pjsip_status_code)
2790 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002791 pj_strncpy(&call->last_text,
2792 &e->body.tsx_state.tsx->status_text,
2793 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002794 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002795 }
2796
Benny Prijono26ff9062006-02-21 23:47:00 +00002797 /* If this is an outgoing INVITE that was created because of
2798 * REFER/transfer, send NOTIFY to transferer.
2799 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002800 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002801 int st_code = -1;
2802 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2803
2804
Benny Prijonoa91a0032006-02-26 21:23:45 +00002805 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002806 case PJSIP_INV_STATE_NULL:
2807 case PJSIP_INV_STATE_CALLING:
2808 /* Do nothing */
2809 break;
2810
2811 case PJSIP_INV_STATE_EARLY:
2812 case PJSIP_INV_STATE_CONNECTING:
2813 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002814 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2815 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2816 else
2817 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002818 break;
2819
Benny Prijono140beae2009-10-11 05:06:43 +00002820 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002821#if 0
2822/* We don't need this, as we've terminated the subscription in
2823 * CONNECTING state.
2824 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002825 /* When state is confirmed, send the final 200/OK and terminate
2826 * subscription.
2827 */
2828 st_code = e->body.tsx_state.tsx->status_code;
2829 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002830#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002831 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002832
2833 case PJSIP_INV_STATE_DISCONNECTED:
2834 st_code = e->body.tsx_state.tsx->status_code;
2835 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2836 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002837
Benny Prijono8b1889b2006-06-06 18:40:40 +00002838 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002839 /* Nothing to do. Just to keep gcc from complaining about
2840 * unused enums.
2841 */
2842 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002843 }
2844
2845 if (st_code != -1) {
2846 pjsip_tx_data *tdata;
2847 pj_status_t status;
2848
Benny Prijonoa91a0032006-02-26 21:23:45 +00002849 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002850 ev_state, st_code,
2851 NULL, &tdata);
2852 if (status != PJ_SUCCESS) {
2853 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2854 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002855 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002856 if (status != PJ_SUCCESS) {
2857 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2858 }
2859 }
2860 }
2861 }
2862
Benny Prijono84126ab2006-02-09 09:30:09 +00002863
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002864 if (pjsua_var.ua_cfg.cb.on_call_state)
2865 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002866
2867 /* call->inv may be NULL now */
2868
Benny Prijono84126ab2006-02-09 09:30:09 +00002869 /* Destroy media session when invite session is disconnected. */
2870 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002871
Benny Prijonoa91a0032006-02-26 21:23:45 +00002872 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002873
Benny Prijono275fd682006-03-22 11:59:11 +00002874 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002875 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002876
Benny Prijono105217f2006-03-06 16:25:59 +00002877 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002878 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002879 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002880
2881 /* Reset call */
2882 reset_call(call->index);
2883
Benny Prijono84126ab2006-02-09 09:30:09 +00002884 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002885
2886 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002887 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002888}
2889
2890/*
2891 * This callback is called by invite session framework when UAC session
2892 * has forked.
2893 */
2894static void pjsua_call_on_forked( pjsip_inv_session *inv,
2895 pjsip_event *e)
2896{
2897 PJ_UNUSED_ARG(inv);
2898 PJ_UNUSED_ARG(e);
2899
2900 PJ_TODO(HANDLE_FORKED_DIALOG);
2901}
2902
2903
2904/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002905 * Callback from UA layer when forked dialog response is received.
2906 */
2907pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2908{
2909 if (dlg->uac_has_2xx &&
2910 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
2911 pjsip_rdata_get_tsx(res) == NULL &&
2912 res->msg_info.msg->line.status.code/100 == 2)
2913 {
2914 pjsip_dialog *forked_dlg;
2915 pjsip_tx_data *bye;
2916 pj_status_t status;
2917
2918 /* Create forked dialog */
2919 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
2920 if (status != PJ_SUCCESS)
2921 return NULL;
2922
2923 pjsip_dlg_inc_lock(forked_dlg);
2924
2925 /* Disconnect the call */
2926 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
2927 -1, &bye);
2928 if (status == PJ_SUCCESS) {
2929 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
2930 }
2931
2932 pjsip_dlg_dec_lock(forked_dlg);
2933
2934 if (status != PJ_SUCCESS) {
2935 return NULL;
2936 }
2937
2938 return forked_dlg;
2939
2940 } else {
2941 return dlg;
2942 }
2943}
2944
2945/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002946 * Disconnect call upon error.
2947 */
2948static void call_disconnect( pjsip_inv_session *inv,
2949 int code )
2950{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002951 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002952 pjsip_tx_data *tdata;
2953 pj_status_t status;
2954
Benny Prijono59b3aed2008-01-15 16:54:54 +00002955 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2956
Benny Prijonoa38ada02006-07-02 14:22:35 +00002957 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002958 if (status != PJ_SUCCESS)
2959 return;
2960
2961 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00002962#if DISABLED_FOR_TICKET_1185
2963 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00002964 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2965 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002966 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002967 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002968
Benny Prijono734fc2d2008-03-17 16:05:35 +00002969 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002970 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002971 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002972 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002973 if (status == PJ_SUCCESS) {
2974 pjsip_create_sdp_body(tdata->pool, local_sdp,
2975 &tdata->msg->body);
2976 }
2977 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00002978#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00002979
2980 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002981}
2982
2983/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002984 * Callback to be called when SDP offer/answer negotiation has just completed
2985 * in the session. This function will start/update media if negotiation
2986 * has succeeded.
2987 */
2988static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2989 pj_status_t status)
2990{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002991 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002992 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002993 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002994 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002995
Benny Prijonob90fd382011-09-18 14:59:56 +00002996 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002997 PJSUA_LOCK();
2998
Benny Prijonoa1e69682007-05-11 15:14:34 +00002999 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003000
3001 if (status != PJ_SUCCESS) {
3002
3003 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3004
Benny Prijono2331d202008-06-26 15:46:52 +00003005 /* Do not deinitialize media since this may be a re-INVITE or
3006 * UPDATE (which in this case the media should not get affected
3007 * by the failed re-INVITE/UPDATE). The media will be shutdown
3008 * when call is disconnected anyway.
3009 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003010 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003011 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003012
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003013 /* Disconnect call if we're not in the middle of initializing an
3014 * UAS dialog and if this is not a re-INVITE
3015 */
3016 if (inv->state != PJSIP_INV_STATE_NULL &&
3017 inv->state != PJSIP_INV_STATE_CONFIRMED)
3018 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003019 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003020 }
3021
Benny Prijonob90fd382011-09-18 14:59:56 +00003022 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003023 }
3024
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003025
3026 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003027 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003028 if (status != PJ_SUCCESS) {
3029 pjsua_perror(THIS_FILE,
3030 "Unable to retrieve currently active local SDP",
3031 status);
3032 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003033 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003034 }
3035
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003036 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3037 if (status != PJ_SUCCESS) {
3038 pjsua_perror(THIS_FILE,
3039 "Unable to retrieve currently active remote SDP",
3040 status);
3041 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003042 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003043 }
3044
Benny Prijono91a6a172007-10-31 08:59:29 +00003045 /* Update remote's NAT type */
3046 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3047 update_remote_nat_type(call, remote_sdp);
3048 }
3049
3050 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003051 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003052 if (status != PJ_SUCCESS) {
3053 pjsua_perror(THIS_FILE, "Unable to create media session",
3054 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003055 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003056 /* No need to deinitialize; media will be shutdown when call
3057 * state is disconnected anyway.
3058 */
3059 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonob90fd382011-09-18 14:59:56 +00003060 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003061 }
3062
Nanang Izzuddinec919002010-11-25 09:27:06 +00003063 /* Ticket #476: make sure only one codec is specified in the answer. */
3064 status = lock_codec(call);
3065 if (status != PJ_SUCCESS) {
3066 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003067 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003068
3069 /* Call application callback, if any */
3070 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3071 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3072
Benny Prijonob90fd382011-09-18 14:59:56 +00003073on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003074 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003075 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003076}
3077
3078
Benny Prijonodd63b992010-10-01 02:03:42 +00003079/* Modify SDP for call hold. */
3080static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3081 pj_pool_t *pool,
3082 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003083{
Benny Prijono316f02a2011-04-07 07:53:25 +00003084 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003085
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003086 /* Call-hold is done by set the media direction to 'sendonly'
3087 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3088 * 'inactive' (PJMEDIA_DIR_NONE).
3089 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3090 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003091 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003092 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003093 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003094 /* https://trac.pjsip.org/repos/ticket/1142:
3095 * configuration to use c=0.0.0.0 for call hold.
3096 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003097
Benny Prijono316f02a2011-04-07 07:53:25 +00003098 for (mi=0; mi<sdp->media_count; ++mi) {
3099 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003100
Benny Prijono316f02a2011-04-07 07:53:25 +00003101 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3102 pjmedia_sdp_conn *conn;
3103 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003104
Benny Prijono316f02a2011-04-07 07:53:25 +00003105 /* Get SDP media connection line */
3106 conn = m->conn;
3107 if (!conn)
3108 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003109
Benny Prijono316f02a2011-04-07 07:53:25 +00003110 /* Modify address */
3111 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003112
Benny Prijono316f02a2011-04-07 07:53:25 +00003113 /* Remove existing directions attributes */
3114 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3115 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3116 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3117 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003118
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003119 /* Add inactive attribute */
3120 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003121 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003122
3123
3124 } else {
3125 pjmedia_sdp_attr *attr;
3126
3127 /* Remove existing directions attributes */
3128 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3129 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3130 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3131 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3132
3133 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3134 /* Add sendonly attribute */
3135 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3136 pjmedia_sdp_media_add_attr(m, attr);
3137 } else {
3138 /* Add inactive attribute */
3139 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3140 pjmedia_sdp_media_add_attr(m, attr);
3141 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003142 }
3143 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003144
Benny Prijonodd63b992010-10-01 02:03:42 +00003145 return PJ_SUCCESS;
3146}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003147
Benny Prijonodd63b992010-10-01 02:03:42 +00003148/* Create SDP for call hold. */
3149static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3150 pjmedia_sdp_session **p_sdp)
3151{
3152 pj_status_t status;
3153 pj_pool_t *pool;
3154 pjmedia_sdp_session *sdp;
3155
3156 /* Use call's provisional pool */
3157 pool = call->inv->pool_prov;
3158
3159 /* Create new offer */
3160 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3161 NULL);
3162 if (status != PJ_SUCCESS) {
3163 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3164 return status;
3165 }
3166
3167 status = modify_sdp_of_call_hold(call, pool, sdp);
3168 if (status != PJ_SUCCESS)
3169 return status;
3170
3171 *p_sdp = sdp;
3172
3173 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003174}
3175
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003176/*
3177 * Called when session received new offer.
3178 */
3179static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3180 const pjmedia_sdp_session *offer)
3181{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003182 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003183 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003184 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003185 pj_status_t status;
3186
3187 PJSUA_LOCK();
3188
Benny Prijonoa1e69682007-05-11 15:14:34 +00003189 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003190
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003191 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003192 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3193 call->index));
Benny Prijonob90fd382011-09-18 14:59:56 +00003194 pj_log_push_indent();
Benny Prijono667952e2007-04-02 19:27:54 +00003195
Benny Prijono40d62b62009-08-12 17:53:47 +00003196 status = pjsua_media_channel_create_sdp(call->index,
3197 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003198 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003199 if (status != PJ_SUCCESS) {
3200 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003201 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003202 }
3203
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003204 /* Validate media count in the generated answer */
3205 pj_assert(answer->media_count == offer->media_count);
3206
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003207 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003208 for (i = 0; i < answer->media_count; ++i) {
3209 pjmedia_sdp_conn *conn;
3210
3211 conn = offer->media[i]->conn;
3212 if (!conn)
3213 conn = offer->conn;
3214
3215 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3216 pj_strcmp2(&conn->addr, "0")==0)
3217 {
3218 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3219
3220 /* Modify answer address */
3221 if (a_conn) {
3222 a_conn->addr = pj_str("0.0.0.0");
3223 } else if (answer->conn == NULL ||
3224 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3225 {
3226 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3227 pjmedia_sdp_conn);
3228 a_conn->net_type = pj_str("IN");
3229 a_conn->addr_type = pj_str("IP4");
3230 a_conn->addr = pj_str("0.0.0.0");
3231 answer->media[i]->conn = a_conn;
3232 }
3233 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003234 }
3235
3236 /* Check if call is on-hold */
3237 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003238 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003239 }
3240
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003241 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3242 if (status != PJ_SUCCESS) {
3243 pjsua_perror(THIS_FILE, "Unable to set answer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003244 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003245 }
3246
Benny Prijonob90fd382011-09-18 14:59:56 +00003247on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003248 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003249 pj_log_pop_indent();
Benny Prijono84126ab2006-02-09 09:30:09 +00003250}
3251
3252
3253/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003254 * Called to generate new offer.
3255 */
3256static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3257 pjmedia_sdp_session **offer)
3258{
3259 pjsua_call *call;
3260 pj_status_t status;
3261
Benny Prijonob90fd382011-09-18 14:59:56 +00003262 pj_log_push_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003263 PJSUA_LOCK();
3264
3265 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3266
3267 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003268 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003269 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003270 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003271 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003272 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003273 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003274 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3275 call->index));
3276
Benny Prijono40d62b62009-08-12 17:53:47 +00003277 status = pjsua_media_channel_create_sdp(call->index,
3278 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003279 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003280 }
3281
3282 if (status != PJ_SUCCESS) {
3283 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003284 goto on_return;
Benny Prijono77998ce2007-06-20 10:03:46 +00003285 }
3286
Benny Prijonob90fd382011-09-18 14:59:56 +00003287on_return:
Benny Prijono77998ce2007-06-20 10:03:46 +00003288 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003289 pj_log_pop_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003290}
3291
3292
3293/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003294 * Callback called by event framework when the xfer subscription state
3295 * has changed.
3296 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003297static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3298{
3299
3300 PJ_UNUSED_ARG(event);
3301
Benny Prijonob90fd382011-09-18 14:59:56 +00003302 pj_log_push_indent();
3303
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003304 /*
3305 * When subscription is accepted (got 200/OK to REFER), check if
3306 * subscription suppressed.
3307 */
3308 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3309
3310 pjsip_rx_data *rdata;
3311 pjsip_generic_string_hdr *refer_sub;
3312 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3313 pjsua_call *call;
3314
Benny Prijonoa1e69682007-05-11 15:14:34 +00003315 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003316
3317 /* Must be receipt of response message */
3318 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3319 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3320 rdata = event->body.tsx_state.src.rdata;
3321
3322 /* Find Refer-Sub header */
3323 refer_sub = (pjsip_generic_string_hdr*)
3324 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3325 &REFER_SUB, NULL);
3326
3327 /* Check if subscription is suppressed */
3328 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3329 /* Since no subscription is desired, assume that call has been
3330 * transfered successfully.
3331 */
3332 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3333 const pj_str_t ACCEPTED = { "Accepted", 8 };
3334 pj_bool_t cont = PJ_FALSE;
3335 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3336 200,
3337 &ACCEPTED,
3338 PJ_TRUE,
3339 &cont);
3340 }
3341
3342 /* Yes, subscription is suppressed.
3343 * Terminate our subscription now.
3344 */
3345 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3346 "event subcription..."));
3347 pjsip_evsub_terminate(sub, PJ_TRUE);
3348
3349 } else {
3350 /* Notify application about call transfer progress.
3351 * Initially notify with 100/Accepted status.
3352 */
3353 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3354 const pj_str_t ACCEPTED = { "Accepted", 8 };
3355 pj_bool_t cont = PJ_FALSE;
3356 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3357 100,
3358 &ACCEPTED,
3359 PJ_FALSE,
3360 &cont);
3361 }
3362 }
3363 }
3364 /*
3365 * On incoming NOTIFY, notify application about call transfer progress.
3366 */
3367 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3368 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3369 {
3370 pjsua_call *call;
3371 pjsip_msg *msg;
3372 pjsip_msg_body *body;
3373 pjsip_status_line status_line;
3374 pj_bool_t is_last;
3375 pj_bool_t cont;
3376 pj_status_t status;
3377
Benny Prijonoa1e69682007-05-11 15:14:34 +00003378 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003379
3380 /* When subscription is terminated, clear the xfer_sub member of
3381 * the inv_data.
3382 */
3383 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3384 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3385 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3386
3387 }
3388
3389 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3390 /* Application is not interested with call progress status */
Benny Prijonob90fd382011-09-18 14:59:56 +00003391 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003392 }
3393
3394 /* This better be a NOTIFY request */
3395 if (event->type == PJSIP_EVENT_TSX_STATE &&
3396 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3397 {
3398 pjsip_rx_data *rdata;
3399
3400 rdata = event->body.tsx_state.src.rdata;
3401
3402 /* Check if there's body */
3403 msg = rdata->msg_info.msg;
3404 body = msg->body;
3405 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003406 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003407 "Warning: received NOTIFY without message body"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003408 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003409 }
3410
3411 /* Check for appropriate content */
3412 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3413 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3414 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003415 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003416 "Warning: received NOTIFY with non message/sipfrag "
3417 "content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003418 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003419 }
3420
3421 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003422 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003423 &status_line);
3424 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003425 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003426 "Warning: received NOTIFY with invalid "
3427 "message/sipfrag content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003428 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003429 }
3430
3431 } else {
3432 status_line.code = 500;
3433 status_line.reason = *pjsip_get_status_text(500);
3434 }
3435
3436 /* Notify application */
3437 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3438 cont = !is_last;
3439 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3440 status_line.code,
3441 &status_line.reason,
3442 is_last, &cont);
3443
3444 if (!cont) {
3445 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3446 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003447
3448 /* If the call transfer has completed but the subscription is
3449 * not terminated, terminate it now.
3450 */
3451 if (status_line.code/100 == 2 && !is_last) {
3452 pjsip_tx_data *tdata;
3453
3454 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3455 0, &tdata);
3456 if (status == PJ_SUCCESS)
3457 status = pjsip_evsub_send_request(sub, tdata);
3458 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003459 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003460
3461on_return:
3462 pj_log_pop_indent();
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003463}
3464
3465
3466/*
3467 * Callback called by event framework when the xfer subscription state
3468 * has changed.
3469 */
3470static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003471{
Benny Prijono26ff9062006-02-21 23:47:00 +00003472 PJ_UNUSED_ARG(event);
3473
Benny Prijonob90fd382011-09-18 14:59:56 +00003474 pj_log_push_indent();
3475
Benny Prijono26ff9062006-02-21 23:47:00 +00003476 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003477 * When subscription is terminated, clear the xfer_sub member of
3478 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003479 */
3480 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003481 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003482
Benny Prijonoa1e69682007-05-11 15:14:34 +00003483 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003484 if (!call)
Benny Prijonob90fd382011-09-18 14:59:56 +00003485 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003486
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003487 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003488 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003489
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003490 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003491 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003492
3493on_return:
3494 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003495}
3496
3497
3498/*
3499 * Follow transfer (REFER) request.
3500 */
3501static void on_call_transfered( pjsip_inv_session *inv,
3502 pjsip_rx_data *rdata )
3503{
3504 pj_status_t status;
3505 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003506 pjsua_call *existing_call;
3507 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003508 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003509 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003510 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003511 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003512 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003513 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003514 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003515 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003516 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003517 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003518 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003519 pjsip_evsub *sub;
3520
Benny Prijonob90fd382011-09-18 14:59:56 +00003521 pj_log_push_indent();
3522
Benny Prijonoa1e69682007-05-11 15:14:34 +00003523 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003524
Benny Prijono26ff9062006-02-21 23:47:00 +00003525 /* Find the Refer-To header */
3526 refer_to = (pjsip_generic_string_hdr*)
3527 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3528
3529 if (refer_to == NULL) {
3530 /* Invalid Request.
3531 * No Refer-To header!
3532 */
3533 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003534 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003535 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003536 }
3537
Benny Prijonoc8141a82006-08-20 09:12:19 +00003538 /* Find optional Refer-Sub header */
3539 refer_sub = (pjsip_generic_string_hdr*)
3540 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3541
3542 if (refer_sub) {
3543 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3544 no_refer_sub = PJ_TRUE;
3545 }
3546
Benny Prijono053f5222006-11-11 16:16:04 +00003547 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3548 * request.
3549 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003550 ref_by_hdr = (pjsip_hdr*)
3551 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003552 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003553
Benny Prijono9fc735d2006-05-28 14:58:12 +00003554 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003555 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003556 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3557 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3558 &refer_to->hvalue,
3559 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003560
3561 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003562 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003563 if (code >= 300) {
3564 /* Application rejects call transfer request */
3565 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003566 goto on_return;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003567 }
3568
Benny Prijono26ff9062006-02-21 23:47:00 +00003569 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3570 (int)inv->dlg->remote.info_str.slen,
3571 inv->dlg->remote.info_str.ptr,
3572 (int)refer_to->hvalue.slen,
3573 refer_to->hvalue.ptr));
3574
Benny Prijonoc8141a82006-08-20 09:12:19 +00003575 if (no_refer_sub) {
3576 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003577 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003578 */
3579 pjsip_tx_data *tdata;
3580 const pj_str_t str_false = { "false", 5};
3581 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003582
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003583 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3584 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003585 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003586 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003587 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003588 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003589 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003590
Benny Prijonoc8141a82006-08-20 09:12:19 +00003591 /* Add Refer-Sub header */
3592 hdr = (pjsip_hdr*)
3593 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3594 &str_false);
3595 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003596
Benny Prijono26ff9062006-02-21 23:47:00 +00003597
Benny Prijonoc8141a82006-08-20 09:12:19 +00003598 /* Send answer */
3599 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3600 tdata);
3601 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003602 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003603 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003604 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003605 }
3606
3607 /* Don't have subscription */
3608 sub = NULL;
3609
3610 } else {
3611 struct pjsip_evsub_user xfer_cb;
3612 pjsip_hdr hdr_list;
3613
3614 /* Init callback */
3615 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003616 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003617
3618 /* Init additional header list to be sent with REFER response */
3619 pj_list_init(&hdr_list);
3620
3621 /* Create transferee event subscription */
3622 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3623 if (status != PJ_SUCCESS) {
3624 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3625 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003626 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003627 }
3628
3629 /* If there's Refer-Sub header and the value is "true", send back
3630 * Refer-Sub in the response with value "true" too.
3631 */
3632 if (refer_sub) {
3633 const pj_str_t str_true = { "true", 4 };
3634 pjsip_hdr *hdr;
3635
3636 hdr = (pjsip_hdr*)
3637 pjsip_generic_string_hdr_create(inv->dlg->pool,
3638 &str_refer_sub,
3639 &str_true);
3640 pj_list_push_back(&hdr_list, hdr);
3641
3642 }
3643
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003644 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003645 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3646
3647 /* Create initial NOTIFY request */
3648 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3649 100, NULL, &tdata);
3650 if (status != PJ_SUCCESS) {
3651 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3652 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003653 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003654 }
3655
3656 /* Send initial NOTIFY request */
3657 status = pjsip_xfer_send_request( sub, tdata);
3658 if (status != PJ_SUCCESS) {
3659 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003660 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003661 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003662 }
3663
3664 /* We're cheating here.
3665 * We need to get a null terminated string from a pj_str_t.
3666 * So grab the pointer from the hvalue and NULL terminate it, knowing
3667 * that the NULL position will be occupied by a newline.
3668 */
3669 uri = refer_to->hvalue.ptr;
3670 uri[refer_to->hvalue.slen] = '\0';
3671
Benny Prijono053f5222006-11-11 16:16:04 +00003672 /* Init msg_data */
3673 pjsua_msg_data_init(&msg_data);
3674
3675 /* If Referred-By header is present in the REFER request, copy this
3676 * to the outgoing INVITE request.
3677 */
3678 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003679 pjsip_hdr *dup = (pjsip_hdr*)
3680 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003681 pj_list_push_back(&msg_data.hdr_list, dup);
3682 }
3683
Benny Prijono26ff9062006-02-21 23:47:00 +00003684 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003685 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003686 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003687 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003688 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003689 if (status != PJ_SUCCESS) {
3690
Benny Prijonoc8141a82006-08-20 09:12:19 +00003691 /* Notify xferer about the error (if we have subscription) */
3692 if (sub) {
3693 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3694 500, NULL, &tdata);
3695 if (status != PJ_SUCCESS) {
3696 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3697 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003698 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003699 }
3700 status = pjsip_xfer_send_request(sub, tdata);
3701 if (status != PJ_SUCCESS) {
3702 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3703 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003704 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003705 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003706 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003707 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003708 }
3709
Benny Prijonoc8141a82006-08-20 09:12:19 +00003710 if (sub) {
3711 /* Put the server subscription in inv_data.
3712 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3713 * reported back to the server subscription.
3714 */
3715 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003716
Benny Prijonoc8141a82006-08-20 09:12:19 +00003717 /* Put the invite_data in the subscription. */
3718 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3719 &pjsua_var.calls[new_call]);
3720 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003721
3722on_return:
3723 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003724}
3725
3726
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003727
Benny Prijono26ff9062006-02-21 23:47:00 +00003728/*
3729 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003730 * session. We use this to trap:
3731 * - incoming REFER request.
3732 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003733 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003734static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3735 pjsip_transaction *tsx,
3736 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003737{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003738 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003739
Benny Prijonob90fd382011-09-18 14:59:56 +00003740 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003741 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003742
Benny Prijono2285e7e2008-12-17 14:28:18 +00003743 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3744
Benny Prijonob90fd382011-09-18 14:59:56 +00003745 if (call == NULL)
3746 goto on_return;
Benny Prijono2285e7e2008-12-17 14:28:18 +00003747
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003748 if (call->inv == NULL) {
3749 /* Shouldn't happen. It happens only when we don't terminate the
3750 * server subscription caused by REFER after the call has been
3751 * transfered (and this call has been disconnected), and we
3752 * receive another REFER for this call.
3753 */
Benny Prijonob90fd382011-09-18 14:59:56 +00003754 goto on_return;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003755 }
3756
Benny Prijonofeb69f42007-10-05 09:12:26 +00003757 /* Notify application callback first */
3758 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3759 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3760 }
3761
Benny Prijono26ff9062006-02-21 23:47:00 +00003762 if (tsx->role==PJSIP_ROLE_UAS &&
3763 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003764 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003765 {
3766 /*
3767 * Incoming REFER request.
3768 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003769 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003770
Benny Prijono26ff9062006-02-21 23:47:00 +00003771 }
Benny Prijonob0808372006-03-02 21:18:58 +00003772 else if (tsx->role==PJSIP_ROLE_UAS &&
3773 tsx->state==PJSIP_TSX_STATE_TRYING &&
3774 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3775 {
3776 /*
3777 * Incoming MESSAGE request!
3778 */
3779 pjsip_rx_data *rdata;
3780 pjsip_msg *msg;
3781 pjsip_accept_hdr *accept_hdr;
3782 pj_status_t status;
3783
3784 rdata = e->body.tsx_state.src.rdata;
3785 msg = rdata->msg_info.msg;
3786
3787 /* Request MUST have message body, with Content-Type equal to
3788 * "text/plain".
3789 */
3790 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3791
3792 pjsip_hdr hdr_list;
3793
3794 pj_list_init(&hdr_list);
3795 pj_list_push_back(&hdr_list, accept_hdr);
3796
3797 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3798 NULL, &hdr_list, NULL );
Benny Prijonob90fd382011-09-18 14:59:56 +00003799 goto on_return;
Benny Prijonob0808372006-03-02 21:18:58 +00003800 }
3801
3802 /* Respond with 200 first, so that remote doesn't retransmit in case
3803 * the UI takes too long to process the message.
3804 */
3805 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3806
3807 /* Process MESSAGE request */
3808 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3809 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003810
Benny Prijonob0808372006-03-02 21:18:58 +00003811 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003812 else if (tsx->role == PJSIP_ROLE_UAC &&
3813 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003814 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003815 /* Handle outgoing pager status */
3816 if (tsx->status_code >= 200) {
3817 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003818
Benny Prijonoa1e69682007-05-11 15:14:34 +00003819 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003820 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003821
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003822 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3823 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3824 &im_data->to,
3825 &im_data->body,
3826 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003827 (pjsip_status_code)
3828 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003829 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003830 }
Benny Prijonofccab712006-02-22 22:23:22 +00003831 }
Benny Prijono834aee32006-02-19 01:38:06 +00003832 }
Benny Prijono834aee32006-02-19 01:38:06 +00003833
Benny Prijonob90fd382011-09-18 14:59:56 +00003834on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003835 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003836 pj_log_pop_indent();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003837}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003838
3839
3840/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003841static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3842 const pjsip_uri *target,
3843 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003844{
3845 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003846 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003847
Benny Prijonob90fd382011-09-18 14:59:56 +00003848 pj_log_push_indent();
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003849 PJSUA_LOCK();
3850
3851 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003852 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3853 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003854 } else {
3855 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3856 "(callback not implemented by application). Disconnecting "
3857 "call.",
3858 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003859 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003860 }
3861
3862 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003863 pj_log_pop_indent();
Benny Prijono08a48b82008-11-27 12:42:07 +00003864
3865 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003866}
3867