blob: 2447b80cf23db5960352b4ef6caf1564847e5b85 [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 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000119 pjsua_call_setting_default(&call->opt);
Benny Prijono105217f2006-03-06 16:25:59 +0000120}
121
122
Benny Prijono275fd682006-03-22 11:59:11 +0000123/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000125 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000127{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 pjsip_inv_callback inv_cb;
129 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000130 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 /* Init calls array. */
134 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
135 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000136
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000137 /* Copy config */
138 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000139
Benny Prijono1cd713b2009-11-11 00:33:00 +0000140 /* Verify settings */
141 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
142 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
143 }
144
Benny Prijono91d06b62008-09-20 12:16:56 +0000145 /* Check the route URI's and force loose route if required */
146 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
147 status = normalize_route_uri(pjsua_var.pool,
148 &pjsua_var.ua_cfg.outbound_proxy[i]);
149 if (status != PJ_SUCCESS)
150 return status;
151 }
152
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000154 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
156 inv_cb.on_new_session = &pjsua_call_on_forked;
157 inv_cb.on_media_update = &pjsua_call_on_media_update;
158 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000159 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000161 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000162
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000163 /* Initialize invite session module: */
164 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
165 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
166
Benny Prijonoc8141a82006-08-20 09:12:19 +0000167 /* Add "norefersub" in Supported header */
168 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
169 NULL, 1, &str_norefersub);
170
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000171 return status;
172}
173
174
175/*
176 * Start call subsystem.
177 */
178pj_status_t pjsua_call_subsys_start(void)
179{
180 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000181 return PJ_SUCCESS;
182}
183
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186 * Get maximum number of calls configured in pjsua.
187 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000188PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000189{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191}
192
193
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194/*
195 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000197PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200}
201
202
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203/*
204 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
207 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
216 if (!pjsua_var.calls[i].inv)
217 continue;
218 ids[c] = i;
219 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000220 }
221
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000223
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000225
226 return PJ_SUCCESS;
227}
228
229
Benny Prijono5773cd62008-01-19 13:01:42 +0000230/* Allocate one call id */
231static pjsua_call_id alloc_call_id(void)
232{
233 pjsua_call_id cid;
234
235#if 1
236 /* New algorithm: round-robin */
237 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
238 pjsua_var.next_call_id < 0)
239 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000240 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000241 }
242
243 for (cid=pjsua_var.next_call_id;
244 cid<(int)pjsua_var.ua_cfg.max_calls;
245 ++cid)
246 {
247 if (pjsua_var.calls[cid].inv == NULL) {
248 ++pjsua_var.next_call_id;
249 return cid;
250 }
251 }
252
253 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
254 if (pjsua_var.calls[cid].inv == NULL) {
255 ++pjsua_var.next_call_id;
256 return cid;
257 }
258 }
259
260#else
261 /* Old algorithm */
262 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
263 if (pjsua_var.calls[cid].inv == NULL)
264 return cid;
265 }
266#endif
267
268 return PJSUA_INVALID_ID;
269}
270
Benny Prijonod8179652008-01-23 20:39:07 +0000271/* Get signaling secure level.
272 * Return:
273 * 0: if signaling is not secure
274 * 1: if TLS transport is used for immediate hop
275 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000276 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000277static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000278{
279 const pj_str_t tls = pj_str(";transport=tls");
280 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000281 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000282
283 if (pj_stristr(dst_uri, &sips))
284 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000285
286 if (!pj_list_empty(&acc->route_set)) {
287 pjsip_route_hdr *r = acc->route_set.next;
288 pjsip_uri *uri = r->name_addr.uri;
289 pjsip_sip_uri *sip_uri;
290
291 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
292 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
293 return 1;
294
295 } else {
296 if (pj_stristr(dst_uri, &tls))
297 return 1;
298 }
299
Benny Prijonod8179652008-01-23 20:39:07 +0000300 return 0;
301}
302
Benny Prijono224b4e22008-06-19 14:10:28 +0000303/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000304static int call_get_secure_level(pjsua_call *call)
305{
306 if (call->inv->dlg->secure)
307 return 2;
308
309 if (!pj_list_empty(&call->inv->dlg->route_set)) {
310 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
311 pjsip_uri *uri = r->name_addr.uri;
312 pjsip_sip_uri *sip_uri;
313
314 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
315 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
316 return 1;
317
318 } else {
319 pjsip_sip_uri *sip_uri;
320
321 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
322 return 2;
323 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
324 return 0;
325
326 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
327 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
328 return 1;
329 }
330
331 return 0;
332}
Benny Prijono224b4e22008-06-19 14:10:28 +0000333*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000334
Sauw Mingec765352011-10-03 02:04:36 +0000335/* Outgoing call callback when media transport creation is completed. */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000336static pj_status_t
337on_make_call_med_tp_complete(pjsua_call_id call_id,
338 const pjsua_med_tp_state_info *info)
339{
340 pjmedia_sdp_session *offer;
341 pjsip_inv_session *inv = NULL;
342 pjsua_call *call = &pjsua_var.calls[call_id];
343 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
344 pjsip_dialog *dlg = call->async_call.dlg;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000345 unsigned options = 0;
Sauw Ming73ecfe82011-09-21 10:20:01 +0000346 pjsip_tx_data *tdata;
347 pj_status_t status = (info? info->status: PJ_SUCCESS);
348
349 PJSUA_LOCK();
350
351 /* Increment the dialog's lock otherwise when invite session creation
352 * fails the dialog will be destroyed prematurely.
353 */
354 pjsip_dlg_inc_lock(dlg);
355
356 if (status != PJ_SUCCESS) {
357 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
358 goto on_error;
359 }
360
Sauw Ming903154f2011-10-03 08:22:48 +0000361 /* pjsua_media_channel_deinit() has been called. */
362 if (call->async_call.med_ch_deinit)
363 goto on_error;
364
Sauw Ming73ecfe82011-09-21 10:20:01 +0000365 /* Create offer */
366 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
367 &offer, NULL);
368 if (status != PJ_SUCCESS) {
369 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
370 goto on_error;
371 }
372
373 /* Create the INVITE session: */
374 options |= PJSIP_INV_SUPPORT_100REL;
375 if (acc->cfg.require_100rel)
376 options |= PJSIP_INV_REQUIRE_100REL;
377 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
378 options |= PJSIP_INV_SUPPORT_TIMER;
379 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
380 options |= PJSIP_INV_REQUIRE_TIMER;
381 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
382 options |= PJSIP_INV_ALWAYS_USE_TIMER;
383 }
384
385 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
386 if (status != PJ_SUCCESS) {
387 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
388 goto on_error;
389 }
390
391 /* Init Session Timers */
392 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
393 if (status != PJ_SUCCESS) {
394 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
395 goto on_error;
396 }
397
398 /* Create and associate our data in the session. */
399 call->inv = inv;
400
401 dlg->mod_data[pjsua_var.mod.id] = call;
402 inv->mod_data[pjsua_var.mod.id] = call;
403
404 /* If account is locked to specific transport, then lock dialog
405 * to this transport too.
406 */
407 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
408 pjsip_tpselector tp_sel;
409
410 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
411 pjsip_dlg_set_transport(dlg, &tp_sel);
412 }
413
414 /* Set dialog Route-Set: */
415 if (!pj_list_empty(&acc->route_set))
416 pjsip_dlg_set_route_set(dlg, &acc->route_set);
417
418
419 /* Set credentials: */
420 if (acc->cred_cnt) {
421 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
422 acc->cred_cnt, acc->cred);
423 }
424
425 /* Set authentication preference */
426 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
427
428 /* Create initial INVITE: */
429
430 status = pjsip_inv_invite(inv, &tdata);
431 if (status != PJ_SUCCESS) {
432 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
433 status);
434 goto on_error;
435 }
436
437
438 /* Add additional headers etc */
439
440 pjsua_process_msg_data( tdata,
441 call->async_call.call_var.out_call.msg_data);
442
443 /* Must increment call counter now */
444 ++pjsua_var.call_cnt;
445
446 /* Send initial INVITE: */
447
448 status = pjsip_inv_send_msg(inv, tdata);
449 if (status != PJ_SUCCESS) {
450 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
451 status);
452
453 /* Upon failure to send first request, the invite
454 * session would have been cleared.
455 */
456 inv = NULL;
457 goto on_error;
458 }
459
460 /* Done. */
461
462 pjsip_dlg_dec_lock(dlg);
463 PJSUA_UNLOCK();
464
465 return PJ_SUCCESS;
466
467on_error:
Sauw Mingc8e12942011-10-25 08:51:02 +0000468 if (inv == NULL && call_id != -1 && pjsua_var.ua_cfg.cb.on_call_state)
469 (*pjsua_var.ua_cfg.cb.on_call_state)(call_id, NULL);
470
Sauw Ming73ecfe82011-09-21 10:20:01 +0000471 if (dlg) {
472 /* This may destroy the dialog */
473 pjsip_dlg_dec_lock(dlg);
474 }
475
476 if (inv != NULL) {
477 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
478 }
479
480 if (call_id != -1) {
481 reset_call(call_id);
482 pjsua_media_channel_deinit(call_id);
483 }
484
485 PJSUA_UNLOCK();
486 return status;
487}
488
Benny Prijonodb844a42008-02-02 17:07:18 +0000489
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000490/*
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000491 * Initialize call settings based on account ID.
492 */
493PJ_DEF(void) pjsua_call_setting_default(pjsua_call_setting *opt)
494{
495 pj_assert(opt);
496
497 pj_bzero(opt, sizeof(*opt));
498 opt->flag = PJSUA_CALL_INCLUDE_DISABLED_MEDIA;
499 opt->audio_cnt = 1;
500
501#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
502 opt->video_cnt = 1;
503 //{
504 // unsigned i;
505 // for (i = 0; i < PJ_ARRAY_SIZE(opt->vid_cap_dev); ++i)
506 // opt->vid_cap_dev[i] = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
507 //}
508#endif
509}
510
511static pj_status_t apply_call_setting(pjsua_call *call,
512 const pjsua_call_setting *opt,
513 const pjmedia_sdp_session *rem_sdp)
514{
515 pj_assert(call);
516
517 if (!opt)
518 return PJ_SUCCESS;
519
520#if !PJMEDIA_HAS_VIDEO
521 pj_assert(opt->video_cnt == 0);
522#endif
523
524 /* If call is established, reinit media channel */
525 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
526 pjsua_call_setting old_opt;
527 pj_status_t status;
528
529 old_opt = call->opt;
530 call->opt = *opt;
531
532 /* Reinit media channel when media count is changed */
533 if (opt->audio_cnt != old_opt.audio_cnt ||
534 opt->video_cnt != old_opt.video_cnt)
535 {
536 pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC;
537 status = pjsua_media_channel_init(call->index, role,
538 call->secure_level,
539 call->inv->pool_prov,
540 rem_sdp, NULL,
541 PJ_FALSE, NULL);
542 if (status != PJ_SUCCESS) {
543 pjsua_perror(THIS_FILE, "Error re-initializing media channel",
544 status);
545 return status;
546 }
547 }
548 } else {
549 call->opt = *opt;
550 }
551
552 return PJ_SUCCESS;
553}
554
555/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000556 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000557 */
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000558PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id,
559 const pj_str_t *dest_uri,
560 const pjsua_call_setting *opt,
561 void *user_data,
562 const pjsua_msg_data *msg_data,
563 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000564{
Benny Prijonob90fd382011-09-18 14:59:56 +0000565 pj_pool_t *tmp_pool = NULL;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000566 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000567 pjsua_acc *acc;
568 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000569 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000570 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000571 pj_status_t status;
572
Benny Prijono9fc735d2006-05-28 14:58:12 +0000573
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000575 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000576 PJ_EINVAL);
577
Benny Prijono320fa4d2006-12-07 10:09:16 +0000578 /* Check arguments */
579 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
580
Benny Prijonob90fd382011-09-18 14:59:56 +0000581 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
582 (int)dest_uri->slen, dest_uri->ptr));
583
584 pj_log_push_indent();
585
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJSUA_LOCK();
587
Benny Prijonof798e502009-03-09 13:08:16 +0000588 /* Create sound port if none is instantiated, to check if sound device
589 * can be used. But only do this with the conference bridge, as with
590 * audio switchboard (i.e. APS-Direct), we can only open the sound
591 * device once the correct format has been known
592 */
593 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
594 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000595 {
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000596 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
Benny Prijonob90fd382011-09-18 14:59:56 +0000597 if (status != PJ_SUCCESS)
598 goto on_error;
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000599 }
600
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 acc = &pjsua_var.acc[acc_id];
602 if (!acc->valid) {
603 pjsua_perror(THIS_FILE, "Unable to make call because account "
604 "is not valid", PJ_EINVALIDOP);
Benny Prijonob90fd382011-09-18 14:59:56 +0000605 status = PJ_EINVALIDOP;
606 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000607 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000608
Benny Prijonoa91a0032006-02-26 21:23:45 +0000609 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000610 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000611
Benny Prijono5773cd62008-01-19 13:01:42 +0000612 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000613 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonob90fd382011-09-18 14:59:56 +0000614 status = PJ_ETOOMANY;
615 goto on_error;
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000616 }
617
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000618 call = &pjsua_var.calls[call_id];
619
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000620 /* Associate session with account */
621 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000622 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000623
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000624 /* Apply call setting */
625 status = apply_call_setting(call, opt, NULL);
626 if (status != PJ_SUCCESS) {
627 pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
628 goto on_error;
629 }
630
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000631 /* Create temporary pool */
632 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
633
Benny Prijono320fa4d2006-12-07 10:09:16 +0000634 /* Verify that destination URI is valid before calling
635 * pjsua_acc_create_uac_contact, or otherwise there
636 * a misleading "Invalid Contact URI" error will be printed
637 * when pjsua_acc_create_uac_contact() fails.
638 */
639 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000640 pjsip_uri *uri;
641 pj_str_t dup;
642
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000643 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
644 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000645
646 if (uri == NULL) {
647 pjsua_perror(THIS_FILE, "Unable to make call",
648 PJSIP_EINVALIDREQURI);
Benny Prijonob90fd382011-09-18 14:59:56 +0000649 status = PJSIP_EINVALIDREQURI;
650 goto on_error;
Benny Prijono320fa4d2006-12-07 10:09:16 +0000651 }
652 }
653
Benny Prijonoe21e7842006-04-09 16:46:05 +0000654 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000655 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000656
Benny Prijonoe21e7842006-04-09 16:46:05 +0000657 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000658 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000659
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000660 /* Create suitable Contact header unless a Contact header has been
661 * set in the account.
662 */
663 if (acc->contact.slen) {
664 contact = acc->contact;
665 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000666 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000667 acc_id, dest_uri);
668 if (status != PJ_SUCCESS) {
669 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
670 status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000671 goto on_error;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000672 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000673 }
674
Benny Prijonoe21e7842006-04-09 16:46:05 +0000675 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000676 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000677 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000678 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000679 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000680 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000681 goto on_error;
Benny Prijono84126ab2006-02-09 09:30:09 +0000682 }
683
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000684 /* Increment the dialog's lock otherwise when invite session creation
685 * fails the dialog will be destroyed prematurely.
686 */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000687// pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000688
Benny Prijonodb844a42008-02-02 17:07:18 +0000689 /* Calculate call's secure level */
690 call->secure_level = get_secure_level(acc_id, dest_uri);
691
Sauw Ming73ecfe82011-09-21 10:20:01 +0000692 /* Attach user data */
693 call->user_data = user_data;
694
Sauw Mingec765352011-10-03 02:04:36 +0000695 /* Store variables required for the callback after the async
696 * media transport creation is completed.
697 */
Sauw Ming848742f2011-09-28 04:20:30 +0000698 if (msg_data) {
699 call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
700 dlg->pool, msg_data);
701 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000702 call->async_call.dlg = dlg;
703
Benny Prijonoc97608e2007-03-23 16:34:20 +0000704 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000705 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000706 call->secure_level, dlg->pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000707 NULL, NULL, PJ_TRUE,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000708 &on_make_call_med_tp_complete);
709 if (status == PJ_SUCCESS) {
710 status = on_make_call_med_tp_complete(call->index, NULL);
711 if (status != PJ_SUCCESS)
712 goto on_error;
713 } else if (status != PJ_EPENDING) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000714 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
715 goto on_error;
716 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000717
Benny Prijono84126ab2006-02-09 09:30:09 +0000718 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000719
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000720 if (p_call_id)
721 *p_call_id = call_id;
722
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000723 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000724 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000725
Benny Prijonob90fd382011-09-18 14:59:56 +0000726 pj_log_pop_indent();
727
Benny Prijono84126ab2006-02-09 09:30:09 +0000728 return PJ_SUCCESS;
729
730
731on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000732 if (dlg) {
Sauw Ming73ecfe82011-09-21 10:20:01 +0000733 pjsip_dlg_inc_lock(dlg);
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000734 /* This may destroy the dialog */
735 pjsip_dlg_dec_lock(dlg);
736 }
737
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000738 if (call_id != -1) {
739 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000740 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000741 }
742
Benny Prijonob90fd382011-09-18 14:59:56 +0000743 if (tmp_pool)
744 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000745 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +0000746
747 pj_log_pop_indent();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000748 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000749}
750
751
Benny Prijono91a6a172007-10-31 08:59:29 +0000752/* Get the NAT type information in remote's SDP */
753static void update_remote_nat_type(pjsua_call *call,
754 const pjmedia_sdp_session *sdp)
755{
756 const pjmedia_sdp_attr *xnat;
757
758 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
759 if (xnat) {
760 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
761 } else {
762 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
763 }
764
765 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
766 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
767}
768
769
Sauw Mingec765352011-10-03 02:04:36 +0000770/* Incoming call callback when media transport creation is completed. */
771static pj_status_t
772on_incoming_call_med_tp_complete(pjsua_call_id call_id,
773 const pjsua_med_tp_state_info *info)
774{
775 pjsua_call *call = &pjsua_var.calls[call_id];
776 const pjmedia_sdp_session *offer=NULL;
777 pjmedia_sdp_session *answer;
778 pjsip_tx_data *response = NULL;
779 unsigned options = 0;
780 int sip_err_code = (info? info->sip_err_code: 0);
781 pj_status_t status = (info? info->status: PJ_SUCCESS);
782
783 PJSUA_LOCK();
784
785 if (status != PJ_SUCCESS) {
786 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
787 goto on_return;
788 }
Sauw Ming903154f2011-10-03 08:22:48 +0000789
790 /* pjsua_media_channel_deinit() has been called. */
791 if (call->async_call.med_ch_deinit) {
792 pjsua_media_channel_deinit(call->index);
793 call->med_ch_cb = NULL;
794 PJSUA_UNLOCK();
795 return PJ_SUCCESS;
796 }
797
Sauw Mingec765352011-10-03 02:04:36 +0000798 /* Get remote SDP offer (if any). */
799 if (call->inv->neg)
800 pjmedia_sdp_neg_get_neg_remote(call->inv->neg, &offer);
801
802 status = pjsua_media_channel_create_sdp(call_id,
803 call->async_call.dlg->pool,
804 offer, &answer, &sip_err_code);
805 if (status != PJ_SUCCESS) {
806 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
807 goto on_return;
808 }
809
810 status = pjsip_inv_set_local_sdp(call->inv, answer);
811 if (status != PJ_SUCCESS) {
812 pjsua_perror(THIS_FILE, "Error setting local SDP", status);
813 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
814 goto on_return;
815 }
816
817 /* Verify that we can handle the request. */
818 status = pjsip_inv_verify_request3(NULL,
819 call->inv->pool_prov, &options, offer,
820 answer, NULL, pjsua_var.endpt, &response);
821 if (status != PJ_SUCCESS) {
822 /*
823 * No we can't handle the incoming INVITE request.
824 */
825 goto on_return;
826 }
827
828on_return:
829 if (status != PJ_SUCCESS) {
830 pjsip_tx_data *tdata;
831 pj_status_t status_;
832
833 status_ = pjsip_inv_end_session(call->inv, sip_err_code, NULL, &tdata);
834 if (status_ == PJ_SUCCESS && tdata)
835 status_ = pjsip_inv_send_msg(call->inv, tdata);
836
837 pjsua_media_channel_deinit(call->index);
838 }
839
840 /* Set the callback to NULL to indicate that the async operation
841 * has completed.
842 */
843 call->med_ch_cb = NULL;
844
845 if (status == PJ_SUCCESS &&
846 !pj_list_empty(&call->async_call.call_var.inc_call.answers))
847 {
848 struct call_answer *answer, *next;
849
850 answer = call->async_call.call_var.inc_call.answers.next;
851 while (answer != &call->async_call.call_var.inc_call.answers) {
852 next = answer->next;
853 pjsua_call_answer(call_id, answer->code, answer->reason,
854 answer->msg_data);
855 pj_list_erase(answer);
856 answer = next;
857 }
858 }
859
860 PJSUA_UNLOCK();
861 return status;
862}
863
864
Benny Prijonodc39fe82006-05-26 12:17:46 +0000865/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000866 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000867 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000868 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000869pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000870{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000871 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000872 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000873 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000874 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
875 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000876 pjsip_tx_data *response = NULL;
877 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000878 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000879 int acc_id;
880 pjsua_call *call;
881 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000882 int sip_err_code;
Sauw Mingec765352011-10-03 02:04:36 +0000883 pjmedia_sdp_session *offer=NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000884 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000885
Benny Prijono26ff9062006-02-21 23:47:00 +0000886 /* Don't want to handle anything but INVITE */
887 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
888 return PJ_FALSE;
889
890 /* Don't want to handle anything that's already associated with
891 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000892 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000893 if (dlg || tsx)
894 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000895
Benny Prijono384dab42009-10-14 01:58:04 +0000896 /* Don't want to accept the call if shutdown is in progress */
897 if (pjsua_var.thread_quit_flag) {
898 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
899 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
900 NULL, NULL);
901 return PJ_TRUE;
902 }
903
Benny Prijonob90fd382011-09-18 14:59:56 +0000904 PJ_LOG(4,(THIS_FILE, "Incoming %s", rdata->msg_info.info));
905 pj_log_push_indent();
906
Benny Prijono148c9dd2006-09-19 13:37:53 +0000907 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000908
Benny Prijono26ff9062006-02-21 23:47:00 +0000909 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000910 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000911
Benny Prijono5773cd62008-01-19 13:01:42 +0000912 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000913 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000914 PJSIP_SC_BUSY_HERE, NULL,
915 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000916 PJ_LOG(2,(THIS_FILE,
917 "Unable to accept incoming call (too many calls)"));
Benny Prijonob90fd382011-09-18 14:59:56 +0000918 goto on_return;
Benny Prijono84126ab2006-02-09 09:30:09 +0000919 }
920
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000921 /* Clear call descriptor */
922 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000923
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000924 call = &pjsua_var.calls[call_id];
925
926 /* Mark call start time. */
927 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000928
Benny Prijono053f5222006-11-11 16:16:04 +0000929 /* Check INVITE request for Replaces header. If Replaces header is
930 * present, the function will make sure that we can handle the request.
931 */
932 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
933 &response);
934 if (status != PJ_SUCCESS) {
935 /*
936 * Something wrong with the Replaces header.
937 */
938 if (response) {
939 pjsip_response_addr res_addr;
940
941 pjsip_get_response_addr(response->pool, rdata, &res_addr);
942 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
943 NULL, NULL);
944
945 } else {
946
947 /* Respond with 500 (Internal Server Error) */
948 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
949 NULL, NULL);
950 }
951
Benny Prijonob90fd382011-09-18 14:59:56 +0000952 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000953 }
954
955 /* If this INVITE request contains Replaces header, notify application
956 * about the request so that application can do subsequent checking
957 * if it wants to.
958 */
959 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
960 pjsua_call *replaced_call;
961 int st_code = 200;
962 pj_str_t st_text = { "OK", 2 };
963
964 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000965 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000966
967 /* Notify application */
968 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
969 rdata, &st_code, &st_text);
970
971 /* Must specify final response */
972 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
973
974 /* Check if application rejects this request. */
975 if (st_code >= 300) {
976
977 if (st_text.slen == 2)
978 st_text = *pjsip_get_status_text(st_code);
979
980 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
981 st_code, &st_text, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +0000982 goto on_return;
Benny Prijono053f5222006-11-11 16:16:04 +0000983 }
984 }
985
Benny Prijonod8179652008-01-23 20:39:07 +0000986 /*
987 * Get which account is most likely to be associated with this incoming
988 * call. We need the account to find which contact URI to put for
989 * the call.
990 */
991 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000992 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000993
Benny Prijonodb844a42008-02-02 17:07:18 +0000994 /* Get call's secure level */
995 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
996 call->secure_level = 2;
997 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
998 call->secure_level = 1;
999 else
1000 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +00001001
Benny Prijonod8179652008-01-23 20:39:07 +00001002 /* Parse SDP from incoming request */
1003 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +00001004 pjsip_rdata_sdp_info *sdp_info;
1005
1006 sdp_info = pjsip_rdata_get_sdp_info(rdata);
1007 offer = sdp_info->sdp;
1008
1009 status = sdp_info->sdp_err;
1010 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
1011 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +00001012
Benny Prijonod8179652008-01-23 20:39:07 +00001013 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +00001014 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +00001015 pjsip_hdr hdr_list;
1016 pjsip_warning_hdr *w;
1017
1018 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +00001019 status);
Benny Prijono2c484e42008-06-26 19:47:23 +00001020
1021 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
1022 pjsip_endpt_name(pjsua_var.endpt),
1023 status);
1024 pj_list_init(&hdr_list);
1025 pj_list_push_back(&hdr_list, w);
1026
Benny Prijono224b4e22008-06-19 14:10:28 +00001027 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +00001028 &reason, &hdr_list, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001029 goto on_return;
Benny Prijonod8179652008-01-23 20:39:07 +00001030 }
Benny Prijono617b8602008-04-07 10:10:31 +00001031
1032 /* Do quick checks on SDP before passing it to transports. More elabore
1033 * checks will be done in pjsip_inv_verify_request2() below.
1034 */
1035 if (offer->media_count==0) {
1036 const pj_str_t reason = pj_str("Missing media in SDP");
1037 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
1038 NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001039 goto on_return;
Benny Prijono617b8602008-04-07 10:10:31 +00001040 }
1041
Benny Prijonod8179652008-01-23 20:39:07 +00001042 } else {
1043 offer = NULL;
1044 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001045
1046 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +00001047 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001048 options |= PJSIP_INV_SUPPORT_TIMER;
Sauw Minge7dbbc82011-10-24 09:28:13 +00001049 if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY)
Benny Prijonodcfc0ba2007-09-30 16:50:27 +00001050 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +00001051 if (pjsua_var.media_cfg.enable_ice)
1052 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +00001053 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
1054 options |= PJSIP_INV_REQUIRE_TIMER;
1055 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
1056 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +00001057
Sauw Mingec765352011-10-03 02:04:36 +00001058 status = pjsip_inv_verify_request2(rdata, &options, offer, NULL, NULL,
Benny Prijonod8179652008-01-23 20:39:07 +00001059 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001060 if (status != PJ_SUCCESS) {
1061
1062 /*
1063 * No we can't handle the incoming INVITE request.
1064 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001065 if (response) {
1066 pjsip_response_addr res_addr;
1067
1068 pjsip_get_response_addr(response->pool, rdata, &res_addr);
1069 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
1070 NULL, NULL);
1071
1072 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001073 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +00001074 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
1075 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001076 }
1077
Benny Prijonob90fd382011-09-18 14:59:56 +00001078 goto on_return;
Benny Prijono1d9b9a42006-09-25 13:40:12 +00001079 }
1080
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001081 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001082 if (pjsua_var.acc[acc_id].contact.slen) {
1083 contact = pjsua_var.acc[acc_id].contact;
1084 } else {
1085 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
1086 acc_id, rdata);
1087 if (status != PJ_SUCCESS) {
1088 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
1089 status);
1090 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
1091 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001092 goto on_return;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001093 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001094 }
1095
Benny Prijono26ff9062006-02-21 23:47:00 +00001096 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +00001097 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001098 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +00001099 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001100 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +00001101 NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00001102 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001103 }
1104
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001105 /* Set credentials */
1106 if (pjsua_var.acc[acc_id].cred_cnt) {
1107 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
1108 pjsua_var.acc[acc_id].cred_cnt,
1109 pjsua_var.acc[acc_id].cred);
1110 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001111
Benny Prijono48ab2b72007-11-08 09:24:30 +00001112 /* Set preference */
1113 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
1114 &pjsua_var.acc[acc_id].cfg.auth_pref);
1115
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +00001116 /* Disable Session Timers if not prefered and the incoming INVITE request
1117 * did not require it.
1118 */
1119 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
1120 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
1121 {
1122 options &= ~(PJSIP_INV_SUPPORT_TIMER);
1123 }
1124
Sauw Minge7dbbc82011-10-24 09:28:13 +00001125 /* If 100rel is optional and UAC supports it, use it. */
1126 if ((options & PJSIP_INV_REQUIRE_100REL)==0 &&
1127 pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_OPTIONAL)
1128 {
1129 const pj_str_t token = { "100rel", 6};
1130 pjsip_dialog_cap_status cap_status;
1131
1132 cap_status = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_SUPPORTED, NULL,
1133 &token);
1134 if (cap_status == PJSIP_DIALOG_CAP_SUPPORTED)
1135 options |= PJSIP_INV_REQUIRE_100REL;
1136 }
1137
Benny Prijono26ff9062006-02-21 23:47:00 +00001138 /* Create invite session: */
Sauw Mingec765352011-10-03 02:04:36 +00001139 status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +00001140 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +00001141 pjsip_hdr hdr_list;
1142 pjsip_warning_hdr *w;
1143
1144 w = pjsip_warning_hdr_create_from_status(dlg->pool,
1145 pjsip_endpt_name(pjsua_var.endpt),
1146 status);
1147 pj_list_init(&hdr_list);
1148 pj_list_push_back(&hdr_list, w);
1149
1150 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
1151
1152 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +00001153 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001154 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001155 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001156 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001157 }
1158
Sauw Ming903154f2011-10-03 08:22:48 +00001159 /* If account is locked to specific transport, then lock dialog
1160 * to this transport too.
1161 */
1162 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1163 pjsip_tpselector tp_sel;
1164
1165 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1166 pjsip_dlg_set_transport(dlg, &tp_sel);
1167 }
1168
Sauw Mingec765352011-10-03 02:04:36 +00001169 /* Create and attach pjsua_var data to the dialog: */
1170 call->inv = inv;
1171 dlg->mod_data[pjsua_var.mod.id] = call;
1172 inv->mod_data[pjsua_var.mod.id] = call;
1173
1174 /* Store variables required for the callback after the async
1175 * media transport creation is completed.
1176 */
1177 call->async_call.dlg = dlg;
1178 pj_list_init(&call->async_call.call_var.inc_call.answers);
1179
1180 /* Init media channel */
1181 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
1182 call->secure_level,
1183 rdata->tp_info.pool,
1184 offer,
1185 &sip_err_code, PJ_TRUE,
Sauw Mingec765352011-10-03 02:04:36 +00001186 &on_incoming_call_med_tp_complete);
1187 if (status == PJ_SUCCESS) {
1188 status = on_incoming_call_med_tp_complete(call_id, NULL);
1189 if (status != PJ_SUCCESS) {
1190 sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1191 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1192 goto on_return;
1193 }
1194 } else if (status != PJ_EPENDING) {
1195 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1196 pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
1197 goto on_return;
1198 }
1199
1200 /* Create answer */
1201/*
1202 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
1203 offer, &answer, &sip_err_code);
1204 if (status != PJ_SUCCESS) {
1205 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
1206 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
1207 sip_err_code, NULL, NULL, NULL, NULL);
1208 goto on_return;
1209 }
1210*/
1211
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001212 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +00001213 status = pjsip_timer_init_session(inv,
1214 &pjsua_var.acc[acc_id].cfg.timer_setting);
1215 if (status != PJ_SUCCESS) {
1216 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
1217 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
1218 NULL, &response);
1219 if (status == PJ_SUCCESS && response)
1220 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001221
Nanang Izzuddin65add622009-08-11 16:26:20 +00001222 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001223
Benny Prijonob90fd382011-09-18 14:59:56 +00001224 goto on_return;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001225 }
1226
Benny Prijonoea9fd392007-11-06 03:41:40 +00001227 /* Update NAT type of remote endpoint, only when there is SDP in
1228 * incoming INVITE!
1229 */
Sauw Mingec765352011-10-03 02:04:36 +00001230 if (pjsua_var.ua_cfg.nat_type_in_sdp && inv->neg &&
Benny Prijonoea9fd392007-11-06 03:41:40 +00001231 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
1232 {
Benny Prijono91a6a172007-10-31 08:59:29 +00001233 const pjmedia_sdp_session *remote_sdp;
1234
1235 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
1236 update_remote_nat_type(call, remote_sdp);
1237 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001238
Benny Prijono2285e7e2008-12-17 14:28:18 +00001239 /* Must answer with some response to initial INVITE. We'll do this before
1240 * attaching the call to the invite session/dialog, so that the application
1241 * will not get notification about this event (on another scenario, it is
1242 * also possible that inv_send_msg() fails and causes the invite session to
1243 * be disconnected. If we have the call attached at this time, this will
1244 * cause the disconnection callback to be called before on_incoming_call()
1245 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +00001246 */
Benny Prijono64f851e2006-02-23 13:49:28 +00001247 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +00001249 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001250 if (response == NULL) {
1251 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1252 status);
1253 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1254 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1255 } else {
1256 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001257 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001258 PJ_FALSE);
1259 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001260 pjsua_media_channel_deinit(call->index);
Benny Prijonob90fd382011-09-18 14:59:56 +00001261 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001262
1263 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001264 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001265 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001266 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001267 goto on_return;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001268 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001269 }
1270
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001272
Benny Prijono053f5222006-11-11 16:16:04 +00001273 /* Check if this request should replace existing call */
1274 if (replaced_dlg) {
1275 pjsip_inv_session *replaced_inv;
1276 struct pjsua_call *replaced_call;
1277 pjsip_tx_data *tdata;
1278
1279 /* Get the invite session in the dialog */
1280 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1281
1282 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001283 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001284
1285 /* Notify application */
1286 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1287 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1288 call_id);
1289
1290 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1291 call_id));
1292
1293 /* Answer the new call with 200 response */
1294 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1295 if (status == PJ_SUCCESS)
1296 status = pjsip_inv_send_msg(inv, tdata);
1297
1298 if (status != PJ_SUCCESS)
1299 pjsua_perror(THIS_FILE, "Error answering session", status);
1300
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001301 /* Note that inv may be invalid if 200/OK has caused error in
1302 * starting the media.
1303 */
Benny Prijono053f5222006-11-11 16:16:04 +00001304
1305 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1306 replaced_call->index));
1307
1308 /* Disconnect replaced invite session */
1309 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1310 &tdata);
1311 if (status == PJ_SUCCESS && tdata)
1312 status = pjsip_inv_send_msg(replaced_inv, tdata);
1313
1314 if (status != PJ_SUCCESS)
1315 pjsua_perror(THIS_FILE, "Error terminating session", status);
1316
1317
1318 } else {
1319
Benny Prijonob5388cf2007-01-04 22:45:08 +00001320 /* Notify application if on_incoming_call() is overriden,
1321 * otherwise hangup the call with 480
1322 */
1323 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001324 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001325 } else {
1326 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1327 NULL, NULL);
1328 }
Benny Prijono053f5222006-11-11 16:16:04 +00001329 }
1330
Benny Prijono8b1889b2006-06-06 18:40:40 +00001331
Benny Prijono26ff9062006-02-21 23:47:00 +00001332 /* This INVITE request has been handled. */
Benny Prijonob90fd382011-09-18 14:59:56 +00001333on_return:
1334 pj_log_pop_indent();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001335 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001336 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001337}
1338
1339
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340
1341/*
1342 * Check if the specified call has active INVITE session and the INVITE
1343 * session has not been disconnected.
1344 */
1345PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1346{
1347 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1348 PJ_EINVAL);
1349 return pjsua_var.calls[call_id].inv != NULL &&
1350 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1351}
1352
1353
1354/*
1355 * Check if call has an active media session.
1356 */
1357PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1358{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001359 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1361 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001362 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonocf986c42008-09-02 11:25:07 +00001363}
1364
1365
Benny Prijono148c9dd2006-09-19 13:37:53 +00001366/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001367pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001368 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001369 pjsua_call **p_call,
1370 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001371{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001372 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001373 pjsua_call *call = NULL;
1374 pj_bool_t has_pjsua_lock = PJ_FALSE;
1375 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001376 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001377
Sauw Ming844c1c92010-09-07 05:12:02 +00001378 pj_gettimeofday(&time_start);
Sauw Minge7dbbc82011-10-24 09:28:13 +00001379 timeout.sec = 0;
Sauw Ming844c1c92010-09-07 05:12:02 +00001380 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1381 pj_time_val_normalize(&timeout);
1382
1383 for (retry=0; ; ++retry) {
1384
1385 if (retry % 10 == 9) {
1386 pj_time_val dtime;
1387
1388 pj_gettimeofday(&dtime);
1389 PJ_TIME_VAL_SUB(dtime, time_start);
1390 if (!PJ_TIME_VAL_LT(dtime, timeout))
1391 break;
1392 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001393
1394 has_pjsua_lock = PJ_FALSE;
1395
1396 status = PJSUA_TRY_LOCK();
1397 if (status != PJ_SUCCESS) {
1398 pj_thread_sleep(retry/10);
1399 continue;
1400 }
1401
1402 has_pjsua_lock = PJ_TRUE;
1403 call = &pjsua_var.calls[call_id];
1404
1405 if (call->inv == NULL) {
1406 PJSUA_UNLOCK();
1407 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1408 return PJSIP_ESESSIONTERMINATED;
1409 }
1410
1411 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1412 if (status != PJ_SUCCESS) {
1413 PJSUA_UNLOCK();
1414 pj_thread_sleep(retry/10);
1415 continue;
1416 }
1417
1418 PJSUA_UNLOCK();
1419
1420 break;
1421 }
1422
1423 if (status != PJ_SUCCESS) {
1424 if (has_pjsua_lock == PJ_FALSE)
1425 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1426 "(possibly system has deadlocked) in %s",
1427 title));
1428 else
1429 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1430 "(possibly system has deadlocked) in %s",
1431 title));
1432 return PJ_ETIMEDOUT;
1433 }
1434
1435 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001436 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001437
1438 return PJ_SUCCESS;
1439}
1440
1441
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442/*
1443 * Get the conference port identification associated with the call.
1444 */
1445PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1446{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001447 pjsua_call *call;
Sauw Minge7dbbc82011-10-24 09:28:13 +00001448 pjsua_conf_port_id port_id = PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001449
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001450 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1451 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001452
Sauw Minge7dbbc82011-10-24 09:28:13 +00001453 /* Use PJSUA_LOCK() instead of acquire_call():
1454 * https://trac.pjsip.org/repos/ticket/1371
1455 */
1456 PJSUA_LOCK();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001457
Sauw Minge7dbbc82011-10-24 09:28:13 +00001458 if (!pjsua_call_is_active(call_id))
1459 goto on_return;
1460
1461 call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001462 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001463
Sauw Minge7dbbc82011-10-24 09:28:13 +00001464on_return:
1465 PJSUA_UNLOCK();
Benny Prijono148c9dd2006-09-19 13:37:53 +00001466
1467 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468}
1469
1470
Benny Prijono148c9dd2006-09-19 13:37:53 +00001471
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001472/*
1473 * Obtain detail information about the specified call.
1474 */
1475PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1476 pjsua_call_info *info)
1477{
1478 pjsua_call *call;
Sauw Mingc8e12942011-10-25 08:51:02 +00001479 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001480 unsigned mi;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481
1482 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1483 PJ_EINVAL);
1484
Benny Prijonoac623b32006-07-03 15:19:31 +00001485 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486
Sauw Minge7dbbc82011-10-24 09:28:13 +00001487 /* Use PJSUA_LOCK() instead of acquire_call():
1488 * https://trac.pjsip.org/repos/ticket/1371
1489 */
1490 PJSUA_LOCK();
1491
1492 call = &pjsua_var.calls[call_id];
Sauw Mingc8e12942011-10-25 08:51:02 +00001493 dlg = (call->inv ? call->inv->dlg : call->async_call.dlg);
1494 if (!dlg) {
Sauw Minge7dbbc82011-10-24 09:28:13 +00001495 PJSUA_UNLOCK();
1496 return PJSIP_ESESSIONTERMINATED;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001497 }
1498
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499 /* id and role */
1500 info->id = call_id;
Sauw Mingc8e12942011-10-25 08:51:02 +00001501 info->role = dlg->role;
Benny Prijono90315512006-09-14 16:05:16 +00001502 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001503
1504 /* local info */
1505 info->local_info.ptr = info->buf_.local_info;
Sauw Mingc8e12942011-10-25 08:51:02 +00001506 pj_strncpy(&info->local_info, &dlg->local.info_str,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001507 sizeof(info->buf_.local_info));
1508
1509 /* local contact */
1510 info->local_contact.ptr = info->buf_.local_contact;
1511 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
Sauw Mingc8e12942011-10-25 08:51:02 +00001512 dlg->local.contact->uri,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 info->local_contact.ptr,
1514 sizeof(info->buf_.local_contact));
1515
1516 /* remote info */
1517 info->remote_info.ptr = info->buf_.remote_info;
Sauw Mingc8e12942011-10-25 08:51:02 +00001518 pj_strncpy(&info->remote_info, &dlg->remote.info_str,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519 sizeof(info->buf_.remote_info));
1520
1521 /* remote contact */
Sauw Mingc8e12942011-10-25 08:51:02 +00001522 if (dlg->remote.contact) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001523 int len;
1524 info->remote_contact.ptr = info->buf_.remote_contact;
1525 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
Sauw Mingc8e12942011-10-25 08:51:02 +00001526 dlg->remote.contact->uri,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001527 info->remote_contact.ptr,
1528 sizeof(info->buf_.remote_contact));
1529 if (len < 0) len = 0;
1530 info->remote_contact.slen = len;
1531 } else {
1532 info->remote_contact.slen = 0;
1533 }
1534
1535 /* call id */
1536 info->call_id.ptr = info->buf_.call_id;
Sauw Mingc8e12942011-10-25 08:51:02 +00001537 pj_strncpy(&info->call_id, &dlg->call_id->id,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001538 sizeof(info->buf_.call_id));
1539
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001540 /* call setting */
1541 pj_memcpy(&info->setting, &call->opt, sizeof(call->opt));
1542
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001543 /* state, state_text */
Sauw Mingc8e12942011-10-25 08:51:02 +00001544 info->state = (call->inv? call->inv->state: PJSIP_INV_STATE_DISCONNECTED);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001545 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1546
1547 /* If call is disconnected, set the last_status from the cause code */
Sauw Mingc8e12942011-10-25 08:51:02 +00001548 if (call->inv && call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001549 /* last_status, last_status_text */
1550 info->last_status = call->inv->cause;
1551
1552 info->last_status_text.ptr = info->buf_.last_status_text;
1553 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1554 sizeof(info->buf_.last_status_text));
1555 } else {
1556 /* last_status, last_status_text */
1557 info->last_status = call->last_code;
1558
1559 info->last_status_text.ptr = info->buf_.last_status_text;
1560 pj_strncpy(&info->last_status_text, &call->last_text,
1561 sizeof(info->buf_.last_status_text));
1562 }
1563
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001564 /* Audio & video count offered by remote */
1565 info->rem_offerer = call->rem_offerer;
1566 if (call->rem_offerer) {
1567 info->rem_audio_cnt = call->rem_aud_cnt;
1568 info->rem_video_cnt = call->rem_vid_cnt;
1569 }
1570
Benny Prijono0bc99a92011-03-17 04:34:43 +00001571 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001572 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001573 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001574 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001575 {
1576 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001577
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001578 info->media[info->media_cnt].index = mi;
1579 info->media[info->media_cnt].status = call_med->state;
1580 info->media[info->media_cnt].dir = call_med->dir;
1581 info->media[info->media_cnt].type = call_med->type;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001583 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001584 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001585 call_med->strm.a.conf_slot;
1586 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001587 pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV;
1588
1589 info->media[info->media_cnt].stream.vid.win_in =
1590 call_med->strm.v.rdr_win_id;
1591
1592 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddind93c68a2011-07-19 08:40:20 +00001593 cap_dev = call_med->strm.v.cap_dev;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001594 }
1595 info->media[info->media_cnt].stream.vid.cap_dev = cap_dev;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001596 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001597 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001598 }
1599 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001600 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001602 if (call->audio_idx != -1) {
1603 info->media_status = call->media[call->audio_idx].state;
1604 info->media_dir = call->media[call->audio_idx].dir;
1605 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001606 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001607
1608 /* calculate duration */
1609 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1610
1611 info->total_duration = call->dis_time;
1612 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1613
1614 if (call->conn_time.sec) {
1615 info->connect_duration = call->dis_time;
1616 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1617 }
1618
1619 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1620
1621 pj_gettimeofday(&info->total_duration);
1622 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1623
1624 pj_gettimeofday(&info->connect_duration);
1625 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1626
1627 } else {
1628 pj_gettimeofday(&info->total_duration);
1629 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1630 }
1631
Sauw Minge7dbbc82011-10-24 09:28:13 +00001632 PJSUA_UNLOCK();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633
1634 return PJ_SUCCESS;
1635}
1636
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001637/*
1638 * Check if call remote peer support the specified capability.
1639 */
1640PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1641 pjsua_call_id call_id,
1642 int htype,
1643 const pj_str_t *hname,
1644 const pj_str_t *token)
1645{
1646 pjsua_call *call;
1647 pjsip_dialog *dlg;
1648 pj_status_t status;
1649 pjsip_dialog_cap_status cap_status;
1650
1651 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1652 if (status != PJ_SUCCESS)
1653 return PJSIP_DIALOG_CAP_UNKNOWN;
1654
1655 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1656
1657 pjsip_dlg_dec_lock(dlg);
1658
1659 return cap_status;
1660}
1661
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662
1663/*
1664 * Attach application specific data to the call.
1665 */
1666PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1667 void *user_data)
1668{
1669 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1670 PJ_EINVAL);
1671 pjsua_var.calls[call_id].user_data = user_data;
1672
1673 return PJ_SUCCESS;
1674}
1675
1676
1677/*
1678 * Get user data attached to the call.
1679 */
1680PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1681{
1682 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1683 NULL);
1684 return pjsua_var.calls[call_id].user_data;
1685}
1686
1687
1688/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001689 * Get remote's NAT type.
1690 */
1691PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1692 pj_stun_nat_type *p_type)
1693{
1694 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1695 PJ_EINVAL);
1696 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1697
1698 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1699 return PJ_SUCCESS;
1700}
1701
1702
1703/*
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001704 * Get media stream info for the specified media index.
1705 */
1706PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
1707 unsigned med_idx,
1708 pjsua_stream_info *psi)
1709{
1710 pjsua_call *call;
1711 pjsua_call_media *call_med;
1712 pj_status_t status;
1713
1714 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1715 PJ_EINVAL);
1716 PJ_ASSERT_RETURN(psi, PJ_EINVAL);
1717
1718 PJSUA_LOCK();
1719
1720 call = &pjsua_var.calls[call_id];
1721
1722 if (med_idx >= call->med_cnt) {
1723 PJSUA_UNLOCK();
1724 return PJ_EINVAL;
1725 }
1726
1727 call_med = &call->media[med_idx];
1728 psi->type = call_med->type;
1729 switch (call_med->type) {
1730 case PJMEDIA_TYPE_AUDIO:
1731 status = pjmedia_stream_get_info(call_med->strm.a.stream,
1732 &psi->info.aud);
1733 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001734#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001735 case PJMEDIA_TYPE_VIDEO:
1736 status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
1737 &psi->info.vid);
1738 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001739#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001740 default:
1741 status = PJMEDIA_EINVALIMEDIATYPE;
1742 break;
1743 }
1744
1745 PJSUA_UNLOCK();
1746 return status;
1747}
1748
1749
1750/*
1751 * Get media stream statistic for the specified media index.
1752 */
1753PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
1754 unsigned med_idx,
1755 pjsua_stream_stat *stat)
1756{
1757 pjsua_call *call;
1758 pjsua_call_media *call_med;
1759 pj_status_t status;
1760
1761 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1762 PJ_EINVAL);
1763 PJ_ASSERT_RETURN(stat, PJ_EINVAL);
1764
1765 PJSUA_LOCK();
1766
1767 call = &pjsua_var.calls[call_id];
1768
1769 if (med_idx >= call->med_cnt) {
1770 PJSUA_UNLOCK();
1771 return PJ_EINVAL;
1772 }
1773
1774 call_med = &call->media[med_idx];
1775 switch (call_med->type) {
1776 case PJMEDIA_TYPE_AUDIO:
1777 status = pjmedia_stream_get_stat(call_med->strm.a.stream,
1778 &stat->rtcp);
1779 if (status == PJ_SUCCESS)
1780 status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
1781 &stat->jbuf);
1782 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001783#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001784 case PJMEDIA_TYPE_VIDEO:
1785 status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
1786 &stat->rtcp);
1787 if (status == PJ_SUCCESS)
1788 status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
1789 &stat->jbuf);
1790 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001791#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001792 default:
1793 status = PJMEDIA_EINVALIMEDIATYPE;
1794 break;
1795 }
1796
1797 PJSUA_UNLOCK();
1798 return status;
1799}
1800
1801
1802/*
1803 * Get media transport info for the specified media index.
1804 */
Benny Prijonoe212bc12011-08-15 09:38:42 +00001805PJ_DEF(pj_status_t)
1806pjsua_call_get_med_transport_info(pjsua_call_id call_id,
1807 unsigned med_idx,
1808 pjmedia_transport_info *t)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001809{
1810 pjsua_call *call;
1811 pjsua_call_media *call_med;
1812 pj_status_t status;
1813
1814 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1815 PJ_EINVAL);
1816 PJ_ASSERT_RETURN(t, PJ_EINVAL);
1817
1818 PJSUA_LOCK();
1819
1820 call = &pjsua_var.calls[call_id];
1821
1822 if (med_idx >= call->med_cnt) {
1823 PJSUA_UNLOCK();
1824 return PJ_EINVAL;
1825 }
1826
1827 call_med = &call->media[med_idx];
1828
1829 pjmedia_transport_info_init(t);
1830 status = pjmedia_transport_get_info(call_med->tp, t);
1831
1832 PJSUA_UNLOCK();
1833 return status;
1834}
1835
1836
1837/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001838 * Send response to incoming INVITE request.
1839 */
1840PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1841 unsigned code,
1842 const pj_str_t *reason,
1843 const pjsua_msg_data *msg_data)
1844{
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001845 return pjsua_call_answer2(call_id, NULL, code, reason, msg_data);
1846}
1847
1848
1849/*
1850 * Send response to incoming INVITE request.
1851 */
1852PJ_DEF(pj_status_t) pjsua_call_answer2(pjsua_call_id call_id,
1853 const pjsua_call_setting *opt,
1854 unsigned code,
1855 const pj_str_t *reason,
1856 const pjsua_msg_data *msg_data)
1857{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001858 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001859 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 pjsip_tx_data *tdata;
1861 pj_status_t status;
1862
1863 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1864 PJ_EINVAL);
1865
Benny Prijonob90fd382011-09-18 14:59:56 +00001866 PJ_LOG(4,(THIS_FILE, "Answering call %d: code=%d", call_id, code));
1867 pj_log_push_indent();
1868
Benny Prijonodc752ca2006-09-22 16:55:42 +00001869 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001870 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001871 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001873 /* Apply call setting */
1874 status = apply_call_setting(call, opt, NULL);
1875 if (status != PJ_SUCCESS) {
1876 pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
1877 goto on_return;
1878 }
1879
Sauw Mingec765352011-10-03 02:04:36 +00001880 PJSUA_LOCK();
1881 /* If media transport creation is not yet completed, we will answer
1882 * the call in the media transport creation callback instead.
1883 */
1884 if (call->med_ch_cb) {
1885 struct call_answer *answer;
1886
1887 PJ_LOG(4,(THIS_FILE, "Pending answering call %d upon completion "
1888 "of media transport", call_id));
1889
1890 answer = PJ_POOL_ZALLOC_T(call->inv->pool_prov, struct call_answer);
1891 answer->code = code;
1892 if (reason) {
1893 pj_strdup(call->inv->pool_prov, answer->reason, reason);
1894 }
1895 if (msg_data) {
1896 answer->msg_data = pjsua_msg_data_clone(call->inv->pool_prov,
1897 msg_data);
1898 }
1899 pj_list_push_back(&call->async_call.call_var.inc_call.answers,
1900 answer);
1901
1902 PJSUA_UNLOCK();
1903 if (dlg) pjsip_dlg_dec_lock(dlg);
1904 pj_log_pop_indent();
1905 return status;
1906 }
1907 PJSUA_UNLOCK();
1908
Benny Prijono2e507c22006-06-23 15:04:11 +00001909 if (call->res_time.sec == 0)
1910 pj_gettimeofday(&call->res_time);
1911
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001912 if (reason && reason->slen == 0)
1913 reason = NULL;
1914
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001915 /* Create response message */
1916 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1917 if (status != PJ_SUCCESS) {
1918 pjsua_perror(THIS_FILE, "Error creating response",
1919 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001920 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001921 }
1922
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001923 /* Call might have been disconnected if application is answering with
1924 * 200/OK and the media failed to start.
1925 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001926 if (call->inv == NULL)
1927 goto on_return;
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001928
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001929 /* Add additional headers etc */
1930 pjsua_process_msg_data( tdata, msg_data);
1931
1932 /* Send the message */
1933 status = pjsip_inv_send_msg(call->inv, tdata);
1934 if (status != PJ_SUCCESS)
1935 pjsua_perror(THIS_FILE, "Error sending response",
1936 status);
1937
Benny Prijonob90fd382011-09-18 14:59:56 +00001938on_return:
1939 if (dlg) pjsip_dlg_dec_lock(dlg);
1940 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001941 return status;
1942}
1943
1944
1945/*
1946 * Hangup call by using method that is appropriate according to the
1947 * call state.
1948 */
1949PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1950 unsigned code,
1951 const pj_str_t *reason,
1952 const pjsua_msg_data *msg_data)
1953{
1954 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00001955 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001956 pj_status_t status;
1957 pjsip_tx_data *tdata;
1958
1959
Benny Prijono148c9dd2006-09-19 13:37:53 +00001960 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1961 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1962 call_id));
1963 }
1964
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001965 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1966 PJ_EINVAL);
1967
Benny Prijonob90fd382011-09-18 14:59:56 +00001968 PJ_LOG(4,(THIS_FILE, "Call %d hanging up: code=%d..", call_id, code));
1969 pj_log_push_indent();
1970
Benny Prijonodc752ca2006-09-22 16:55:42 +00001971 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001972 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00001973 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001974
1975 if (code==0) {
1976 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1977 code = PJSIP_SC_OK;
1978 else if (call->inv->role == PJSIP_ROLE_UAS)
1979 code = PJSIP_SC_DECLINE;
1980 else
1981 code = PJSIP_SC_REQUEST_TERMINATED;
1982 }
1983
1984 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1985 if (status != PJ_SUCCESS) {
1986 pjsua_perror(THIS_FILE,
1987 "Failed to create end session message",
1988 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00001989 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001990 }
1991
1992 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1993 * as p_tdata when INVITE transaction has not been answered
1994 * with any provisional responses.
1995 */
Benny Prijonob90fd382011-09-18 14:59:56 +00001996 if (tdata == NULL)
1997 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001998
1999 /* Add additional headers etc */
2000 pjsua_process_msg_data( tdata, msg_data);
2001
2002 /* Send the message */
2003 status = pjsip_inv_send_msg(call->inv, tdata);
2004 if (status != PJ_SUCCESS) {
2005 pjsua_perror(THIS_FILE,
2006 "Failed to send end session message",
2007 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002008 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009 }
2010
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002011 /* Stop lock codec timer, if it is active */
2012 if (call->lock_codec.reinv_timer.id) {
2013 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2014 &call->lock_codec.reinv_timer);
2015 call->lock_codec.reinv_timer.id = PJ_FALSE;
2016 }
2017
Benny Prijonob90fd382011-09-18 14:59:56 +00002018on_return:
2019 if (dlg) pjsip_dlg_dec_lock(dlg);
2020 pj_log_pop_indent();
2021 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002022}
2023
2024
2025/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002026 * Accept or reject redirection.
2027 */
2028PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
2029 pjsip_redirect_op cmd)
2030{
2031 pjsua_call *call;
2032 pjsip_dialog *dlg;
2033 pj_status_t status;
2034
2035 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2036 PJ_EINVAL);
2037
2038 status = acquire_call("pjsua_call_process_redirect()", call_id,
2039 &call, &dlg);
2040 if (status != PJ_SUCCESS)
2041 return status;
2042
2043 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
2044
2045 pjsip_dlg_dec_lock(dlg);
2046
2047 return status;
2048}
2049
2050
2051/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002052 * Put the specified call on hold.
2053 */
2054PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
2055 const pjsua_msg_data *msg_data)
2056{
2057 pjmedia_sdp_session *sdp;
2058 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002059 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002060 pjsip_tx_data *tdata;
2061 pj_status_t status;
2062
2063 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2064 PJ_EINVAL);
2065
Benny Prijonob90fd382011-09-18 14:59:56 +00002066 PJ_LOG(4,(THIS_FILE, "Putting call %d on hold", call_id));
2067 pj_log_push_indent();
2068
Benny Prijonodc752ca2006-09-22 16:55:42 +00002069 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002070 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002071 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002072
2073 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
2074 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002075 status = PJSIP_ESESSIONSTATE;
2076 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002077 }
2078
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002079 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonob90fd382011-09-18 14:59:56 +00002080 if (status != PJ_SUCCESS)
2081 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002082
2083 /* Create re-INVITE with new offer */
2084 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
2085 if (status != PJ_SUCCESS) {
2086 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002087 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002088 }
2089
2090 /* Add additional headers etc */
2091 pjsua_process_msg_data( tdata, msg_data);
2092
Sauw Minge7dbbc82011-10-24 09:28:13 +00002093 /* Record the tx_data to keep track the operation */
2094 call->hold_msg = (void*) tdata;
2095
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 /* Send the request */
2097 status = pjsip_inv_send_msg( call->inv, tdata);
2098 if (status != PJ_SUCCESS) {
2099 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Sauw Minge7dbbc82011-10-24 09:28:13 +00002100 call->hold_msg = NULL;
Benny Prijonob90fd382011-09-18 14:59:56 +00002101 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002102 }
2103
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002104 /* Set flag that local put the call on hold */
2105 call->local_hold = PJ_TRUE;
2106
Benny Prijonob90fd382011-09-18 14:59:56 +00002107on_return:
2108 if (dlg) pjsip_dlg_dec_lock(dlg);
2109 pj_log_pop_indent();
2110 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111}
2112
2113
2114/*
2115 * Send re-INVITE (to release hold).
2116 */
2117PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
Benny Prijonodec3a372011-03-16 03:52:20 +00002118 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002119 const pjsua_msg_data *msg_data)
2120{
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002121 pjsua_call *call;
2122 pjsip_dialog *dlg = NULL;
2123 pj_status_t status;
2124
2125 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
2126 if (status != PJ_SUCCESS)
2127 goto on_return;
2128
2129 if (options != call->opt.flag)
2130 call->opt.flag = options;
2131
2132 status = pjsua_call_reinvite2(call_id, NULL, msg_data);
2133
2134on_return:
2135 if (dlg) pjsip_dlg_dec_lock(dlg);
2136 return status;
2137}
2138
2139
2140/*
2141 * Send re-INVITE (to release hold).
2142 */
2143PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id,
2144 const pjsua_call_setting *opt,
2145 const pjsua_msg_data *msg_data)
2146{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002147 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00002148 pj_str_t *new_contact = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002149 pjsip_tx_data *tdata;
2150 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002151 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002152 pj_status_t status;
2153
2154
2155 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2156 PJ_EINVAL);
2157
Benny Prijonob90fd382011-09-18 14:59:56 +00002158 PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
2159 pj_log_push_indent();
2160
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002161 status = acquire_call("pjsua_call_reinvite2()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002162 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002163 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002164
2165 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
2166 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002167 status = PJSIP_ESESSIONSTATE;
2168 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 }
2170
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002171 status = apply_call_setting(call, opt, NULL);
2172 if (status != PJ_SUCCESS) {
2173 pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
2174 goto on_return;
2175 }
2176
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177 /* Create SDP */
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002178 if (call->local_hold && (call->opt.flag & PJSUA_CALL_UNHOLD)==0) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002179 status = create_sdp_of_call_hold(call, &sdp);
2180 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00002181 status = pjsua_media_channel_create_sdp(call->index,
2182 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002183 NULL, &sdp, NULL);
2184 call->local_hold = PJ_FALSE;
2185 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002186 if (status != PJ_SUCCESS) {
2187 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2188 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002189 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002190 }
2191
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002192 if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &
Benny Prijonodec3a372011-03-16 03:52:20 +00002193 pjsua_acc_is_valid(call->acc_id))
2194 {
2195 new_contact = &pjsua_var.acc[call->acc_id].contact;
2196 }
2197
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002198 /* Create re-INVITE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002199 status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002200 if (status != PJ_SUCCESS) {
2201 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002202 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002203 }
2204
2205 /* Add additional headers etc */
2206 pjsua_process_msg_data( tdata, msg_data);
2207
2208 /* Send the request */
2209 status = pjsip_inv_send_msg( call->inv, tdata);
2210 if (status != PJ_SUCCESS) {
2211 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002212 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002213 }
2214
Benny Prijonob90fd382011-09-18 14:59:56 +00002215on_return:
Nanang Izzuddinc12a19d2011-10-03 05:23:59 +00002216 if (dlg) pjsip_dlg_dec_lock(dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002217 pj_log_pop_indent();
2218 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219}
2220
2221
2222/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002223 * Send UPDATE request.
2224 */
2225PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
2226 unsigned options,
2227 const pjsua_msg_data *msg_data)
2228{
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002229 pjsua_call *call;
2230 pjsip_dialog *dlg = NULL;
2231 pj_status_t status;
2232
2233 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
2234 if (status != PJ_SUCCESS)
2235 goto on_return;
2236
2237 if (options != call->opt.flag)
2238 call->opt.flag = options;
2239
2240 status = pjsua_call_update2(call_id, NULL, msg_data);
2241
2242on_return:
2243 if (dlg) pjsip_dlg_dec_lock(dlg);
2244 return status;
2245}
2246
2247
2248/*
2249 * Send UPDATE request.
2250 */
2251PJ_DEF(pj_status_t) pjsua_call_update2(pjsua_call_id call_id,
2252 const pjsua_call_setting *opt,
2253 const pjsua_msg_data *msg_data)
2254{
Benny Prijonoc08682e2007-10-04 06:17:58 +00002255 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00002256 pj_str_t *new_contact = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002257 pjsip_tx_data *tdata;
2258 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002259 pjsip_dialog *dlg = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002260 pj_status_t status;
2261
Benny Prijonoc08682e2007-10-04 06:17:58 +00002262 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2263 PJ_EINVAL);
2264
Benny Prijonob90fd382011-09-18 14:59:56 +00002265 PJ_LOG(4,(THIS_FILE, "Sending UPDATE on call %d", call_id));
2266 pj_log_push_indent();
2267
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002268 status = acquire_call("pjsua_call_update2()", call_id, &call, &dlg);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002269 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002270 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002271
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002272 status = apply_call_setting(call, opt, NULL);
2273 if (status != PJ_SUCCESS) {
2274 pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
2275 goto on_return;
2276 }
2277
Benny Prijonoc08682e2007-10-04 06:17:58 +00002278 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00002279 status = pjsua_media_channel_create_sdp(call->index,
2280 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002281 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002282 if (status != PJ_SUCCESS) {
2283 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
2284 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002285 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002286 }
2287
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002288 if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &
Benny Prijonodec3a372011-03-16 03:52:20 +00002289 pjsua_acc_is_valid(call->acc_id))
2290 {
2291 new_contact = &pjsua_var.acc[call->acc_id].contact;
2292 }
2293
Benny Prijono224b4e22008-06-19 14:10:28 +00002294 /* Create UPDATE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00002295 status = pjsip_inv_update(call->inv, new_contact, sdp, &tdata);
Benny Prijonoc08682e2007-10-04 06:17:58 +00002296 if (status != PJ_SUCCESS) {
2297 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002298 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002299 }
2300
2301 /* Add additional headers etc */
2302 pjsua_process_msg_data( tdata, msg_data);
2303
2304 /* Send the request */
2305 status = pjsip_inv_send_msg( call->inv, tdata);
2306 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002307 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002308 goto on_return;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002309 }
2310
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002311 call->local_hold = PJ_FALSE;
2312
Benny Prijonob90fd382011-09-18 14:59:56 +00002313on_return:
2314 if (dlg) pjsip_dlg_dec_lock(dlg);
2315 pj_log_pop_indent();
2316 return status;
Benny Prijonoc08682e2007-10-04 06:17:58 +00002317}
2318
2319
2320/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002321 * Initiate call transfer to the specified address.
2322 */
2323PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
2324 const pj_str_t *dest,
2325 const pjsua_msg_data *msg_data)
2326{
2327 pjsip_evsub *sub;
2328 pjsip_tx_data *tdata;
2329 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002330 pjsip_dialog *dlg = NULL;
Benny Prijono053f5222006-11-11 16:16:04 +00002331 pjsip_generic_string_hdr *gs_hdr;
2332 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00002333 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002334 pj_status_t status;
2335
2336
Benny Prijonob90fd382011-09-18 14:59:56 +00002337 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls &&
2338 dest, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002339
Benny Prijonob90fd382011-09-18 14:59:56 +00002340 PJ_LOG(4,(THIS_FILE, "Transfering call %d to %.*s", call_id,
2341 (int)dest->slen, dest->ptr));
2342 pj_log_push_indent();
2343
Benny Prijonodc752ca2006-09-22 16:55:42 +00002344 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002345 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002346 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002347
Benny Prijonod524e822006-09-22 12:48:18 +00002348 /* Create xfer client subscription. */
2349 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002350 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00002351
2352 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002353 if (status != PJ_SUCCESS) {
2354 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002355 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002356 }
2357
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002358 /* Associate this call with the client subscription */
2359 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
2360
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002361 /*
2362 * Create REFER request.
2363 */
2364 status = pjsip_xfer_initiate(sub, dest, &tdata);
2365 if (status != PJ_SUCCESS) {
2366 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002367 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002368 }
2369
Benny Prijono053f5222006-11-11 16:16:04 +00002370 /* Add Referred-By header */
2371 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
2372 &dlg->local.info_str);
2373 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
2374
2375
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002376 /* Add additional headers etc */
2377 pjsua_process_msg_data( tdata, msg_data);
2378
2379 /* Send. */
2380 status = pjsip_xfer_send_request(sub, tdata);
2381 if (status != PJ_SUCCESS) {
2382 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00002383 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002384 }
2385
2386 /* For simplicity (that's what this program is intended to be!),
2387 * leave the original invite session as it is. More advanced application
2388 * may want to hold the INVITE, or terminate the invite, or whatever.
2389 */
Benny Prijonob90fd382011-09-18 14:59:56 +00002390on_return:
2391 if (dlg) pjsip_dlg_dec_lock(dlg);
2392 pj_log_pop_indent();
2393 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002394
2395}
2396
2397
2398/*
Benny Prijono053f5222006-11-11 16:16:04 +00002399 * Initiate attended call transfer to the specified address.
2400 */
2401PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
2402 pjsua_call_id dest_call_id,
2403 unsigned options,
2404 const pjsua_msg_data *msg_data)
2405{
2406 pjsua_call *dest_call;
2407 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00002408 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00002409 pj_str_t str_dest;
2410 int len;
2411 pjsip_uri *uri;
2412 pj_status_t status;
2413
2414
2415 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2416 PJ_EINVAL);
2417 PJ_ASSERT_RETURN(dest_call_id>=0 &&
2418 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
2419 PJ_EINVAL);
2420
Benny Prijonob90fd382011-09-18 14:59:56 +00002421 PJ_LOG(4,(THIS_FILE, "Transfering call %d replacing with call %d",
2422 call_id, dest_call_id));
2423 pj_log_push_indent();
2424
Benny Prijono053f5222006-11-11 16:16:04 +00002425 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
2426 &dest_call, &dest_dlg);
Benny Prijonob90fd382011-09-18 14:59:56 +00002427 if (status != PJ_SUCCESS) {
2428 pj_log_pop_indent();
Benny Prijono053f5222006-11-11 16:16:04 +00002429 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00002430 }
Benny Prijono053f5222006-11-11 16:16:04 +00002431
2432 /*
2433 * Create REFER destination URI with Replaces field.
2434 */
2435
2436 /* Make sure we have sufficient buffer's length */
Benny Prijonob90fd382011-09-18 14:59:56 +00002437 PJ_ASSERT_ON_FAIL(dest_dlg->remote.info_str.slen +
Benny Prijono053f5222006-11-11 16:16:04 +00002438 dest_dlg->call_id->id.slen +
2439 dest_dlg->remote.info->tag.slen +
2440 dest_dlg->local.info->tag.slen + 32
Benny Prijonob90fd382011-09-18 14:59:56 +00002441 < (long)sizeof(str_dest_buf),
2442 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002443
2444 /* Print URI */
2445 str_dest_buf[0] = '<';
2446 str_dest.slen = 1;
2447
Benny Prijonoa1e69682007-05-11 15:14:34 +00002448 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00002449 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
2450 str_dest_buf+1, sizeof(str_dest_buf)-1);
Benny Prijonob90fd382011-09-18 14:59:56 +00002451 if (len < 0) {
2452 status = PJSIP_EURITOOLONG;
2453 goto on_error;
2454 }
Benny Prijono053f5222006-11-11 16:16:04 +00002455
2456 str_dest.slen += len;
2457
2458
2459 /* Build the URI */
2460 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
2461 sizeof(str_dest_buf) - str_dest.slen,
2462 "?%s"
2463 "Replaces=%.*s"
2464 "%%3Bto-tag%%3D%.*s"
2465 "%%3Bfrom-tag%%3D%.*s>",
2466 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
2467 "" : "Require=replaces&"),
2468 (int)dest_dlg->call_id->id.slen,
2469 dest_dlg->call_id->id.ptr,
2470 (int)dest_dlg->remote.info->tag.slen,
2471 dest_dlg->remote.info->tag.ptr,
2472 (int)dest_dlg->local.info->tag.slen,
2473 dest_dlg->local.info->tag.ptr);
2474
Benny Prijonob90fd382011-09-18 14:59:56 +00002475 PJ_ASSERT_ON_FAIL(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
2476 { status=PJSIP_EURITOOLONG; goto on_error; });
Benny Prijono053f5222006-11-11 16:16:04 +00002477
2478 str_dest.ptr = str_dest_buf;
2479 str_dest.slen += len;
2480
2481 pjsip_dlg_dec_lock(dest_dlg);
2482
Benny Prijonob90fd382011-09-18 14:59:56 +00002483 status = pjsua_call_xfer(call_id, &str_dest, msg_data);
2484
2485 pj_log_pop_indent();
2486 return status;
2487
2488on_error:
2489 if (dest_dlg) pjsip_dlg_dec_lock(dest_dlg);
2490 pj_log_pop_indent();
2491 return status;
Benny Prijono053f5222006-11-11 16:16:04 +00002492}
2493
2494
2495/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002496 * Send DTMF digits to remote using RFC 2833 payload formats.
2497 */
2498PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
2499 const pj_str_t *digits)
2500{
2501 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002502 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002503 pj_status_t status;
2504
2505 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2506 PJ_EINVAL);
2507
Benny Prijonob90fd382011-09-18 14:59:56 +00002508 PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s",
2509 call_id, (int)digits->slen, digits->ptr));
2510 pj_log_push_indent();
2511
Benny Prijonodc752ca2006-09-22 16:55:42 +00002512 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002513 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002514 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002515
Benny Prijono0bc99a92011-03-17 04:34:43 +00002516 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002517 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonob90fd382011-09-18 14:59:56 +00002518 status = PJ_EINVALIDOP;
2519 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002520 }
2521
Benny Prijono0bc99a92011-03-17 04:34:43 +00002522 status = pjmedia_stream_dial_dtmf(
2523 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002524
Benny Prijonob90fd382011-09-18 14:59:56 +00002525on_return:
2526 if (dlg) pjsip_dlg_dec_lock(dlg);
2527 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002528 return status;
2529}
2530
2531
2532/**
2533 * Send instant messaging inside INVITE session.
2534 */
2535PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
2536 const pj_str_t *mime_type,
2537 const pj_str_t *content,
2538 const pjsua_msg_data *msg_data,
2539 void *user_data)
2540{
2541 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002542 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002543 const pj_str_t mime_text_plain = pj_str("text/plain");
2544 pjsip_media_type ctype;
2545 pjsua_im_data *im_data;
2546 pjsip_tx_data *tdata;
2547 pj_status_t status;
2548
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002549 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2550 PJ_EINVAL);
2551
Benny Prijonob90fd382011-09-18 14:59:56 +00002552 PJ_LOG(4,(THIS_FILE, "Call %d sending %d bytes MESSAGE..",
2553 call_id, (int)content->slen));
2554 pj_log_push_indent();
2555
Benny Prijonodc752ca2006-09-22 16:55:42 +00002556 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002557 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002558 goto on_return;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002559
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002560 /* Set default media type if none is specified */
2561 if (mime_type == NULL) {
2562 mime_type = &mime_text_plain;
2563 }
2564
2565 /* Create request message. */
2566 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2567 -1, &tdata);
2568 if (status != PJ_SUCCESS) {
2569 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2570 goto on_return;
2571 }
2572
2573 /* Add accept header. */
2574 pjsip_msg_add_hdr( tdata->msg,
2575 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2576
2577 /* Parse MIME type */
2578 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2579
2580 /* Create "text/plain" message body. */
2581 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2582 &ctype.subtype, content);
2583 if (tdata->msg->body == NULL) {
2584 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2585 pjsip_tx_data_dec_ref(tdata);
2586 goto on_return;
2587 }
2588
2589 /* Add additional headers etc */
2590 pjsua_process_msg_data( tdata, msg_data);
2591
2592 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002593 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002594 im_data->acc_id = call->acc_id;
2595 im_data->call_id = call_id;
2596 im_data->to = call->inv->dlg->remote.info_str;
2597 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2598 im_data->user_data = user_data;
2599
2600
2601 /* Send the request. */
2602 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2603 pjsua_var.mod.id, im_data);
2604 if (status != PJ_SUCCESS) {
2605 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2606 goto on_return;
2607 }
2608
2609on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002610 if (dlg) pjsip_dlg_dec_lock(dlg);
2611 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002612 return status;
2613}
2614
2615
2616/*
2617 * Send IM typing indication inside INVITE session.
2618 */
2619PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2620 pj_bool_t is_typing,
2621 const pjsua_msg_data*msg_data)
2622{
2623 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002624 pjsip_dialog *dlg = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002625 pjsip_tx_data *tdata;
2626 pj_status_t status;
2627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002628 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2629 PJ_EINVAL);
2630
Benny Prijonob90fd382011-09-18 14:59:56 +00002631 PJ_LOG(4,(THIS_FILE, "Call %d sending typing indication..",
2632 call_id));
2633 pj_log_push_indent();
2634
Benny Prijonodc752ca2006-09-22 16:55:42 +00002635 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002636 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002637 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002638
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002639 /* Create request message. */
2640 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2641 -1, &tdata);
2642 if (status != PJ_SUCCESS) {
2643 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2644 goto on_return;
2645 }
2646
2647 /* Create "application/im-iscomposing+xml" msg body. */
2648 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2649 NULL, NULL, -1);
2650
2651 /* Add additional headers etc */
2652 pjsua_process_msg_data( tdata, msg_data);
2653
2654 /* Send the request. */
2655 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2656 if (status != PJ_SUCCESS) {
2657 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2658 goto on_return;
2659 }
2660
2661on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002662 if (dlg) pjsip_dlg_dec_lock(dlg);
2663 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002664 return status;
2665}
2666
2667
2668/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002669 * Send arbitrary request.
2670 */
2671PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2672 const pj_str_t *method_str,
2673 const pjsua_msg_data *msg_data)
2674{
2675 pjsua_call *call;
Benny Prijonob90fd382011-09-18 14:59:56 +00002676 pjsip_dialog *dlg = NULL;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002677 pjsip_method method;
2678 pjsip_tx_data *tdata;
2679 pj_status_t status;
2680
2681 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2682 PJ_EINVAL);
2683
Benny Prijonob90fd382011-09-18 14:59:56 +00002684 PJ_LOG(4,(THIS_FILE, "Call %d sending %.*s request..",
2685 call_id, (int)method_str->slen, method_str->ptr));
2686 pj_log_push_indent();
2687
Benny Prijonofeb69f42007-10-05 09:12:26 +00002688 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2689 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002690 goto on_return;
Benny Prijonofeb69f42007-10-05 09:12:26 +00002691
2692 /* Init method */
2693 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2694
2695 /* Create request message. */
2696 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2697 if (status != PJ_SUCCESS) {
2698 pjsua_perror(THIS_FILE, "Unable to create request", status);
2699 goto on_return;
2700 }
2701
2702 /* Add additional headers etc */
2703 pjsua_process_msg_data( tdata, msg_data);
2704
2705 /* Send the request. */
2706 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2707 if (status != PJ_SUCCESS) {
2708 pjsua_perror(THIS_FILE, "Unable to send request", status);
2709 goto on_return;
2710 }
2711
2712on_return:
Benny Prijonob90fd382011-09-18 14:59:56 +00002713 if (dlg) pjsip_dlg_dec_lock(dlg);
2714 pj_log_pop_indent();
Benny Prijonofeb69f42007-10-05 09:12:26 +00002715 return status;
2716}
2717
2718
2719/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002720 * Terminate all calls.
2721 */
2722PJ_DEF(void) pjsua_call_hangup_all(void)
2723{
2724 unsigned i;
2725
Benny Prijonob90fd382011-09-18 14:59:56 +00002726 PJ_LOG(4,(THIS_FILE, "Hangup all calls.."));
2727 pj_log_push_indent();
2728
Sauw Minge7dbbc82011-10-24 09:28:13 +00002729 // This may deadlock, see https://trac.pjsip.org/repos/ticket/1305
2730 //PJSUA_LOCK();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002731
2732 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2733 if (pjsua_var.calls[i].inv)
2734 pjsua_call_hangup(i, 0, NULL, NULL);
2735 }
2736
Sauw Minge7dbbc82011-10-24 09:28:13 +00002737 //PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00002738 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002739}
2740
2741
Benny Prijono1e601552010-10-20 05:31:08 +00002742/* Proto */
2743static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002744
Benny Prijono1e601552010-10-20 05:31:08 +00002745/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002746static void reinv_timer_cb(pj_timer_heap_t *th,
2747 pj_timer_entry *entry)
2748{
2749 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2750 pjsip_dialog *dlg;
2751 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002752 pj_status_t status;
2753
2754 PJ_UNUSED_ARG(th);
2755
2756 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2757
2758 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2759 if (status != PJ_SUCCESS)
2760 return;
2761
Benny Prijono1e601552010-10-20 05:31:08 +00002762 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002763
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002764 pjsip_dlg_dec_lock(dlg);
2765}
2766
2767
2768/* Check if the specified format can be skipped in counting codecs */
2769static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2770 const pj_str_t *fmt)
2771{
Benny Prijono1e601552010-10-20 05:31:08 +00002772 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002773 unsigned pt;
2774
2775 pt = pj_strtoul(fmt);
2776
2777 /* Check for comfort noise */
2778 if (pt == PJMEDIA_RTP_PT_CN)
2779 return PJ_TRUE;
2780
2781 /* Dynamic PT, check the format name */
2782 if (pt >= 96) {
2783 pjmedia_sdp_attr *a;
2784 pjmedia_sdp_rtpmap rtpmap;
2785
2786 /* Get the format name */
2787 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2788 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2789 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002790 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002791 return PJ_TRUE;
2792 } else {
2793 /* Invalid SDP, should not reach here */
2794 pj_assert(!"SDP should have been validated!");
2795 return PJ_TRUE;
2796 }
2797 }
2798
2799 return PJ_FALSE;
2800}
2801
2802
Benny Prijono1e601552010-10-20 05:31:08 +00002803/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2804 * out of several codecs presented by callee in his answer.
2805 */
2806static pj_status_t perform_lock_codec(pjsua_call *call)
2807{
2808 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2809 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002810 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002811 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002812 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002813 pjsip_tx_data *tdata;
2814 pj_status_t status;
2815
2816 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2817 PJ_EINVALIDOP);
2818
2819 /* Verify if another SDP negotiation is in progress, e.g: session timer
2820 * or another re-INVITE.
2821 */
2822 if (call->inv==NULL || call->inv->neg==NULL ||
2823 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2824 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002825 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002826 }
2827
Benny Prijono02493272010-11-17 09:15:04 +00002828 /* Don't do this if call is disconnecting! */
2829 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2830 call->inv->cause >= 200)
2831 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002832 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002833 }
2834
Benny Prijono1e601552010-10-20 05:31:08 +00002835 /* Verify if another SDP negotiation has been completed by comparing
2836 * the SDP version.
2837 */
2838 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2839 if (status != PJ_SUCCESS)
2840 return status;
2841 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2842 return PJMEDIA_SDP_EINVER;
2843
2844 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2845
2846 /* Update the new offer so it contains only a codec. Note that formats
2847 * order in the offer should have been matched to the answer, so we can
2848 * just directly update the offer without looking-up the answer.
2849 */
2850 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002851
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002852 for (i = 0; i < call->med_cnt; ++i) {
2853 unsigned j = 0, codec_cnt = 0;
2854 const pjmedia_sdp_media *ref_m;
2855 pjmedia_sdp_media *m;
2856 pjsua_call_media *call_med = &call->media[i];
2857
2858 /* Verify if media is deactivated */
2859 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2860 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2861 call_med->dir == PJMEDIA_DIR_NONE)
2862 {
Benny Prijono1e601552010-10-20 05:31:08 +00002863 continue;
2864 }
2865
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002866 ref_m = local_sdp->media[i];
2867 m = new_sdp->media[i];
2868
2869 /* Verify that media must be active. */
2870 pj_assert(ref_m->desc.port);
2871
2872 while (j < m->desc.fmt_count) {
2873 pjmedia_sdp_attr *a;
2874 pj_str_t *fmt = &m->desc.fmt[j];
2875
2876 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2877 ++j;
2878 continue;
2879 }
2880
2881 /* Remove format */
2882 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2883 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2884 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2885 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2886 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2887 m->desc.fmt_count, j);
2888 --m->desc.fmt_count;
2889 }
2890
2891 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002892 }
2893
Nanang Izzuddinec919002010-11-25 09:27:06 +00002894 /* Last check if SDP trully needs to be updated. It is possible that OA
2895 * negotiations have completed and SDP has changed but we didn't
2896 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002897 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002898 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002899 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002900
2901 /* Send UPDATE or re-INVITE */
2902 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2903 PJSIP_H_ALLOW,
2904 NULL, &STR_UPDATE) ==
2905 PJSIP_DIALOG_CAP_SUPPORTED;
2906 if (rem_can_update) {
2907 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2908 } else {
2909 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2910 }
2911
2912 if (status==PJ_EINVALIDOP &&
2913 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2914 {
2915 /* Ups, let's reschedule again */
2916 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2917 pj_time_val_normalize(&delay);
2918 call->lock_codec.reinv_timer.id = PJ_TRUE;
2919 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2920 &call->lock_codec.reinv_timer, &delay);
2921 return status;
2922 } else if (status != PJ_SUCCESS) {
2923 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2924 status);
2925 return status;
2926 }
2927
2928 /* Send the UPDATE/re-INVITE request */
2929 status = pjsip_inv_send_msg(call->inv, tdata);
2930 if (status != PJ_SUCCESS) {
2931 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2932 status);
2933 return status;
2934 }
2935
2936 return status;
2937}
2938
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002939/* Check if remote answerer has given us more than one codecs. If so,
2940 * create another offer with one codec only to lock down the codec.
2941 */
2942static pj_status_t lock_codec(pjsua_call *call)
2943{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002944 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002945 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002946 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002947 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002948 unsigned i;
2949 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002950 pj_status_t status;
2951
Nanang Izzuddinec919002010-11-25 09:27:06 +00002952 /* Stop lock codec timer, if it is active */
2953 if (call->lock_codec.reinv_timer.id) {
2954 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2955 &call->lock_codec.reinv_timer);
2956 call->lock_codec.reinv_timer.id = PJ_FALSE;
2957 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002958
Nanang Izzuddinec919002010-11-25 09:27:06 +00002959 /* Skip this if we are the answerer */
2960 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2961 return PJ_SUCCESS;
2962 }
2963
Nanang Izzuddinec919002010-11-25 09:27:06 +00002964 /* Delay this when the SDP negotiation done in call state EARLY and
2965 * remote does not support UPDATE method.
2966 */
2967 if (inv->state == PJSIP_INV_STATE_EARLY &&
2968 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2969 PJSIP_DIALOG_CAP_SUPPORTED)
2970 {
2971 call->lock_codec.pending = PJ_TRUE;
2972 return PJ_SUCCESS;
2973 }
2974
2975 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002976 if (status != PJ_SUCCESS)
2977 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002978 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002979 if (status != PJ_SUCCESS)
2980 return status;
2981
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002982 /* Find multiple codecs answer in all media */
2983 for (i = 0; i < call->med_cnt; ++i) {
2984 pjsua_call_media *call_med = &call->media[i];
2985 const pjmedia_sdp_media *rem_m, *loc_m;
2986 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002987
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002988 /* Skip this if the media is inactive or error */
2989 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2990 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2991 call_med->dir == PJMEDIA_DIR_NONE)
2992 {
2993 continue;
2994 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002995
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002996 /* Remote may answer with less media lines. */
2997 if (i >= remote_sdp->media_count)
2998 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002999
Nanang Izzuddin5f326402011-05-20 08:47:14 +00003000 rem_m = remote_sdp->media[i];
3001 loc_m = local_sdp->media[i];
3002
3003 /* Verify that media must be active. */
3004 pj_assert(loc_m->desc.port && rem_m->desc.port);
3005
3006 /* Count the formats in the answer. */
3007 if (rem_m->desc.fmt_count==1) {
3008 codec_cnt = 1;
3009 } else {
3010 unsigned j;
3011 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
3012 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
3013 ++codec_cnt;
3014 }
3015 }
3016
3017 if (codec_cnt > 1) {
3018 has_mult_fmt = PJ_TRUE;
3019 break;
3020 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003021 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00003022
3023 /* Each media in the answer already contains single codec. */
3024 if (!has_mult_fmt) {
3025 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003026 return PJ_SUCCESS;
3027 }
3028
Nanang Izzuddinec919002010-11-25 09:27:06 +00003029 /* Remote keeps answering with multiple codecs, let's just give up
3030 * locking codec to avoid infinite retry loop.
3031 */
3032 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
3033 return PJ_SUCCESS;
3034
Benny Prijonob90fd382011-09-18 14:59:56 +00003035 PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003036 "updating media session to use only one codec.."));
3037
Benny Prijono1e601552010-10-20 05:31:08 +00003038 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003039
Benny Prijono1e601552010-10-20 05:31:08 +00003040 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
3041 * See: https://trac.pjsip.org/repos/ticket/1149
3042 */
3043 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3044 (void*)(pj_size_t)call->index,
3045 &reinv_timer_cb);
3046 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3047 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003048
3049 return PJ_SUCCESS;
3050}
3051
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003052/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003053 * This callback receives notification from invite session when the
3054 * session state has changed.
3055 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003056static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3057 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003058{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003059 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003060
Benny Prijonob90fd382011-09-18 14:59:56 +00003061 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003062 PJSUA_LOCK();
3063
Benny Prijonoa1e69682007-05-11 15:14:34 +00003064 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003065
3066 if (!call) {
3067 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003068 pj_log_pop_indent();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003069 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003070 }
3071
Benny Prijonoe21e7842006-04-09 16:46:05 +00003072
3073 /* Get call times */
3074 switch (inv->state) {
3075 case PJSIP_INV_STATE_EARLY:
3076 case PJSIP_INV_STATE_CONNECTING:
3077 if (call->res_time.sec == 0)
3078 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003079 call->last_code = (pjsip_status_code)
3080 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003081 pj_strncpy(&call->last_text,
3082 &e->body.tsx_state.tsx->status_text,
3083 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003084 break;
3085 case PJSIP_INV_STATE_CONFIRMED:
3086 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003087
Nanang Izzuddinec919002010-11-25 09:27:06 +00003088 /* See if lock codec was pended as media update was done in the
3089 * EARLY state and remote does not support UPDATE.
3090 */
3091 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003092 pj_status_t status;
3093 status = lock_codec(call);
3094 if (status != PJ_SUCCESS) {
3095 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00003096 }
3097 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003098 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003099 break;
3100 case PJSIP_INV_STATE_DISCONNECTED:
3101 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003102 if (call->res_time.sec == 0)
3103 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003104 if (e->type == PJSIP_EVENT_TSX_STATE &&
3105 e->body.tsx_state.tsx->status_code > call->last_code)
3106 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003107 call->last_code = (pjsip_status_code)
3108 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003109 pj_strncpy(&call->last_text,
3110 &e->body.tsx_state.tsx->status_text,
3111 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003112 } else {
3113 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3114 pj_strncpy(&call->last_text,
3115 pjsip_get_status_text(call->last_code),
3116 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003117 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003118
3119 /* Stop lock codec timer, if it is active */
3120 if (call->lock_codec.reinv_timer.id) {
3121 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3122 &call->lock_codec.reinv_timer);
3123 call->lock_codec.reinv_timer.id = PJ_FALSE;
3124 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003125 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003126 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003127 call->last_code = (pjsip_status_code)
3128 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003129 pj_strncpy(&call->last_text,
3130 &e->body.tsx_state.tsx->status_text,
3131 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003132 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003133 }
3134
Benny Prijono26ff9062006-02-21 23:47:00 +00003135 /* If this is an outgoing INVITE that was created because of
3136 * REFER/transfer, send NOTIFY to transferer.
3137 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003138 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003139 int st_code = -1;
3140 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3141
3142
Benny Prijonoa91a0032006-02-26 21:23:45 +00003143 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003144 case PJSIP_INV_STATE_NULL:
3145 case PJSIP_INV_STATE_CALLING:
3146 /* Do nothing */
3147 break;
3148
3149 case PJSIP_INV_STATE_EARLY:
3150 case PJSIP_INV_STATE_CONNECTING:
3151 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003152 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3153 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3154 else
3155 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003156 break;
3157
Benny Prijono140beae2009-10-11 05:06:43 +00003158 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003159#if 0
3160/* We don't need this, as we've terminated the subscription in
3161 * CONNECTING state.
3162 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003163 /* When state is confirmed, send the final 200/OK and terminate
3164 * subscription.
3165 */
3166 st_code = e->body.tsx_state.tsx->status_code;
3167 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003168#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003169 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003170
3171 case PJSIP_INV_STATE_DISCONNECTED:
3172 st_code = e->body.tsx_state.tsx->status_code;
3173 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3174 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003175
Benny Prijono8b1889b2006-06-06 18:40:40 +00003176 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003177 /* Nothing to do. Just to keep gcc from complaining about
3178 * unused enums.
3179 */
3180 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003181 }
3182
3183 if (st_code != -1) {
3184 pjsip_tx_data *tdata;
3185 pj_status_t status;
3186
Benny Prijonoa91a0032006-02-26 21:23:45 +00003187 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003188 ev_state, st_code,
3189 NULL, &tdata);
3190 if (status != PJ_SUCCESS) {
3191 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3192 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003193 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003194 if (status != PJ_SUCCESS) {
3195 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3196 }
3197 }
3198 }
3199 }
3200
Benny Prijono84126ab2006-02-09 09:30:09 +00003201
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003202 if (pjsua_var.ua_cfg.cb.on_call_state)
3203 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003204
3205 /* call->inv may be NULL now */
3206
Benny Prijono84126ab2006-02-09 09:30:09 +00003207 /* Destroy media session when invite session is disconnected. */
3208 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003209
Benny Prijonoa91a0032006-02-26 21:23:45 +00003210 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003211
Benny Prijono275fd682006-03-22 11:59:11 +00003212 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003213 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003214
Benny Prijono105217f2006-03-06 16:25:59 +00003215 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003216 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003217 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003218
3219 /* Reset call */
3220 reset_call(call->index);
3221
Benny Prijono84126ab2006-02-09 09:30:09 +00003222 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003223
3224 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003225 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003226}
3227
3228/*
3229 * This callback is called by invite session framework when UAC session
3230 * has forked.
3231 */
3232static void pjsua_call_on_forked( pjsip_inv_session *inv,
3233 pjsip_event *e)
3234{
3235 PJ_UNUSED_ARG(inv);
3236 PJ_UNUSED_ARG(e);
3237
3238 PJ_TODO(HANDLE_FORKED_DIALOG);
3239}
3240
3241
3242/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003243 * Callback from UA layer when forked dialog response is received.
3244 */
3245pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3246{
3247 if (dlg->uac_has_2xx &&
3248 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3249 pjsip_rdata_get_tsx(res) == NULL &&
3250 res->msg_info.msg->line.status.code/100 == 2)
3251 {
3252 pjsip_dialog *forked_dlg;
3253 pjsip_tx_data *bye;
3254 pj_status_t status;
3255
3256 /* Create forked dialog */
3257 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3258 if (status != PJ_SUCCESS)
3259 return NULL;
3260
3261 pjsip_dlg_inc_lock(forked_dlg);
3262
3263 /* Disconnect the call */
3264 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3265 -1, &bye);
3266 if (status == PJ_SUCCESS) {
3267 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3268 }
3269
3270 pjsip_dlg_dec_lock(forked_dlg);
3271
3272 if (status != PJ_SUCCESS) {
3273 return NULL;
3274 }
3275
3276 return forked_dlg;
3277
3278 } else {
3279 return dlg;
3280 }
3281}
3282
3283/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003284 * Disconnect call upon error.
3285 */
3286static void call_disconnect( pjsip_inv_session *inv,
3287 int code )
3288{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003289 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003290 pjsip_tx_data *tdata;
3291 pj_status_t status;
3292
Benny Prijono59b3aed2008-01-15 16:54:54 +00003293 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3294
Benny Prijonoa38ada02006-07-02 14:22:35 +00003295 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003296 if (status != PJ_SUCCESS)
3297 return;
3298
3299 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003300#if DISABLED_FOR_TICKET_1185
3301 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00003302 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3303 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003304 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003305 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003306
Benny Prijono734fc2d2008-03-17 16:05:35 +00003307 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003308 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003309 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003310 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003311 if (status == PJ_SUCCESS) {
3312 pjsip_create_sdp_body(tdata->pool, local_sdp,
3313 &tdata->msg->body);
3314 }
3315 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00003316#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00003317
3318 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003319}
3320
3321/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003322 * Callback to be called when SDP offer/answer negotiation has just completed
3323 * in the session. This function will start/update media if negotiation
3324 * has succeeded.
3325 */
3326static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3327 pj_status_t status)
3328{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003329 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003330 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003331 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003332 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003333
Benny Prijonob90fd382011-09-18 14:59:56 +00003334 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003335 PJSUA_LOCK();
3336
Benny Prijonoa1e69682007-05-11 15:14:34 +00003337 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003338
3339 if (status != PJ_SUCCESS) {
3340
3341 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3342
Benny Prijono2331d202008-06-26 15:46:52 +00003343 /* Do not deinitialize media since this may be a re-INVITE or
3344 * UPDATE (which in this case the media should not get affected
3345 * by the failed re-INVITE/UPDATE). The media will be shutdown
3346 * when call is disconnected anyway.
3347 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003348 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003349 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003350
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003351 /* Disconnect call if we're not in the middle of initializing an
3352 * UAS dialog and if this is not a re-INVITE
3353 */
3354 if (inv->state != PJSIP_INV_STATE_NULL &&
3355 inv->state != PJSIP_INV_STATE_CONFIRMED)
3356 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003357 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003358 }
3359
Benny Prijonob90fd382011-09-18 14:59:56 +00003360 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003361 }
3362
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003363
3364 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003365 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003366 if (status != PJ_SUCCESS) {
3367 pjsua_perror(THIS_FILE,
3368 "Unable to retrieve currently active local SDP",
3369 status);
3370 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003371 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003372 }
3373
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003374 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3375 if (status != PJ_SUCCESS) {
3376 pjsua_perror(THIS_FILE,
3377 "Unable to retrieve currently active remote SDP",
3378 status);
3379 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonob90fd382011-09-18 14:59:56 +00003380 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003381 }
3382
Benny Prijono91a6a172007-10-31 08:59:29 +00003383 /* Update remote's NAT type */
3384 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3385 update_remote_nat_type(call, remote_sdp);
3386 }
3387
3388 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003389 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003390 if (status != PJ_SUCCESS) {
3391 pjsua_perror(THIS_FILE, "Unable to create media session",
3392 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003393 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003394 /* No need to deinitialize; media will be shutdown when call
3395 * state is disconnected anyway.
3396 */
3397 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonob90fd382011-09-18 14:59:56 +00003398 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003399 }
3400
Nanang Izzuddinec919002010-11-25 09:27:06 +00003401 /* Ticket #476: make sure only one codec is specified in the answer. */
3402 status = lock_codec(call);
3403 if (status != PJ_SUCCESS) {
3404 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003405 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003406
3407 /* Call application callback, if any */
3408 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3409 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3410
Benny Prijonob90fd382011-09-18 14:59:56 +00003411on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003412 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003413 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003414}
3415
3416
Benny Prijonodd63b992010-10-01 02:03:42 +00003417/* Modify SDP for call hold. */
3418static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3419 pj_pool_t *pool,
3420 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003421{
Benny Prijono316f02a2011-04-07 07:53:25 +00003422 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003423
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003424 /* Call-hold is done by set the media direction to 'sendonly'
3425 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3426 * 'inactive' (PJMEDIA_DIR_NONE).
3427 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3428 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003429 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003430 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003431 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003432 /* https://trac.pjsip.org/repos/ticket/1142:
3433 * configuration to use c=0.0.0.0 for call hold.
3434 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003435
Benny Prijono316f02a2011-04-07 07:53:25 +00003436 for (mi=0; mi<sdp->media_count; ++mi) {
3437 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003438
Benny Prijono316f02a2011-04-07 07:53:25 +00003439 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3440 pjmedia_sdp_conn *conn;
3441 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003442
Benny Prijono316f02a2011-04-07 07:53:25 +00003443 /* Get SDP media connection line */
3444 conn = m->conn;
3445 if (!conn)
3446 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003447
Benny Prijono316f02a2011-04-07 07:53:25 +00003448 /* Modify address */
3449 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003450
Benny Prijono316f02a2011-04-07 07:53:25 +00003451 /* Remove existing directions attributes */
3452 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3453 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3454 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3455 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003456
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003457 /* Add inactive attribute */
3458 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003459 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003460
3461
3462 } else {
3463 pjmedia_sdp_attr *attr;
3464
3465 /* Remove existing directions attributes */
3466 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3467 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3468 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3469 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3470
3471 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3472 /* Add sendonly attribute */
3473 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3474 pjmedia_sdp_media_add_attr(m, attr);
3475 } else {
3476 /* Add inactive attribute */
3477 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3478 pjmedia_sdp_media_add_attr(m, attr);
3479 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003480 }
3481 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003482
Benny Prijonodd63b992010-10-01 02:03:42 +00003483 return PJ_SUCCESS;
3484}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003485
Benny Prijonodd63b992010-10-01 02:03:42 +00003486/* Create SDP for call hold. */
3487static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3488 pjmedia_sdp_session **p_sdp)
3489{
3490 pj_status_t status;
3491 pj_pool_t *pool;
3492 pjmedia_sdp_session *sdp;
3493
3494 /* Use call's provisional pool */
3495 pool = call->inv->pool_prov;
3496
3497 /* Create new offer */
3498 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3499 NULL);
3500 if (status != PJ_SUCCESS) {
3501 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3502 return status;
3503 }
3504
3505 status = modify_sdp_of_call_hold(call, pool, sdp);
3506 if (status != PJ_SUCCESS)
3507 return status;
3508
3509 *p_sdp = sdp;
3510
3511 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003512}
3513
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003514/*
3515 * Called when session received new offer.
3516 */
3517static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3518 const pjmedia_sdp_session *offer)
3519{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003520 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003521 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003522 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003523 pj_status_t status;
3524
3525 PJSUA_LOCK();
3526
Benny Prijonoa1e69682007-05-11 15:14:34 +00003527 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003528
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003529 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003530 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3531 call->index));
Benny Prijonob90fd382011-09-18 14:59:56 +00003532 pj_log_push_indent();
Benny Prijono667952e2007-04-02 19:27:54 +00003533
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00003534 /* Re-init media for the new remote offer before creating SDP */
3535 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
3536 call->secure_level,
3537 call->inv->pool_prov,
3538 offer, NULL,
3539 PJ_FALSE, NULL);
3540 if (status != PJ_SUCCESS) {
3541 pjsua_perror(THIS_FILE, "Error re-initializing media channel", status);
3542 goto on_return;
3543 }
3544
Benny Prijono40d62b62009-08-12 17:53:47 +00003545 status = pjsua_media_channel_create_sdp(call->index,
3546 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003547 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003548 if (status != PJ_SUCCESS) {
3549 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003550 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003551 }
3552
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003553 /* Validate media count in the generated answer */
3554 pj_assert(answer->media_count == offer->media_count);
3555
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003556 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003557 for (i = 0; i < answer->media_count; ++i) {
3558 pjmedia_sdp_conn *conn;
3559
3560 conn = offer->media[i]->conn;
3561 if (!conn)
3562 conn = offer->conn;
3563
3564 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3565 pj_strcmp2(&conn->addr, "0")==0)
3566 {
3567 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3568
3569 /* Modify answer address */
3570 if (a_conn) {
3571 a_conn->addr = pj_str("0.0.0.0");
3572 } else if (answer->conn == NULL ||
3573 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3574 {
3575 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3576 pjmedia_sdp_conn);
3577 a_conn->net_type = pj_str("IN");
3578 a_conn->addr_type = pj_str("IP4");
3579 a_conn->addr = pj_str("0.0.0.0");
3580 answer->media[i]->conn = a_conn;
3581 }
3582 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003583 }
3584
3585 /* Check if call is on-hold */
3586 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003587 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003588 }
3589
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003590 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3591 if (status != PJ_SUCCESS) {
3592 pjsua_perror(THIS_FILE, "Unable to set answer", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003593 goto on_return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003594 }
3595
Benny Prijonob90fd382011-09-18 14:59:56 +00003596on_return:
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003597 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003598 pj_log_pop_indent();
Benny Prijono84126ab2006-02-09 09:30:09 +00003599}
3600
3601
3602/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003603 * Called to generate new offer.
3604 */
3605static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3606 pjmedia_sdp_session **offer)
3607{
3608 pjsua_call *call;
3609 pj_status_t status;
3610
Benny Prijonob90fd382011-09-18 14:59:56 +00003611 pj_log_push_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003612 PJSUA_LOCK();
3613
3614 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3615
3616 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003617 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003618 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003619 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003620 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003621 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003622 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003623 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3624 call->index));
3625
Benny Prijono40d62b62009-08-12 17:53:47 +00003626 status = pjsua_media_channel_create_sdp(call->index,
3627 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003628 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003629 }
3630
3631 if (status != PJ_SUCCESS) {
3632 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003633 goto on_return;
Benny Prijono77998ce2007-06-20 10:03:46 +00003634 }
3635
Benny Prijonob90fd382011-09-18 14:59:56 +00003636on_return:
Benny Prijono77998ce2007-06-20 10:03:46 +00003637 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00003638 pj_log_pop_indent();
Benny Prijono77998ce2007-06-20 10:03:46 +00003639}
3640
3641
3642/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003643 * Callback called by event framework when the xfer subscription state
3644 * has changed.
3645 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003646static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3647{
3648
3649 PJ_UNUSED_ARG(event);
3650
Benny Prijonob90fd382011-09-18 14:59:56 +00003651 pj_log_push_indent();
3652
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003653 /*
3654 * When subscription is accepted (got 200/OK to REFER), check if
3655 * subscription suppressed.
3656 */
3657 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3658
3659 pjsip_rx_data *rdata;
3660 pjsip_generic_string_hdr *refer_sub;
3661 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3662 pjsua_call *call;
3663
Benny Prijonoa1e69682007-05-11 15:14:34 +00003664 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003665
3666 /* Must be receipt of response message */
3667 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3668 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3669 rdata = event->body.tsx_state.src.rdata;
3670
3671 /* Find Refer-Sub header */
3672 refer_sub = (pjsip_generic_string_hdr*)
3673 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3674 &REFER_SUB, NULL);
3675
3676 /* Check if subscription is suppressed */
3677 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3678 /* Since no subscription is desired, assume that call has been
3679 * transfered successfully.
3680 */
3681 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3682 const pj_str_t ACCEPTED = { "Accepted", 8 };
3683 pj_bool_t cont = PJ_FALSE;
3684 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3685 200,
3686 &ACCEPTED,
3687 PJ_TRUE,
3688 &cont);
3689 }
3690
3691 /* Yes, subscription is suppressed.
3692 * Terminate our subscription now.
3693 */
3694 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3695 "event subcription..."));
3696 pjsip_evsub_terminate(sub, PJ_TRUE);
3697
3698 } else {
3699 /* Notify application about call transfer progress.
3700 * Initially notify with 100/Accepted status.
3701 */
3702 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3703 const pj_str_t ACCEPTED = { "Accepted", 8 };
3704 pj_bool_t cont = PJ_FALSE;
3705 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3706 100,
3707 &ACCEPTED,
3708 PJ_FALSE,
3709 &cont);
3710 }
3711 }
3712 }
3713 /*
3714 * On incoming NOTIFY, notify application about call transfer progress.
3715 */
3716 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3717 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3718 {
3719 pjsua_call *call;
3720 pjsip_msg *msg;
3721 pjsip_msg_body *body;
3722 pjsip_status_line status_line;
3723 pj_bool_t is_last;
3724 pj_bool_t cont;
3725 pj_status_t status;
3726
Benny Prijonoa1e69682007-05-11 15:14:34 +00003727 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003728
3729 /* When subscription is terminated, clear the xfer_sub member of
3730 * the inv_data.
3731 */
3732 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3733 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3734 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3735
3736 }
3737
3738 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3739 /* Application is not interested with call progress status */
Benny Prijonob90fd382011-09-18 14:59:56 +00003740 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003741 }
3742
3743 /* This better be a NOTIFY request */
3744 if (event->type == PJSIP_EVENT_TSX_STATE &&
3745 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3746 {
3747 pjsip_rx_data *rdata;
3748
3749 rdata = event->body.tsx_state.src.rdata;
3750
3751 /* Check if there's body */
3752 msg = rdata->msg_info.msg;
3753 body = msg->body;
3754 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003755 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003756 "Warning: received NOTIFY without message body"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003757 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003758 }
3759
3760 /* Check for appropriate content */
3761 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3762 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3763 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003764 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003765 "Warning: received NOTIFY with non message/sipfrag "
3766 "content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003767 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003768 }
3769
3770 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003771 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003772 &status_line);
3773 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003774 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003775 "Warning: received NOTIFY with invalid "
3776 "message/sipfrag content"));
Benny Prijonob90fd382011-09-18 14:59:56 +00003777 goto on_return;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003778 }
3779
3780 } else {
3781 status_line.code = 500;
3782 status_line.reason = *pjsip_get_status_text(500);
3783 }
3784
3785 /* Notify application */
3786 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3787 cont = !is_last;
3788 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3789 status_line.code,
3790 &status_line.reason,
3791 is_last, &cont);
3792
3793 if (!cont) {
3794 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3795 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003796
3797 /* If the call transfer has completed but the subscription is
3798 * not terminated, terminate it now.
3799 */
3800 if (status_line.code/100 == 2 && !is_last) {
3801 pjsip_tx_data *tdata;
3802
3803 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3804 0, &tdata);
3805 if (status == PJ_SUCCESS)
3806 status = pjsip_evsub_send_request(sub, tdata);
3807 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003808 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003809
3810on_return:
3811 pj_log_pop_indent();
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003812}
3813
3814
3815/*
3816 * Callback called by event framework when the xfer subscription state
3817 * has changed.
3818 */
3819static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003820{
Benny Prijono26ff9062006-02-21 23:47:00 +00003821 PJ_UNUSED_ARG(event);
3822
Benny Prijonob90fd382011-09-18 14:59:56 +00003823 pj_log_push_indent();
3824
Benny Prijono26ff9062006-02-21 23:47:00 +00003825 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003826 * When subscription is terminated, clear the xfer_sub member of
3827 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003828 */
3829 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003830 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003831
Benny Prijonoa1e69682007-05-11 15:14:34 +00003832 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003833 if (!call)
Benny Prijonob90fd382011-09-18 14:59:56 +00003834 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003835
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003836 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003837 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003838
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003839 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003840 }
Benny Prijonob90fd382011-09-18 14:59:56 +00003841
3842on_return:
3843 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00003844}
3845
3846
3847/*
3848 * Follow transfer (REFER) request.
3849 */
3850static void on_call_transfered( pjsip_inv_session *inv,
3851 pjsip_rx_data *rdata )
3852{
3853 pj_status_t status;
3854 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003855 pjsua_call *existing_call;
3856 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003857 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003858 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003859 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003860 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003861 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003862 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003863 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003864 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003865 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003866 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003867 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003868 pjsip_evsub *sub;
3869
Benny Prijonob90fd382011-09-18 14:59:56 +00003870 pj_log_push_indent();
3871
Benny Prijonoa1e69682007-05-11 15:14:34 +00003872 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003873
Benny Prijono26ff9062006-02-21 23:47:00 +00003874 /* Find the Refer-To header */
3875 refer_to = (pjsip_generic_string_hdr*)
3876 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3877
3878 if (refer_to == NULL) {
3879 /* Invalid Request.
3880 * No Refer-To header!
3881 */
3882 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003883 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003884 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00003885 }
3886
Benny Prijonoc8141a82006-08-20 09:12:19 +00003887 /* Find optional Refer-Sub header */
3888 refer_sub = (pjsip_generic_string_hdr*)
3889 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3890
3891 if (refer_sub) {
3892 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3893 no_refer_sub = PJ_TRUE;
3894 }
3895
Benny Prijono053f5222006-11-11 16:16:04 +00003896 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3897 * request.
3898 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003899 ref_by_hdr = (pjsip_hdr*)
3900 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003901 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003902
Benny Prijono9fc735d2006-05-28 14:58:12 +00003903 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003904 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003905 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3906 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3907 &refer_to->hvalue,
3908 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003909
3910 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003911 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003912 if (code >= 300) {
3913 /* Application rejects call transfer request */
3914 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003915 goto on_return;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003916 }
3917
Benny Prijono26ff9062006-02-21 23:47:00 +00003918 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3919 (int)inv->dlg->remote.info_str.slen,
3920 inv->dlg->remote.info_str.ptr,
3921 (int)refer_to->hvalue.slen,
3922 refer_to->hvalue.ptr));
3923
Benny Prijonoc8141a82006-08-20 09:12:19 +00003924 if (no_refer_sub) {
3925 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003926 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003927 */
3928 pjsip_tx_data *tdata;
3929 const pj_str_t str_false = { "false", 5};
3930 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003931
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003932 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3933 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003934 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003935 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003936 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003937 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003938 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003939
Benny Prijonoc8141a82006-08-20 09:12:19 +00003940 /* Add Refer-Sub header */
3941 hdr = (pjsip_hdr*)
3942 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3943 &str_false);
3944 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003945
Benny Prijono26ff9062006-02-21 23:47:00 +00003946
Benny Prijonoc8141a82006-08-20 09:12:19 +00003947 /* Send answer */
3948 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3949 tdata);
3950 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003951 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003952 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00003953 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003954 }
3955
3956 /* Don't have subscription */
3957 sub = NULL;
3958
3959 } else {
3960 struct pjsip_evsub_user xfer_cb;
3961 pjsip_hdr hdr_list;
3962
3963 /* Init callback */
3964 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003965 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003966
3967 /* Init additional header list to be sent with REFER response */
3968 pj_list_init(&hdr_list);
3969
3970 /* Create transferee event subscription */
3971 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3972 if (status != PJ_SUCCESS) {
3973 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3974 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijonob90fd382011-09-18 14:59:56 +00003975 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003976 }
3977
3978 /* If there's Refer-Sub header and the value is "true", send back
3979 * Refer-Sub in the response with value "true" too.
3980 */
3981 if (refer_sub) {
3982 const pj_str_t str_true = { "true", 4 };
3983 pjsip_hdr *hdr;
3984
3985 hdr = (pjsip_hdr*)
3986 pjsip_generic_string_hdr_create(inv->dlg->pool,
3987 &str_refer_sub,
3988 &str_true);
3989 pj_list_push_back(&hdr_list, hdr);
3990
3991 }
3992
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003993 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003994 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3995
3996 /* Create initial NOTIFY request */
3997 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3998 100, NULL, &tdata);
3999 if (status != PJ_SUCCESS) {
4000 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4001 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00004002 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004003 }
4004
4005 /* Send initial NOTIFY request */
4006 status = pjsip_xfer_send_request( sub, tdata);
4007 if (status != PJ_SUCCESS) {
4008 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
Benny Prijonob90fd382011-09-18 14:59:56 +00004009 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004010 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004011 }
4012
4013 /* We're cheating here.
4014 * We need to get a null terminated string from a pj_str_t.
4015 * So grab the pointer from the hvalue and NULL terminate it, knowing
4016 * that the NULL position will be occupied by a newline.
4017 */
4018 uri = refer_to->hvalue.ptr;
4019 uri[refer_to->hvalue.slen] = '\0';
4020
Benny Prijono053f5222006-11-11 16:16:04 +00004021 /* Init msg_data */
4022 pjsua_msg_data_init(&msg_data);
4023
4024 /* If Referred-By header is present in the REFER request, copy this
4025 * to the outgoing INVITE request.
4026 */
4027 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004028 pjsip_hdr *dup = (pjsip_hdr*)
4029 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004030 pj_list_push_back(&msg_data.hdr_list, dup);
4031 }
4032
Benny Prijono26ff9062006-02-21 23:47:00 +00004033 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004034 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004035 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004036 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004037 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004038 if (status != PJ_SUCCESS) {
4039
Benny Prijonoc8141a82006-08-20 09:12:19 +00004040 /* Notify xferer about the error (if we have subscription) */
4041 if (sub) {
4042 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4043 500, NULL, &tdata);
4044 if (status != PJ_SUCCESS) {
4045 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4046 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00004047 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004048 }
4049 status = pjsip_xfer_send_request(sub, tdata);
4050 if (status != PJ_SUCCESS) {
4051 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4052 status);
Benny Prijonob90fd382011-09-18 14:59:56 +00004053 goto on_return;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004054 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004055 }
Benny Prijonob90fd382011-09-18 14:59:56 +00004056 goto on_return;
Benny Prijono26ff9062006-02-21 23:47:00 +00004057 }
4058
Benny Prijonoc8141a82006-08-20 09:12:19 +00004059 if (sub) {
4060 /* Put the server subscription in inv_data.
4061 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4062 * reported back to the server subscription.
4063 */
4064 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004065
Benny Prijonoc8141a82006-08-20 09:12:19 +00004066 /* Put the invite_data in the subscription. */
4067 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4068 &pjsua_var.calls[new_call]);
4069 }
Benny Prijonob90fd382011-09-18 14:59:56 +00004070
4071on_return:
4072 pj_log_pop_indent();
Benny Prijono26ff9062006-02-21 23:47:00 +00004073}
4074
4075
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004076
Benny Prijono26ff9062006-02-21 23:47:00 +00004077/*
4078 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004079 * session. We use this to trap:
4080 * - incoming REFER request.
4081 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004082 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004083static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4084 pjsip_transaction *tsx,
4085 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004086{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004087 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004088
Benny Prijonob90fd382011-09-18 14:59:56 +00004089 pj_log_push_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004090 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004091
Benny Prijono2285e7e2008-12-17 14:28:18 +00004092 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4093
Benny Prijonob90fd382011-09-18 14:59:56 +00004094 if (call == NULL)
4095 goto on_return;
Benny Prijono2285e7e2008-12-17 14:28:18 +00004096
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004097 if (call->inv == NULL) {
4098 /* Shouldn't happen. It happens only when we don't terminate the
4099 * server subscription caused by REFER after the call has been
4100 * transfered (and this call has been disconnected), and we
4101 * receive another REFER for this call.
4102 */
Benny Prijonob90fd382011-09-18 14:59:56 +00004103 goto on_return;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004104 }
4105
Benny Prijonofeb69f42007-10-05 09:12:26 +00004106 /* Notify application callback first */
4107 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4108 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4109 }
4110
Benny Prijono26ff9062006-02-21 23:47:00 +00004111 if (tsx->role==PJSIP_ROLE_UAS &&
4112 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004113 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004114 {
4115 /*
4116 * Incoming REFER request.
4117 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004118 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004119
Benny Prijono26ff9062006-02-21 23:47:00 +00004120 }
Benny Prijonob0808372006-03-02 21:18:58 +00004121 else if (tsx->role==PJSIP_ROLE_UAS &&
4122 tsx->state==PJSIP_TSX_STATE_TRYING &&
4123 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4124 {
4125 /*
4126 * Incoming MESSAGE request!
4127 */
4128 pjsip_rx_data *rdata;
4129 pjsip_msg *msg;
4130 pjsip_accept_hdr *accept_hdr;
4131 pj_status_t status;
4132
4133 rdata = e->body.tsx_state.src.rdata;
4134 msg = rdata->msg_info.msg;
4135
4136 /* Request MUST have message body, with Content-Type equal to
4137 * "text/plain".
4138 */
4139 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4140
4141 pjsip_hdr hdr_list;
4142
4143 pj_list_init(&hdr_list);
4144 pj_list_push_back(&hdr_list, accept_hdr);
4145
4146 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4147 NULL, &hdr_list, NULL );
Benny Prijonob90fd382011-09-18 14:59:56 +00004148 goto on_return;
Benny Prijonob0808372006-03-02 21:18:58 +00004149 }
4150
4151 /* Respond with 200 first, so that remote doesn't retransmit in case
4152 * the UI takes too long to process the message.
4153 */
4154 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4155
4156 /* Process MESSAGE request */
4157 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4158 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004159
Benny Prijonob0808372006-03-02 21:18:58 +00004160 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004161 else if (tsx->role == PJSIP_ROLE_UAC &&
4162 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004163 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004164 /* Handle outgoing pager status */
4165 if (tsx->status_code >= 200) {
4166 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004167
Benny Prijonoa1e69682007-05-11 15:14:34 +00004168 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004169 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004170
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004171 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4172 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4173 &im_data->to,
4174 &im_data->body,
4175 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004176 (pjsip_status_code)
4177 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004178 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004179 }
Benny Prijonofccab712006-02-22 22:23:22 +00004180 }
Sauw Minge7dbbc82011-10-24 09:28:13 +00004181 } else if (tsx->role == PJSIP_ROLE_UAC &&
4182 tsx->last_tx == (pjsip_tx_data*)call->hold_msg &&
4183 tsx->state >= PJSIP_TSX_STATE_COMPLETED)
4184 {
4185 /* Monitor the status of call hold request */
4186 call->hold_msg = NULL;
4187 if (tsx->status_code/100 != 2) {
4188 /* Outgoing call hold failed */
4189 call->local_hold = PJ_FALSE;
4190 PJ_LOG(3,(THIS_FILE, "Error putting call %d on hold (reason=%d)",
4191 call->index, tsx->status_code));
4192 }
Benny Prijono834aee32006-02-19 01:38:06 +00004193 }
Benny Prijono834aee32006-02-19 01:38:06 +00004194
Benny Prijonob90fd382011-09-18 14:59:56 +00004195on_return:
Sauw Minge7dbbc82011-10-24 09:28:13 +00004196
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004197 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00004198 pj_log_pop_indent();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004199}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004200
4201
4202/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004203static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4204 const pjsip_uri *target,
4205 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004206{
4207 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004208 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004209
Benny Prijonob90fd382011-09-18 14:59:56 +00004210 pj_log_push_indent();
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004211 PJSUA_LOCK();
4212
4213 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004214 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4215 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004216 } else {
4217 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4218 "(callback not implemented by application). Disconnecting "
4219 "call.",
4220 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004221 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004222 }
4223
4224 PJSUA_UNLOCK();
Benny Prijonob90fd382011-09-18 14:59:56 +00004225 pj_log_pop_indent();
Benny Prijono08a48b82008-11-27 12:42:07 +00004226
4227 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004228}
4229