blob: ce4f0bda3db1fffcce2326b24e5cb4501b176790 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_call.c"
25
26
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000027/* Retry interval of sending re-INVITE for locking a codec when remote
28 * SDP answer contains multiple codec, in milliseconds.
29 */
30#define LOCK_CODEC_RETRY_INTERVAL 200
31
Benny Prijono1e601552010-10-20 05:31:08 +000032/*
33 * Max UPDATE/re-INVITE retry to lock codec
34 */
35#define LOCK_CODEC_MAX_RETRY 5
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000036
Benny Prijonoeebe9af2006-06-13 22:57:13 +000037/* This callback receives notification from invite session when the
38 * session state has changed.
39 */
40static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
41 pjsip_event *e);
42
43/* This callback is called by invite session framework when UAC session
44 * has forked.
45 */
46static void pjsua_call_on_forked( pjsip_inv_session *inv,
47 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000048
49/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050 * Callback to be called when SDP offer/answer negotiation has just completed
51 * in the session. This function will start/update media if negotiation
52 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000053 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054static void pjsua_call_on_media_update(pjsip_inv_session *inv,
55 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000056
57/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000058 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000059 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
61 const pjmedia_sdp_session *offer);
62
63/*
Benny Prijono77998ce2007-06-20 10:03:46 +000064 * Called to generate new offer.
65 */
66static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
67 pjmedia_sdp_session **offer);
68
69/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000070 * This callback is called when transaction state has changed in INVITE
71 * session. We use this to trap:
72 * - incoming REFER request.
73 * - incoming MESSAGE request.
74 */
75static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
76 pjsip_transaction *tsx,
77 pjsip_event *e);
78
Benny Prijono5e51a4e2008-11-27 00:06:46 +000079/*
80 * Redirection handler.
81 */
Benny Prijono08a48b82008-11-27 12:42:07 +000082static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
83 const pjsip_uri *target,
84 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000085
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086
Nanang Izzuddin99d69522008-08-04 15:01:38 +000087/* Create SDP for call hold. */
88static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
Benny Prijonodd63b992010-10-01 02:03:42 +000089 pjmedia_sdp_session **p_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000090
Benny Prijonod524e822006-09-22 12:48:18 +000091/*
92 * Callback called by event framework when the xfer subscription state
93 * has changed.
94 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000095static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
96static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
97
98/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 * Reset call descriptor.
100 */
101static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000102{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000104 unsigned i;
Benny Prijono105217f2006-03-06 16:25:59 +0000105
Benny Prijono0bc99a92011-03-17 04:34:43 +0000106 pj_bzero(call, sizeof(*call));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107 call->index = id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->last_text.ptr = call->last_text_buf_;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000109 for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {
110 pjsua_call_media *call_med = &call->media[i];
111 call_med->ssrc = pj_rand();
112 call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
Nanang Izzuddin62053a62011-07-12 11:08:32 +0000113 call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
114 call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000115 call_med->call = call;
116 call_med->idx = i;
117 call_med->tp_auto_del = PJ_TRUE;
118 }
Benny Prijono105217f2006-03-06 16:25:59 +0000119}
120
121
Benny Prijono275fd682006-03-22 11:59:11 +0000122/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000124 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000126{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 pjsip_inv_callback inv_cb;
128 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000129 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000131
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000132 /* Init calls array. */
133 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
134 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 /* Copy config */
137 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000138
Benny Prijono1cd713b2009-11-11 00:33:00 +0000139 /* Verify settings */
140 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
141 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
142 }
143
Benny Prijono91d06b62008-09-20 12:16:56 +0000144 /* Check the route URI's and force loose route if required */
145 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
146 status = normalize_route_uri(pjsua_var.pool,
147 &pjsua_var.ua_cfg.outbound_proxy[i]);
148 if (status != PJ_SUCCESS)
149 return status;
150 }
151
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000153 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
155 inv_cb.on_new_session = &pjsua_call_on_forked;
156 inv_cb.on_media_update = &pjsua_call_on_media_update;
157 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000158 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000160 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 /* Initialize invite session module: */
163 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
164 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
165
Benny Prijonoc8141a82006-08-20 09:12:19 +0000166 /* Add "norefersub" in Supported header */
167 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
168 NULL, 1, &str_norefersub);
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 return status;
171}
172
173
174/*
175 * Start call subsystem.
176 */
177pj_status_t pjsua_call_subsys_start(void)
178{
179 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000180 return PJ_SUCCESS;
181}
182
183
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185 * Get maximum number of calls configured in pjsua.
186 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000187PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190}
191
192
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193/*
194 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000196PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199}
200
201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202/*
203 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000204 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
206 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000209
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
215 if (!pjsua_var.calls[i].inv)
216 continue;
217 ids[c] = i;
218 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219 }
220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000222
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000223 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000224
225 return PJ_SUCCESS;
226}
227
228
Benny Prijono5773cd62008-01-19 13:01:42 +0000229/* Allocate one call id */
230static pjsua_call_id alloc_call_id(void)
231{
232 pjsua_call_id cid;
233
234#if 1
235 /* New algorithm: round-robin */
236 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
237 pjsua_var.next_call_id < 0)
238 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000239 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000240 }
241
242 for (cid=pjsua_var.next_call_id;
243 cid<(int)pjsua_var.ua_cfg.max_calls;
244 ++cid)
245 {
246 if (pjsua_var.calls[cid].inv == NULL) {
247 ++pjsua_var.next_call_id;
248 return cid;
249 }
250 }
251
252 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
253 if (pjsua_var.calls[cid].inv == NULL) {
254 ++pjsua_var.next_call_id;
255 return cid;
256 }
257 }
258
259#else
260 /* Old algorithm */
261 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
262 if (pjsua_var.calls[cid].inv == NULL)
263 return cid;
264 }
265#endif
266
267 return PJSUA_INVALID_ID;
268}
269
Benny Prijonod8179652008-01-23 20:39:07 +0000270/* Get signaling secure level.
271 * Return:
272 * 0: if signaling is not secure
273 * 1: if TLS transport is used for immediate hop
274 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000275 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000276static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000277{
278 const pj_str_t tls = pj_str(";transport=tls");
279 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000280 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000281
282 if (pj_stristr(dst_uri, &sips))
283 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000284
285 if (!pj_list_empty(&acc->route_set)) {
286 pjsip_route_hdr *r = acc->route_set.next;
287 pjsip_uri *uri = r->name_addr.uri;
288 pjsip_sip_uri *sip_uri;
289
290 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
291 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
292 return 1;
293
294 } else {
295 if (pj_stristr(dst_uri, &tls))
296 return 1;
297 }
298
Benny Prijonod8179652008-01-23 20:39:07 +0000299 return 0;
300}
301
Benny Prijono224b4e22008-06-19 14:10:28 +0000302/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000303static int call_get_secure_level(pjsua_call *call)
304{
305 if (call->inv->dlg->secure)
306 return 2;
307
308 if (!pj_list_empty(&call->inv->dlg->route_set)) {
309 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
310 pjsip_uri *uri = r->name_addr.uri;
311 pjsip_sip_uri *sip_uri;
312
313 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
314 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
315 return 1;
316
317 } else {
318 pjsip_sip_uri *sip_uri;
319
320 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
321 return 2;
322 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
323 return 0;
324
325 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
326 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
327 return 1;
328 }
329
330 return 0;
331}
Benny Prijono224b4e22008-06-19 14:10:28 +0000332*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000333
334
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000335/*
336 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000337 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000338PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
339 const pj_str_t *dest_uri,
340 unsigned options,
341 void *user_data,
342 const pjsua_msg_data *msg_data,
343 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000344{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000345 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000346 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000347 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000348 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 pjsua_acc *acc;
350 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000351 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000352 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000353 pjsip_tx_data *tdata;
354 pj_status_t status;
355
Benny Prijono9fc735d2006-05-28 14:58:12 +0000356
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000357 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000358 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000359 PJ_EINVAL);
360
Benny Prijono320fa4d2006-12-07 10:09:16 +0000361 /* Check arguments */
362 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
363
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000364 PJSUA_LOCK();
365
Benny Prijonof798e502009-03-09 13:08:16 +0000366 /* Create sound port if none is instantiated, to check if sound device
367 * can be used. But only do this with the conference bridge, as with
368 * audio switchboard (i.e. APS-Direct), we can only open the sound
369 * device once the correct format has been known
370 */
371 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
372 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000373 {
374 pj_status_t status;
375
376 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
377 if (status != PJ_SUCCESS) {
378 PJSUA_UNLOCK();
379 return status;
380 }
381 }
382
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 acc = &pjsua_var.acc[acc_id];
384 if (!acc->valid) {
385 pjsua_perror(THIS_FILE, "Unable to make call because account "
386 "is not valid", PJ_EINVALIDOP);
387 PJSUA_UNLOCK();
388 return PJ_EINVALIDOP;
389 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000390
Benny Prijonoa91a0032006-02-26 21:23:45 +0000391 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000392 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000393
Benny Prijono5773cd62008-01-19 13:01:42 +0000394 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000395 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000397 return PJ_ETOOMANY;
398 }
399
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000400 call = &pjsua_var.calls[call_id];
401
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000402 /* Associate session with account */
403 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000404 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000405
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000406 /* Create temporary pool */
407 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
408
Benny Prijono320fa4d2006-12-07 10:09:16 +0000409 /* Verify that destination URI is valid before calling
410 * pjsua_acc_create_uac_contact, or otherwise there
411 * a misleading "Invalid Contact URI" error will be printed
412 * when pjsua_acc_create_uac_contact() fails.
413 */
414 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000415 pjsip_uri *uri;
416 pj_str_t dup;
417
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000418 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
419 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000420
421 if (uri == NULL) {
422 pjsua_perror(THIS_FILE, "Unable to make call",
423 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000424 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000425 PJSUA_UNLOCK();
426 return PJSIP_EINVALIDREQURI;
427 }
428 }
429
Benny Prijono093d3022006-09-24 00:07:11 +0000430 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
431 (int)dest_uri->slen, dest_uri->ptr));
432
Benny Prijonoe21e7842006-04-09 16:46:05 +0000433 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000435
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000438
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000439 /* Create suitable Contact header unless a Contact header has been
440 * set in the account.
441 */
442 if (acc->contact.slen) {
443 contact = acc->contact;
444 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000445 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000446 acc_id, dest_uri);
447 if (status != PJ_SUCCESS) {
448 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
449 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000450 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000451 PJSUA_UNLOCK();
452 return status;
453 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000454 }
455
Benny Prijonoe21e7842006-04-09 16:46:05 +0000456 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000457 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000458 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000459 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000460 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000461 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000462 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000464 return status;
465 }
466
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000467 /* Increment the dialog's lock otherwise when invite session creation
468 * fails the dialog will be destroyed prematurely.
469 */
470 pjsip_dlg_inc_lock(dlg);
471
Benny Prijonodb844a42008-02-02 17:07:18 +0000472 /* Calculate call's secure level */
473 call->secure_level = get_secure_level(acc_id, dest_uri);
474
Benny Prijonoc97608e2007-03-23 16:34:20 +0000475 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000476 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000477 call->secure_level, dlg->pool,
478 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000479 if (status != PJ_SUCCESS) {
480 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
481 goto on_error;
482 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000483
Benny Prijono224b4e22008-06-19 14:10:28 +0000484 /* Create offer */
485 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000486 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000487 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000488 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000489 goto on_error;
490 }
491
492 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000493 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000494 if (acc->cfg.require_100rel)
495 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000496 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
497 options |= PJSIP_INV_SUPPORT_TIMER;
498 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
499 options |= PJSIP_INV_REQUIRE_TIMER;
500 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
501 options |= PJSIP_INV_ALWAYS_USE_TIMER;
502 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000503
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000504 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000505 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000506 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000507 goto on_error;
508 }
509
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000510 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000511 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
512 if (status != PJ_SUCCESS) {
513 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
514 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000515 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000516
517 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000519
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000520 dlg->mod_data[pjsua_var.mod.id] = call;
521 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000522
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 /* Attach user data */
524 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000525
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000526 /* If account is locked to specific transport, then lock dialog
527 * to this transport too.
528 */
529 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
530 pjsip_tpselector tp_sel;
531
532 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
533 pjsip_dlg_set_transport(dlg, &tp_sel);
534 }
535
Benny Prijono84126ab2006-02-09 09:30:09 +0000536 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000537 if (!pj_list_empty(&acc->route_set))
538 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000539
540
541 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000543 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000545 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000546
Benny Prijono48ab2b72007-11-08 09:24:30 +0000547 /* Set authentication preference */
548 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000549
550 /* Create initial INVITE: */
551
552 status = pjsip_inv_invite(inv, &tdata);
553 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000554 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
555 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000556 goto on_error;
557 }
558
559
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 /* Add additional headers etc */
561
562 pjsua_process_msg_data( tdata, msg_data);
563
Benny Prijono093d3022006-09-24 00:07:11 +0000564 /* Must increment call counter now */
565 ++pjsua_var.call_cnt;
566
Benny Prijono84126ab2006-02-09 09:30:09 +0000567 /* Send initial INVITE: */
568
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000569 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000570 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000571 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
572 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000574 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000575 * session would have been cleared.
576 */
577 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000578 goto on_error;
579 }
580
Benny Prijono84126ab2006-02-09 09:30:09 +0000581 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 if (p_call_id)
584 *p_call_id = call_id;
585
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000586 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000587 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000589
590 return PJ_SUCCESS;
591
592
593on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000594 if (dlg) {
595 /* This may destroy the dialog */
596 pjsip_dlg_dec_lock(dlg);
597 }
598
Benny Prijono1c2bf462006-03-05 11:54:02 +0000599 if (inv != NULL) {
600 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000601 }
602
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000603 if (call_id != -1) {
604 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000605 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000606 }
607
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000608 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000610 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000611}
612
613
Benny Prijono91a6a172007-10-31 08:59:29 +0000614/* Get the NAT type information in remote's SDP */
615static void update_remote_nat_type(pjsua_call *call,
616 const pjmedia_sdp_session *sdp)
617{
618 const pjmedia_sdp_attr *xnat;
619
620 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
621 if (xnat) {
622 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
623 } else {
624 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
625 }
626
627 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
628 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
629}
630
631
Benny Prijonodc39fe82006-05-26 12:17:46 +0000632/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000634 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000635 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000636pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000637{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000638 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000639 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000640 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000641 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
642 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000643 pjsip_tx_data *response = NULL;
644 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000645 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000646 int acc_id;
647 pjsua_call *call;
648 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000649 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000650 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000651 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000652
Benny Prijono26ff9062006-02-21 23:47:00 +0000653 /* Don't want to handle anything but INVITE */
654 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
655 return PJ_FALSE;
656
657 /* Don't want to handle anything that's already associated with
658 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000659 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000660 if (dlg || tsx)
661 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000662
Benny Prijono384dab42009-10-14 01:58:04 +0000663 /* Don't want to accept the call if shutdown is in progress */
664 if (pjsua_var.thread_quit_flag) {
665 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
666 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
667 NULL, NULL);
668 return PJ_TRUE;
669 }
670
Benny Prijono148c9dd2006-09-19 13:37:53 +0000671 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000672
Benny Prijono26ff9062006-02-21 23:47:00 +0000673 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000674 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000675
Benny Prijono5773cd62008-01-19 13:01:42 +0000676 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000677 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000678 PJSIP_SC_BUSY_HERE, NULL,
679 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000680 PJ_LOG(2,(THIS_FILE,
681 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000682 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000683 return PJ_TRUE;
684 }
685
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000686 /* Clear call descriptor */
687 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000688
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000689 call = &pjsua_var.calls[call_id];
690
691 /* Mark call start time. */
692 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000693
Benny Prijono053f5222006-11-11 16:16:04 +0000694 /* Check INVITE request for Replaces header. If Replaces header is
695 * present, the function will make sure that we can handle the request.
696 */
697 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
698 &response);
699 if (status != PJ_SUCCESS) {
700 /*
701 * Something wrong with the Replaces header.
702 */
703 if (response) {
704 pjsip_response_addr res_addr;
705
706 pjsip_get_response_addr(response->pool, rdata, &res_addr);
707 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
708 NULL, NULL);
709
710 } else {
711
712 /* Respond with 500 (Internal Server Error) */
713 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
714 NULL, NULL);
715 }
716
717 PJSUA_UNLOCK();
718 return PJ_TRUE;
719 }
720
721 /* If this INVITE request contains Replaces header, notify application
722 * about the request so that application can do subsequent checking
723 * if it wants to.
724 */
725 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
726 pjsua_call *replaced_call;
727 int st_code = 200;
728 pj_str_t st_text = { "OK", 2 };
729
730 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000731 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000732
733 /* Notify application */
734 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
735 rdata, &st_code, &st_text);
736
737 /* Must specify final response */
738 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
739
740 /* Check if application rejects this request. */
741 if (st_code >= 300) {
742
743 if (st_text.slen == 2)
744 st_text = *pjsip_get_status_text(st_code);
745
746 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
747 st_code, &st_text, NULL, NULL, NULL);
748 PJSUA_UNLOCK();
749 return PJ_TRUE;
750 }
751 }
752
Benny Prijonod8179652008-01-23 20:39:07 +0000753 /*
754 * Get which account is most likely to be associated with this incoming
755 * call. We need the account to find which contact URI to put for
756 * the call.
757 */
758 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000759 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000760
Benny Prijonodb844a42008-02-02 17:07:18 +0000761 /* Get call's secure level */
762 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
763 call->secure_level = 2;
764 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
765 call->secure_level = 1;
766 else
767 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000768
Benny Prijonod8179652008-01-23 20:39:07 +0000769 /* Parse SDP from incoming request */
770 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000771 pjsip_rdata_sdp_info *sdp_info;
772
773 sdp_info = pjsip_rdata_get_sdp_info(rdata);
774 offer = sdp_info->sdp;
775
776 status = sdp_info->sdp_err;
777 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
778 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000779
Benny Prijonod8179652008-01-23 20:39:07 +0000780 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000781 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000782 pjsip_hdr hdr_list;
783 pjsip_warning_hdr *w;
784
785 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000786 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000787
788 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
789 pjsip_endpt_name(pjsua_var.endpt),
790 status);
791 pj_list_init(&hdr_list);
792 pj_list_push_back(&hdr_list, w);
793
Benny Prijono224b4e22008-06-19 14:10:28 +0000794 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000795 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000796 PJSUA_UNLOCK();
797 return PJ_TRUE;
798 }
Benny Prijono617b8602008-04-07 10:10:31 +0000799
800 /* Do quick checks on SDP before passing it to transports. More elabore
801 * checks will be done in pjsip_inv_verify_request2() below.
802 */
803 if (offer->media_count==0) {
804 const pj_str_t reason = pj_str("Missing media in SDP");
805 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
806 NULL, NULL, NULL);
807 PJSUA_UNLOCK();
808 return PJ_TRUE;
809 }
810
Benny Prijonod8179652008-01-23 20:39:07 +0000811 } else {
812 offer = NULL;
813 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000814
Benny Prijono224b4e22008-06-19 14:10:28 +0000815 /* Init media channel */
816 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
817 call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000818 rdata->tp_info.pool,
819 offer,
Benny Prijono224b4e22008-06-19 14:10:28 +0000820 &sip_err_code);
821 if (status != PJ_SUCCESS) {
822 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
823 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
824 sip_err_code, NULL, NULL, NULL, NULL);
825 PJSUA_UNLOCK();
826 return PJ_TRUE;
827 }
828
829 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000830 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000831 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000832 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000833 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000834 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
835 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000836 PJSUA_UNLOCK();
837 return PJ_TRUE;
838 }
839
Benny Prijono224b4e22008-06-19 14:10:28 +0000840
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000841 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000842 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000843 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000844 if (pjsua_var.acc[acc_id].cfg.require_100rel)
845 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000846 if (pjsua_var.media_cfg.enable_ice)
847 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000848 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
849 options |= PJSIP_INV_REQUIRE_TIMER;
850 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
851 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000852
Benny Prijonod8179652008-01-23 20:39:07 +0000853 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
854 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000855 if (status != PJ_SUCCESS) {
856
857 /*
858 * No we can't handle the incoming INVITE request.
859 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000860 if (response) {
861 pjsip_response_addr res_addr;
862
863 pjsip_get_response_addr(response->pool, rdata, &res_addr);
864 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
865 NULL, NULL);
866
867 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000868 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000869 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
870 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000871 }
872
Benny Prijonoc97608e2007-03-23 16:34:20 +0000873 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000874 PJSUA_UNLOCK();
875 return PJ_TRUE;
876 }
877
878
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000879 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000880 if (pjsua_var.acc[acc_id].contact.slen) {
881 contact = pjsua_var.acc[acc_id].contact;
882 } else {
883 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
884 acc_id, rdata);
885 if (status != PJ_SUCCESS) {
886 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
887 status);
888 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
889 NULL, NULL);
890 pjsua_media_channel_deinit(call->index);
891 PJSUA_UNLOCK();
892 return PJ_TRUE;
893 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000894 }
895
Benny Prijono26ff9062006-02-21 23:47:00 +0000896 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000898 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000899 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000900 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000901 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000902 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000903 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000904 return PJ_TRUE;
905 }
906
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000907 /* Set credentials */
908 if (pjsua_var.acc[acc_id].cred_cnt) {
909 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
910 pjsua_var.acc[acc_id].cred_cnt,
911 pjsua_var.acc[acc_id].cred);
912 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000913
Benny Prijono48ab2b72007-11-08 09:24:30 +0000914 /* Set preference */
915 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
916 &pjsua_var.acc[acc_id].cfg.auth_pref);
917
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000918 /* Disable Session Timers if not prefered and the incoming INVITE request
919 * did not require it.
920 */
921 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
922 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
923 {
924 options &= ~(PJSIP_INV_SUPPORT_TIMER);
925 }
926
Benny Prijono26ff9062006-02-21 23:47:00 +0000927 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000928 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000929 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000930 pjsip_hdr hdr_list;
931 pjsip_warning_hdr *w;
932
933 w = pjsip_warning_hdr_create_from_status(dlg->pool,
934 pjsip_endpt_name(pjsua_var.endpt),
935 status);
936 pj_list_init(&hdr_list);
937 pj_list_push_back(&hdr_list, w);
938
939 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
940
941 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000942 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000943 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000944 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000945 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000946 return PJ_TRUE;
947 }
948
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000949 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000950 status = pjsip_timer_init_session(inv,
951 &pjsua_var.acc[acc_id].cfg.timer_setting);
952 if (status != PJ_SUCCESS) {
953 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
954 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
955 NULL, &response);
956 if (status == PJ_SUCCESS && response)
957 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000958
Nanang Izzuddin65add622009-08-11 16:26:20 +0000959 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000960
Nanang Izzuddin65add622009-08-11 16:26:20 +0000961 PJSUA_UNLOCK();
962 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000963 }
964
Benny Prijonoea9fd392007-11-06 03:41:40 +0000965 /* Update NAT type of remote endpoint, only when there is SDP in
966 * incoming INVITE!
967 */
968 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
969 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
970 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000971 const pjmedia_sdp_session *remote_sdp;
972
973 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
974 update_remote_nat_type(call, remote_sdp);
975 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000976
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000977 /* If account is locked to specific transport, then lock dialog
978 * to this transport too.
979 */
980 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
981 pjsip_tpselector tp_sel;
982
983 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
984 pjsip_dlg_set_transport(dlg, &tp_sel);
985 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000986
Benny Prijono2285e7e2008-12-17 14:28:18 +0000987 /* Must answer with some response to initial INVITE. We'll do this before
988 * attaching the call to the invite session/dialog, so that the application
989 * will not get notification about this event (on another scenario, it is
990 * also possible that inv_send_msg() fails and causes the invite session to
991 * be disconnected. If we have the call attached at this time, this will
992 * cause the disconnection callback to be called before on_incoming_call()
993 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000994 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000995 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000996 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000997 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000998 if (response == NULL) {
999 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1000 status);
1001 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1002 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1003 } else {
1004 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001005 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001006 PJ_FALSE);
1007 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001008 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001009 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001010 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001011
1012 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001013 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001014 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001015 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001016 PJSUA_UNLOCK();
1017 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001018 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001019 }
1020
Benny Prijono2285e7e2008-12-17 14:28:18 +00001021 /* Create and attach pjsua_var data to the dialog: */
1022 call->inv = inv;
1023
1024 dlg->mod_data[pjsua_var.mod.id] = call;
1025 inv->mod_data[pjsua_var.mod.id] = call;
1026
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001027 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001028
Benny Prijono105217f2006-03-06 16:25:59 +00001029
Benny Prijono053f5222006-11-11 16:16:04 +00001030 /* Check if this request should replace existing call */
1031 if (replaced_dlg) {
1032 pjsip_inv_session *replaced_inv;
1033 struct pjsua_call *replaced_call;
1034 pjsip_tx_data *tdata;
1035
1036 /* Get the invite session in the dialog */
1037 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1038
1039 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001040 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001041
1042 /* Notify application */
1043 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1044 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1045 call_id);
1046
1047 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1048 call_id));
1049
1050 /* Answer the new call with 200 response */
1051 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1052 if (status == PJ_SUCCESS)
1053 status = pjsip_inv_send_msg(inv, tdata);
1054
1055 if (status != PJ_SUCCESS)
1056 pjsua_perror(THIS_FILE, "Error answering session", status);
1057
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001058 /* Note that inv may be invalid if 200/OK has caused error in
1059 * starting the media.
1060 */
Benny Prijono053f5222006-11-11 16:16:04 +00001061
1062 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1063 replaced_call->index));
1064
1065 /* Disconnect replaced invite session */
1066 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1067 &tdata);
1068 if (status == PJ_SUCCESS && tdata)
1069 status = pjsip_inv_send_msg(replaced_inv, tdata);
1070
1071 if (status != PJ_SUCCESS)
1072 pjsua_perror(THIS_FILE, "Error terminating session", status);
1073
1074
1075 } else {
1076
Benny Prijonob5388cf2007-01-04 22:45:08 +00001077 /* Notify application if on_incoming_call() is overriden,
1078 * otherwise hangup the call with 480
1079 */
1080 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001081 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001082 } else {
1083 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1084 NULL, NULL);
1085 }
Benny Prijono053f5222006-11-11 16:16:04 +00001086 }
1087
Benny Prijono8b1889b2006-06-06 18:40:40 +00001088
Benny Prijono26ff9062006-02-21 23:47:00 +00001089 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001090 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001091 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001092}
1093
1094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095
1096/*
1097 * Check if the specified call has active INVITE session and the INVITE
1098 * session has not been disconnected.
1099 */
1100PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1101{
1102 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1103 PJ_EINVAL);
1104 return pjsua_var.calls[call_id].inv != NULL &&
1105 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1106}
1107
1108
1109/*
1110 * Check if call has an active media session.
1111 */
1112PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1113{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001114 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001115 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1116 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001117 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonocf986c42008-09-02 11:25:07 +00001118}
1119
1120
Benny Prijono148c9dd2006-09-19 13:37:53 +00001121/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001122pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001123 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001124 pjsua_call **p_call,
1125 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001126{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001127 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001128 pjsua_call *call = NULL;
1129 pj_bool_t has_pjsua_lock = PJ_FALSE;
1130 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001131 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001132
Sauw Ming844c1c92010-09-07 05:12:02 +00001133 pj_gettimeofday(&time_start);
1134 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1135 pj_time_val_normalize(&timeout);
1136
1137 for (retry=0; ; ++retry) {
1138
1139 if (retry % 10 == 9) {
1140 pj_time_val dtime;
1141
1142 pj_gettimeofday(&dtime);
1143 PJ_TIME_VAL_SUB(dtime, time_start);
1144 if (!PJ_TIME_VAL_LT(dtime, timeout))
1145 break;
1146 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001147
1148 has_pjsua_lock = PJ_FALSE;
1149
1150 status = PJSUA_TRY_LOCK();
1151 if (status != PJ_SUCCESS) {
1152 pj_thread_sleep(retry/10);
1153 continue;
1154 }
1155
1156 has_pjsua_lock = PJ_TRUE;
1157 call = &pjsua_var.calls[call_id];
1158
1159 if (call->inv == NULL) {
1160 PJSUA_UNLOCK();
1161 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1162 return PJSIP_ESESSIONTERMINATED;
1163 }
1164
1165 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1166 if (status != PJ_SUCCESS) {
1167 PJSUA_UNLOCK();
1168 pj_thread_sleep(retry/10);
1169 continue;
1170 }
1171
1172 PJSUA_UNLOCK();
1173
1174 break;
1175 }
1176
1177 if (status != PJ_SUCCESS) {
1178 if (has_pjsua_lock == PJ_FALSE)
1179 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1180 "(possibly system has deadlocked) in %s",
1181 title));
1182 else
1183 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1184 "(possibly system has deadlocked) in %s",
1185 title));
1186 return PJ_ETIMEDOUT;
1187 }
1188
1189 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001190 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001191
1192 return PJ_SUCCESS;
1193}
1194
1195
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001196/*
1197 * Get the conference port identification associated with the call.
1198 */
1199PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1200{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001201 pjsua_call *call;
1202 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001203 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001204 pj_status_t status;
1205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1207 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001208
Benny Prijonodc752ca2006-09-22 16:55:42 +00001209 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001210 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001211 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001212
Benny Prijono0bc99a92011-03-17 04:34:43 +00001213 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214
Benny Prijonodc752ca2006-09-22 16:55:42 +00001215 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001216
1217 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001218}
1219
1220
Benny Prijono148c9dd2006-09-19 13:37:53 +00001221
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222/*
1223 * Obtain detail information about the specified call.
1224 */
1225PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1226 pjsua_call_info *info)
1227{
1228 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001229 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001230 unsigned mi;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001232
1233 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1234 PJ_EINVAL);
1235
Benny Prijonoac623b32006-07-03 15:19:31 +00001236 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237
Benny Prijonodc752ca2006-09-22 16:55:42 +00001238 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001239 if (status != PJ_SUCCESS) {
1240 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 }
1242
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001243 /* id and role */
1244 info->id = call_id;
1245 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001246 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247
1248 /* local info */
1249 info->local_info.ptr = info->buf_.local_info;
1250 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1251 sizeof(info->buf_.local_info));
1252
1253 /* local contact */
1254 info->local_contact.ptr = info->buf_.local_contact;
1255 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1256 call->inv->dlg->local.contact->uri,
1257 info->local_contact.ptr,
1258 sizeof(info->buf_.local_contact));
1259
1260 /* remote info */
1261 info->remote_info.ptr = info->buf_.remote_info;
1262 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1263 sizeof(info->buf_.remote_info));
1264
1265 /* remote contact */
1266 if (call->inv->dlg->remote.contact) {
1267 int len;
1268 info->remote_contact.ptr = info->buf_.remote_contact;
1269 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1270 call->inv->dlg->remote.contact->uri,
1271 info->remote_contact.ptr,
1272 sizeof(info->buf_.remote_contact));
1273 if (len < 0) len = 0;
1274 info->remote_contact.slen = len;
1275 } else {
1276 info->remote_contact.slen = 0;
1277 }
1278
1279 /* call id */
1280 info->call_id.ptr = info->buf_.call_id;
1281 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1282 sizeof(info->buf_.call_id));
1283
1284 /* state, state_text */
1285 info->state = call->inv->state;
1286 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1287
1288 /* If call is disconnected, set the last_status from the cause code */
1289 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1290 /* last_status, last_status_text */
1291 info->last_status = call->inv->cause;
1292
1293 info->last_status_text.ptr = info->buf_.last_status_text;
1294 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1295 sizeof(info->buf_.last_status_text));
1296 } else {
1297 /* last_status, last_status_text */
1298 info->last_status = call->last_code;
1299
1300 info->last_status_text.ptr = info->buf_.last_status_text;
1301 pj_strncpy(&info->last_status_text, &call->last_text,
1302 sizeof(info->buf_.last_status_text));
1303 }
1304
Benny Prijono0bc99a92011-03-17 04:34:43 +00001305 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001306 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001307 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001308 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001309 {
1310 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001311
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001312 info->media[info->media_cnt].index = mi;
1313 info->media[info->media_cnt].status = call_med->state;
1314 info->media[info->media_cnt].dir = call_med->dir;
1315 info->media[info->media_cnt].type = call_med->type;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001317 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001318 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001319 call_med->strm.a.conf_slot;
1320 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001321 pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV;
1322
1323 info->media[info->media_cnt].stream.vid.win_in =
1324 call_med->strm.v.rdr_win_id;
1325
1326 if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
Nanang Izzuddind93c68a2011-07-19 08:40:20 +00001327 cap_dev = call_med->strm.v.cap_dev;
Nanang Izzuddin62053a62011-07-12 11:08:32 +00001328 }
1329 info->media[info->media_cnt].stream.vid.cap_dev = cap_dev;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001330 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001331 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001332 }
1333 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001334 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001336 if (call->audio_idx != -1) {
1337 info->media_status = call->media[call->audio_idx].state;
1338 info->media_dir = call->media[call->audio_idx].dir;
1339 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001340 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341
1342 /* calculate duration */
1343 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1344
1345 info->total_duration = call->dis_time;
1346 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1347
1348 if (call->conn_time.sec) {
1349 info->connect_duration = call->dis_time;
1350 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1351 }
1352
1353 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1354
1355 pj_gettimeofday(&info->total_duration);
1356 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1357
1358 pj_gettimeofday(&info->connect_duration);
1359 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1360
1361 } else {
1362 pj_gettimeofday(&info->total_duration);
1363 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1364 }
1365
Benny Prijonodc752ca2006-09-22 16:55:42 +00001366 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367
1368 return PJ_SUCCESS;
1369}
1370
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001371/*
1372 * Check if call remote peer support the specified capability.
1373 */
1374PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1375 pjsua_call_id call_id,
1376 int htype,
1377 const pj_str_t *hname,
1378 const pj_str_t *token)
1379{
1380 pjsua_call *call;
1381 pjsip_dialog *dlg;
1382 pj_status_t status;
1383 pjsip_dialog_cap_status cap_status;
1384
1385 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1386 if (status != PJ_SUCCESS)
1387 return PJSIP_DIALOG_CAP_UNKNOWN;
1388
1389 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1390
1391 pjsip_dlg_dec_lock(dlg);
1392
1393 return cap_status;
1394}
1395
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396
1397/*
1398 * Attach application specific data to the call.
1399 */
1400PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1401 void *user_data)
1402{
1403 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1404 PJ_EINVAL);
1405 pjsua_var.calls[call_id].user_data = user_data;
1406
1407 return PJ_SUCCESS;
1408}
1409
1410
1411/*
1412 * Get user data attached to the call.
1413 */
1414PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1415{
1416 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1417 NULL);
1418 return pjsua_var.calls[call_id].user_data;
1419}
1420
1421
1422/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001423 * Get remote's NAT type.
1424 */
1425PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1426 pj_stun_nat_type *p_type)
1427{
1428 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1429 PJ_EINVAL);
1430 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1431
1432 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1433 return PJ_SUCCESS;
1434}
1435
1436
1437/*
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001438 * Get media stream info for the specified media index.
1439 */
1440PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
1441 unsigned med_idx,
1442 pjsua_stream_info *psi)
1443{
1444 pjsua_call *call;
1445 pjsua_call_media *call_med;
1446 pj_status_t status;
1447
1448 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1449 PJ_EINVAL);
1450 PJ_ASSERT_RETURN(psi, PJ_EINVAL);
1451
1452 PJSUA_LOCK();
1453
1454 call = &pjsua_var.calls[call_id];
1455
1456 if (med_idx >= call->med_cnt) {
1457 PJSUA_UNLOCK();
1458 return PJ_EINVAL;
1459 }
1460
1461 call_med = &call->media[med_idx];
1462 psi->type = call_med->type;
1463 switch (call_med->type) {
1464 case PJMEDIA_TYPE_AUDIO:
1465 status = pjmedia_stream_get_info(call_med->strm.a.stream,
1466 &psi->info.aud);
1467 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001468#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001469 case PJMEDIA_TYPE_VIDEO:
1470 status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
1471 &psi->info.vid);
1472 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001473#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001474 default:
1475 status = PJMEDIA_EINVALIMEDIATYPE;
1476 break;
1477 }
1478
1479 PJSUA_UNLOCK();
1480 return status;
1481}
1482
1483
1484/*
1485 * Get media stream statistic for the specified media index.
1486 */
1487PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
1488 unsigned med_idx,
1489 pjsua_stream_stat *stat)
1490{
1491 pjsua_call *call;
1492 pjsua_call_media *call_med;
1493 pj_status_t status;
1494
1495 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1496 PJ_EINVAL);
1497 PJ_ASSERT_RETURN(stat, PJ_EINVAL);
1498
1499 PJSUA_LOCK();
1500
1501 call = &pjsua_var.calls[call_id];
1502
1503 if (med_idx >= call->med_cnt) {
1504 PJSUA_UNLOCK();
1505 return PJ_EINVAL;
1506 }
1507
1508 call_med = &call->media[med_idx];
1509 switch (call_med->type) {
1510 case PJMEDIA_TYPE_AUDIO:
1511 status = pjmedia_stream_get_stat(call_med->strm.a.stream,
1512 &stat->rtcp);
1513 if (status == PJ_SUCCESS)
1514 status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
1515 &stat->jbuf);
1516 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001517#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001518 case PJMEDIA_TYPE_VIDEO:
1519 status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
1520 &stat->rtcp);
1521 if (status == PJ_SUCCESS)
1522 status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
1523 &stat->jbuf);
1524 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001525#endif
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001526 default:
1527 status = PJMEDIA_EINVALIMEDIATYPE;
1528 break;
1529 }
1530
1531 PJSUA_UNLOCK();
1532 return status;
1533}
1534
1535
1536/*
1537 * Get media transport info for the specified media index.
1538 */
Benny Prijonoe212bc12011-08-15 09:38:42 +00001539PJ_DEF(pj_status_t)
1540pjsua_call_get_med_transport_info(pjsua_call_id call_id,
1541 unsigned med_idx,
1542 pjmedia_transport_info *t)
Nanang Izzuddinf5269a22011-07-14 04:42:18 +00001543{
1544 pjsua_call *call;
1545 pjsua_call_media *call_med;
1546 pj_status_t status;
1547
1548 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1549 PJ_EINVAL);
1550 PJ_ASSERT_RETURN(t, PJ_EINVAL);
1551
1552 PJSUA_LOCK();
1553
1554 call = &pjsua_var.calls[call_id];
1555
1556 if (med_idx >= call->med_cnt) {
1557 PJSUA_UNLOCK();
1558 return PJ_EINVAL;
1559 }
1560
1561 call_med = &call->media[med_idx];
1562
1563 pjmedia_transport_info_init(t);
1564 status = pjmedia_transport_get_info(call_med->tp, t);
1565
1566 PJSUA_UNLOCK();
1567 return status;
1568}
1569
1570
1571/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 * Send response to incoming INVITE request.
1573 */
1574PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1575 unsigned code,
1576 const pj_str_t *reason,
1577 const pjsua_msg_data *msg_data)
1578{
1579 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001580 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001581 pjsip_tx_data *tdata;
1582 pj_status_t status;
1583
1584 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1585 PJ_EINVAL);
1586
Benny Prijonodc752ca2006-09-22 16:55:42 +00001587 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001588 if (status != PJ_SUCCESS)
1589 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590
Benny Prijono2e507c22006-06-23 15:04:11 +00001591 if (call->res_time.sec == 0)
1592 pj_gettimeofday(&call->res_time);
1593
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001594 if (reason && reason->slen == 0)
1595 reason = NULL;
1596
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597 /* Create response message */
1598 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1599 if (status != PJ_SUCCESS) {
1600 pjsua_perror(THIS_FILE, "Error creating response",
1601 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001602 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 return status;
1604 }
1605
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001606 /* Call might have been disconnected if application is answering with
1607 * 200/OK and the media failed to start.
1608 */
1609 if (call->inv == NULL) {
1610 pjsip_dlg_dec_lock(dlg);
1611 return PJSIP_ESESSIONTERMINATED;
1612 }
1613
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001614 /* Add additional headers etc */
1615 pjsua_process_msg_data( tdata, msg_data);
1616
1617 /* Send the message */
1618 status = pjsip_inv_send_msg(call->inv, tdata);
1619 if (status != PJ_SUCCESS)
1620 pjsua_perror(THIS_FILE, "Error sending response",
1621 status);
1622
Benny Prijonodc752ca2006-09-22 16:55:42 +00001623 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624
1625 return status;
1626}
1627
1628
1629/*
1630 * Hangup call by using method that is appropriate according to the
1631 * call state.
1632 */
1633PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1634 unsigned code,
1635 const pj_str_t *reason,
1636 const pjsua_msg_data *msg_data)
1637{
1638 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001639 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 pj_status_t status;
1641 pjsip_tx_data *tdata;
1642
1643
Benny Prijono148c9dd2006-09-19 13:37:53 +00001644 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1645 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1646 call_id));
1647 }
1648
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1650 PJ_EINVAL);
1651
Benny Prijonodc752ca2006-09-22 16:55:42 +00001652 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001653 if (status != PJ_SUCCESS)
1654 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001655
1656 if (code==0) {
1657 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1658 code = PJSIP_SC_OK;
1659 else if (call->inv->role == PJSIP_ROLE_UAS)
1660 code = PJSIP_SC_DECLINE;
1661 else
1662 code = PJSIP_SC_REQUEST_TERMINATED;
1663 }
1664
1665 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1666 if (status != PJ_SUCCESS) {
1667 pjsua_perror(THIS_FILE,
1668 "Failed to create end session message",
1669 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001670 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001671 return status;
1672 }
1673
1674 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1675 * as p_tdata when INVITE transaction has not been answered
1676 * with any provisional responses.
1677 */
1678 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001679 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 return PJ_SUCCESS;
1681 }
1682
1683 /* Add additional headers etc */
1684 pjsua_process_msg_data( tdata, msg_data);
1685
1686 /* Send the message */
1687 status = pjsip_inv_send_msg(call->inv, tdata);
1688 if (status != PJ_SUCCESS) {
1689 pjsua_perror(THIS_FILE,
1690 "Failed to send end session message",
1691 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001692 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001693 return status;
1694 }
1695
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001696 /* Stop lock codec timer, if it is active */
1697 if (call->lock_codec.reinv_timer.id) {
1698 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1699 &call->lock_codec.reinv_timer);
1700 call->lock_codec.reinv_timer.id = PJ_FALSE;
1701 }
1702
Benny Prijonodc752ca2006-09-22 16:55:42 +00001703 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001704
1705 return PJ_SUCCESS;
1706}
1707
1708
1709/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001710 * Accept or reject redirection.
1711 */
1712PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1713 pjsip_redirect_op cmd)
1714{
1715 pjsua_call *call;
1716 pjsip_dialog *dlg;
1717 pj_status_t status;
1718
1719 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1720 PJ_EINVAL);
1721
1722 status = acquire_call("pjsua_call_process_redirect()", call_id,
1723 &call, &dlg);
1724 if (status != PJ_SUCCESS)
1725 return status;
1726
1727 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1728
1729 pjsip_dlg_dec_lock(dlg);
1730
1731 return status;
1732}
1733
1734
1735/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 * Put the specified call on hold.
1737 */
1738PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1739 const pjsua_msg_data *msg_data)
1740{
1741 pjmedia_sdp_session *sdp;
1742 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001743 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001744 pjsip_tx_data *tdata;
1745 pj_status_t status;
1746
1747 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1748 PJ_EINVAL);
1749
Benny Prijonodc752ca2006-09-22 16:55:42 +00001750 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001751 if (status != PJ_SUCCESS)
1752 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001754
1755 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1756 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001757 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001758 return PJSIP_ESESSIONSTATE;
1759 }
1760
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001761 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001762 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001763 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001764 return status;
1765 }
1766
1767 /* Create re-INVITE with new offer */
1768 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1769 if (status != PJ_SUCCESS) {
1770 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001771 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 return status;
1773 }
1774
1775 /* Add additional headers etc */
1776 pjsua_process_msg_data( tdata, msg_data);
1777
1778 /* Send the request */
1779 status = pjsip_inv_send_msg( call->inv, tdata);
1780 if (status != PJ_SUCCESS) {
1781 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001782 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001783 return status;
1784 }
1785
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001786 /* Set flag that local put the call on hold */
1787 call->local_hold = PJ_TRUE;
1788
Benny Prijonodc752ca2006-09-22 16:55:42 +00001789 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001790
1791 return PJ_SUCCESS;
1792}
1793
1794
1795/*
1796 * Send re-INVITE (to release hold).
1797 */
1798PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
Benny Prijonodec3a372011-03-16 03:52:20 +00001799 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001800 const pjsua_msg_data *msg_data)
1801{
1802 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00001803 pj_str_t *new_contact = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001804 pjsip_tx_data *tdata;
1805 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001806 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001807 pj_status_t status;
1808
1809
1810 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1811 PJ_EINVAL);
1812
Benny Prijonodc752ca2006-09-22 16:55:42 +00001813 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001814 if (status != PJ_SUCCESS)
1815 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001816
1817 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1818 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001819 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 return PJSIP_ESESSIONSTATE;
1821 }
1822
1823 /* Create SDP */
Benny Prijonodec3a372011-03-16 03:52:20 +00001824 if (call->local_hold && (options & PJSUA_CALL_UNHOLD)==0) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001825 status = create_sdp_of_call_hold(call, &sdp);
1826 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001827 status = pjsua_media_channel_create_sdp(call->index,
1828 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001829 NULL, &sdp, NULL);
1830 call->local_hold = PJ_FALSE;
1831 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001832 if (status != PJ_SUCCESS) {
1833 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1834 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001835 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836 return status;
1837 }
1838
Benny Prijonodec3a372011-03-16 03:52:20 +00001839 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
1840 pjsua_acc_is_valid(call->acc_id))
1841 {
1842 new_contact = &pjsua_var.acc[call->acc_id].contact;
1843 }
1844
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845 /* Create re-INVITE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00001846 status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001847 if (status != PJ_SUCCESS) {
1848 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001849 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 return status;
1851 }
1852
1853 /* Add additional headers etc */
1854 pjsua_process_msg_data( tdata, msg_data);
1855
1856 /* Send the request */
1857 status = pjsip_inv_send_msg( call->inv, tdata);
1858 if (status != PJ_SUCCESS) {
1859 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001860 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001861 return status;
1862 }
1863
Benny Prijonodc752ca2006-09-22 16:55:42 +00001864 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001865
1866 return PJ_SUCCESS;
1867}
1868
1869
1870/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001871 * Send UPDATE request.
1872 */
1873PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1874 unsigned options,
1875 const pjsua_msg_data *msg_data)
1876{
1877 pjmedia_sdp_session *sdp;
Benny Prijonodec3a372011-03-16 03:52:20 +00001878 pj_str_t *new_contact = NULL;
Benny Prijonoc08682e2007-10-04 06:17:58 +00001879 pjsip_tx_data *tdata;
1880 pjsua_call *call;
1881 pjsip_dialog *dlg;
1882 pj_status_t status;
1883
1884 PJ_UNUSED_ARG(options);
1885
1886 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1887 PJ_EINVAL);
1888
1889 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1890 if (status != PJ_SUCCESS)
1891 return status;
1892
Benny Prijonoc08682e2007-10-04 06:17:58 +00001893 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001894 status = pjsua_media_channel_create_sdp(call->index,
1895 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001896 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001897 if (status != PJ_SUCCESS) {
1898 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1899 status);
1900 pjsip_dlg_dec_lock(dlg);
1901 return status;
1902 }
1903
Benny Prijonodec3a372011-03-16 03:52:20 +00001904 if ((options & PJSUA_CALL_UPDATE_CONTACT) &
1905 pjsua_acc_is_valid(call->acc_id))
1906 {
1907 new_contact = &pjsua_var.acc[call->acc_id].contact;
1908 }
1909
Benny Prijono224b4e22008-06-19 14:10:28 +00001910 /* Create UPDATE with new offer */
Benny Prijonodec3a372011-03-16 03:52:20 +00001911 status = pjsip_inv_update(call->inv, new_contact, sdp, &tdata);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001912 if (status != PJ_SUCCESS) {
1913 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1914 pjsip_dlg_dec_lock(dlg);
1915 return status;
1916 }
1917
1918 /* Add additional headers etc */
1919 pjsua_process_msg_data( tdata, msg_data);
1920
1921 /* Send the request */
1922 status = pjsip_inv_send_msg( call->inv, tdata);
1923 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001924 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001925 pjsip_dlg_dec_lock(dlg);
1926 return status;
1927 }
1928
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001929 call->local_hold = PJ_FALSE;
1930
Benny Prijonoc08682e2007-10-04 06:17:58 +00001931 pjsip_dlg_dec_lock(dlg);
1932
1933 return PJ_SUCCESS;
1934}
1935
1936
1937/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001938 * Initiate call transfer to the specified address.
1939 */
1940PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1941 const pj_str_t *dest,
1942 const pjsua_msg_data *msg_data)
1943{
1944 pjsip_evsub *sub;
1945 pjsip_tx_data *tdata;
1946 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001947 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001948 pjsip_generic_string_hdr *gs_hdr;
1949 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001950 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 pj_status_t status;
1952
1953
1954 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1955 PJ_EINVAL);
1956
Benny Prijonodc752ca2006-09-22 16:55:42 +00001957 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001958 if (status != PJ_SUCCESS)
1959 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001960
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961
Benny Prijonod524e822006-09-22 12:48:18 +00001962 /* Create xfer client subscription. */
1963 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001964 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001965
1966 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001967 if (status != PJ_SUCCESS) {
1968 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001969 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001970 return status;
1971 }
1972
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001973 /* Associate this call with the client subscription */
1974 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1975
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001976 /*
1977 * Create REFER request.
1978 */
1979 status = pjsip_xfer_initiate(sub, dest, &tdata);
1980 if (status != PJ_SUCCESS) {
1981 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001982 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 return status;
1984 }
1985
Benny Prijono053f5222006-11-11 16:16:04 +00001986 /* Add Referred-By header */
1987 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1988 &dlg->local.info_str);
1989 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1990
1991
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001992 /* Add additional headers etc */
1993 pjsua_process_msg_data( tdata, msg_data);
1994
1995 /* Send. */
1996 status = pjsip_xfer_send_request(sub, tdata);
1997 if (status != PJ_SUCCESS) {
1998 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001999 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002000 return status;
2001 }
2002
2003 /* For simplicity (that's what this program is intended to be!),
2004 * leave the original invite session as it is. More advanced application
2005 * may want to hold the INVITE, or terminate the invite, or whatever.
2006 */
2007
Benny Prijonodc752ca2006-09-22 16:55:42 +00002008 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009
2010 return PJ_SUCCESS;
2011
2012}
2013
2014
2015/*
Benny Prijono053f5222006-11-11 16:16:04 +00002016 * Initiate attended call transfer to the specified address.
2017 */
2018PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
2019 pjsua_call_id dest_call_id,
2020 unsigned options,
2021 const pjsua_msg_data *msg_data)
2022{
2023 pjsua_call *dest_call;
2024 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00002025 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00002026 pj_str_t str_dest;
2027 int len;
2028 pjsip_uri *uri;
2029 pj_status_t status;
2030
2031
2032 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2033 PJ_EINVAL);
2034 PJ_ASSERT_RETURN(dest_call_id>=0 &&
2035 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
2036 PJ_EINVAL);
2037
2038 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
2039 &dest_call, &dest_dlg);
2040 if (status != PJ_SUCCESS)
2041 return status;
2042
2043 /*
2044 * Create REFER destination URI with Replaces field.
2045 */
2046
2047 /* Make sure we have sufficient buffer's length */
2048 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
2049 dest_dlg->call_id->id.slen +
2050 dest_dlg->remote.info->tag.slen +
2051 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00002052 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00002053
2054 /* Print URI */
2055 str_dest_buf[0] = '<';
2056 str_dest.slen = 1;
2057
Benny Prijonoa1e69682007-05-11 15:14:34 +00002058 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00002059 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
2060 str_dest_buf+1, sizeof(str_dest_buf)-1);
2061 if (len < 0)
2062 return PJSIP_EURITOOLONG;
2063
2064 str_dest.slen += len;
2065
2066
2067 /* Build the URI */
2068 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
2069 sizeof(str_dest_buf) - str_dest.slen,
2070 "?%s"
2071 "Replaces=%.*s"
2072 "%%3Bto-tag%%3D%.*s"
2073 "%%3Bfrom-tag%%3D%.*s>",
2074 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
2075 "" : "Require=replaces&"),
2076 (int)dest_dlg->call_id->id.slen,
2077 dest_dlg->call_id->id.ptr,
2078 (int)dest_dlg->remote.info->tag.slen,
2079 dest_dlg->remote.info->tag.ptr,
2080 (int)dest_dlg->local.info->tag.slen,
2081 dest_dlg->local.info->tag.ptr);
2082
2083 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
2084 PJSIP_EURITOOLONG);
2085
2086 str_dest.ptr = str_dest_buf;
2087 str_dest.slen += len;
2088
2089 pjsip_dlg_dec_lock(dest_dlg);
2090
2091 return pjsua_call_xfer(call_id, &str_dest, msg_data);
2092}
2093
2094
2095/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 * Send DTMF digits to remote using RFC 2833 payload formats.
2097 */
2098PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
2099 const pj_str_t *digits)
2100{
2101 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002102 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002103 pj_status_t status;
2104
2105 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2106 PJ_EINVAL);
2107
Benny Prijonodc752ca2006-09-22 16:55:42 +00002108 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002109 if (status != PJ_SUCCESS)
2110 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111
Benny Prijono0bc99a92011-03-17 04:34:43 +00002112 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00002114 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115 return PJ_EINVALIDOP;
2116 }
2117
Benny Prijono0bc99a92011-03-17 04:34:43 +00002118 status = pjmedia_stream_dial_dtmf(
2119 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002120
Benny Prijonodc752ca2006-09-22 16:55:42 +00002121 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002122
2123 return status;
2124}
2125
2126
2127/**
2128 * Send instant messaging inside INVITE session.
2129 */
2130PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
2131 const pj_str_t *mime_type,
2132 const pj_str_t *content,
2133 const pjsua_msg_data *msg_data,
2134 void *user_data)
2135{
2136 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002137 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002138 const pj_str_t mime_text_plain = pj_str("text/plain");
2139 pjsip_media_type ctype;
2140 pjsua_im_data *im_data;
2141 pjsip_tx_data *tdata;
2142 pj_status_t status;
2143
2144
2145 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2146 PJ_EINVAL);
2147
Benny Prijonodc752ca2006-09-22 16:55:42 +00002148 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002149 if (status != PJ_SUCCESS)
2150 return status;
2151
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002152 /* Set default media type if none is specified */
2153 if (mime_type == NULL) {
2154 mime_type = &mime_text_plain;
2155 }
2156
2157 /* Create request message. */
2158 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2159 -1, &tdata);
2160 if (status != PJ_SUCCESS) {
2161 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2162 goto on_return;
2163 }
2164
2165 /* Add accept header. */
2166 pjsip_msg_add_hdr( tdata->msg,
2167 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2168
2169 /* Parse MIME type */
2170 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2171
2172 /* Create "text/plain" message body. */
2173 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2174 &ctype.subtype, content);
2175 if (tdata->msg->body == NULL) {
2176 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2177 pjsip_tx_data_dec_ref(tdata);
2178 goto on_return;
2179 }
2180
2181 /* Add additional headers etc */
2182 pjsua_process_msg_data( tdata, msg_data);
2183
2184 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002185 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002186 im_data->acc_id = call->acc_id;
2187 im_data->call_id = call_id;
2188 im_data->to = call->inv->dlg->remote.info_str;
2189 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2190 im_data->user_data = user_data;
2191
2192
2193 /* Send the request. */
2194 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2195 pjsua_var.mod.id, im_data);
2196 if (status != PJ_SUCCESS) {
2197 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2198 goto on_return;
2199 }
2200
2201on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002202 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002203 return status;
2204}
2205
2206
2207/*
2208 * Send IM typing indication inside INVITE session.
2209 */
2210PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2211 pj_bool_t is_typing,
2212 const pjsua_msg_data*msg_data)
2213{
2214 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002215 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 pjsip_tx_data *tdata;
2217 pj_status_t status;
2218
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2220 PJ_EINVAL);
2221
Benny Prijonodc752ca2006-09-22 16:55:42 +00002222 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002223 if (status != PJ_SUCCESS)
2224 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002225
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226 /* Create request message. */
2227 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2228 -1, &tdata);
2229 if (status != PJ_SUCCESS) {
2230 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2231 goto on_return;
2232 }
2233
2234 /* Create "application/im-iscomposing+xml" msg body. */
2235 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2236 NULL, NULL, -1);
2237
2238 /* Add additional headers etc */
2239 pjsua_process_msg_data( tdata, msg_data);
2240
2241 /* Send the request. */
2242 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2243 if (status != PJ_SUCCESS) {
2244 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2245 goto on_return;
2246 }
2247
2248on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002249 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002250 return status;
2251}
2252
2253
2254/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002255 * Send arbitrary request.
2256 */
2257PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2258 const pj_str_t *method_str,
2259 const pjsua_msg_data *msg_data)
2260{
2261 pjsua_call *call;
2262 pjsip_dialog *dlg;
2263 pjsip_method method;
2264 pjsip_tx_data *tdata;
2265 pj_status_t status;
2266
2267 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2268 PJ_EINVAL);
2269
2270 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2271 if (status != PJ_SUCCESS)
2272 return status;
2273
2274 /* Init method */
2275 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2276
2277 /* Create request message. */
2278 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2279 if (status != PJ_SUCCESS) {
2280 pjsua_perror(THIS_FILE, "Unable to create request", status);
2281 goto on_return;
2282 }
2283
2284 /* Add additional headers etc */
2285 pjsua_process_msg_data( tdata, msg_data);
2286
2287 /* Send the request. */
2288 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2289 if (status != PJ_SUCCESS) {
2290 pjsua_perror(THIS_FILE, "Unable to send request", status);
2291 goto on_return;
2292 }
2293
2294on_return:
2295 pjsip_dlg_dec_lock(dlg);
2296 return status;
2297}
2298
2299
2300/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002301 * Terminate all calls.
2302 */
2303PJ_DEF(void) pjsua_call_hangup_all(void)
2304{
2305 unsigned i;
2306
2307 PJSUA_LOCK();
2308
2309 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2310 if (pjsua_var.calls[i].inv)
2311 pjsua_call_hangup(i, 0, NULL, NULL);
2312 }
2313
2314 PJSUA_UNLOCK();
2315}
2316
2317
Benny Prijono1e601552010-10-20 05:31:08 +00002318/* Proto */
2319static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002320
Benny Prijono1e601552010-10-20 05:31:08 +00002321/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002322static void reinv_timer_cb(pj_timer_heap_t *th,
2323 pj_timer_entry *entry)
2324{
2325 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2326 pjsip_dialog *dlg;
2327 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002328 pj_status_t status;
2329
2330 PJ_UNUSED_ARG(th);
2331
2332 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2333
2334 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2335 if (status != PJ_SUCCESS)
2336 return;
2337
Benny Prijono1e601552010-10-20 05:31:08 +00002338 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002339
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002340 pjsip_dlg_dec_lock(dlg);
2341}
2342
2343
2344/* Check if the specified format can be skipped in counting codecs */
2345static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2346 const pj_str_t *fmt)
2347{
Benny Prijono1e601552010-10-20 05:31:08 +00002348 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002349 unsigned pt;
2350
2351 pt = pj_strtoul(fmt);
2352
2353 /* Check for comfort noise */
2354 if (pt == PJMEDIA_RTP_PT_CN)
2355 return PJ_TRUE;
2356
2357 /* Dynamic PT, check the format name */
2358 if (pt >= 96) {
2359 pjmedia_sdp_attr *a;
2360 pjmedia_sdp_rtpmap rtpmap;
2361
2362 /* Get the format name */
2363 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2364 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2365 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002366 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002367 return PJ_TRUE;
2368 } else {
2369 /* Invalid SDP, should not reach here */
2370 pj_assert(!"SDP should have been validated!");
2371 return PJ_TRUE;
2372 }
2373 }
2374
2375 return PJ_FALSE;
2376}
2377
2378
Benny Prijono1e601552010-10-20 05:31:08 +00002379/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2380 * out of several codecs presented by callee in his answer.
2381 */
2382static pj_status_t perform_lock_codec(pjsua_call *call)
2383{
2384 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2385 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002386 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002387 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002388 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002389 pjsip_tx_data *tdata;
2390 pj_status_t status;
2391
2392 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2393 PJ_EINVALIDOP);
2394
2395 /* Verify if another SDP negotiation is in progress, e.g: session timer
2396 * or another re-INVITE.
2397 */
2398 if (call->inv==NULL || call->inv->neg==NULL ||
2399 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2400 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002401 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002402 }
2403
Benny Prijono02493272010-11-17 09:15:04 +00002404 /* Don't do this if call is disconnecting! */
2405 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2406 call->inv->cause >= 200)
2407 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002408 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002409 }
2410
Benny Prijono1e601552010-10-20 05:31:08 +00002411 /* Verify if another SDP negotiation has been completed by comparing
2412 * the SDP version.
2413 */
2414 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2415 if (status != PJ_SUCCESS)
2416 return status;
2417 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2418 return PJMEDIA_SDP_EINVER;
2419
2420 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2421
2422 /* Update the new offer so it contains only a codec. Note that formats
2423 * order in the offer should have been matched to the answer, so we can
2424 * just directly update the offer without looking-up the answer.
2425 */
2426 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002427
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002428 for (i = 0; i < call->med_cnt; ++i) {
2429 unsigned j = 0, codec_cnt = 0;
2430 const pjmedia_sdp_media *ref_m;
2431 pjmedia_sdp_media *m;
2432 pjsua_call_media *call_med = &call->media[i];
2433
2434 /* Verify if media is deactivated */
2435 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2436 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2437 call_med->dir == PJMEDIA_DIR_NONE)
2438 {
Benny Prijono1e601552010-10-20 05:31:08 +00002439 continue;
2440 }
2441
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002442 ref_m = local_sdp->media[i];
2443 m = new_sdp->media[i];
2444
2445 /* Verify that media must be active. */
2446 pj_assert(ref_m->desc.port);
2447
2448 while (j < m->desc.fmt_count) {
2449 pjmedia_sdp_attr *a;
2450 pj_str_t *fmt = &m->desc.fmt[j];
2451
2452 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2453 ++j;
2454 continue;
2455 }
2456
2457 /* Remove format */
2458 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2459 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2460 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2461 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2462 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2463 m->desc.fmt_count, j);
2464 --m->desc.fmt_count;
2465 }
2466
2467 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002468 }
2469
Nanang Izzuddinec919002010-11-25 09:27:06 +00002470 /* Last check if SDP trully needs to be updated. It is possible that OA
2471 * negotiations have completed and SDP has changed but we didn't
2472 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002473 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002474 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002475 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002476
2477 /* Send UPDATE or re-INVITE */
2478 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2479 PJSIP_H_ALLOW,
2480 NULL, &STR_UPDATE) ==
2481 PJSIP_DIALOG_CAP_SUPPORTED;
2482 if (rem_can_update) {
2483 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2484 } else {
2485 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2486 }
2487
2488 if (status==PJ_EINVALIDOP &&
2489 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2490 {
2491 /* Ups, let's reschedule again */
2492 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2493 pj_time_val_normalize(&delay);
2494 call->lock_codec.reinv_timer.id = PJ_TRUE;
2495 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2496 &call->lock_codec.reinv_timer, &delay);
2497 return status;
2498 } else if (status != PJ_SUCCESS) {
2499 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2500 status);
2501 return status;
2502 }
2503
2504 /* Send the UPDATE/re-INVITE request */
2505 status = pjsip_inv_send_msg(call->inv, tdata);
2506 if (status != PJ_SUCCESS) {
2507 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2508 status);
2509 return status;
2510 }
2511
2512 return status;
2513}
2514
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002515/* Check if remote answerer has given us more than one codecs. If so,
2516 * create another offer with one codec only to lock down the codec.
2517 */
2518static pj_status_t lock_codec(pjsua_call *call)
2519{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002520 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002521 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002522 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002523 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002524 unsigned i;
2525 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002526 pj_status_t status;
2527
Nanang Izzuddinec919002010-11-25 09:27:06 +00002528 /* Stop lock codec timer, if it is active */
2529 if (call->lock_codec.reinv_timer.id) {
2530 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2531 &call->lock_codec.reinv_timer);
2532 call->lock_codec.reinv_timer.id = PJ_FALSE;
2533 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002534
Nanang Izzuddinec919002010-11-25 09:27:06 +00002535 /* Skip this if we are the answerer */
2536 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2537 return PJ_SUCCESS;
2538 }
2539
Nanang Izzuddinec919002010-11-25 09:27:06 +00002540 /* Delay this when the SDP negotiation done in call state EARLY and
2541 * remote does not support UPDATE method.
2542 */
2543 if (inv->state == PJSIP_INV_STATE_EARLY &&
2544 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2545 PJSIP_DIALOG_CAP_SUPPORTED)
2546 {
2547 call->lock_codec.pending = PJ_TRUE;
2548 return PJ_SUCCESS;
2549 }
2550
2551 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002552 if (status != PJ_SUCCESS)
2553 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002554 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002555 if (status != PJ_SUCCESS)
2556 return status;
2557
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002558 /* Find multiple codecs answer in all media */
2559 for (i = 0; i < call->med_cnt; ++i) {
2560 pjsua_call_media *call_med = &call->media[i];
2561 const pjmedia_sdp_media *rem_m, *loc_m;
2562 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002563
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002564 /* Skip this if the media is inactive or error */
2565 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2566 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2567 call_med->dir == PJMEDIA_DIR_NONE)
2568 {
2569 continue;
2570 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002571
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002572 /* Remote may answer with less media lines. */
2573 if (i >= remote_sdp->media_count)
2574 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002575
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002576 rem_m = remote_sdp->media[i];
2577 loc_m = local_sdp->media[i];
2578
2579 /* Verify that media must be active. */
2580 pj_assert(loc_m->desc.port && rem_m->desc.port);
2581
2582 /* Count the formats in the answer. */
2583 if (rem_m->desc.fmt_count==1) {
2584 codec_cnt = 1;
2585 } else {
2586 unsigned j;
2587 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
2588 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
2589 ++codec_cnt;
2590 }
2591 }
2592
2593 if (codec_cnt > 1) {
2594 has_mult_fmt = PJ_TRUE;
2595 break;
2596 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002597 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002598
2599 /* Each media in the answer already contains single codec. */
2600 if (!has_mult_fmt) {
2601 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002602 return PJ_SUCCESS;
2603 }
2604
Nanang Izzuddinec919002010-11-25 09:27:06 +00002605 /* Remote keeps answering with multiple codecs, let's just give up
2606 * locking codec to avoid infinite retry loop.
2607 */
2608 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
2609 return PJ_SUCCESS;
2610
Benny Prijono1e601552010-10-20 05:31:08 +00002611 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002612 "updating media session to use only one codec.."));
2613
Benny Prijono1e601552010-10-20 05:31:08 +00002614 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002615
Benny Prijono1e601552010-10-20 05:31:08 +00002616 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
2617 * See: https://trac.pjsip.org/repos/ticket/1149
2618 */
2619 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
2620 (void*)(pj_size_t)call->index,
2621 &reinv_timer_cb);
2622 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2623 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002624
2625 return PJ_SUCCESS;
2626}
2627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002628/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002629 * This callback receives notification from invite session when the
2630 * session state has changed.
2631 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002632static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2633 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002634{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002635 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002636
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002637 PJSUA_LOCK();
2638
Benny Prijonoa1e69682007-05-11 15:14:34 +00002639 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002640
2641 if (!call) {
2642 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002643 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002644 }
2645
Benny Prijonoe21e7842006-04-09 16:46:05 +00002646
2647 /* Get call times */
2648 switch (inv->state) {
2649 case PJSIP_INV_STATE_EARLY:
2650 case PJSIP_INV_STATE_CONNECTING:
2651 if (call->res_time.sec == 0)
2652 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002653 call->last_code = (pjsip_status_code)
2654 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002655 pj_strncpy(&call->last_text,
2656 &e->body.tsx_state.tsx->status_text,
2657 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002658 break;
2659 case PJSIP_INV_STATE_CONFIRMED:
2660 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002661
Nanang Izzuddinec919002010-11-25 09:27:06 +00002662 /* See if lock codec was pended as media update was done in the
2663 * EARLY state and remote does not support UPDATE.
2664 */
2665 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002666 pj_status_t status;
2667 status = lock_codec(call);
2668 if (status != PJ_SUCCESS) {
2669 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00002670 }
2671 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002672 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002673 break;
2674 case PJSIP_INV_STATE_DISCONNECTED:
2675 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002676 if (call->res_time.sec == 0)
2677 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002678 if (e->type == PJSIP_EVENT_TSX_STATE &&
2679 e->body.tsx_state.tsx->status_code > call->last_code)
2680 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002681 call->last_code = (pjsip_status_code)
2682 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002683 pj_strncpy(&call->last_text,
2684 &e->body.tsx_state.tsx->status_text,
2685 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002686 } else {
2687 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2688 pj_strncpy(&call->last_text,
2689 pjsip_get_status_text(call->last_code),
2690 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002691 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002692
2693 /* Stop lock codec timer, if it is active */
2694 if (call->lock_codec.reinv_timer.id) {
2695 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2696 &call->lock_codec.reinv_timer);
2697 call->lock_codec.reinv_timer.id = PJ_FALSE;
2698 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002699 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002700 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002701 call->last_code = (pjsip_status_code)
2702 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002703 pj_strncpy(&call->last_text,
2704 &e->body.tsx_state.tsx->status_text,
2705 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002706 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002707 }
2708
Benny Prijono26ff9062006-02-21 23:47:00 +00002709 /* If this is an outgoing INVITE that was created because of
2710 * REFER/transfer, send NOTIFY to transferer.
2711 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002712 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002713 int st_code = -1;
2714 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2715
2716
Benny Prijonoa91a0032006-02-26 21:23:45 +00002717 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002718 case PJSIP_INV_STATE_NULL:
2719 case PJSIP_INV_STATE_CALLING:
2720 /* Do nothing */
2721 break;
2722
2723 case PJSIP_INV_STATE_EARLY:
2724 case PJSIP_INV_STATE_CONNECTING:
2725 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002726 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2727 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2728 else
2729 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002730 break;
2731
Benny Prijono140beae2009-10-11 05:06:43 +00002732 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002733#if 0
2734/* We don't need this, as we've terminated the subscription in
2735 * CONNECTING state.
2736 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002737 /* When state is confirmed, send the final 200/OK and terminate
2738 * subscription.
2739 */
2740 st_code = e->body.tsx_state.tsx->status_code;
2741 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002742#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002743 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002744
2745 case PJSIP_INV_STATE_DISCONNECTED:
2746 st_code = e->body.tsx_state.tsx->status_code;
2747 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2748 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002749
Benny Prijono8b1889b2006-06-06 18:40:40 +00002750 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002751 /* Nothing to do. Just to keep gcc from complaining about
2752 * unused enums.
2753 */
2754 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002755 }
2756
2757 if (st_code != -1) {
2758 pjsip_tx_data *tdata;
2759 pj_status_t status;
2760
Benny Prijonoa91a0032006-02-26 21:23:45 +00002761 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002762 ev_state, st_code,
2763 NULL, &tdata);
2764 if (status != PJ_SUCCESS) {
2765 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2766 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002767 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002768 if (status != PJ_SUCCESS) {
2769 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2770 }
2771 }
2772 }
2773 }
2774
Benny Prijono84126ab2006-02-09 09:30:09 +00002775
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002776 if (pjsua_var.ua_cfg.cb.on_call_state)
2777 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002778
2779 /* call->inv may be NULL now */
2780
Benny Prijono84126ab2006-02-09 09:30:09 +00002781 /* Destroy media session when invite session is disconnected. */
2782 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002783
Benny Prijonoa91a0032006-02-26 21:23:45 +00002784 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002785
Benny Prijono275fd682006-03-22 11:59:11 +00002786 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002787 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002788
Benny Prijono105217f2006-03-06 16:25:59 +00002789 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002790 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002791 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002792
2793 /* Reset call */
2794 reset_call(call->index);
2795
Benny Prijono84126ab2006-02-09 09:30:09 +00002796 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002797
2798 PJSUA_UNLOCK();
2799}
2800
2801/*
2802 * This callback is called by invite session framework when UAC session
2803 * has forked.
2804 */
2805static void pjsua_call_on_forked( pjsip_inv_session *inv,
2806 pjsip_event *e)
2807{
2808 PJ_UNUSED_ARG(inv);
2809 PJ_UNUSED_ARG(e);
2810
2811 PJ_TODO(HANDLE_FORKED_DIALOG);
2812}
2813
2814
2815/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002816 * Callback from UA layer when forked dialog response is received.
2817 */
2818pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2819{
2820 if (dlg->uac_has_2xx &&
2821 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
2822 pjsip_rdata_get_tsx(res) == NULL &&
2823 res->msg_info.msg->line.status.code/100 == 2)
2824 {
2825 pjsip_dialog *forked_dlg;
2826 pjsip_tx_data *bye;
2827 pj_status_t status;
2828
2829 /* Create forked dialog */
2830 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
2831 if (status != PJ_SUCCESS)
2832 return NULL;
2833
2834 pjsip_dlg_inc_lock(forked_dlg);
2835
2836 /* Disconnect the call */
2837 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
2838 -1, &bye);
2839 if (status == PJ_SUCCESS) {
2840 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
2841 }
2842
2843 pjsip_dlg_dec_lock(forked_dlg);
2844
2845 if (status != PJ_SUCCESS) {
2846 return NULL;
2847 }
2848
2849 return forked_dlg;
2850
2851 } else {
2852 return dlg;
2853 }
2854}
2855
2856/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002857 * Disconnect call upon error.
2858 */
2859static void call_disconnect( pjsip_inv_session *inv,
2860 int code )
2861{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002862 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002863 pjsip_tx_data *tdata;
2864 pj_status_t status;
2865
Benny Prijono59b3aed2008-01-15 16:54:54 +00002866 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2867
Benny Prijonoa38ada02006-07-02 14:22:35 +00002868 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002869 if (status != PJ_SUCCESS)
2870 return;
2871
2872 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00002873#if DISABLED_FOR_TICKET_1185
2874 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00002875 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2876 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002877 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002878 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002879
Benny Prijono734fc2d2008-03-17 16:05:35 +00002880 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002881 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002882 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002883 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002884 if (status == PJ_SUCCESS) {
2885 pjsip_create_sdp_body(tdata->pool, local_sdp,
2886 &tdata->msg->body);
2887 }
2888 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00002889#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00002890
2891 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002892}
2893
2894/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002895 * Callback to be called when SDP offer/answer negotiation has just completed
2896 * in the session. This function will start/update media if negotiation
2897 * has succeeded.
2898 */
2899static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2900 pj_status_t status)
2901{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002902 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002903 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002904 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002905 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002906
2907 PJSUA_LOCK();
2908
Benny Prijonoa1e69682007-05-11 15:14:34 +00002909 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002910
2911 if (status != PJ_SUCCESS) {
2912
2913 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2914
Benny Prijono2331d202008-06-26 15:46:52 +00002915 /* Do not deinitialize media since this may be a re-INVITE or
2916 * UPDATE (which in this case the media should not get affected
2917 * by the failed re-INVITE/UPDATE). The media will be shutdown
2918 * when call is disconnected anyway.
2919 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002920 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00002921 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002922
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002923 /* Disconnect call if we're not in the middle of initializing an
2924 * UAS dialog and if this is not a re-INVITE
2925 */
2926 if (inv->state != PJSIP_INV_STATE_NULL &&
2927 inv->state != PJSIP_INV_STATE_CONFIRMED)
2928 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002929 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002930 }
2931
2932 PJSUA_UNLOCK();
2933 return;
2934 }
2935
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002936
2937 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00002938 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002939 if (status != PJ_SUCCESS) {
2940 pjsua_perror(THIS_FILE,
2941 "Unable to retrieve currently active local SDP",
2942 status);
2943 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2944 PJSUA_UNLOCK();
2945 return;
2946 }
2947
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002948 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2949 if (status != PJ_SUCCESS) {
2950 pjsua_perror(THIS_FILE,
2951 "Unable to retrieve currently active remote SDP",
2952 status);
2953 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2954 PJSUA_UNLOCK();
2955 return;
2956 }
2957
Benny Prijono91a6a172007-10-31 08:59:29 +00002958 /* Update remote's NAT type */
2959 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2960 update_remote_nat_type(call, remote_sdp);
2961 }
2962
2963 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002964 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002965 if (status != PJ_SUCCESS) {
2966 pjsua_perror(THIS_FILE, "Unable to create media session",
2967 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002968 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00002969 /* No need to deinitialize; media will be shutdown when call
2970 * state is disconnected anyway.
2971 */
2972 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002973 PJSUA_UNLOCK();
2974 return;
2975 }
2976
Nanang Izzuddinec919002010-11-25 09:27:06 +00002977 /* Ticket #476: make sure only one codec is specified in the answer. */
2978 status = lock_codec(call);
2979 if (status != PJ_SUCCESS) {
2980 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002981 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002982
2983 /* Call application callback, if any */
2984 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2985 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2986
2987
2988 PJSUA_UNLOCK();
2989}
2990
2991
Benny Prijonodd63b992010-10-01 02:03:42 +00002992/* Modify SDP for call hold. */
2993static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
2994 pj_pool_t *pool,
2995 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002996{
Benny Prijono316f02a2011-04-07 07:53:25 +00002997 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002998
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002999 /* Call-hold is done by set the media direction to 'sendonly'
3000 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3001 * 'inactive' (PJMEDIA_DIR_NONE).
3002 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3003 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003004 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003005 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003006 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003007 /* https://trac.pjsip.org/repos/ticket/1142:
3008 * configuration to use c=0.0.0.0 for call hold.
3009 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003010
Benny Prijono316f02a2011-04-07 07:53:25 +00003011 for (mi=0; mi<sdp->media_count; ++mi) {
3012 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003013
Benny Prijono316f02a2011-04-07 07:53:25 +00003014 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3015 pjmedia_sdp_conn *conn;
3016 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003017
Benny Prijono316f02a2011-04-07 07:53:25 +00003018 /* Get SDP media connection line */
3019 conn = m->conn;
3020 if (!conn)
3021 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003022
Benny Prijono316f02a2011-04-07 07:53:25 +00003023 /* Modify address */
3024 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003025
Benny Prijono316f02a2011-04-07 07:53:25 +00003026 /* Remove existing directions attributes */
3027 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3028 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3029 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3030 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003031
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003032 /* Add inactive attribute */
3033 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003034 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003035
3036
3037 } else {
3038 pjmedia_sdp_attr *attr;
3039
3040 /* Remove existing directions attributes */
3041 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3042 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3043 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3044 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3045
3046 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3047 /* Add sendonly attribute */
3048 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3049 pjmedia_sdp_media_add_attr(m, attr);
3050 } else {
3051 /* Add inactive attribute */
3052 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3053 pjmedia_sdp_media_add_attr(m, attr);
3054 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003055 }
3056 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003057
Benny Prijonodd63b992010-10-01 02:03:42 +00003058 return PJ_SUCCESS;
3059}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003060
Benny Prijonodd63b992010-10-01 02:03:42 +00003061/* Create SDP for call hold. */
3062static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3063 pjmedia_sdp_session **p_sdp)
3064{
3065 pj_status_t status;
3066 pj_pool_t *pool;
3067 pjmedia_sdp_session *sdp;
3068
3069 /* Use call's provisional pool */
3070 pool = call->inv->pool_prov;
3071
3072 /* Create new offer */
3073 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3074 NULL);
3075 if (status != PJ_SUCCESS) {
3076 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3077 return status;
3078 }
3079
3080 status = modify_sdp_of_call_hold(call, pool, sdp);
3081 if (status != PJ_SUCCESS)
3082 return status;
3083
3084 *p_sdp = sdp;
3085
3086 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003087}
3088
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003089/*
3090 * Called when session received new offer.
3091 */
3092static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3093 const pjmedia_sdp_session *offer)
3094{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003095 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003096 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003097 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003098 pj_status_t status;
3099
3100 PJSUA_LOCK();
3101
Benny Prijonoa1e69682007-05-11 15:14:34 +00003102 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003103
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003104 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003105 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3106 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003107
Benny Prijono40d62b62009-08-12 17:53:47 +00003108 status = pjsua_media_channel_create_sdp(call->index,
3109 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003110 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003111 if (status != PJ_SUCCESS) {
3112 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3113 PJSUA_UNLOCK();
3114 return;
3115 }
3116
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003117 /* Validate media count in the generated answer */
3118 pj_assert(answer->media_count == offer->media_count);
3119
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003120 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003121 for (i = 0; i < answer->media_count; ++i) {
3122 pjmedia_sdp_conn *conn;
3123
3124 conn = offer->media[i]->conn;
3125 if (!conn)
3126 conn = offer->conn;
3127
3128 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3129 pj_strcmp2(&conn->addr, "0")==0)
3130 {
3131 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3132
3133 /* Modify answer address */
3134 if (a_conn) {
3135 a_conn->addr = pj_str("0.0.0.0");
3136 } else if (answer->conn == NULL ||
3137 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3138 {
3139 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3140 pjmedia_sdp_conn);
3141 a_conn->net_type = pj_str("IN");
3142 a_conn->addr_type = pj_str("IP4");
3143 a_conn->addr = pj_str("0.0.0.0");
3144 answer->media[i]->conn = a_conn;
3145 }
3146 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003147 }
3148
3149 /* Check if call is on-hold */
3150 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003151 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003152 }
3153
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003154 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3155 if (status != PJ_SUCCESS) {
3156 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3157 PJSUA_UNLOCK();
3158 return;
3159 }
3160
3161 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003162}
3163
3164
3165/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003166 * Called to generate new offer.
3167 */
3168static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3169 pjmedia_sdp_session **offer)
3170{
3171 pjsua_call *call;
3172 pj_status_t status;
3173
3174 PJSUA_LOCK();
3175
3176 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3177
3178 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003179 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003180 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003181 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003182 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003183 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003184 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003185 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3186 call->index));
3187
Benny Prijono40d62b62009-08-12 17:53:47 +00003188 status = pjsua_media_channel_create_sdp(call->index,
3189 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003190 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003191 }
3192
3193 if (status != PJ_SUCCESS) {
3194 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3195 PJSUA_UNLOCK();
3196 return;
3197 }
3198
Benny Prijono77998ce2007-06-20 10:03:46 +00003199 PJSUA_UNLOCK();
3200}
3201
3202
3203/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003204 * Callback called by event framework when the xfer subscription state
3205 * has changed.
3206 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003207static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3208{
3209
3210 PJ_UNUSED_ARG(event);
3211
3212 /*
3213 * When subscription is accepted (got 200/OK to REFER), check if
3214 * subscription suppressed.
3215 */
3216 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3217
3218 pjsip_rx_data *rdata;
3219 pjsip_generic_string_hdr *refer_sub;
3220 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3221 pjsua_call *call;
3222
Benny Prijonoa1e69682007-05-11 15:14:34 +00003223 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003224
3225 /* Must be receipt of response message */
3226 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3227 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3228 rdata = event->body.tsx_state.src.rdata;
3229
3230 /* Find Refer-Sub header */
3231 refer_sub = (pjsip_generic_string_hdr*)
3232 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3233 &REFER_SUB, NULL);
3234
3235 /* Check if subscription is suppressed */
3236 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3237 /* Since no subscription is desired, assume that call has been
3238 * transfered successfully.
3239 */
3240 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3241 const pj_str_t ACCEPTED = { "Accepted", 8 };
3242 pj_bool_t cont = PJ_FALSE;
3243 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3244 200,
3245 &ACCEPTED,
3246 PJ_TRUE,
3247 &cont);
3248 }
3249
3250 /* Yes, subscription is suppressed.
3251 * Terminate our subscription now.
3252 */
3253 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3254 "event subcription..."));
3255 pjsip_evsub_terminate(sub, PJ_TRUE);
3256
3257 } else {
3258 /* Notify application about call transfer progress.
3259 * Initially notify with 100/Accepted status.
3260 */
3261 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3262 const pj_str_t ACCEPTED = { "Accepted", 8 };
3263 pj_bool_t cont = PJ_FALSE;
3264 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3265 100,
3266 &ACCEPTED,
3267 PJ_FALSE,
3268 &cont);
3269 }
3270 }
3271 }
3272 /*
3273 * On incoming NOTIFY, notify application about call transfer progress.
3274 */
3275 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3276 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3277 {
3278 pjsua_call *call;
3279 pjsip_msg *msg;
3280 pjsip_msg_body *body;
3281 pjsip_status_line status_line;
3282 pj_bool_t is_last;
3283 pj_bool_t cont;
3284 pj_status_t status;
3285
Benny Prijonoa1e69682007-05-11 15:14:34 +00003286 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003287
3288 /* When subscription is terminated, clear the xfer_sub member of
3289 * the inv_data.
3290 */
3291 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3292 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3293 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3294
3295 }
3296
3297 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3298 /* Application is not interested with call progress status */
3299 return;
3300 }
3301
3302 /* This better be a NOTIFY request */
3303 if (event->type == PJSIP_EVENT_TSX_STATE &&
3304 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3305 {
3306 pjsip_rx_data *rdata;
3307
3308 rdata = event->body.tsx_state.src.rdata;
3309
3310 /* Check if there's body */
3311 msg = rdata->msg_info.msg;
3312 body = msg->body;
3313 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003314 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003315 "Warning: received NOTIFY without message body"));
3316 return;
3317 }
3318
3319 /* Check for appropriate content */
3320 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3321 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3322 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003323 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003324 "Warning: received NOTIFY with non message/sipfrag "
3325 "content"));
3326 return;
3327 }
3328
3329 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003330 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003331 &status_line);
3332 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003333 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003334 "Warning: received NOTIFY with invalid "
3335 "message/sipfrag content"));
3336 return;
3337 }
3338
3339 } else {
3340 status_line.code = 500;
3341 status_line.reason = *pjsip_get_status_text(500);
3342 }
3343
3344 /* Notify application */
3345 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3346 cont = !is_last;
3347 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3348 status_line.code,
3349 &status_line.reason,
3350 is_last, &cont);
3351
3352 if (!cont) {
3353 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3354 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003355
3356 /* If the call transfer has completed but the subscription is
3357 * not terminated, terminate it now.
3358 */
3359 if (status_line.code/100 == 2 && !is_last) {
3360 pjsip_tx_data *tdata;
3361
3362 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3363 0, &tdata);
3364 if (status == PJ_SUCCESS)
3365 status = pjsip_evsub_send_request(sub, tdata);
3366 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003367 }
3368}
3369
3370
3371/*
3372 * Callback called by event framework when the xfer subscription state
3373 * has changed.
3374 */
3375static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003376{
3377
3378 PJ_UNUSED_ARG(event);
3379
3380 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003381 * When subscription is terminated, clear the xfer_sub member of
3382 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003383 */
3384 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003385 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003386
Benny Prijonoa1e69682007-05-11 15:14:34 +00003387 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003388 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003389 return;
3390
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003391 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003392 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003393
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003394 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003395 }
3396}
3397
3398
3399/*
3400 * Follow transfer (REFER) request.
3401 */
3402static void on_call_transfered( pjsip_inv_session *inv,
3403 pjsip_rx_data *rdata )
3404{
3405 pj_status_t status;
3406 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003407 pjsua_call *existing_call;
3408 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003409 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003410 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003411 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003412 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003413 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003414 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003415 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003416 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003417 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003418 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003419 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003420 pjsip_evsub *sub;
3421
Benny Prijonoa1e69682007-05-11 15:14:34 +00003422 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003423
Benny Prijono26ff9062006-02-21 23:47:00 +00003424 /* Find the Refer-To header */
3425 refer_to = (pjsip_generic_string_hdr*)
3426 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3427
3428 if (refer_to == NULL) {
3429 /* Invalid Request.
3430 * No Refer-To header!
3431 */
3432 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003433 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003434 return;
3435 }
3436
Benny Prijonoc8141a82006-08-20 09:12:19 +00003437 /* Find optional Refer-Sub header */
3438 refer_sub = (pjsip_generic_string_hdr*)
3439 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3440
3441 if (refer_sub) {
3442 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3443 no_refer_sub = PJ_TRUE;
3444 }
3445
Benny Prijono053f5222006-11-11 16:16:04 +00003446 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3447 * request.
3448 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003449 ref_by_hdr = (pjsip_hdr*)
3450 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003451 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003452
Benny Prijono9fc735d2006-05-28 14:58:12 +00003453 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003454 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003455 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3456 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3457 &refer_to->hvalue,
3458 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003459
3460 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003461 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003462 if (code >= 300) {
3463 /* Application rejects call transfer request */
3464 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3465 return;
3466 }
3467
Benny Prijono26ff9062006-02-21 23:47:00 +00003468 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3469 (int)inv->dlg->remote.info_str.slen,
3470 inv->dlg->remote.info_str.ptr,
3471 (int)refer_to->hvalue.slen,
3472 refer_to->hvalue.ptr));
3473
Benny Prijonoc8141a82006-08-20 09:12:19 +00003474 if (no_refer_sub) {
3475 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003476 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003477 */
3478 pjsip_tx_data *tdata;
3479 const pj_str_t str_false = { "false", 5};
3480 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003481
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003482 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3483 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003484 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003485 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003486 status);
3487 return;
3488 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003489
Benny Prijonoc8141a82006-08-20 09:12:19 +00003490 /* Add Refer-Sub header */
3491 hdr = (pjsip_hdr*)
3492 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3493 &str_false);
3494 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003495
Benny Prijono26ff9062006-02-21 23:47:00 +00003496
Benny Prijonoc8141a82006-08-20 09:12:19 +00003497 /* Send answer */
3498 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3499 tdata);
3500 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003501 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003502 status);
3503 return;
3504 }
3505
3506 /* Don't have subscription */
3507 sub = NULL;
3508
3509 } else {
3510 struct pjsip_evsub_user xfer_cb;
3511 pjsip_hdr hdr_list;
3512
3513 /* Init callback */
3514 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003515 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003516
3517 /* Init additional header list to be sent with REFER response */
3518 pj_list_init(&hdr_list);
3519
3520 /* Create transferee event subscription */
3521 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3522 if (status != PJ_SUCCESS) {
3523 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3524 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3525 return;
3526 }
3527
3528 /* If there's Refer-Sub header and the value is "true", send back
3529 * Refer-Sub in the response with value "true" too.
3530 */
3531 if (refer_sub) {
3532 const pj_str_t str_true = { "true", 4 };
3533 pjsip_hdr *hdr;
3534
3535 hdr = (pjsip_hdr*)
3536 pjsip_generic_string_hdr_create(inv->dlg->pool,
3537 &str_refer_sub,
3538 &str_true);
3539 pj_list_push_back(&hdr_list, hdr);
3540
3541 }
3542
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003543 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003544 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3545
3546 /* Create initial NOTIFY request */
3547 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3548 100, NULL, &tdata);
3549 if (status != PJ_SUCCESS) {
3550 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3551 status);
3552 return;
3553 }
3554
3555 /* Send initial NOTIFY request */
3556 status = pjsip_xfer_send_request( sub, tdata);
3557 if (status != PJ_SUCCESS) {
3558 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3559 return;
3560 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003561 }
3562
3563 /* We're cheating here.
3564 * We need to get a null terminated string from a pj_str_t.
3565 * So grab the pointer from the hvalue and NULL terminate it, knowing
3566 * that the NULL position will be occupied by a newline.
3567 */
3568 uri = refer_to->hvalue.ptr;
3569 uri[refer_to->hvalue.slen] = '\0';
3570
Benny Prijono053f5222006-11-11 16:16:04 +00003571 /* Init msg_data */
3572 pjsua_msg_data_init(&msg_data);
3573
3574 /* If Referred-By header is present in the REFER request, copy this
3575 * to the outgoing INVITE request.
3576 */
3577 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003578 pjsip_hdr *dup = (pjsip_hdr*)
3579 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003580 pj_list_push_back(&msg_data.hdr_list, dup);
3581 }
3582
Benny Prijono26ff9062006-02-21 23:47:00 +00003583 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003584 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003585 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003586 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003587 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003588 if (status != PJ_SUCCESS) {
3589
Benny Prijonoc8141a82006-08-20 09:12:19 +00003590 /* Notify xferer about the error (if we have subscription) */
3591 if (sub) {
3592 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3593 500, NULL, &tdata);
3594 if (status != PJ_SUCCESS) {
3595 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3596 status);
3597 return;
3598 }
3599 status = pjsip_xfer_send_request(sub, tdata);
3600 if (status != PJ_SUCCESS) {
3601 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3602 status);
3603 return;
3604 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003605 }
3606 return;
3607 }
3608
Benny Prijonoc8141a82006-08-20 09:12:19 +00003609 if (sub) {
3610 /* Put the server subscription in inv_data.
3611 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3612 * reported back to the server subscription.
3613 */
3614 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003615
Benny Prijonoc8141a82006-08-20 09:12:19 +00003616 /* Put the invite_data in the subscription. */
3617 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3618 &pjsua_var.calls[new_call]);
3619 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003620}
3621
3622
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003623
Benny Prijono26ff9062006-02-21 23:47:00 +00003624/*
3625 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003626 * session. We use this to trap:
3627 * - incoming REFER request.
3628 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003629 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003630static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3631 pjsip_transaction *tsx,
3632 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003633{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003634 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003635
3636 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003637
Benny Prijono2285e7e2008-12-17 14:28:18 +00003638 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3639
3640 if (call == NULL) {
3641 PJSUA_UNLOCK();
3642 return;
3643 }
3644
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003645 if (call->inv == NULL) {
3646 /* Shouldn't happen. It happens only when we don't terminate the
3647 * server subscription caused by REFER after the call has been
3648 * transfered (and this call has been disconnected), and we
3649 * receive another REFER for this call.
3650 */
3651 PJSUA_UNLOCK();
3652 return;
3653 }
3654
Benny Prijonofeb69f42007-10-05 09:12:26 +00003655 /* Notify application callback first */
3656 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3657 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3658 }
3659
Benny Prijono26ff9062006-02-21 23:47:00 +00003660 if (tsx->role==PJSIP_ROLE_UAS &&
3661 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003662 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003663 {
3664 /*
3665 * Incoming REFER request.
3666 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003667 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003668
Benny Prijono26ff9062006-02-21 23:47:00 +00003669 }
Benny Prijonob0808372006-03-02 21:18:58 +00003670 else if (tsx->role==PJSIP_ROLE_UAS &&
3671 tsx->state==PJSIP_TSX_STATE_TRYING &&
3672 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3673 {
3674 /*
3675 * Incoming MESSAGE request!
3676 */
3677 pjsip_rx_data *rdata;
3678 pjsip_msg *msg;
3679 pjsip_accept_hdr *accept_hdr;
3680 pj_status_t status;
3681
3682 rdata = e->body.tsx_state.src.rdata;
3683 msg = rdata->msg_info.msg;
3684
3685 /* Request MUST have message body, with Content-Type equal to
3686 * "text/plain".
3687 */
3688 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3689
3690 pjsip_hdr hdr_list;
3691
3692 pj_list_init(&hdr_list);
3693 pj_list_push_back(&hdr_list, accept_hdr);
3694
3695 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3696 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003697 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003698 return;
3699 }
3700
3701 /* Respond with 200 first, so that remote doesn't retransmit in case
3702 * the UI takes too long to process the message.
3703 */
3704 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3705
3706 /* Process MESSAGE request */
3707 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3708 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003709
Benny Prijonob0808372006-03-02 21:18:58 +00003710 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003711 else if (tsx->role == PJSIP_ROLE_UAC &&
3712 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003713 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003714 /* Handle outgoing pager status */
3715 if (tsx->status_code >= 200) {
3716 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003717
Benny Prijonoa1e69682007-05-11 15:14:34 +00003718 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003719 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003720
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003721 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3722 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3723 &im_data->to,
3724 &im_data->body,
3725 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003726 (pjsip_status_code)
3727 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003728 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003729 }
Benny Prijonofccab712006-02-22 22:23:22 +00003730 }
Benny Prijono834aee32006-02-19 01:38:06 +00003731 }
Benny Prijono834aee32006-02-19 01:38:06 +00003732
Benny Prijono26ff9062006-02-21 23:47:00 +00003733
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003734 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003735}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003736
3737
3738/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003739static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3740 const pjsip_uri *target,
3741 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003742{
3743 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003744 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003745
3746 PJSUA_LOCK();
3747
3748 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003749 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3750 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003751 } else {
3752 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3753 "(callback not implemented by application). Disconnecting "
3754 "call.",
3755 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003756 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003757 }
3758
3759 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003760
3761 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003762}
3763