blob: 441bc770e82627c6f655b73f217f1702de644004 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 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;
113 call_med->call = call;
114 call_med->idx = i;
115 call_med->tp_auto_del = PJ_TRUE;
116 }
Benny Prijono105217f2006-03-06 16:25:59 +0000117}
118
119
Benny Prijono275fd682006-03-22 11:59:11 +0000120/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000121 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000122 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000124{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125 pjsip_inv_callback inv_cb;
126 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000127 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000129
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 /* Init calls array. */
131 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
132 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000133
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 /* Copy config */
135 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000136
Benny Prijono1cd713b2009-11-11 00:33:00 +0000137 /* Verify settings */
138 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
139 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
140 }
141
Benny Prijono91d06b62008-09-20 12:16:56 +0000142 /* Check the route URI's and force loose route if required */
143 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
144 status = normalize_route_uri(pjsua_var.pool,
145 &pjsua_var.ua_cfg.outbound_proxy[i]);
146 if (status != PJ_SUCCESS)
147 return status;
148 }
149
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000151 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
153 inv_cb.on_new_session = &pjsua_call_on_forked;
154 inv_cb.on_media_update = &pjsua_call_on_media_update;
155 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000156 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000158 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000159
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 /* Initialize invite session module: */
161 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
162 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
163
Benny Prijonoc8141a82006-08-20 09:12:19 +0000164 /* Add "norefersub" in Supported header */
165 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
166 NULL, 1, &str_norefersub);
167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 return status;
169}
170
171
172/*
173 * Start call subsystem.
174 */
175pj_status_t pjsua_call_subsys_start(void)
176{
177 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000178 return PJ_SUCCESS;
179}
180
181
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183 * Get maximum number of calls configured in pjsua.
184 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000185PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188}
189
190
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191/*
192 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000194PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197}
198
199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200/*
201 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000202 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
204 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000209
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
213 if (!pjsua_var.calls[i].inv)
214 continue;
215 ids[c] = i;
216 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000217 }
218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000222
223 return PJ_SUCCESS;
224}
225
226
Benny Prijono5773cd62008-01-19 13:01:42 +0000227/* Allocate one call id */
228static pjsua_call_id alloc_call_id(void)
229{
230 pjsua_call_id cid;
231
232#if 1
233 /* New algorithm: round-robin */
234 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
235 pjsua_var.next_call_id < 0)
236 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000237 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000238 }
239
240 for (cid=pjsua_var.next_call_id;
241 cid<(int)pjsua_var.ua_cfg.max_calls;
242 ++cid)
243 {
244 if (pjsua_var.calls[cid].inv == NULL) {
245 ++pjsua_var.next_call_id;
246 return cid;
247 }
248 }
249
250 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
251 if (pjsua_var.calls[cid].inv == NULL) {
252 ++pjsua_var.next_call_id;
253 return cid;
254 }
255 }
256
257#else
258 /* Old algorithm */
259 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
260 if (pjsua_var.calls[cid].inv == NULL)
261 return cid;
262 }
263#endif
264
265 return PJSUA_INVALID_ID;
266}
267
Benny Prijonod8179652008-01-23 20:39:07 +0000268/* Get signaling secure level.
269 * Return:
270 * 0: if signaling is not secure
271 * 1: if TLS transport is used for immediate hop
272 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000273 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000274static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000275{
276 const pj_str_t tls = pj_str(";transport=tls");
277 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000278 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000279
280 if (pj_stristr(dst_uri, &sips))
281 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000282
283 if (!pj_list_empty(&acc->route_set)) {
284 pjsip_route_hdr *r = acc->route_set.next;
285 pjsip_uri *uri = r->name_addr.uri;
286 pjsip_sip_uri *sip_uri;
287
288 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
289 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
290 return 1;
291
292 } else {
293 if (pj_stristr(dst_uri, &tls))
294 return 1;
295 }
296
Benny Prijonod8179652008-01-23 20:39:07 +0000297 return 0;
298}
299
Benny Prijono224b4e22008-06-19 14:10:28 +0000300/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000301static int call_get_secure_level(pjsua_call *call)
302{
303 if (call->inv->dlg->secure)
304 return 2;
305
306 if (!pj_list_empty(&call->inv->dlg->route_set)) {
307 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
308 pjsip_uri *uri = r->name_addr.uri;
309 pjsip_sip_uri *sip_uri;
310
311 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
312 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
313 return 1;
314
315 } else {
316 pjsip_sip_uri *sip_uri;
317
318 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
319 return 2;
320 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
321 return 0;
322
323 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
324 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
325 return 1;
326 }
327
328 return 0;
329}
Benny Prijono224b4e22008-06-19 14:10:28 +0000330*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000331
332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333/*
334 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000335 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000336PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
337 const pj_str_t *dest_uri,
338 unsigned options,
339 void *user_data,
340 const pjsua_msg_data *msg_data,
341 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000342{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000343 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000344 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000346 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 pjsua_acc *acc;
348 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000349 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000350 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000351 pjsip_tx_data *tdata;
352 pj_status_t status;
353
Benny Prijono9fc735d2006-05-28 14:58:12 +0000354
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000355 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000356 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000357 PJ_EINVAL);
358
Benny Prijono320fa4d2006-12-07 10:09:16 +0000359 /* Check arguments */
360 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
361
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362 PJSUA_LOCK();
363
Benny Prijonof798e502009-03-09 13:08:16 +0000364 /* Create sound port if none is instantiated, to check if sound device
365 * can be used. But only do this with the conference bridge, as with
366 * audio switchboard (i.e. APS-Direct), we can only open the sound
367 * device once the correct format has been known
368 */
369 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
370 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000371 {
372 pj_status_t status;
373
374 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
375 if (status != PJ_SUCCESS) {
376 PJSUA_UNLOCK();
377 return status;
378 }
379 }
380
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000381 acc = &pjsua_var.acc[acc_id];
382 if (!acc->valid) {
383 pjsua_perror(THIS_FILE, "Unable to make call because account "
384 "is not valid", PJ_EINVALIDOP);
385 PJSUA_UNLOCK();
386 return PJ_EINVALIDOP;
387 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000388
Benny Prijonoa91a0032006-02-26 21:23:45 +0000389 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000390 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000391
Benny Prijono5773cd62008-01-19 13:01:42 +0000392 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000393 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000394 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000395 return PJ_ETOOMANY;
396 }
397
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000398 call = &pjsua_var.calls[call_id];
399
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000400 /* Associate session with account */
401 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000402 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000403
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000404 /* Create temporary pool */
405 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
406
Benny Prijono320fa4d2006-12-07 10:09:16 +0000407 /* Verify that destination URI is valid before calling
408 * pjsua_acc_create_uac_contact, or otherwise there
409 * a misleading "Invalid Contact URI" error will be printed
410 * when pjsua_acc_create_uac_contact() fails.
411 */
412 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 pjsip_uri *uri;
414 pj_str_t dup;
415
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000416 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
417 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000418
419 if (uri == NULL) {
420 pjsua_perror(THIS_FILE, "Unable to make call",
421 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000422 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000423 PJSUA_UNLOCK();
424 return PJSIP_EINVALIDREQURI;
425 }
426 }
427
Benny Prijono093d3022006-09-24 00:07:11 +0000428 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
429 (int)dest_uri->slen, dest_uri->ptr));
430
Benny Prijonoe21e7842006-04-09 16:46:05 +0000431 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000433
Benny Prijonoe21e7842006-04-09 16:46:05 +0000434 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000437 /* Create suitable Contact header unless a Contact header has been
438 * set in the account.
439 */
440 if (acc->contact.slen) {
441 contact = acc->contact;
442 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000443 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000444 acc_id, dest_uri);
445 if (status != PJ_SUCCESS) {
446 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
447 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000448 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000449 PJSUA_UNLOCK();
450 return status;
451 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000452 }
453
Benny Prijonoe21e7842006-04-09 16:46:05 +0000454 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000455 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000456 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000459 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000460 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 return status;
463 }
464
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000465 /* Increment the dialog's lock otherwise when invite session creation
466 * fails the dialog will be destroyed prematurely.
467 */
468 pjsip_dlg_inc_lock(dlg);
469
Benny Prijonodb844a42008-02-02 17:07:18 +0000470 /* Calculate call's secure level */
471 call->secure_level = get_secure_level(acc_id, dest_uri);
472
Benny Prijonoc97608e2007-03-23 16:34:20 +0000473 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000474 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000475 call->secure_level, dlg->pool,
476 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000477 if (status != PJ_SUCCESS) {
478 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
479 goto on_error;
480 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000481
Benny Prijono224b4e22008-06-19 14:10:28 +0000482 /* Create offer */
483 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000484 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000485 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000486 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000487 goto on_error;
488 }
489
490 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000491 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000492 if (acc->cfg.require_100rel)
493 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000494 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
495 options |= PJSIP_INV_SUPPORT_TIMER;
496 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
497 options |= PJSIP_INV_REQUIRE_TIMER;
498 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
499 options |= PJSIP_INV_ALWAYS_USE_TIMER;
500 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000501
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000502 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000503 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000504 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000505 goto on_error;
506 }
507
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000508 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000509 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
510 if (status != PJ_SUCCESS) {
511 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
512 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000513 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000514
515 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000517
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 dlg->mod_data[pjsua_var.mod.id] = call;
519 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000520
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 /* Attach user data */
522 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000523
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000524 /* If account is locked to specific transport, then lock dialog
525 * to this transport too.
526 */
527 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
528 pjsip_tpselector tp_sel;
529
530 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
531 pjsip_dlg_set_transport(dlg, &tp_sel);
532 }
533
Benny Prijono84126ab2006-02-09 09:30:09 +0000534 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 if (!pj_list_empty(&acc->route_set))
536 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000537
538
539 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000541 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000543 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000544
Benny Prijono48ab2b72007-11-08 09:24:30 +0000545 /* Set authentication preference */
546 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000547
548 /* Create initial INVITE: */
549
550 status = pjsip_inv_invite(inv, &tdata);
551 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000552 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
553 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000554 goto on_error;
555 }
556
557
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 /* Add additional headers etc */
559
560 pjsua_process_msg_data( tdata, msg_data);
561
Benny Prijono093d3022006-09-24 00:07:11 +0000562 /* Must increment call counter now */
563 ++pjsua_var.call_cnt;
564
Benny Prijono84126ab2006-02-09 09:30:09 +0000565 /* Send initial INVITE: */
566
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000567 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000568 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000569 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
570 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000572 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573 * session would have been cleared.
574 */
575 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000576 goto on_error;
577 }
578
Benny Prijono84126ab2006-02-09 09:30:09 +0000579 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 if (p_call_id)
582 *p_call_id = call_id;
583
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000584 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000585 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000587
588 return PJ_SUCCESS;
589
590
591on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000592 if (dlg) {
593 /* This may destroy the dialog */
594 pjsip_dlg_dec_lock(dlg);
595 }
596
Benny Prijono1c2bf462006-03-05 11:54:02 +0000597 if (inv != NULL) {
598 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000599 }
600
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 if (call_id != -1) {
602 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000603 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000604 }
605
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000606 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000607 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000608 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000609}
610
611
Benny Prijono91a6a172007-10-31 08:59:29 +0000612/* Get the NAT type information in remote's SDP */
613static void update_remote_nat_type(pjsua_call *call,
614 const pjmedia_sdp_session *sdp)
615{
616 const pjmedia_sdp_attr *xnat;
617
618 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
619 if (xnat) {
620 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
621 } else {
622 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
623 }
624
625 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
626 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
627}
628
629
Benny Prijonodc39fe82006-05-26 12:17:46 +0000630/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000631 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000634pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000635{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000636 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000637 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000638 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000639 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
640 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000641 pjsip_tx_data *response = NULL;
642 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000643 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 int acc_id;
645 pjsua_call *call;
646 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000647 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000648 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000649 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000650
Benny Prijono26ff9062006-02-21 23:47:00 +0000651 /* Don't want to handle anything but INVITE */
652 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
653 return PJ_FALSE;
654
655 /* Don't want to handle anything that's already associated with
656 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000657 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 if (dlg || tsx)
659 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000660
Benny Prijono384dab42009-10-14 01:58:04 +0000661 /* Don't want to accept the call if shutdown is in progress */
662 if (pjsua_var.thread_quit_flag) {
663 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
664 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
665 NULL, NULL);
666 return PJ_TRUE;
667 }
668
Benny Prijono148c9dd2006-09-19 13:37:53 +0000669 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000670
Benny Prijono26ff9062006-02-21 23:47:00 +0000671 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000672 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000673
Benny Prijono5773cd62008-01-19 13:01:42 +0000674 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000675 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 PJSIP_SC_BUSY_HERE, NULL,
677 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000678 PJ_LOG(2,(THIS_FILE,
679 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000680 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000681 return PJ_TRUE;
682 }
683
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000684 /* Clear call descriptor */
685 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000686
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 call = &pjsua_var.calls[call_id];
688
689 /* Mark call start time. */
690 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000691
Benny Prijono053f5222006-11-11 16:16:04 +0000692 /* Check INVITE request for Replaces header. If Replaces header is
693 * present, the function will make sure that we can handle the request.
694 */
695 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
696 &response);
697 if (status != PJ_SUCCESS) {
698 /*
699 * Something wrong with the Replaces header.
700 */
701 if (response) {
702 pjsip_response_addr res_addr;
703
704 pjsip_get_response_addr(response->pool, rdata, &res_addr);
705 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
706 NULL, NULL);
707
708 } else {
709
710 /* Respond with 500 (Internal Server Error) */
711 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
712 NULL, NULL);
713 }
714
715 PJSUA_UNLOCK();
716 return PJ_TRUE;
717 }
718
719 /* If this INVITE request contains Replaces header, notify application
720 * about the request so that application can do subsequent checking
721 * if it wants to.
722 */
723 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
724 pjsua_call *replaced_call;
725 int st_code = 200;
726 pj_str_t st_text = { "OK", 2 };
727
728 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000729 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000730
731 /* Notify application */
732 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
733 rdata, &st_code, &st_text);
734
735 /* Must specify final response */
736 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
737
738 /* Check if application rejects this request. */
739 if (st_code >= 300) {
740
741 if (st_text.slen == 2)
742 st_text = *pjsip_get_status_text(st_code);
743
744 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
745 st_code, &st_text, NULL, NULL, NULL);
746 PJSUA_UNLOCK();
747 return PJ_TRUE;
748 }
749 }
750
Benny Prijonod8179652008-01-23 20:39:07 +0000751 /*
752 * Get which account is most likely to be associated with this incoming
753 * call. We need the account to find which contact URI to put for
754 * the call.
755 */
756 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000757 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000758
Benny Prijonodb844a42008-02-02 17:07:18 +0000759 /* Get call's secure level */
760 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
761 call->secure_level = 2;
762 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
763 call->secure_level = 1;
764 else
765 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000766
Benny Prijonod8179652008-01-23 20:39:07 +0000767 /* Parse SDP from incoming request */
768 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000769 pjsip_rdata_sdp_info *sdp_info;
770
771 sdp_info = pjsip_rdata_get_sdp_info(rdata);
772 offer = sdp_info->sdp;
773
774 status = sdp_info->sdp_err;
775 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
776 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000777
Benny Prijonod8179652008-01-23 20:39:07 +0000778 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000779 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000780 pjsip_hdr hdr_list;
781 pjsip_warning_hdr *w;
782
783 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000784 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000785
786 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
787 pjsip_endpt_name(pjsua_var.endpt),
788 status);
789 pj_list_init(&hdr_list);
790 pj_list_push_back(&hdr_list, w);
791
Benny Prijono224b4e22008-06-19 14:10:28 +0000792 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000793 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000794 PJSUA_UNLOCK();
795 return PJ_TRUE;
796 }
Benny Prijono617b8602008-04-07 10:10:31 +0000797
798 /* Do quick checks on SDP before passing it to transports. More elabore
799 * checks will be done in pjsip_inv_verify_request2() below.
800 */
801 if (offer->media_count==0) {
802 const pj_str_t reason = pj_str("Missing media in SDP");
803 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
804 NULL, NULL, NULL);
805 PJSUA_UNLOCK();
806 return PJ_TRUE;
807 }
808
Benny Prijonod8179652008-01-23 20:39:07 +0000809 } else {
810 offer = NULL;
811 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000812
Benny Prijono224b4e22008-06-19 14:10:28 +0000813 /* Init media channel */
814 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
815 call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000816 rdata->tp_info.pool,
817 offer,
Benny Prijono224b4e22008-06-19 14:10:28 +0000818 &sip_err_code);
819 if (status != PJ_SUCCESS) {
820 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
821 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
822 sip_err_code, NULL, NULL, NULL, NULL);
823 PJSUA_UNLOCK();
824 return PJ_TRUE;
825 }
826
827 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000828 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000829 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000830 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000831 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000832 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
833 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000834 PJSUA_UNLOCK();
835 return PJ_TRUE;
836 }
837
Benny Prijono224b4e22008-06-19 14:10:28 +0000838
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000839 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000840 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000841 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000842 if (pjsua_var.acc[acc_id].cfg.require_100rel)
843 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000844 if (pjsua_var.media_cfg.enable_ice)
845 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000846 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
847 options |= PJSIP_INV_REQUIRE_TIMER;
848 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
849 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000850
Benny Prijonod8179652008-01-23 20:39:07 +0000851 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
852 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000853 if (status != PJ_SUCCESS) {
854
855 /*
856 * No we can't handle the incoming INVITE request.
857 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000858 if (response) {
859 pjsip_response_addr res_addr;
860
861 pjsip_get_response_addr(response->pool, rdata, &res_addr);
862 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
863 NULL, NULL);
864
865 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000866 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000867 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
868 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000869 }
870
Benny Prijonoc97608e2007-03-23 16:34:20 +0000871 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000872 PJSUA_UNLOCK();
873 return PJ_TRUE;
874 }
875
876
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000877 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000878 if (pjsua_var.acc[acc_id].contact.slen) {
879 contact = pjsua_var.acc[acc_id].contact;
880 } else {
881 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
882 acc_id, rdata);
883 if (status != PJ_SUCCESS) {
884 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
885 status);
886 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
887 NULL, NULL);
888 pjsua_media_channel_deinit(call->index);
889 PJSUA_UNLOCK();
890 return PJ_TRUE;
891 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000892 }
893
Benny Prijono26ff9062006-02-21 23:47:00 +0000894 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000895 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000896 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000898 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000899 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000900 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000901 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000902 return PJ_TRUE;
903 }
904
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000905 /* Set credentials */
906 if (pjsua_var.acc[acc_id].cred_cnt) {
907 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
908 pjsua_var.acc[acc_id].cred_cnt,
909 pjsua_var.acc[acc_id].cred);
910 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000911
Benny Prijono48ab2b72007-11-08 09:24:30 +0000912 /* Set preference */
913 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
914 &pjsua_var.acc[acc_id].cfg.auth_pref);
915
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000916 /* Disable Session Timers if not prefered and the incoming INVITE request
917 * did not require it.
918 */
919 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
920 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
921 {
922 options &= ~(PJSIP_INV_SUPPORT_TIMER);
923 }
924
Benny Prijono26ff9062006-02-21 23:47:00 +0000925 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000926 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000927 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000928 pjsip_hdr hdr_list;
929 pjsip_warning_hdr *w;
930
931 w = pjsip_warning_hdr_create_from_status(dlg->pool,
932 pjsip_endpt_name(pjsua_var.endpt),
933 status);
934 pj_list_init(&hdr_list);
935 pj_list_push_back(&hdr_list, w);
936
937 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
938
939 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000940 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000941 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000942 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000943 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000944 return PJ_TRUE;
945 }
946
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000947 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000948 status = pjsip_timer_init_session(inv,
949 &pjsua_var.acc[acc_id].cfg.timer_setting);
950 if (status != PJ_SUCCESS) {
951 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
952 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
953 NULL, &response);
954 if (status == PJ_SUCCESS && response)
955 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000956
Nanang Izzuddin65add622009-08-11 16:26:20 +0000957 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000958
Nanang Izzuddin65add622009-08-11 16:26:20 +0000959 PJSUA_UNLOCK();
960 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000961 }
962
Benny Prijonoea9fd392007-11-06 03:41:40 +0000963 /* Update NAT type of remote endpoint, only when there is SDP in
964 * incoming INVITE!
965 */
966 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
967 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
968 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000969 const pjmedia_sdp_session *remote_sdp;
970
971 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
972 update_remote_nat_type(call, remote_sdp);
973 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000974
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000975 /* If account is locked to specific transport, then lock dialog
976 * to this transport too.
977 */
978 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
979 pjsip_tpselector tp_sel;
980
981 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
982 pjsip_dlg_set_transport(dlg, &tp_sel);
983 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000984
Benny Prijono2285e7e2008-12-17 14:28:18 +0000985 /* Must answer with some response to initial INVITE. We'll do this before
986 * attaching the call to the invite session/dialog, so that the application
987 * will not get notification about this event (on another scenario, it is
988 * also possible that inv_send_msg() fails and causes the invite session to
989 * be disconnected. If we have the call attached at this time, this will
990 * cause the disconnection callback to be called before on_incoming_call()
991 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000992 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000993 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000994 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000995 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000996 if (response == NULL) {
997 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
998 status);
999 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1000 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1001 } else {
1002 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001003 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001004 PJ_FALSE);
1005 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001006 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001007 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001008 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001009
1010 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001011 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001012 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001013 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001014 PJSUA_UNLOCK();
1015 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001016 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001017 }
1018
Benny Prijono2285e7e2008-12-17 14:28:18 +00001019 /* Create and attach pjsua_var data to the dialog: */
1020 call->inv = inv;
1021
1022 dlg->mod_data[pjsua_var.mod.id] = call;
1023 inv->mod_data[pjsua_var.mod.id] = call;
1024
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001026
Benny Prijono105217f2006-03-06 16:25:59 +00001027
Benny Prijono053f5222006-11-11 16:16:04 +00001028 /* Check if this request should replace existing call */
1029 if (replaced_dlg) {
1030 pjsip_inv_session *replaced_inv;
1031 struct pjsua_call *replaced_call;
1032 pjsip_tx_data *tdata;
1033
1034 /* Get the invite session in the dialog */
1035 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1036
1037 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001038 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001039
1040 /* Notify application */
1041 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1042 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1043 call_id);
1044
1045 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1046 call_id));
1047
1048 /* Answer the new call with 200 response */
1049 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1050 if (status == PJ_SUCCESS)
1051 status = pjsip_inv_send_msg(inv, tdata);
1052
1053 if (status != PJ_SUCCESS)
1054 pjsua_perror(THIS_FILE, "Error answering session", status);
1055
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001056 /* Note that inv may be invalid if 200/OK has caused error in
1057 * starting the media.
1058 */
Benny Prijono053f5222006-11-11 16:16:04 +00001059
1060 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1061 replaced_call->index));
1062
1063 /* Disconnect replaced invite session */
1064 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1065 &tdata);
1066 if (status == PJ_SUCCESS && tdata)
1067 status = pjsip_inv_send_msg(replaced_inv, tdata);
1068
1069 if (status != PJ_SUCCESS)
1070 pjsua_perror(THIS_FILE, "Error terminating session", status);
1071
1072
1073 } else {
1074
Benny Prijonob5388cf2007-01-04 22:45:08 +00001075 /* Notify application if on_incoming_call() is overriden,
1076 * otherwise hangup the call with 480
1077 */
1078 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001079 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001080 } else {
1081 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1082 NULL, NULL);
1083 }
Benny Prijono053f5222006-11-11 16:16:04 +00001084 }
1085
Benny Prijono8b1889b2006-06-06 18:40:40 +00001086
Benny Prijono26ff9062006-02-21 23:47:00 +00001087 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001088 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001089 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001090}
1091
1092
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001093
1094/*
1095 * Check if the specified call has active INVITE session and the INVITE
1096 * session has not been disconnected.
1097 */
1098PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1099{
1100 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1101 PJ_EINVAL);
1102 return pjsua_var.calls[call_id].inv != NULL &&
1103 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1104}
1105
1106
1107/*
1108 * Check if call has an active media session.
1109 */
1110PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1111{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001112 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1114 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001115 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116}
1117
1118
Benny Prijono148c9dd2006-09-19 13:37:53 +00001119/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001120pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001121 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001122 pjsua_call **p_call,
1123 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001124{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001125 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001126 pjsua_call *call = NULL;
1127 pj_bool_t has_pjsua_lock = PJ_FALSE;
1128 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001129 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001130
Sauw Ming844c1c92010-09-07 05:12:02 +00001131 pj_gettimeofday(&time_start);
1132 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1133 pj_time_val_normalize(&timeout);
1134
1135 for (retry=0; ; ++retry) {
1136
1137 if (retry % 10 == 9) {
1138 pj_time_val dtime;
1139
1140 pj_gettimeofday(&dtime);
1141 PJ_TIME_VAL_SUB(dtime, time_start);
1142 if (!PJ_TIME_VAL_LT(dtime, timeout))
1143 break;
1144 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001145
1146 has_pjsua_lock = PJ_FALSE;
1147
1148 status = PJSUA_TRY_LOCK();
1149 if (status != PJ_SUCCESS) {
1150 pj_thread_sleep(retry/10);
1151 continue;
1152 }
1153
1154 has_pjsua_lock = PJ_TRUE;
1155 call = &pjsua_var.calls[call_id];
1156
1157 if (call->inv == NULL) {
1158 PJSUA_UNLOCK();
1159 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1160 return PJSIP_ESESSIONTERMINATED;
1161 }
1162
1163 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1164 if (status != PJ_SUCCESS) {
1165 PJSUA_UNLOCK();
1166 pj_thread_sleep(retry/10);
1167 continue;
1168 }
1169
1170 PJSUA_UNLOCK();
1171
1172 break;
1173 }
1174
1175 if (status != PJ_SUCCESS) {
1176 if (has_pjsua_lock == PJ_FALSE)
1177 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1178 "(possibly system has deadlocked) in %s",
1179 title));
1180 else
1181 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1182 "(possibly system has deadlocked) in %s",
1183 title));
1184 return PJ_ETIMEDOUT;
1185 }
1186
1187 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001188 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001189
1190 return PJ_SUCCESS;
1191}
1192
1193
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001194/*
1195 * Get the conference port identification associated with the call.
1196 */
1197PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1198{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001199 pjsua_call *call;
1200 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001201 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001202 pj_status_t status;
1203
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001204 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1205 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001206
Benny Prijonodc752ca2006-09-22 16:55:42 +00001207 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001208 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001209 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001210
Benny Prijono0bc99a92011-03-17 04:34:43 +00001211 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001212
Benny Prijonodc752ca2006-09-22 16:55:42 +00001213 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214
1215 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001216}
1217
1218
Benny Prijono148c9dd2006-09-19 13:37:53 +00001219
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001220/*
1221 * Obtain detail information about the specified call.
1222 */
1223PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1224 pjsua_call_info *info)
1225{
1226 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001227 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001228 unsigned mi;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001229 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230
1231 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1232 PJ_EINVAL);
1233
Benny Prijonoac623b32006-07-03 15:19:31 +00001234 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001235
Benny Prijonodc752ca2006-09-22 16:55:42 +00001236 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001237 if (status != PJ_SUCCESS) {
1238 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239 }
1240
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 /* id and role */
1242 info->id = call_id;
1243 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001244 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245
1246 /* local info */
1247 info->local_info.ptr = info->buf_.local_info;
1248 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1249 sizeof(info->buf_.local_info));
1250
1251 /* local contact */
1252 info->local_contact.ptr = info->buf_.local_contact;
1253 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1254 call->inv->dlg->local.contact->uri,
1255 info->local_contact.ptr,
1256 sizeof(info->buf_.local_contact));
1257
1258 /* remote info */
1259 info->remote_info.ptr = info->buf_.remote_info;
1260 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1261 sizeof(info->buf_.remote_info));
1262
1263 /* remote contact */
1264 if (call->inv->dlg->remote.contact) {
1265 int len;
1266 info->remote_contact.ptr = info->buf_.remote_contact;
1267 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1268 call->inv->dlg->remote.contact->uri,
1269 info->remote_contact.ptr,
1270 sizeof(info->buf_.remote_contact));
1271 if (len < 0) len = 0;
1272 info->remote_contact.slen = len;
1273 } else {
1274 info->remote_contact.slen = 0;
1275 }
1276
1277 /* call id */
1278 info->call_id.ptr = info->buf_.call_id;
1279 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1280 sizeof(info->buf_.call_id));
1281
1282 /* state, state_text */
1283 info->state = call->inv->state;
1284 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1285
1286 /* If call is disconnected, set the last_status from the cause code */
1287 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1288 /* last_status, last_status_text */
1289 info->last_status = call->inv->cause;
1290
1291 info->last_status_text.ptr = info->buf_.last_status_text;
1292 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1293 sizeof(info->buf_.last_status_text));
1294 } else {
1295 /* last_status, last_status_text */
1296 info->last_status = call->last_code;
1297
1298 info->last_status_text.ptr = info->buf_.last_status_text;
1299 pj_strncpy(&info->last_status_text, &call->last_text,
1300 sizeof(info->buf_.last_status_text));
1301 }
1302
Benny Prijono0bc99a92011-03-17 04:34:43 +00001303 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001304 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001305 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001306 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001307 {
1308 pjsua_call_media *call_med = &call->media[mi];
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001309
1310 info->media[info->media_cnt].index = mi;
1311 info->media[info->media_cnt].status = call_med->state;
1312 info->media[info->media_cnt].dir = call_med->dir;
1313 info->media[info->media_cnt].type = call_med->type;
1314
1315 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001316 info->media[info->media_cnt].stream.aud.conf_slot =
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001317 call_med->strm.a.conf_slot;
1318 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono9f468d12011-07-07 07:46:33 +00001319 PJ_TODO(vid_fill_in_call_info);
1320 info->media[info->media_cnt].stream.vid.win_in = PJSUA_INVALID_ID;
1321 info->media[info->media_cnt].stream.vid.cap_dev =
1322 PJMEDIA_VID_INVALID_DEV;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001323 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001324 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001325 }
1326 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001327 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001329 if (call->audio_idx != -1) {
1330 info->media_status = call->media[call->audio_idx].state;
1331 info->media_dir = call->media[call->audio_idx].dir;
1332 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001333 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001334
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335 /* calculate duration */
1336 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1337
1338 info->total_duration = call->dis_time;
1339 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1340
1341 if (call->conn_time.sec) {
1342 info->connect_duration = call->dis_time;
1343 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1344 }
1345
1346 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1347
1348 pj_gettimeofday(&info->total_duration);
1349 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1350
1351 pj_gettimeofday(&info->connect_duration);
1352 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1353
1354 } else {
1355 pj_gettimeofday(&info->total_duration);
1356 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1357 }
1358
Benny Prijonodc752ca2006-09-22 16:55:42 +00001359 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360
1361 return PJ_SUCCESS;
1362}
1363
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001364/*
1365 * Check if call remote peer support the specified capability.
1366 */
1367PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1368 pjsua_call_id call_id,
1369 int htype,
1370 const pj_str_t *hname,
1371 const pj_str_t *token)
1372{
1373 pjsua_call *call;
1374 pjsip_dialog *dlg;
1375 pj_status_t status;
1376 pjsip_dialog_cap_status cap_status;
1377
1378 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1379 if (status != PJ_SUCCESS)
1380 return PJSIP_DIALOG_CAP_UNKNOWN;
1381
1382 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1383
1384 pjsip_dlg_dec_lock(dlg);
1385
1386 return cap_status;
1387}
1388
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389
1390/*
1391 * Attach application specific data to the call.
1392 */
1393PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1394 void *user_data)
1395{
1396 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1397 PJ_EINVAL);
1398 pjsua_var.calls[call_id].user_data = user_data;
1399
1400 return PJ_SUCCESS;
1401}
1402
1403
1404/*
1405 * Get user data attached to the call.
1406 */
1407PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1408{
1409 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1410 NULL);
1411 return pjsua_var.calls[call_id].user_data;
1412}
1413
1414
1415/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001416 * Get remote's NAT type.
1417 */
1418PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1419 pj_stun_nat_type *p_type)
1420{
1421 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1422 PJ_EINVAL);
1423 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1424
1425 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1426 return PJ_SUCCESS;
1427}
1428
1429
1430/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 * Send response to incoming INVITE request.
1432 */
1433PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1434 unsigned code,
1435 const pj_str_t *reason,
1436 const pjsua_msg_data *msg_data)
1437{
1438 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001439 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440 pjsip_tx_data *tdata;
1441 pj_status_t status;
1442
1443 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1444 PJ_EINVAL);
1445
Benny Prijonodc752ca2006-09-22 16:55:42 +00001446 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001447 if (status != PJ_SUCCESS)
1448 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001449
Benny Prijono2e507c22006-06-23 15:04:11 +00001450 if (call->res_time.sec == 0)
1451 pj_gettimeofday(&call->res_time);
1452
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001453 if (reason && reason->slen == 0)
1454 reason = NULL;
1455
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456 /* Create response message */
1457 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1458 if (status != PJ_SUCCESS) {
1459 pjsua_perror(THIS_FILE, "Error creating response",
1460 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001461 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001462 return status;
1463 }
1464
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001465 /* Call might have been disconnected if application is answering with
1466 * 200/OK and the media failed to start.
1467 */
1468 if (call->inv == NULL) {
1469 pjsip_dlg_dec_lock(dlg);
1470 return PJSIP_ESESSIONTERMINATED;
1471 }
1472
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 /* Add additional headers etc */
1474 pjsua_process_msg_data( tdata, msg_data);
1475
1476 /* Send the message */
1477 status = pjsip_inv_send_msg(call->inv, tdata);
1478 if (status != PJ_SUCCESS)
1479 pjsua_perror(THIS_FILE, "Error sending response",
1480 status);
1481
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483
1484 return status;
1485}
1486
1487
1488/*
1489 * Hangup call by using method that is appropriate according to the
1490 * call state.
1491 */
1492PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1493 unsigned code,
1494 const pj_str_t *reason,
1495 const pjsua_msg_data *msg_data)
1496{
1497 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001498 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499 pj_status_t status;
1500 pjsip_tx_data *tdata;
1501
1502
Benny Prijono148c9dd2006-09-19 13:37:53 +00001503 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1504 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1505 call_id));
1506 }
1507
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001508 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1509 PJ_EINVAL);
1510
Benny Prijonodc752ca2006-09-22 16:55:42 +00001511 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001512 if (status != PJ_SUCCESS)
1513 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001514
1515 if (code==0) {
1516 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1517 code = PJSIP_SC_OK;
1518 else if (call->inv->role == PJSIP_ROLE_UAS)
1519 code = PJSIP_SC_DECLINE;
1520 else
1521 code = PJSIP_SC_REQUEST_TERMINATED;
1522 }
1523
1524 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1525 if (status != PJ_SUCCESS) {
1526 pjsua_perror(THIS_FILE,
1527 "Failed to create end session message",
1528 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001529 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001530 return status;
1531 }
1532
1533 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1534 * as p_tdata when INVITE transaction has not been answered
1535 * with any provisional responses.
1536 */
1537 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001538 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539 return PJ_SUCCESS;
1540 }
1541
1542 /* Add additional headers etc */
1543 pjsua_process_msg_data( tdata, msg_data);
1544
1545 /* Send the message */
1546 status = pjsip_inv_send_msg(call->inv, tdata);
1547 if (status != PJ_SUCCESS) {
1548 pjsua_perror(THIS_FILE,
1549 "Failed to send end session message",
1550 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001551 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552 return status;
1553 }
1554
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001555 /* Stop lock codec timer, if it is active */
1556 if (call->lock_codec.reinv_timer.id) {
1557 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1558 &call->lock_codec.reinv_timer);
1559 call->lock_codec.reinv_timer.id = PJ_FALSE;
1560 }
1561
Benny Prijonodc752ca2006-09-22 16:55:42 +00001562 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001563
1564 return PJ_SUCCESS;
1565}
1566
1567
1568/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001569 * Accept or reject redirection.
1570 */
1571PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1572 pjsip_redirect_op cmd)
1573{
1574 pjsua_call *call;
1575 pjsip_dialog *dlg;
1576 pj_status_t status;
1577
1578 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1579 PJ_EINVAL);
1580
1581 status = acquire_call("pjsua_call_process_redirect()", call_id,
1582 &call, &dlg);
1583 if (status != PJ_SUCCESS)
1584 return status;
1585
1586 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1587
1588 pjsip_dlg_dec_lock(dlg);
1589
1590 return status;
1591}
1592
1593
1594/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595 * Put the specified call on hold.
1596 */
1597PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1598 const pjsua_msg_data *msg_data)
1599{
1600 pjmedia_sdp_session *sdp;
1601 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001602 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 pjsip_tx_data *tdata;
1604 pj_status_t status;
1605
1606 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1607 PJ_EINVAL);
1608
Benny Prijonodc752ca2006-09-22 16:55:42 +00001609 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001610 if (status != PJ_SUCCESS)
1611 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613
1614 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1615 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001616 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 return PJSIP_ESESSIONSTATE;
1618 }
1619
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001620 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001621 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001622 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001623 return status;
1624 }
1625
1626 /* Create re-INVITE with new offer */
1627 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1628 if (status != PJ_SUCCESS) {
1629 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001630 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 return status;
1632 }
1633
1634 /* Add additional headers etc */
1635 pjsua_process_msg_data( tdata, msg_data);
1636
1637 /* Send the request */
1638 status = pjsip_inv_send_msg( call->inv, tdata);
1639 if (status != PJ_SUCCESS) {
1640 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001641 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642 return status;
1643 }
1644
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001645 /* Set flag that local put the call on hold */
1646 call->local_hold = PJ_TRUE;
1647
Benny Prijonodc752ca2006-09-22 16:55:42 +00001648 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649
1650 return PJ_SUCCESS;
1651}
1652
1653
1654/*
1655 * Send re-INVITE (to release hold).
1656 */
1657PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1658 pj_bool_t unhold,
1659 const pjsua_msg_data *msg_data)
1660{
1661 pjmedia_sdp_session *sdp;
1662 pjsip_tx_data *tdata;
1663 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001664 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 pj_status_t status;
1666
1667
1668 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1669 PJ_EINVAL);
1670
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001672 if (status != PJ_SUCCESS)
1673 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001674
1675 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1676 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001677 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001678 return PJSIP_ESESSIONSTATE;
1679 }
1680
1681 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001682 if (call->local_hold && !unhold) {
1683 status = create_sdp_of_call_hold(call, &sdp);
1684 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001685 status = pjsua_media_channel_create_sdp(call->index,
1686 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001687 NULL, &sdp, NULL);
1688 call->local_hold = PJ_FALSE;
1689 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001690 if (status != PJ_SUCCESS) {
1691 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1692 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001693 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694 return status;
1695 }
1696
1697 /* Create re-INVITE with new offer */
1698 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1699 if (status != PJ_SUCCESS) {
1700 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001701 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702 return status;
1703 }
1704
1705 /* Add additional headers etc */
1706 pjsua_process_msg_data( tdata, msg_data);
1707
1708 /* Send the request */
1709 status = pjsip_inv_send_msg( call->inv, tdata);
1710 if (status != PJ_SUCCESS) {
1711 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001712 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001713 return status;
1714 }
1715
Benny Prijonodc752ca2006-09-22 16:55:42 +00001716 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001717
1718 return PJ_SUCCESS;
1719}
1720
1721
1722/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001723 * Send UPDATE request.
1724 */
1725PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1726 unsigned options,
1727 const pjsua_msg_data *msg_data)
1728{
1729 pjmedia_sdp_session *sdp;
1730 pjsip_tx_data *tdata;
1731 pjsua_call *call;
1732 pjsip_dialog *dlg;
1733 pj_status_t status;
1734
1735 PJ_UNUSED_ARG(options);
1736
1737 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1738 PJ_EINVAL);
1739
1740 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1741 if (status != PJ_SUCCESS)
1742 return status;
1743
Benny Prijonoc08682e2007-10-04 06:17:58 +00001744 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001745 status = pjsua_media_channel_create_sdp(call->index,
1746 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001747 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001748 if (status != PJ_SUCCESS) {
1749 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1750 status);
1751 pjsip_dlg_dec_lock(dlg);
1752 return status;
1753 }
1754
Benny Prijono224b4e22008-06-19 14:10:28 +00001755 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001756 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1757 if (status != PJ_SUCCESS) {
1758 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1759 pjsip_dlg_dec_lock(dlg);
1760 return status;
1761 }
1762
1763 /* Add additional headers etc */
1764 pjsua_process_msg_data( tdata, msg_data);
1765
1766 /* Send the request */
1767 status = pjsip_inv_send_msg( call->inv, tdata);
1768 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001769 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001770 pjsip_dlg_dec_lock(dlg);
1771 return status;
1772 }
1773
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001774 call->local_hold = PJ_FALSE;
1775
Benny Prijonoc08682e2007-10-04 06:17:58 +00001776 pjsip_dlg_dec_lock(dlg);
1777
1778 return PJ_SUCCESS;
1779}
1780
1781
1782/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001783 * Initiate call transfer to the specified address.
1784 */
1785PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1786 const pj_str_t *dest,
1787 const pjsua_msg_data *msg_data)
1788{
1789 pjsip_evsub *sub;
1790 pjsip_tx_data *tdata;
1791 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001792 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001793 pjsip_generic_string_hdr *gs_hdr;
1794 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001795 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001796 pj_status_t status;
1797
1798
1799 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1800 PJ_EINVAL);
1801
Benny Prijonodc752ca2006-09-22 16:55:42 +00001802 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001803 if (status != PJ_SUCCESS)
1804 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001805
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806
Benny Prijonod524e822006-09-22 12:48:18 +00001807 /* Create xfer client subscription. */
1808 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001809 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001810
1811 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001812 if (status != PJ_SUCCESS) {
1813 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001814 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001815 return status;
1816 }
1817
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001818 /* Associate this call with the client subscription */
1819 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1820
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001821 /*
1822 * Create REFER request.
1823 */
1824 status = pjsip_xfer_initiate(sub, dest, &tdata);
1825 if (status != PJ_SUCCESS) {
1826 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001827 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 return status;
1829 }
1830
Benny Prijono053f5222006-11-11 16:16:04 +00001831 /* Add Referred-By header */
1832 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1833 &dlg->local.info_str);
1834 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1835
1836
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001837 /* Add additional headers etc */
1838 pjsua_process_msg_data( tdata, msg_data);
1839
1840 /* Send. */
1841 status = pjsip_xfer_send_request(sub, tdata);
1842 if (status != PJ_SUCCESS) {
1843 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001844 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845 return status;
1846 }
1847
1848 /* For simplicity (that's what this program is intended to be!),
1849 * leave the original invite session as it is. More advanced application
1850 * may want to hold the INVITE, or terminate the invite, or whatever.
1851 */
1852
Benny Prijonodc752ca2006-09-22 16:55:42 +00001853 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001854
1855 return PJ_SUCCESS;
1856
1857}
1858
1859
1860/*
Benny Prijono053f5222006-11-11 16:16:04 +00001861 * Initiate attended call transfer to the specified address.
1862 */
1863PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1864 pjsua_call_id dest_call_id,
1865 unsigned options,
1866 const pjsua_msg_data *msg_data)
1867{
1868 pjsua_call *dest_call;
1869 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001870 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001871 pj_str_t str_dest;
1872 int len;
1873 pjsip_uri *uri;
1874 pj_status_t status;
1875
1876
1877 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1878 PJ_EINVAL);
1879 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1880 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1881 PJ_EINVAL);
1882
1883 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1884 &dest_call, &dest_dlg);
1885 if (status != PJ_SUCCESS)
1886 return status;
1887
1888 /*
1889 * Create REFER destination URI with Replaces field.
1890 */
1891
1892 /* Make sure we have sufficient buffer's length */
1893 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1894 dest_dlg->call_id->id.slen +
1895 dest_dlg->remote.info->tag.slen +
1896 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001897 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001898
1899 /* Print URI */
1900 str_dest_buf[0] = '<';
1901 str_dest.slen = 1;
1902
Benny Prijonoa1e69682007-05-11 15:14:34 +00001903 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001904 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1905 str_dest_buf+1, sizeof(str_dest_buf)-1);
1906 if (len < 0)
1907 return PJSIP_EURITOOLONG;
1908
1909 str_dest.slen += len;
1910
1911
1912 /* Build the URI */
1913 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1914 sizeof(str_dest_buf) - str_dest.slen,
1915 "?%s"
1916 "Replaces=%.*s"
1917 "%%3Bto-tag%%3D%.*s"
1918 "%%3Bfrom-tag%%3D%.*s>",
1919 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1920 "" : "Require=replaces&"),
1921 (int)dest_dlg->call_id->id.slen,
1922 dest_dlg->call_id->id.ptr,
1923 (int)dest_dlg->remote.info->tag.slen,
1924 dest_dlg->remote.info->tag.ptr,
1925 (int)dest_dlg->local.info->tag.slen,
1926 dest_dlg->local.info->tag.ptr);
1927
1928 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1929 PJSIP_EURITOOLONG);
1930
1931 str_dest.ptr = str_dest_buf;
1932 str_dest.slen += len;
1933
1934 pjsip_dlg_dec_lock(dest_dlg);
1935
1936 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1937}
1938
1939
1940/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001941 * Send DTMF digits to remote using RFC 2833 payload formats.
1942 */
1943PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1944 const pj_str_t *digits)
1945{
1946 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001947 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001948 pj_status_t status;
1949
1950 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1951 PJ_EINVAL);
1952
Benny Prijonodc752ca2006-09-22 16:55:42 +00001953 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001954 if (status != PJ_SUCCESS)
1955 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001956
Benny Prijono0bc99a92011-03-17 04:34:43 +00001957 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001958 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001959 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001960 return PJ_EINVALIDOP;
1961 }
1962
Benny Prijono0bc99a92011-03-17 04:34:43 +00001963 status = pjmedia_stream_dial_dtmf(
1964 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001965
Benny Prijonodc752ca2006-09-22 16:55:42 +00001966 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001967
1968 return status;
1969}
1970
1971
1972/**
1973 * Send instant messaging inside INVITE session.
1974 */
1975PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1976 const pj_str_t *mime_type,
1977 const pj_str_t *content,
1978 const pjsua_msg_data *msg_data,
1979 void *user_data)
1980{
1981 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001982 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 const pj_str_t mime_text_plain = pj_str("text/plain");
1984 pjsip_media_type ctype;
1985 pjsua_im_data *im_data;
1986 pjsip_tx_data *tdata;
1987 pj_status_t status;
1988
1989
1990 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1991 PJ_EINVAL);
1992
Benny Prijonodc752ca2006-09-22 16:55:42 +00001993 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001994 if (status != PJ_SUCCESS)
1995 return status;
1996
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001997 /* Set default media type if none is specified */
1998 if (mime_type == NULL) {
1999 mime_type = &mime_text_plain;
2000 }
2001
2002 /* Create request message. */
2003 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2004 -1, &tdata);
2005 if (status != PJ_SUCCESS) {
2006 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2007 goto on_return;
2008 }
2009
2010 /* Add accept header. */
2011 pjsip_msg_add_hdr( tdata->msg,
2012 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2013
2014 /* Parse MIME type */
2015 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2016
2017 /* Create "text/plain" message body. */
2018 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2019 &ctype.subtype, content);
2020 if (tdata->msg->body == NULL) {
2021 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2022 pjsip_tx_data_dec_ref(tdata);
2023 goto on_return;
2024 }
2025
2026 /* Add additional headers etc */
2027 pjsua_process_msg_data( tdata, msg_data);
2028
2029 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002030 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002031 im_data->acc_id = call->acc_id;
2032 im_data->call_id = call_id;
2033 im_data->to = call->inv->dlg->remote.info_str;
2034 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2035 im_data->user_data = user_data;
2036
2037
2038 /* Send the request. */
2039 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2040 pjsua_var.mod.id, im_data);
2041 if (status != PJ_SUCCESS) {
2042 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2043 goto on_return;
2044 }
2045
2046on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002047 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048 return status;
2049}
2050
2051
2052/*
2053 * Send IM typing indication inside INVITE session.
2054 */
2055PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2056 pj_bool_t is_typing,
2057 const pjsua_msg_data*msg_data)
2058{
2059 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002060 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002061 pjsip_tx_data *tdata;
2062 pj_status_t status;
2063
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002064 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2065 PJ_EINVAL);
2066
Benny Prijonodc752ca2006-09-22 16:55:42 +00002067 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002068 if (status != PJ_SUCCESS)
2069 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071 /* Create request message. */
2072 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2073 -1, &tdata);
2074 if (status != PJ_SUCCESS) {
2075 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2076 goto on_return;
2077 }
2078
2079 /* Create "application/im-iscomposing+xml" msg body. */
2080 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2081 NULL, NULL, -1);
2082
2083 /* Add additional headers etc */
2084 pjsua_process_msg_data( tdata, msg_data);
2085
2086 /* Send the request. */
2087 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2088 if (status != PJ_SUCCESS) {
2089 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2090 goto on_return;
2091 }
2092
2093on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002094 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002095 return status;
2096}
2097
2098
2099/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002100 * Send arbitrary request.
2101 */
2102PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2103 const pj_str_t *method_str,
2104 const pjsua_msg_data *msg_data)
2105{
2106 pjsua_call *call;
2107 pjsip_dialog *dlg;
2108 pjsip_method method;
2109 pjsip_tx_data *tdata;
2110 pj_status_t status;
2111
2112 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2113 PJ_EINVAL);
2114
2115 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2116 if (status != PJ_SUCCESS)
2117 return status;
2118
2119 /* Init method */
2120 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2121
2122 /* Create request message. */
2123 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2124 if (status != PJ_SUCCESS) {
2125 pjsua_perror(THIS_FILE, "Unable to create request", status);
2126 goto on_return;
2127 }
2128
2129 /* Add additional headers etc */
2130 pjsua_process_msg_data( tdata, msg_data);
2131
2132 /* Send the request. */
2133 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2134 if (status != PJ_SUCCESS) {
2135 pjsua_perror(THIS_FILE, "Unable to send request", status);
2136 goto on_return;
2137 }
2138
2139on_return:
2140 pjsip_dlg_dec_lock(dlg);
2141 return status;
2142}
2143
2144
2145/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002146 * Terminate all calls.
2147 */
2148PJ_DEF(void) pjsua_call_hangup_all(void)
2149{
2150 unsigned i;
2151
2152 PJSUA_LOCK();
2153
2154 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2155 if (pjsua_var.calls[i].inv)
2156 pjsua_call_hangup(i, 0, NULL, NULL);
2157 }
2158
2159 PJSUA_UNLOCK();
2160}
2161
2162
Benny Prijono1e601552010-10-20 05:31:08 +00002163/* Proto */
2164static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002165
Benny Prijono1e601552010-10-20 05:31:08 +00002166/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002167static void reinv_timer_cb(pj_timer_heap_t *th,
2168 pj_timer_entry *entry)
2169{
2170 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2171 pjsip_dialog *dlg;
2172 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002173 pj_status_t status;
2174
2175 PJ_UNUSED_ARG(th);
2176
2177 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2178
2179 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2180 if (status != PJ_SUCCESS)
2181 return;
2182
Benny Prijono1e601552010-10-20 05:31:08 +00002183 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002184
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002185 pjsip_dlg_dec_lock(dlg);
2186}
2187
2188
2189/* Check if the specified format can be skipped in counting codecs */
2190static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
2191 const pj_str_t *fmt)
2192{
Benny Prijono1e601552010-10-20 05:31:08 +00002193 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002194 unsigned pt;
2195
2196 pt = pj_strtoul(fmt);
2197
2198 /* Check for comfort noise */
2199 if (pt == PJMEDIA_RTP_PT_CN)
2200 return PJ_TRUE;
2201
2202 /* Dynamic PT, check the format name */
2203 if (pt >= 96) {
2204 pjmedia_sdp_attr *a;
2205 pjmedia_sdp_rtpmap rtpmap;
2206
2207 /* Get the format name */
2208 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2209 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
2210 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00002211 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002212 return PJ_TRUE;
2213 } else {
2214 /* Invalid SDP, should not reach here */
2215 pj_assert(!"SDP should have been validated!");
2216 return PJ_TRUE;
2217 }
2218 }
2219
2220 return PJ_FALSE;
2221}
2222
2223
Benny Prijono1e601552010-10-20 05:31:08 +00002224/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
2225 * out of several codecs presented by callee in his answer.
2226 */
2227static pj_status_t perform_lock_codec(pjsua_call *call)
2228{
2229 const pj_str_t STR_UPDATE = {"UPDATE", 6};
2230 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002231 unsigned i;
Benny Prijono1e601552010-10-20 05:31:08 +00002232 pj_bool_t rem_can_update;
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002233 pj_bool_t need_lock_codec = PJ_FALSE;
Benny Prijono1e601552010-10-20 05:31:08 +00002234 pjsip_tx_data *tdata;
2235 pj_status_t status;
2236
2237 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
2238 PJ_EINVALIDOP);
2239
2240 /* Verify if another SDP negotiation is in progress, e.g: session timer
2241 * or another re-INVITE.
2242 */
2243 if (call->inv==NULL || call->inv->neg==NULL ||
2244 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2245 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002246 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00002247 }
2248
Benny Prijono02493272010-11-17 09:15:04 +00002249 /* Don't do this if call is disconnecting! */
2250 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
2251 call->inv->cause >= 200)
2252 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00002253 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00002254 }
2255
Benny Prijono1e601552010-10-20 05:31:08 +00002256 /* Verify if another SDP negotiation has been completed by comparing
2257 * the SDP version.
2258 */
2259 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2260 if (status != PJ_SUCCESS)
2261 return status;
2262 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
2263 return PJMEDIA_SDP_EINVER;
2264
2265 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
2266
2267 /* Update the new offer so it contains only a codec. Note that formats
2268 * order in the offer should have been matched to the answer, so we can
2269 * just directly update the offer without looking-up the answer.
2270 */
2271 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00002272
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002273 for (i = 0; i < call->med_cnt; ++i) {
2274 unsigned j = 0, codec_cnt = 0;
2275 const pjmedia_sdp_media *ref_m;
2276 pjmedia_sdp_media *m;
2277 pjsua_call_media *call_med = &call->media[i];
2278
2279 /* Verify if media is deactivated */
2280 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2281 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2282 call_med->dir == PJMEDIA_DIR_NONE)
2283 {
Benny Prijono1e601552010-10-20 05:31:08 +00002284 continue;
2285 }
2286
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002287 ref_m = local_sdp->media[i];
2288 m = new_sdp->media[i];
2289
2290 /* Verify that media must be active. */
2291 pj_assert(ref_m->desc.port);
2292
2293 while (j < m->desc.fmt_count) {
2294 pjmedia_sdp_attr *a;
2295 pj_str_t *fmt = &m->desc.fmt[j];
2296
2297 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
2298 ++j;
2299 continue;
2300 }
2301
2302 /* Remove format */
2303 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
2304 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2305 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
2306 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
2307 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
2308 m->desc.fmt_count, j);
2309 --m->desc.fmt_count;
2310 }
2311
2312 need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
Benny Prijono1e601552010-10-20 05:31:08 +00002313 }
2314
Nanang Izzuddinec919002010-11-25 09:27:06 +00002315 /* Last check if SDP trully needs to be updated. It is possible that OA
2316 * negotiations have completed and SDP has changed but we didn't
2317 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00002318 */
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002319 if (!need_lock_codec)
Benny Prijono1e601552010-10-20 05:31:08 +00002320 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00002321
2322 /* Send UPDATE or re-INVITE */
2323 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
2324 PJSIP_H_ALLOW,
2325 NULL, &STR_UPDATE) ==
2326 PJSIP_DIALOG_CAP_SUPPORTED;
2327 if (rem_can_update) {
2328 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
2329 } else {
2330 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
2331 }
2332
2333 if (status==PJ_EINVALIDOP &&
2334 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
2335 {
2336 /* Ups, let's reschedule again */
2337 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
2338 pj_time_val_normalize(&delay);
2339 call->lock_codec.reinv_timer.id = PJ_TRUE;
2340 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2341 &call->lock_codec.reinv_timer, &delay);
2342 return status;
2343 } else if (status != PJ_SUCCESS) {
2344 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
2345 status);
2346 return status;
2347 }
2348
2349 /* Send the UPDATE/re-INVITE request */
2350 status = pjsip_inv_send_msg(call->inv, tdata);
2351 if (status != PJ_SUCCESS) {
2352 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
2353 status);
2354 return status;
2355 }
2356
2357 return status;
2358}
2359
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002360/* Check if remote answerer has given us more than one codecs. If so,
2361 * create another offer with one codec only to lock down the codec.
2362 */
2363static pj_status_t lock_codec(pjsua_call *call)
2364{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002365 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002366 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono1e601552010-10-20 05:31:08 +00002367 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00002368 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002369 unsigned i;
2370 pj_bool_t has_mult_fmt = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002371 pj_status_t status;
2372
Nanang Izzuddinec919002010-11-25 09:27:06 +00002373 /* Stop lock codec timer, if it is active */
2374 if (call->lock_codec.reinv_timer.id) {
2375 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2376 &call->lock_codec.reinv_timer);
2377 call->lock_codec.reinv_timer.id = PJ_FALSE;
2378 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002379
Nanang Izzuddinec919002010-11-25 09:27:06 +00002380 /* Skip this if we are the answerer */
2381 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
2382 return PJ_SUCCESS;
2383 }
2384
Nanang Izzuddinec919002010-11-25 09:27:06 +00002385 /* Delay this when the SDP negotiation done in call state EARLY and
2386 * remote does not support UPDATE method.
2387 */
2388 if (inv->state == PJSIP_INV_STATE_EARLY &&
2389 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
2390 PJSIP_DIALOG_CAP_SUPPORTED)
2391 {
2392 call->lock_codec.pending = PJ_TRUE;
2393 return PJ_SUCCESS;
2394 }
2395
2396 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002397 if (status != PJ_SUCCESS)
2398 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002399 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002400 if (status != PJ_SUCCESS)
2401 return status;
2402
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002403 /* Find multiple codecs answer in all media */
2404 for (i = 0; i < call->med_cnt; ++i) {
2405 pjsua_call_media *call_med = &call->media[i];
2406 const pjmedia_sdp_media *rem_m, *loc_m;
2407 unsigned codec_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002408
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002409 /* Skip this if the media is inactive or error */
2410 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
2411 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
2412 call_med->dir == PJMEDIA_DIR_NONE)
2413 {
2414 continue;
2415 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002416
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002417 /* Remote may answer with less media lines. */
2418 if (i >= remote_sdp->media_count)
2419 continue;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002420
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002421 rem_m = remote_sdp->media[i];
2422 loc_m = local_sdp->media[i];
2423
2424 /* Verify that media must be active. */
2425 pj_assert(loc_m->desc.port && rem_m->desc.port);
2426
2427 /* Count the formats in the answer. */
2428 if (rem_m->desc.fmt_count==1) {
2429 codec_cnt = 1;
2430 } else {
2431 unsigned j;
2432 for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
2433 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
2434 ++codec_cnt;
2435 }
2436 }
2437
2438 if (codec_cnt > 1) {
2439 has_mult_fmt = PJ_TRUE;
2440 break;
2441 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002442 }
Nanang Izzuddin5f326402011-05-20 08:47:14 +00002443
2444 /* Each media in the answer already contains single codec. */
2445 if (!has_mult_fmt) {
2446 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002447 return PJ_SUCCESS;
2448 }
2449
Nanang Izzuddinec919002010-11-25 09:27:06 +00002450 /* Remote keeps answering with multiple codecs, let's just give up
2451 * locking codec to avoid infinite retry loop.
2452 */
2453 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
2454 return PJ_SUCCESS;
2455
Benny Prijono1e601552010-10-20 05:31:08 +00002456 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002457 "updating media session to use only one codec.."));
2458
Benny Prijono1e601552010-10-20 05:31:08 +00002459 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002460
Benny Prijono1e601552010-10-20 05:31:08 +00002461 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
2462 * See: https://trac.pjsip.org/repos/ticket/1149
2463 */
2464 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
2465 (void*)(pj_size_t)call->index,
2466 &reinv_timer_cb);
2467 pjsip_endpt_schedule_timer(pjsua_var.endpt,
2468 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002469
2470 return PJ_SUCCESS;
2471}
2472
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002473/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002474 * This callback receives notification from invite session when the
2475 * session state has changed.
2476 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002477static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2478 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002479{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002480 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002481
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002482 PJSUA_LOCK();
2483
Benny Prijonoa1e69682007-05-11 15:14:34 +00002484 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002485
2486 if (!call) {
2487 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002488 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002489 }
2490
Benny Prijonoe21e7842006-04-09 16:46:05 +00002491
2492 /* Get call times */
2493 switch (inv->state) {
2494 case PJSIP_INV_STATE_EARLY:
2495 case PJSIP_INV_STATE_CONNECTING:
2496 if (call->res_time.sec == 0)
2497 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002498 call->last_code = (pjsip_status_code)
2499 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002500 pj_strncpy(&call->last_text,
2501 &e->body.tsx_state.tsx->status_text,
2502 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002503 break;
2504 case PJSIP_INV_STATE_CONFIRMED:
2505 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002506
Nanang Izzuddinec919002010-11-25 09:27:06 +00002507 /* See if lock codec was pended as media update was done in the
2508 * EARLY state and remote does not support UPDATE.
2509 */
2510 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002511 pj_status_t status;
2512 status = lock_codec(call);
2513 if (status != PJ_SUCCESS) {
2514 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00002515 }
2516 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002517 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002518 break;
2519 case PJSIP_INV_STATE_DISCONNECTED:
2520 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002521 if (call->res_time.sec == 0)
2522 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002523 if (e->type == PJSIP_EVENT_TSX_STATE &&
2524 e->body.tsx_state.tsx->status_code > call->last_code)
2525 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002526 call->last_code = (pjsip_status_code)
2527 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002528 pj_strncpy(&call->last_text,
2529 &e->body.tsx_state.tsx->status_text,
2530 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002531 } else {
2532 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2533 pj_strncpy(&call->last_text,
2534 pjsip_get_status_text(call->last_code),
2535 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002536 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002537
2538 /* Stop lock codec timer, if it is active */
2539 if (call->lock_codec.reinv_timer.id) {
2540 pjsip_endpt_cancel_timer(pjsua_var.endpt,
2541 &call->lock_codec.reinv_timer);
2542 call->lock_codec.reinv_timer.id = PJ_FALSE;
2543 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002544 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002545 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002546 call->last_code = (pjsip_status_code)
2547 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002548 pj_strncpy(&call->last_text,
2549 &e->body.tsx_state.tsx->status_text,
2550 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002551 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002552 }
2553
Benny Prijono26ff9062006-02-21 23:47:00 +00002554 /* If this is an outgoing INVITE that was created because of
2555 * REFER/transfer, send NOTIFY to transferer.
2556 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002557 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002558 int st_code = -1;
2559 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2560
2561
Benny Prijonoa91a0032006-02-26 21:23:45 +00002562 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002563 case PJSIP_INV_STATE_NULL:
2564 case PJSIP_INV_STATE_CALLING:
2565 /* Do nothing */
2566 break;
2567
2568 case PJSIP_INV_STATE_EARLY:
2569 case PJSIP_INV_STATE_CONNECTING:
2570 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002571 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2572 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2573 else
2574 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002575 break;
2576
Benny Prijono140beae2009-10-11 05:06:43 +00002577 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002578#if 0
2579/* We don't need this, as we've terminated the subscription in
2580 * CONNECTING state.
2581 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002582 /* When state is confirmed, send the final 200/OK and terminate
2583 * subscription.
2584 */
2585 st_code = e->body.tsx_state.tsx->status_code;
2586 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002587#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002588 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002589
2590 case PJSIP_INV_STATE_DISCONNECTED:
2591 st_code = e->body.tsx_state.tsx->status_code;
2592 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2593 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002594
Benny Prijono8b1889b2006-06-06 18:40:40 +00002595 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002596 /* Nothing to do. Just to keep gcc from complaining about
2597 * unused enums.
2598 */
2599 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002600 }
2601
2602 if (st_code != -1) {
2603 pjsip_tx_data *tdata;
2604 pj_status_t status;
2605
Benny Prijonoa91a0032006-02-26 21:23:45 +00002606 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002607 ev_state, st_code,
2608 NULL, &tdata);
2609 if (status != PJ_SUCCESS) {
2610 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2611 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002612 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002613 if (status != PJ_SUCCESS) {
2614 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2615 }
2616 }
2617 }
2618 }
2619
Benny Prijono84126ab2006-02-09 09:30:09 +00002620
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002621 if (pjsua_var.ua_cfg.cb.on_call_state)
2622 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002623
2624 /* call->inv may be NULL now */
2625
Benny Prijono84126ab2006-02-09 09:30:09 +00002626 /* Destroy media session when invite session is disconnected. */
2627 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002628
Benny Prijonoa91a0032006-02-26 21:23:45 +00002629 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002630
Benny Prijono275fd682006-03-22 11:59:11 +00002631 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002632 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002633
Benny Prijono105217f2006-03-06 16:25:59 +00002634 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002635 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002636 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002637
2638 /* Reset call */
2639 reset_call(call->index);
2640
Benny Prijono84126ab2006-02-09 09:30:09 +00002641 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002642
2643 PJSUA_UNLOCK();
2644}
2645
2646/*
2647 * This callback is called by invite session framework when UAC session
2648 * has forked.
2649 */
2650static void pjsua_call_on_forked( pjsip_inv_session *inv,
2651 pjsip_event *e)
2652{
2653 PJ_UNUSED_ARG(inv);
2654 PJ_UNUSED_ARG(e);
2655
2656 PJ_TODO(HANDLE_FORKED_DIALOG);
2657}
2658
2659
2660/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002661 * Callback from UA layer when forked dialog response is received.
2662 */
2663pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2664{
2665 if (dlg->uac_has_2xx &&
2666 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
2667 pjsip_rdata_get_tsx(res) == NULL &&
2668 res->msg_info.msg->line.status.code/100 == 2)
2669 {
2670 pjsip_dialog *forked_dlg;
2671 pjsip_tx_data *bye;
2672 pj_status_t status;
2673
2674 /* Create forked dialog */
2675 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
2676 if (status != PJ_SUCCESS)
2677 return NULL;
2678
2679 pjsip_dlg_inc_lock(forked_dlg);
2680
2681 /* Disconnect the call */
2682 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
2683 -1, &bye);
2684 if (status == PJ_SUCCESS) {
2685 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
2686 }
2687
2688 pjsip_dlg_dec_lock(forked_dlg);
2689
2690 if (status != PJ_SUCCESS) {
2691 return NULL;
2692 }
2693
2694 return forked_dlg;
2695
2696 } else {
2697 return dlg;
2698 }
2699}
2700
2701/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002702 * Disconnect call upon error.
2703 */
2704static void call_disconnect( pjsip_inv_session *inv,
2705 int code )
2706{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002707 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002708 pjsip_tx_data *tdata;
2709 pj_status_t status;
2710
Benny Prijono59b3aed2008-01-15 16:54:54 +00002711 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2712
Benny Prijonoa38ada02006-07-02 14:22:35 +00002713 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002714 if (status != PJ_SUCCESS)
2715 return;
2716
2717 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00002718#if DISABLED_FOR_TICKET_1185
2719 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00002720 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2721 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002722 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002723 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002724
Benny Prijono734fc2d2008-03-17 16:05:35 +00002725 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002726 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002727 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002728 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002729 if (status == PJ_SUCCESS) {
2730 pjsip_create_sdp_body(tdata->pool, local_sdp,
2731 &tdata->msg->body);
2732 }
2733 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00002734#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00002735
2736 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002737}
2738
2739/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002740 * Callback to be called when SDP offer/answer negotiation has just completed
2741 * in the session. This function will start/update media if negotiation
2742 * has succeeded.
2743 */
2744static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2745 pj_status_t status)
2746{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002747 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002748 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002749 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00002750 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002751
2752 PJSUA_LOCK();
2753
Benny Prijonoa1e69682007-05-11 15:14:34 +00002754 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002755
2756 if (status != PJ_SUCCESS) {
2757
2758 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2759
Benny Prijono2331d202008-06-26 15:46:52 +00002760 /* Do not deinitialize media since this may be a re-INVITE or
2761 * UPDATE (which in this case the media should not get affected
2762 * by the failed re-INVITE/UPDATE). The media will be shutdown
2763 * when call is disconnected anyway.
2764 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002765 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00002766 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002767
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002768 /* Disconnect call if we're not in the middle of initializing an
2769 * UAS dialog and if this is not a re-INVITE
2770 */
2771 if (inv->state != PJSIP_INV_STATE_NULL &&
2772 inv->state != PJSIP_INV_STATE_CONFIRMED)
2773 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002774 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002775 }
2776
2777 PJSUA_UNLOCK();
2778 return;
2779 }
2780
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002781
2782 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00002783 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002784 if (status != PJ_SUCCESS) {
2785 pjsua_perror(THIS_FILE,
2786 "Unable to retrieve currently active local SDP",
2787 status);
2788 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2789 PJSUA_UNLOCK();
2790 return;
2791 }
2792
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002793 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2794 if (status != PJ_SUCCESS) {
2795 pjsua_perror(THIS_FILE,
2796 "Unable to retrieve currently active remote SDP",
2797 status);
2798 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2799 PJSUA_UNLOCK();
2800 return;
2801 }
2802
Benny Prijono91a6a172007-10-31 08:59:29 +00002803 /* Update remote's NAT type */
2804 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2805 update_remote_nat_type(call, remote_sdp);
2806 }
2807
2808 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002809 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002810 if (status != PJ_SUCCESS) {
2811 pjsua_perror(THIS_FILE, "Unable to create media session",
2812 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002813 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00002814 /* No need to deinitialize; media will be shutdown when call
2815 * state is disconnected anyway.
2816 */
2817 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002818 PJSUA_UNLOCK();
2819 return;
2820 }
2821
Nanang Izzuddinec919002010-11-25 09:27:06 +00002822 /* Ticket #476: make sure only one codec is specified in the answer. */
2823 status = lock_codec(call);
2824 if (status != PJ_SUCCESS) {
2825 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002826 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002827
2828 /* Call application callback, if any */
2829 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2830 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2831
2832
2833 PJSUA_UNLOCK();
2834}
2835
2836
Benny Prijonodd63b992010-10-01 02:03:42 +00002837/* Modify SDP for call hold. */
2838static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
2839 pj_pool_t *pool,
2840 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002841{
Benny Prijono316f02a2011-04-07 07:53:25 +00002842 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002843
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002844 /* Call-hold is done by set the media direction to 'sendonly'
2845 * (PJMEDIA_DIR_ENCODING), except when current media direction is
2846 * 'inactive' (PJMEDIA_DIR_NONE).
2847 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
2848 */
Benny Prijonoe0860132009-06-05 10:14:20 +00002849 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00002850 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00002851 */
Benny Prijonodd63b992010-10-01 02:03:42 +00002852 /* https://trac.pjsip.org/repos/ticket/1142:
2853 * configuration to use c=0.0.0.0 for call hold.
2854 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002855
Benny Prijono316f02a2011-04-07 07:53:25 +00002856 for (mi=0; mi<sdp->media_count; ++mi) {
2857 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002858
Benny Prijono316f02a2011-04-07 07:53:25 +00002859 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
2860 pjmedia_sdp_conn *conn;
2861 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00002862
Benny Prijono316f02a2011-04-07 07:53:25 +00002863 /* Get SDP media connection line */
2864 conn = m->conn;
2865 if (!conn)
2866 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00002867
Benny Prijono316f02a2011-04-07 07:53:25 +00002868 /* Modify address */
2869 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00002870
Benny Prijono316f02a2011-04-07 07:53:25 +00002871 /* Remove existing directions attributes */
2872 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
2873 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
2874 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
2875 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00002876
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002877 /* Add inactive attribute */
2878 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002879 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00002880
2881
2882 } else {
2883 pjmedia_sdp_attr *attr;
2884
2885 /* Remove existing directions attributes */
2886 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
2887 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
2888 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
2889 pjmedia_sdp_media_remove_all_attr(m, "inactive");
2890
2891 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
2892 /* Add sendonly attribute */
2893 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
2894 pjmedia_sdp_media_add_attr(m, attr);
2895 } else {
2896 /* Add inactive attribute */
2897 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
2898 pjmedia_sdp_media_add_attr(m, attr);
2899 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002900 }
2901 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002902
Benny Prijonodd63b992010-10-01 02:03:42 +00002903 return PJ_SUCCESS;
2904}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002905
Benny Prijonodd63b992010-10-01 02:03:42 +00002906/* Create SDP for call hold. */
2907static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
2908 pjmedia_sdp_session **p_sdp)
2909{
2910 pj_status_t status;
2911 pj_pool_t *pool;
2912 pjmedia_sdp_session *sdp;
2913
2914 /* Use call's provisional pool */
2915 pool = call->inv->pool_prov;
2916
2917 /* Create new offer */
2918 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
2919 NULL);
2920 if (status != PJ_SUCCESS) {
2921 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2922 return status;
2923 }
2924
2925 status = modify_sdp_of_call_hold(call, pool, sdp);
2926 if (status != PJ_SUCCESS)
2927 return status;
2928
2929 *p_sdp = sdp;
2930
2931 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002932}
2933
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002934/*
2935 * Called when session received new offer.
2936 */
2937static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2938 const pjmedia_sdp_session *offer)
2939{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002940 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002941 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00002942 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002943 pj_status_t status;
2944
2945 PJSUA_LOCK();
2946
Benny Prijonoa1e69682007-05-11 15:14:34 +00002947 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002948
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002949 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002950 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2951 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002952
Benny Prijono40d62b62009-08-12 17:53:47 +00002953 status = pjsua_media_channel_create_sdp(call->index,
2954 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002955 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002956 if (status != PJ_SUCCESS) {
2957 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2958 PJSUA_UNLOCK();
2959 return;
2960 }
2961
Nanang Izzuddine03faad2011-04-07 14:51:28 +00002962 /* Validate media count in the generated answer */
2963 pj_assert(answer->media_count == offer->media_count);
2964
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002965 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00002966 for (i = 0; i < answer->media_count; ++i) {
2967 pjmedia_sdp_conn *conn;
2968
2969 conn = offer->media[i]->conn;
2970 if (!conn)
2971 conn = offer->conn;
2972
2973 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2974 pj_strcmp2(&conn->addr, "0")==0)
2975 {
2976 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
2977
2978 /* Modify answer address */
2979 if (a_conn) {
2980 a_conn->addr = pj_str("0.0.0.0");
2981 } else if (answer->conn == NULL ||
2982 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
2983 {
2984 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
2985 pjmedia_sdp_conn);
2986 a_conn->net_type = pj_str("IN");
2987 a_conn->addr_type = pj_str("IP4");
2988 a_conn->addr = pj_str("0.0.0.0");
2989 answer->media[i]->conn = a_conn;
2990 }
2991 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002992 }
2993
2994 /* Check if call is on-hold */
2995 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00002996 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00002997 }
2998
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002999 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3000 if (status != PJ_SUCCESS) {
3001 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3002 PJSUA_UNLOCK();
3003 return;
3004 }
3005
3006 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003007}
3008
3009
3010/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003011 * Called to generate new offer.
3012 */
3013static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3014 pjmedia_sdp_session **offer)
3015{
3016 pjsua_call *call;
3017 pj_status_t status;
3018
3019 PJSUA_LOCK();
3020
3021 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3022
3023 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003024 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003025 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003026 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003027 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003028 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003029 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003030 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3031 call->index));
3032
Benny Prijono40d62b62009-08-12 17:53:47 +00003033 status = pjsua_media_channel_create_sdp(call->index,
3034 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003035 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003036 }
3037
3038 if (status != PJ_SUCCESS) {
3039 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3040 PJSUA_UNLOCK();
3041 return;
3042 }
3043
Benny Prijono77998ce2007-06-20 10:03:46 +00003044 PJSUA_UNLOCK();
3045}
3046
3047
3048/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003049 * Callback called by event framework when the xfer subscription state
3050 * has changed.
3051 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003052static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3053{
3054
3055 PJ_UNUSED_ARG(event);
3056
3057 /*
3058 * When subscription is accepted (got 200/OK to REFER), check if
3059 * subscription suppressed.
3060 */
3061 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3062
3063 pjsip_rx_data *rdata;
3064 pjsip_generic_string_hdr *refer_sub;
3065 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3066 pjsua_call *call;
3067
Benny Prijonoa1e69682007-05-11 15:14:34 +00003068 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003069
3070 /* Must be receipt of response message */
3071 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3072 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3073 rdata = event->body.tsx_state.src.rdata;
3074
3075 /* Find Refer-Sub header */
3076 refer_sub = (pjsip_generic_string_hdr*)
3077 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3078 &REFER_SUB, NULL);
3079
3080 /* Check if subscription is suppressed */
3081 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3082 /* Since no subscription is desired, assume that call has been
3083 * transfered successfully.
3084 */
3085 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3086 const pj_str_t ACCEPTED = { "Accepted", 8 };
3087 pj_bool_t cont = PJ_FALSE;
3088 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3089 200,
3090 &ACCEPTED,
3091 PJ_TRUE,
3092 &cont);
3093 }
3094
3095 /* Yes, subscription is suppressed.
3096 * Terminate our subscription now.
3097 */
3098 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3099 "event subcription..."));
3100 pjsip_evsub_terminate(sub, PJ_TRUE);
3101
3102 } else {
3103 /* Notify application about call transfer progress.
3104 * Initially notify with 100/Accepted status.
3105 */
3106 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3107 const pj_str_t ACCEPTED = { "Accepted", 8 };
3108 pj_bool_t cont = PJ_FALSE;
3109 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3110 100,
3111 &ACCEPTED,
3112 PJ_FALSE,
3113 &cont);
3114 }
3115 }
3116 }
3117 /*
3118 * On incoming NOTIFY, notify application about call transfer progress.
3119 */
3120 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3121 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3122 {
3123 pjsua_call *call;
3124 pjsip_msg *msg;
3125 pjsip_msg_body *body;
3126 pjsip_status_line status_line;
3127 pj_bool_t is_last;
3128 pj_bool_t cont;
3129 pj_status_t status;
3130
Benny Prijonoa1e69682007-05-11 15:14:34 +00003131 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003132
3133 /* When subscription is terminated, clear the xfer_sub member of
3134 * the inv_data.
3135 */
3136 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3137 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3138 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3139
3140 }
3141
3142 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3143 /* Application is not interested with call progress status */
3144 return;
3145 }
3146
3147 /* This better be a NOTIFY request */
3148 if (event->type == PJSIP_EVENT_TSX_STATE &&
3149 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3150 {
3151 pjsip_rx_data *rdata;
3152
3153 rdata = event->body.tsx_state.src.rdata;
3154
3155 /* Check if there's body */
3156 msg = rdata->msg_info.msg;
3157 body = msg->body;
3158 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003159 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003160 "Warning: received NOTIFY without message body"));
3161 return;
3162 }
3163
3164 /* Check for appropriate content */
3165 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3166 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3167 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003168 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003169 "Warning: received NOTIFY with non message/sipfrag "
3170 "content"));
3171 return;
3172 }
3173
3174 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003175 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003176 &status_line);
3177 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003178 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003179 "Warning: received NOTIFY with invalid "
3180 "message/sipfrag content"));
3181 return;
3182 }
3183
3184 } else {
3185 status_line.code = 500;
3186 status_line.reason = *pjsip_get_status_text(500);
3187 }
3188
3189 /* Notify application */
3190 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3191 cont = !is_last;
3192 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3193 status_line.code,
3194 &status_line.reason,
3195 is_last, &cont);
3196
3197 if (!cont) {
3198 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3199 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003200
3201 /* If the call transfer has completed but the subscription is
3202 * not terminated, terminate it now.
3203 */
3204 if (status_line.code/100 == 2 && !is_last) {
3205 pjsip_tx_data *tdata;
3206
3207 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3208 0, &tdata);
3209 if (status == PJ_SUCCESS)
3210 status = pjsip_evsub_send_request(sub, tdata);
3211 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003212 }
3213}
3214
3215
3216/*
3217 * Callback called by event framework when the xfer subscription state
3218 * has changed.
3219 */
3220static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003221{
3222
3223 PJ_UNUSED_ARG(event);
3224
3225 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003226 * When subscription is terminated, clear the xfer_sub member of
3227 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003228 */
3229 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003230 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003231
Benny Prijonoa1e69682007-05-11 15:14:34 +00003232 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003233 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003234 return;
3235
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003236 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003237 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003238
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003239 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003240 }
3241}
3242
3243
3244/*
3245 * Follow transfer (REFER) request.
3246 */
3247static void on_call_transfered( pjsip_inv_session *inv,
3248 pjsip_rx_data *rdata )
3249{
3250 pj_status_t status;
3251 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003252 pjsua_call *existing_call;
3253 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003254 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003255 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003256 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003257 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003258 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003259 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003260 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003261 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003262 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003263 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003264 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003265 pjsip_evsub *sub;
3266
Benny Prijonoa1e69682007-05-11 15:14:34 +00003267 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003268
Benny Prijono26ff9062006-02-21 23:47:00 +00003269 /* Find the Refer-To header */
3270 refer_to = (pjsip_generic_string_hdr*)
3271 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3272
3273 if (refer_to == NULL) {
3274 /* Invalid Request.
3275 * No Refer-To header!
3276 */
3277 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003278 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003279 return;
3280 }
3281
Benny Prijonoc8141a82006-08-20 09:12:19 +00003282 /* Find optional Refer-Sub header */
3283 refer_sub = (pjsip_generic_string_hdr*)
3284 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3285
3286 if (refer_sub) {
3287 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3288 no_refer_sub = PJ_TRUE;
3289 }
3290
Benny Prijono053f5222006-11-11 16:16:04 +00003291 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3292 * request.
3293 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003294 ref_by_hdr = (pjsip_hdr*)
3295 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003296 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003297
Benny Prijono9fc735d2006-05-28 14:58:12 +00003298 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003299 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003300 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3301 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3302 &refer_to->hvalue,
3303 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003304
3305 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003306 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003307 if (code >= 300) {
3308 /* Application rejects call transfer request */
3309 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3310 return;
3311 }
3312
Benny Prijono26ff9062006-02-21 23:47:00 +00003313 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3314 (int)inv->dlg->remote.info_str.slen,
3315 inv->dlg->remote.info_str.ptr,
3316 (int)refer_to->hvalue.slen,
3317 refer_to->hvalue.ptr));
3318
Benny Prijonoc8141a82006-08-20 09:12:19 +00003319 if (no_refer_sub) {
3320 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003321 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003322 */
3323 pjsip_tx_data *tdata;
3324 const pj_str_t str_false = { "false", 5};
3325 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003326
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003327 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3328 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003329 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003330 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003331 status);
3332 return;
3333 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003334
Benny Prijonoc8141a82006-08-20 09:12:19 +00003335 /* Add Refer-Sub header */
3336 hdr = (pjsip_hdr*)
3337 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3338 &str_false);
3339 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003340
Benny Prijono26ff9062006-02-21 23:47:00 +00003341
Benny Prijonoc8141a82006-08-20 09:12:19 +00003342 /* Send answer */
3343 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3344 tdata);
3345 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003346 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003347 status);
3348 return;
3349 }
3350
3351 /* Don't have subscription */
3352 sub = NULL;
3353
3354 } else {
3355 struct pjsip_evsub_user xfer_cb;
3356 pjsip_hdr hdr_list;
3357
3358 /* Init callback */
3359 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003360 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003361
3362 /* Init additional header list to be sent with REFER response */
3363 pj_list_init(&hdr_list);
3364
3365 /* Create transferee event subscription */
3366 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3367 if (status != PJ_SUCCESS) {
3368 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3369 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3370 return;
3371 }
3372
3373 /* If there's Refer-Sub header and the value is "true", send back
3374 * Refer-Sub in the response with value "true" too.
3375 */
3376 if (refer_sub) {
3377 const pj_str_t str_true = { "true", 4 };
3378 pjsip_hdr *hdr;
3379
3380 hdr = (pjsip_hdr*)
3381 pjsip_generic_string_hdr_create(inv->dlg->pool,
3382 &str_refer_sub,
3383 &str_true);
3384 pj_list_push_back(&hdr_list, hdr);
3385
3386 }
3387
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003388 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003389 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3390
3391 /* Create initial NOTIFY request */
3392 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3393 100, NULL, &tdata);
3394 if (status != PJ_SUCCESS) {
3395 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3396 status);
3397 return;
3398 }
3399
3400 /* Send initial NOTIFY request */
3401 status = pjsip_xfer_send_request( sub, tdata);
3402 if (status != PJ_SUCCESS) {
3403 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3404 return;
3405 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003406 }
3407
3408 /* We're cheating here.
3409 * We need to get a null terminated string from a pj_str_t.
3410 * So grab the pointer from the hvalue and NULL terminate it, knowing
3411 * that the NULL position will be occupied by a newline.
3412 */
3413 uri = refer_to->hvalue.ptr;
3414 uri[refer_to->hvalue.slen] = '\0';
3415
Benny Prijono053f5222006-11-11 16:16:04 +00003416 /* Init msg_data */
3417 pjsua_msg_data_init(&msg_data);
3418
3419 /* If Referred-By header is present in the REFER request, copy this
3420 * to the outgoing INVITE request.
3421 */
3422 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003423 pjsip_hdr *dup = (pjsip_hdr*)
3424 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003425 pj_list_push_back(&msg_data.hdr_list, dup);
3426 }
3427
Benny Prijono26ff9062006-02-21 23:47:00 +00003428 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003429 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003430 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003431 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003432 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003433 if (status != PJ_SUCCESS) {
3434
Benny Prijonoc8141a82006-08-20 09:12:19 +00003435 /* Notify xferer about the error (if we have subscription) */
3436 if (sub) {
3437 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3438 500, NULL, &tdata);
3439 if (status != PJ_SUCCESS) {
3440 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3441 status);
3442 return;
3443 }
3444 status = pjsip_xfer_send_request(sub, tdata);
3445 if (status != PJ_SUCCESS) {
3446 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3447 status);
3448 return;
3449 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003450 }
3451 return;
3452 }
3453
Benny Prijonoc8141a82006-08-20 09:12:19 +00003454 if (sub) {
3455 /* Put the server subscription in inv_data.
3456 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3457 * reported back to the server subscription.
3458 */
3459 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003460
Benny Prijonoc8141a82006-08-20 09:12:19 +00003461 /* Put the invite_data in the subscription. */
3462 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3463 &pjsua_var.calls[new_call]);
3464 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003465}
3466
3467
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003468
Benny Prijono26ff9062006-02-21 23:47:00 +00003469/*
3470 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003471 * session. We use this to trap:
3472 * - incoming REFER request.
3473 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003474 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003475static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3476 pjsip_transaction *tsx,
3477 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003478{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003479 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003480
3481 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003482
Benny Prijono2285e7e2008-12-17 14:28:18 +00003483 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3484
3485 if (call == NULL) {
3486 PJSUA_UNLOCK();
3487 return;
3488 }
3489
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003490 if (call->inv == NULL) {
3491 /* Shouldn't happen. It happens only when we don't terminate the
3492 * server subscription caused by REFER after the call has been
3493 * transfered (and this call has been disconnected), and we
3494 * receive another REFER for this call.
3495 */
3496 PJSUA_UNLOCK();
3497 return;
3498 }
3499
Benny Prijonofeb69f42007-10-05 09:12:26 +00003500 /* Notify application callback first */
3501 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3502 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3503 }
3504
Benny Prijono26ff9062006-02-21 23:47:00 +00003505 if (tsx->role==PJSIP_ROLE_UAS &&
3506 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003507 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003508 {
3509 /*
3510 * Incoming REFER request.
3511 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003512 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003513
Benny Prijono26ff9062006-02-21 23:47:00 +00003514 }
Benny Prijonob0808372006-03-02 21:18:58 +00003515 else if (tsx->role==PJSIP_ROLE_UAS &&
3516 tsx->state==PJSIP_TSX_STATE_TRYING &&
3517 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3518 {
3519 /*
3520 * Incoming MESSAGE request!
3521 */
3522 pjsip_rx_data *rdata;
3523 pjsip_msg *msg;
3524 pjsip_accept_hdr *accept_hdr;
3525 pj_status_t status;
3526
3527 rdata = e->body.tsx_state.src.rdata;
3528 msg = rdata->msg_info.msg;
3529
3530 /* Request MUST have message body, with Content-Type equal to
3531 * "text/plain".
3532 */
3533 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3534
3535 pjsip_hdr hdr_list;
3536
3537 pj_list_init(&hdr_list);
3538 pj_list_push_back(&hdr_list, accept_hdr);
3539
3540 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3541 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003542 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003543 return;
3544 }
3545
3546 /* Respond with 200 first, so that remote doesn't retransmit in case
3547 * the UI takes too long to process the message.
3548 */
3549 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3550
3551 /* Process MESSAGE request */
3552 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3553 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003554
Benny Prijonob0808372006-03-02 21:18:58 +00003555 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003556 else if (tsx->role == PJSIP_ROLE_UAC &&
3557 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003558 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003559 /* Handle outgoing pager status */
3560 if (tsx->status_code >= 200) {
3561 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003562
Benny Prijonoa1e69682007-05-11 15:14:34 +00003563 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003564 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003565
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003566 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3567 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3568 &im_data->to,
3569 &im_data->body,
3570 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003571 (pjsip_status_code)
3572 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003573 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003574 }
Benny Prijonofccab712006-02-22 22:23:22 +00003575 }
Benny Prijono834aee32006-02-19 01:38:06 +00003576 }
Benny Prijono834aee32006-02-19 01:38:06 +00003577
Benny Prijono26ff9062006-02-21 23:47:00 +00003578
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003579 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003580}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003581
3582
3583/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003584static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3585 const pjsip_uri *target,
3586 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003587{
3588 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003589 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003590
3591 PJSUA_LOCK();
3592
3593 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003594 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3595 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003596 } else {
3597 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3598 "(callback not implemented by application). Disconnecting "
3599 "call.",
3600 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003601 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003602 }
3603
3604 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003605
3606 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003607}
3608