blob: 746ef0ad9ab8706ae7ca4f8dc829fcc6610ebf5d [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
27/* This callback receives notification from invite session when the
28 * session state has changed.
29 */
30static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
31 pjsip_event *e);
32
33/* This callback is called by invite session framework when UAC session
34 * has forked.
35 */
36static void pjsua_call_on_forked( pjsip_inv_session *inv,
37 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000038
39/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 * Callback to be called when SDP offer/answer negotiation has just completed
41 * in the session. This function will start/update media if negotiation
42 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000043 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000044static void pjsua_call_on_media_update(pjsip_inv_session *inv,
45 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000046
47/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000048 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000049 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
51 const pjmedia_sdp_session *offer);
52
53/*
Benny Prijono77998ce2007-06-20 10:03:46 +000054 * Called to generate new offer.
55 */
56static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
57 pjmedia_sdp_session **offer);
58
59/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060 * This callback is called when transaction state has changed in INVITE
61 * session. We use this to trap:
62 * - incoming REFER request.
63 * - incoming MESSAGE request.
64 */
65static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
66 pjsip_transaction *tsx,
67 pjsip_event *e);
68
Benny Prijono5e51a4e2008-11-27 00:06:46 +000069/*
70 * Redirection handler.
71 */
Benny Prijono08a48b82008-11-27 12:42:07 +000072static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
73 const pjsip_uri *target,
74 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000075
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076
Nanang Izzuddin99d69522008-08-04 15:01:38 +000077/* Create SDP for call hold. */
78static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
79 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000080
Benny Prijonod524e822006-09-22 12:48:18 +000081/*
82 * Callback called by event framework when the xfer subscription state
83 * has changed.
84 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000085static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
86static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
87
88/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000089 * Reset call descriptor.
90 */
91static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000092{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000094
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 call->index = id;
96 call->inv = NULL;
97 call->user_data = NULL;
98 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +000099 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000100 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000101 call->rtp_tx_seq = 0;
102 call->rtp_tx_ts = 0;
103 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000104 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000105 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 call->conf_slot = PJSUA_INVALID_ID;
107 call->last_text.ptr = call->last_text_buf_;
108 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000109 call->conn_time.sec = 0;
110 call->conn_time.msec = 0;
111 call->res_time.sec = 0;
112 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000113 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000114 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000115 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000116}
117
118
Benny Prijono275fd682006-03-22 11:59:11 +0000119/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000120 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000121 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000123{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 pjsip_inv_callback inv_cb;
125 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000126 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000128
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 /* Init calls array. */
130 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
131 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 /* Copy config */
134 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijono91d06b62008-09-20 12:16:56 +0000136 /* Check the route URI's and force loose route if required */
137 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
138 status = normalize_route_uri(pjsua_var.pool,
139 &pjsua_var.ua_cfg.outbound_proxy[i]);
140 if (status != PJ_SUCCESS)
141 return status;
142 }
143
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000145 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
147 inv_cb.on_new_session = &pjsua_call_on_forked;
148 inv_cb.on_media_update = &pjsua_call_on_media_update;
149 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000150 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000152 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000153
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 /* Initialize invite session module: */
155 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
156 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
157
Benny Prijonoc8141a82006-08-20 09:12:19 +0000158 /* Add "norefersub" in Supported header */
159 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
160 NULL, 1, &str_norefersub);
161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 return status;
163}
164
165
166/*
167 * Start call subsystem.
168 */
169pj_status_t pjsua_call_subsys_start(void)
170{
171 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000172 return PJ_SUCCESS;
173}
174
175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000177 * Get maximum number of calls configured in pjsua.
178 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000179PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182}
183
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185/*
186 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000187 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000188PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000189{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191}
192
193
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194/*
195 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
198 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
207 if (!pjsua_var.calls[i].inv)
208 continue;
209 ids[c] = i;
210 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211 }
212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216
217 return PJ_SUCCESS;
218}
219
220
Benny Prijono5773cd62008-01-19 13:01:42 +0000221/* Allocate one call id */
222static pjsua_call_id alloc_call_id(void)
223{
224 pjsua_call_id cid;
225
226#if 1
227 /* New algorithm: round-robin */
228 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
229 pjsua_var.next_call_id < 0)
230 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000231 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000232 }
233
234 for (cid=pjsua_var.next_call_id;
235 cid<(int)pjsua_var.ua_cfg.max_calls;
236 ++cid)
237 {
238 if (pjsua_var.calls[cid].inv == NULL) {
239 ++pjsua_var.next_call_id;
240 return cid;
241 }
242 }
243
244 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
245 if (pjsua_var.calls[cid].inv == NULL) {
246 ++pjsua_var.next_call_id;
247 return cid;
248 }
249 }
250
251#else
252 /* Old algorithm */
253 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
254 if (pjsua_var.calls[cid].inv == NULL)
255 return cid;
256 }
257#endif
258
259 return PJSUA_INVALID_ID;
260}
261
Benny Prijonod8179652008-01-23 20:39:07 +0000262/* Get signaling secure level.
263 * Return:
264 * 0: if signaling is not secure
265 * 1: if TLS transport is used for immediate hop
266 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000267 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000268static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000269{
270 const pj_str_t tls = pj_str(";transport=tls");
271 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000272 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000273
274 if (pj_stristr(dst_uri, &sips))
275 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000276
277 if (!pj_list_empty(&acc->route_set)) {
278 pjsip_route_hdr *r = acc->route_set.next;
279 pjsip_uri *uri = r->name_addr.uri;
280 pjsip_sip_uri *sip_uri;
281
282 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
283 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
284 return 1;
285
286 } else {
287 if (pj_stristr(dst_uri, &tls))
288 return 1;
289 }
290
Benny Prijonod8179652008-01-23 20:39:07 +0000291 return 0;
292}
293
Benny Prijono224b4e22008-06-19 14:10:28 +0000294/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000295static int call_get_secure_level(pjsua_call *call)
296{
297 if (call->inv->dlg->secure)
298 return 2;
299
300 if (!pj_list_empty(&call->inv->dlg->route_set)) {
301 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
302 pjsip_uri *uri = r->name_addr.uri;
303 pjsip_sip_uri *sip_uri;
304
305 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
306 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
307 return 1;
308
309 } else {
310 pjsip_sip_uri *sip_uri;
311
312 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
313 return 2;
314 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
315 return 0;
316
317 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
318 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
319 return 1;
320 }
321
322 return 0;
323}
Benny Prijono224b4e22008-06-19 14:10:28 +0000324*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000325
326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327/*
328 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000329 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
331 const pj_str_t *dest_uri,
332 unsigned options,
333 void *user_data,
334 const pjsua_msg_data *msg_data,
335 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000336{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000337 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000338 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000339 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000340 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 pjsua_acc *acc;
342 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000343 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000344 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 pjsip_tx_data *tdata;
346 pj_status_t status;
347
Benny Prijono9fc735d2006-05-28 14:58:12 +0000348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000350 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000351 PJ_EINVAL);
352
Benny Prijono320fa4d2006-12-07 10:09:16 +0000353 /* Check arguments */
354 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
355
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 PJSUA_LOCK();
357
Benny Prijonof798e502009-03-09 13:08:16 +0000358 /* Create sound port if none is instantiated, to check if sound device
359 * can be used. But only do this with the conference bridge, as with
360 * audio switchboard (i.e. APS-Direct), we can only open the sound
361 * device once the correct format has been known
362 */
363 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
364 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000365 {
366 pj_status_t status;
367
368 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
369 if (status != PJ_SUCCESS) {
370 PJSUA_UNLOCK();
371 return status;
372 }
373 }
374
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000375 acc = &pjsua_var.acc[acc_id];
376 if (!acc->valid) {
377 pjsua_perror(THIS_FILE, "Unable to make call because account "
378 "is not valid", PJ_EINVALIDOP);
379 PJSUA_UNLOCK();
380 return PJ_EINVALIDOP;
381 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000382
Benny Prijonoa91a0032006-02-26 21:23:45 +0000383 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000384 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000385
Benny Prijono5773cd62008-01-19 13:01:42 +0000386 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
388 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000389 return PJ_ETOOMANY;
390 }
391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 call = &pjsua_var.calls[call_id];
393
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000394 /* Create temporary pool */
395 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
396
Benny Prijono320fa4d2006-12-07 10:09:16 +0000397 /* Verify that destination URI is valid before calling
398 * pjsua_acc_create_uac_contact, or otherwise there
399 * a misleading "Invalid Contact URI" error will be printed
400 * when pjsua_acc_create_uac_contact() fails.
401 */
402 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000403 pjsip_uri *uri;
404 pj_str_t dup;
405
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000406 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
407 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000408
409 if (uri == NULL) {
410 pjsua_perror(THIS_FILE, "Unable to make call",
411 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000412 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 PJSUA_UNLOCK();
414 return PJSIP_EINVALIDREQURI;
415 }
416 }
417
Benny Prijono093d3022006-09-24 00:07:11 +0000418 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
419 (int)dest_uri->slen, dest_uri->ptr));
420
Benny Prijonoe21e7842006-04-09 16:46:05 +0000421 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000422 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000423
Benny Prijonoe21e7842006-04-09 16:46:05 +0000424 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000426
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000427 /* Create suitable Contact header unless a Contact header has been
428 * set in the account.
429 */
430 if (acc->contact.slen) {
431 contact = acc->contact;
432 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000433 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000434 acc_id, dest_uri);
435 if (status != PJ_SUCCESS) {
436 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
437 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000438 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000439 PJSUA_UNLOCK();
440 return status;
441 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000442 }
443
Benny Prijonoe21e7842006-04-09 16:46:05 +0000444 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000445 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000446 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000448 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000449 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000450 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000452 return status;
453 }
454
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000455 /* Increment the dialog's lock otherwise when invite session creation
456 * fails the dialog will be destroyed prematurely.
457 */
458 pjsip_dlg_inc_lock(dlg);
459
Benny Prijonodb844a42008-02-02 17:07:18 +0000460 /* Calculate call's secure level */
461 call->secure_level = get_secure_level(acc_id, dest_uri);
462
Benny Prijonoc97608e2007-03-23 16:34:20 +0000463 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000464 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000465 call->secure_level, dlg->pool,
466 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000467 if (status != PJ_SUCCESS) {
468 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
469 goto on_error;
470 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000471
Benny Prijono224b4e22008-06-19 14:10:28 +0000472 /* Create offer */
473 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000474 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000475 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000476 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000477 goto on_error;
478 }
479
480 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000481 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000482 if (acc->cfg.require_100rel)
483 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000484
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000485 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000486 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000487 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000488 goto on_error;
489 }
490
491
492 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000493 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000494 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000495
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 dlg->mod_data[pjsua_var.mod.id] = call;
497 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000498
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000499 /* Attach user data */
500 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000501
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000502 /* If account is locked to specific transport, then lock dialog
503 * to this transport too.
504 */
505 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
506 pjsip_tpselector tp_sel;
507
508 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
509 pjsip_dlg_set_transport(dlg, &tp_sel);
510 }
511
Benny Prijono84126ab2006-02-09 09:30:09 +0000512 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000513 if (!pj_list_empty(&acc->route_set))
514 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000515
516
517 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000519 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000520 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000521 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000522
Benny Prijono48ab2b72007-11-08 09:24:30 +0000523 /* Set authentication preference */
524 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000525
526 /* Create initial INVITE: */
527
528 status = pjsip_inv_invite(inv, &tdata);
529 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000530 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
531 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000532 goto on_error;
533 }
534
535
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 /* Add additional headers etc */
537
538 pjsua_process_msg_data( tdata, msg_data);
539
Benny Prijono093d3022006-09-24 00:07:11 +0000540 /* Must increment call counter now */
541 ++pjsua_var.call_cnt;
542
Benny Prijono84126ab2006-02-09 09:30:09 +0000543 /* Send initial INVITE: */
544
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000545 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000546 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000547 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
548 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000549
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000550 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000551 * session would have been cleared.
552 */
553 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000554 goto on_error;
555 }
556
Benny Prijono84126ab2006-02-09 09:30:09 +0000557 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000558
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 if (p_call_id)
560 *p_call_id = call_id;
561
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000562 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000563 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000564 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000565
566 return PJ_SUCCESS;
567
568
569on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000570 if (dlg) {
571 /* This may destroy the dialog */
572 pjsip_dlg_dec_lock(dlg);
573 }
574
Benny Prijono1c2bf462006-03-05 11:54:02 +0000575 if (inv != NULL) {
576 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000577 }
578
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579 if (call_id != -1) {
580 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000581 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000582 }
583
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000584 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000585 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000586 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000587}
588
589
Benny Prijono91a6a172007-10-31 08:59:29 +0000590/* Get the NAT type information in remote's SDP */
591static void update_remote_nat_type(pjsua_call *call,
592 const pjmedia_sdp_session *sdp)
593{
594 const pjmedia_sdp_attr *xnat;
595
596 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
597 if (xnat) {
598 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
599 } else {
600 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
601 }
602
603 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
604 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
605}
606
607
Benny Prijonodc39fe82006-05-26 12:17:46 +0000608/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000609 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000610 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000611 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000612pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000613{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000614 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000615 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000616 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000617 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
618 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000619 pjsip_tx_data *response = NULL;
620 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000621 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000622 int acc_id;
623 pjsua_call *call;
624 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000625 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000626 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000627 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000628
Benny Prijono26ff9062006-02-21 23:47:00 +0000629 /* Don't want to handle anything but INVITE */
630 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
631 return PJ_FALSE;
632
633 /* Don't want to handle anything that's already associated with
634 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000635 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000636 if (dlg || tsx)
637 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000638
Benny Prijono148c9dd2006-09-19 13:37:53 +0000639 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000640
Benny Prijono26ff9062006-02-21 23:47:00 +0000641 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000642 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000643
Benny Prijono5773cd62008-01-19 13:01:42 +0000644 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000645 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000646 PJSIP_SC_BUSY_HERE, NULL,
647 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000648 PJ_LOG(2,(THIS_FILE,
649 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000650 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000651 return PJ_TRUE;
652 }
653
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 /* Clear call descriptor */
655 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000656
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657 call = &pjsua_var.calls[call_id];
658
659 /* Mark call start time. */
660 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000661
Benny Prijono053f5222006-11-11 16:16:04 +0000662 /* Check INVITE request for Replaces header. If Replaces header is
663 * present, the function will make sure that we can handle the request.
664 */
665 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
666 &response);
667 if (status != PJ_SUCCESS) {
668 /*
669 * Something wrong with the Replaces header.
670 */
671 if (response) {
672 pjsip_response_addr res_addr;
673
674 pjsip_get_response_addr(response->pool, rdata, &res_addr);
675 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
676 NULL, NULL);
677
678 } else {
679
680 /* Respond with 500 (Internal Server Error) */
681 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
682 NULL, NULL);
683 }
684
685 PJSUA_UNLOCK();
686 return PJ_TRUE;
687 }
688
689 /* If this INVITE request contains Replaces header, notify application
690 * about the request so that application can do subsequent checking
691 * if it wants to.
692 */
693 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
694 pjsua_call *replaced_call;
695 int st_code = 200;
696 pj_str_t st_text = { "OK", 2 };
697
698 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000699 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000700
701 /* Notify application */
702 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
703 rdata, &st_code, &st_text);
704
705 /* Must specify final response */
706 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
707
708 /* Check if application rejects this request. */
709 if (st_code >= 300) {
710
711 if (st_text.slen == 2)
712 st_text = *pjsip_get_status_text(st_code);
713
714 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
715 st_code, &st_text, NULL, NULL, NULL);
716 PJSUA_UNLOCK();
717 return PJ_TRUE;
718 }
719 }
720
Benny Prijonod8179652008-01-23 20:39:07 +0000721 /*
722 * Get which account is most likely to be associated with this incoming
723 * call. We need the account to find which contact URI to put for
724 * the call.
725 */
726 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
727
Benny Prijonodb844a42008-02-02 17:07:18 +0000728 /* Get call's secure level */
729 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
730 call->secure_level = 2;
731 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
732 call->secure_level = 1;
733 else
734 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000735
Benny Prijonod8179652008-01-23 20:39:07 +0000736 /* Parse SDP from incoming request */
737 if (rdata->msg_info.msg->body) {
738 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000739 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000740 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000741 if (status == PJ_SUCCESS) {
742 /* Validate */
743 status = pjmedia_sdp_validate(offer);
744 }
745
Benny Prijonod8179652008-01-23 20:39:07 +0000746 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000747 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000748 pjsip_hdr hdr_list;
749 pjsip_warning_hdr *w;
750
751 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000752 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000753
754 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
755 pjsip_endpt_name(pjsua_var.endpt),
756 status);
757 pj_list_init(&hdr_list);
758 pj_list_push_back(&hdr_list, w);
759
Benny Prijono224b4e22008-06-19 14:10:28 +0000760 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000761 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000762 PJSUA_UNLOCK();
763 return PJ_TRUE;
764 }
Benny Prijono617b8602008-04-07 10:10:31 +0000765
766 /* Do quick checks on SDP before passing it to transports. More elabore
767 * checks will be done in pjsip_inv_verify_request2() below.
768 */
769 if (offer->media_count==0) {
770 const pj_str_t reason = pj_str("Missing media in SDP");
771 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
772 NULL, NULL, NULL);
773 PJSUA_UNLOCK();
774 return PJ_TRUE;
775 }
776
Benny Prijonod8179652008-01-23 20:39:07 +0000777 } else {
778 offer = NULL;
779 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000780
Benny Prijono224b4e22008-06-19 14:10:28 +0000781 /* Init media channel */
782 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
783 call->secure_level,
784 rdata->tp_info.pool, offer,
785 &sip_err_code);
786 if (status != PJ_SUCCESS) {
787 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
788 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
789 sip_err_code, NULL, NULL, NULL, NULL);
790 PJSUA_UNLOCK();
791 return PJ_TRUE;
792 }
793
794 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000795 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000796 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000797 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000798 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000799 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
800 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000801 PJSUA_UNLOCK();
802 return PJ_TRUE;
803 }
804
Benny Prijono224b4e22008-06-19 14:10:28 +0000805
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000806 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000807 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000808 if (pjsua_var.acc[acc_id].cfg.require_100rel)
809 options |= PJSIP_INV_REQUIRE_100REL;
810
Benny Prijonod8179652008-01-23 20:39:07 +0000811 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
812 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000813 if (status != PJ_SUCCESS) {
814
815 /*
816 * No we can't handle the incoming INVITE request.
817 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000818 if (response) {
819 pjsip_response_addr res_addr;
820
821 pjsip_get_response_addr(response->pool, rdata, &res_addr);
822 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
823 NULL, NULL);
824
825 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000826 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000827 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
828 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000829 }
830
Benny Prijonoc97608e2007-03-23 16:34:20 +0000831 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000832 PJSUA_UNLOCK();
833 return PJ_TRUE;
834 }
835
836
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000837 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000838 if (pjsua_var.acc[acc_id].contact.slen) {
839 contact = pjsua_var.acc[acc_id].contact;
840 } else {
841 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
842 acc_id, rdata);
843 if (status != PJ_SUCCESS) {
844 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
845 status);
846 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
847 NULL, NULL);
848 pjsua_media_channel_deinit(call->index);
849 PJSUA_UNLOCK();
850 return PJ_TRUE;
851 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000852 }
853
Benny Prijono26ff9062006-02-21 23:47:00 +0000854 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000855 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000856 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000857 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000858 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000859 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000860 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000861 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000862 return PJ_TRUE;
863 }
864
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000865 /* Set credentials */
866 if (pjsua_var.acc[acc_id].cred_cnt) {
867 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
868 pjsua_var.acc[acc_id].cred_cnt,
869 pjsua_var.acc[acc_id].cred);
870 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000871
Benny Prijono48ab2b72007-11-08 09:24:30 +0000872 /* Set preference */
873 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
874 &pjsua_var.acc[acc_id].cfg.auth_pref);
875
Benny Prijono26ff9062006-02-21 23:47:00 +0000876 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000877 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000878 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000879 pjsip_hdr hdr_list;
880 pjsip_warning_hdr *w;
881
882 w = pjsip_warning_hdr_create_from_status(dlg->pool,
883 pjsip_endpt_name(pjsua_var.endpt),
884 status);
885 pj_list_init(&hdr_list);
886 pj_list_push_back(&hdr_list, w);
887
888 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
889
890 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000891 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000892 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000893 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000894 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000895 return PJ_TRUE;
896 }
897
Benny Prijonoea9fd392007-11-06 03:41:40 +0000898 /* Update NAT type of remote endpoint, only when there is SDP in
899 * incoming INVITE!
900 */
901 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
902 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
903 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000904 const pjmedia_sdp_session *remote_sdp;
905
906 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
907 update_remote_nat_type(call, remote_sdp);
908 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000909
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000910 /* If account is locked to specific transport, then lock dialog
911 * to this transport too.
912 */
913 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
914 pjsip_tpselector tp_sel;
915
916 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
917 pjsip_dlg_set_transport(dlg, &tp_sel);
918 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000919
Benny Prijono2285e7e2008-12-17 14:28:18 +0000920 /* Must answer with some response to initial INVITE. We'll do this before
921 * attaching the call to the invite session/dialog, so that the application
922 * will not get notification about this event (on another scenario, it is
923 * also possible that inv_send_msg() fails and causes the invite session to
924 * be disconnected. If we have the call attached at this time, this will
925 * cause the disconnection callback to be called before on_incoming_call()
926 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000927 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000928 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000929 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000930 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000931 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
932 status);
933
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000934 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
935 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000936 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000937 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000938 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000939
940 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000941 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000942 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000943 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +0000944 PJSUA_UNLOCK();
945 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000946 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000947 }
948
Benny Prijono2285e7e2008-12-17 14:28:18 +0000949 /* Create and attach pjsua_var data to the dialog: */
950 call->inv = inv;
951
952 dlg->mod_data[pjsua_var.mod.id] = call;
953 inv->mod_data[pjsua_var.mod.id] = call;
954
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000955 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000956
Benny Prijono105217f2006-03-06 16:25:59 +0000957
Benny Prijono053f5222006-11-11 16:16:04 +0000958 /* Check if this request should replace existing call */
959 if (replaced_dlg) {
960 pjsip_inv_session *replaced_inv;
961 struct pjsua_call *replaced_call;
962 pjsip_tx_data *tdata;
963
964 /* Get the invite session in the dialog */
965 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
966
967 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000968 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000969
970 /* Notify application */
971 if (pjsua_var.ua_cfg.cb.on_call_replaced)
972 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
973 call_id);
974
975 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
976 call_id));
977
978 /* Answer the new call with 200 response */
979 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
980 if (status == PJ_SUCCESS)
981 status = pjsip_inv_send_msg(inv, tdata);
982
983 if (status != PJ_SUCCESS)
984 pjsua_perror(THIS_FILE, "Error answering session", status);
985
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000986 /* Note that inv may be invalid if 200/OK has caused error in
987 * starting the media.
988 */
Benny Prijono053f5222006-11-11 16:16:04 +0000989
990 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
991 replaced_call->index));
992
993 /* Disconnect replaced invite session */
994 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
995 &tdata);
996 if (status == PJ_SUCCESS && tdata)
997 status = pjsip_inv_send_msg(replaced_inv, tdata);
998
999 if (status != PJ_SUCCESS)
1000 pjsua_perror(THIS_FILE, "Error terminating session", status);
1001
1002
1003 } else {
1004
Benny Prijonob5388cf2007-01-04 22:45:08 +00001005 /* Notify application if on_incoming_call() is overriden,
1006 * otherwise hangup the call with 480
1007 */
1008 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001009 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001010 } else {
1011 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1012 NULL, NULL);
1013 }
Benny Prijono053f5222006-11-11 16:16:04 +00001014 }
1015
Benny Prijono8b1889b2006-06-06 18:40:40 +00001016
Benny Prijono26ff9062006-02-21 23:47:00 +00001017 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001018 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001019 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001020}
1021
1022
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001023
1024/*
1025 * Check if the specified call has active INVITE session and the INVITE
1026 * session has not been disconnected.
1027 */
1028PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1029{
1030 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1031 PJ_EINVAL);
1032 return pjsua_var.calls[call_id].inv != NULL &&
1033 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1034}
1035
1036
1037/*
1038 * Check if call has an active media session.
1039 */
1040PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1041{
1042 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1043 PJ_EINVAL);
1044 return pjsua_var.calls[call_id].session != NULL;
1045}
1046
1047
Benny Prijonocf986c42008-09-02 11:25:07 +00001048/*
1049 * Retrieve the media session associated with this call.
1050 */
1051PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1052{
1053 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1054 NULL);
1055 return pjsua_var.calls[call_id].session;
1056}
1057
1058
1059/*
1060 * Retrieve the media transport instance that is used for this call.
1061 */
1062PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1063{
1064 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1065 NULL);
1066 return pjsua_var.calls[cid].med_tp;
1067}
1068
1069
Benny Prijono148c9dd2006-09-19 13:37:53 +00001070/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001071pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001072 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001073 pjsua_call **p_call,
1074 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001075{
1076 enum { MAX_RETRY=50 };
1077 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001078 pjsua_call *call = NULL;
1079 pj_bool_t has_pjsua_lock = PJ_FALSE;
1080 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001081
1082 for (retry=0; retry<MAX_RETRY; ++retry) {
1083
1084 has_pjsua_lock = PJ_FALSE;
1085
1086 status = PJSUA_TRY_LOCK();
1087 if (status != PJ_SUCCESS) {
1088 pj_thread_sleep(retry/10);
1089 continue;
1090 }
1091
1092 has_pjsua_lock = PJ_TRUE;
1093 call = &pjsua_var.calls[call_id];
1094
1095 if (call->inv == NULL) {
1096 PJSUA_UNLOCK();
1097 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1098 return PJSIP_ESESSIONTERMINATED;
1099 }
1100
1101 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1102 if (status != PJ_SUCCESS) {
1103 PJSUA_UNLOCK();
1104 pj_thread_sleep(retry/10);
1105 continue;
1106 }
1107
1108 PJSUA_UNLOCK();
1109
1110 break;
1111 }
1112
1113 if (status != PJ_SUCCESS) {
1114 if (has_pjsua_lock == PJ_FALSE)
1115 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1116 "(possibly system has deadlocked) in %s",
1117 title));
1118 else
1119 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1120 "(possibly system has deadlocked) in %s",
1121 title));
1122 return PJ_ETIMEDOUT;
1123 }
1124
1125 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001126 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001127
1128 return PJ_SUCCESS;
1129}
1130
1131
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001132/*
1133 * Get the conference port identification associated with the call.
1134 */
1135PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1136{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001137 pjsua_call *call;
1138 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001139 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001140 pj_status_t status;
1141
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001142 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1143 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001144
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001146 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001147 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001148
1149 port_id = call->conf_slot;
1150
Benny Prijonodc752ca2006-09-22 16:55:42 +00001151 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001152
1153 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001154}
1155
1156
Benny Prijono148c9dd2006-09-19 13:37:53 +00001157
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001158/*
1159 * Obtain detail information about the specified call.
1160 */
1161PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1162 pjsua_call_info *info)
1163{
1164 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001165 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001166 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167
1168 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1169 PJ_EINVAL);
1170
Benny Prijonoac623b32006-07-03 15:19:31 +00001171 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172
Benny Prijonodc752ca2006-09-22 16:55:42 +00001173 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001174 if (status != PJ_SUCCESS) {
1175 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001176 }
1177
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001178 /* id and role */
1179 info->id = call_id;
1180 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001181 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001182
1183 /* local info */
1184 info->local_info.ptr = info->buf_.local_info;
1185 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1186 sizeof(info->buf_.local_info));
1187
1188 /* local contact */
1189 info->local_contact.ptr = info->buf_.local_contact;
1190 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1191 call->inv->dlg->local.contact->uri,
1192 info->local_contact.ptr,
1193 sizeof(info->buf_.local_contact));
1194
1195 /* remote info */
1196 info->remote_info.ptr = info->buf_.remote_info;
1197 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1198 sizeof(info->buf_.remote_info));
1199
1200 /* remote contact */
1201 if (call->inv->dlg->remote.contact) {
1202 int len;
1203 info->remote_contact.ptr = info->buf_.remote_contact;
1204 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1205 call->inv->dlg->remote.contact->uri,
1206 info->remote_contact.ptr,
1207 sizeof(info->buf_.remote_contact));
1208 if (len < 0) len = 0;
1209 info->remote_contact.slen = len;
1210 } else {
1211 info->remote_contact.slen = 0;
1212 }
1213
1214 /* call id */
1215 info->call_id.ptr = info->buf_.call_id;
1216 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1217 sizeof(info->buf_.call_id));
1218
1219 /* state, state_text */
1220 info->state = call->inv->state;
1221 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1222
1223 /* If call is disconnected, set the last_status from the cause code */
1224 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1225 /* last_status, last_status_text */
1226 info->last_status = call->inv->cause;
1227
1228 info->last_status_text.ptr = info->buf_.last_status_text;
1229 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1230 sizeof(info->buf_.last_status_text));
1231 } else {
1232 /* last_status, last_status_text */
1233 info->last_status = call->last_code;
1234
1235 info->last_status_text.ptr = info->buf_.last_status_text;
1236 pj_strncpy(&info->last_status_text, &call->last_text,
1237 sizeof(info->buf_.last_status_text));
1238 }
1239
1240 /* media status and dir */
1241 info->media_status = call->media_st;
1242 info->media_dir = call->media_dir;
1243
1244
1245 /* conference slot number */
1246 info->conf_slot = call->conf_slot;
1247
1248 /* calculate duration */
1249 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1250
1251 info->total_duration = call->dis_time;
1252 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1253
1254 if (call->conn_time.sec) {
1255 info->connect_duration = call->dis_time;
1256 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1257 }
1258
1259 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1260
1261 pj_gettimeofday(&info->total_duration);
1262 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1263
1264 pj_gettimeofday(&info->connect_duration);
1265 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1266
1267 } else {
1268 pj_gettimeofday(&info->total_duration);
1269 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1270 }
1271
Benny Prijonodc752ca2006-09-22 16:55:42 +00001272 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001273
1274 return PJ_SUCCESS;
1275}
1276
1277
1278/*
1279 * Attach application specific data to the call.
1280 */
1281PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1282 void *user_data)
1283{
1284 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1285 PJ_EINVAL);
1286 pjsua_var.calls[call_id].user_data = user_data;
1287
1288 return PJ_SUCCESS;
1289}
1290
1291
1292/*
1293 * Get user data attached to the call.
1294 */
1295PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1296{
1297 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1298 NULL);
1299 return pjsua_var.calls[call_id].user_data;
1300}
1301
1302
1303/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001304 * Get remote's NAT type.
1305 */
1306PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1307 pj_stun_nat_type *p_type)
1308{
1309 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1310 PJ_EINVAL);
1311 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1312
1313 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1314 return PJ_SUCCESS;
1315}
1316
1317
1318/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001319 * Send response to incoming INVITE request.
1320 */
1321PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1322 unsigned code,
1323 const pj_str_t *reason,
1324 const pjsua_msg_data *msg_data)
1325{
1326 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001327 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328 pjsip_tx_data *tdata;
1329 pj_status_t status;
1330
1331 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1332 PJ_EINVAL);
1333
Benny Prijonodc752ca2006-09-22 16:55:42 +00001334 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001335 if (status != PJ_SUCCESS)
1336 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001337
Benny Prijono2e507c22006-06-23 15:04:11 +00001338 if (call->res_time.sec == 0)
1339 pj_gettimeofday(&call->res_time);
1340
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001341 if (reason && reason->slen == 0)
1342 reason = NULL;
1343
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001344 /* Create response message */
1345 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1346 if (status != PJ_SUCCESS) {
1347 pjsua_perror(THIS_FILE, "Error creating response",
1348 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001349 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001350 return status;
1351 }
1352
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001353 /* Call might have been disconnected if application is answering with
1354 * 200/OK and the media failed to start.
1355 */
1356 if (call->inv == NULL) {
1357 pjsip_dlg_dec_lock(dlg);
1358 return PJSIP_ESESSIONTERMINATED;
1359 }
1360
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001361 /* Add additional headers etc */
1362 pjsua_process_msg_data( tdata, msg_data);
1363
1364 /* Send the message */
1365 status = pjsip_inv_send_msg(call->inv, tdata);
1366 if (status != PJ_SUCCESS)
1367 pjsua_perror(THIS_FILE, "Error sending response",
1368 status);
1369
Benny Prijonodc752ca2006-09-22 16:55:42 +00001370 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001371
1372 return status;
1373}
1374
1375
1376/*
1377 * Hangup call by using method that is appropriate according to the
1378 * call state.
1379 */
1380PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1381 unsigned code,
1382 const pj_str_t *reason,
1383 const pjsua_msg_data *msg_data)
1384{
1385 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001386 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001387 pj_status_t status;
1388 pjsip_tx_data *tdata;
1389
1390
Benny Prijono148c9dd2006-09-19 13:37:53 +00001391 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1392 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1393 call_id));
1394 }
1395
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1397 PJ_EINVAL);
1398
Benny Prijonodc752ca2006-09-22 16:55:42 +00001399 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001400 if (status != PJ_SUCCESS)
1401 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402
1403 if (code==0) {
1404 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1405 code = PJSIP_SC_OK;
1406 else if (call->inv->role == PJSIP_ROLE_UAS)
1407 code = PJSIP_SC_DECLINE;
1408 else
1409 code = PJSIP_SC_REQUEST_TERMINATED;
1410 }
1411
1412 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1413 if (status != PJ_SUCCESS) {
1414 pjsua_perror(THIS_FILE,
1415 "Failed to create end session message",
1416 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001417 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001418 return status;
1419 }
1420
1421 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1422 * as p_tdata when INVITE transaction has not been answered
1423 * with any provisional responses.
1424 */
1425 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001426 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001427 return PJ_SUCCESS;
1428 }
1429
1430 /* Add additional headers etc */
1431 pjsua_process_msg_data( tdata, msg_data);
1432
1433 /* Send the message */
1434 status = pjsip_inv_send_msg(call->inv, tdata);
1435 if (status != PJ_SUCCESS) {
1436 pjsua_perror(THIS_FILE,
1437 "Failed to send end session message",
1438 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001439 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440 return status;
1441 }
1442
Benny Prijonodc752ca2006-09-22 16:55:42 +00001443 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444
1445 return PJ_SUCCESS;
1446}
1447
1448
1449/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001450 * Accept or reject redirection.
1451 */
1452PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1453 pjsip_redirect_op cmd)
1454{
1455 pjsua_call *call;
1456 pjsip_dialog *dlg;
1457 pj_status_t status;
1458
1459 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1460 PJ_EINVAL);
1461
1462 status = acquire_call("pjsua_call_process_redirect()", call_id,
1463 &call, &dlg);
1464 if (status != PJ_SUCCESS)
1465 return status;
1466
1467 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1468
1469 pjsip_dlg_dec_lock(dlg);
1470
1471 return status;
1472}
1473
1474
1475/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001476 * Put the specified call on hold.
1477 */
1478PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1479 const pjsua_msg_data *msg_data)
1480{
1481 pjmedia_sdp_session *sdp;
1482 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001483 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001484 pjsip_tx_data *tdata;
1485 pj_status_t status;
1486
1487 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1488 PJ_EINVAL);
1489
Benny Prijonodc752ca2006-09-22 16:55:42 +00001490 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001491 if (status != PJ_SUCCESS)
1492 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494
1495 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1496 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001497 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001498 return PJSIP_ESESSIONSTATE;
1499 }
1500
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001501 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001503 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504 return status;
1505 }
1506
1507 /* Create re-INVITE with new offer */
1508 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1509 if (status != PJ_SUCCESS) {
1510 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001511 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512 return status;
1513 }
1514
1515 /* Add additional headers etc */
1516 pjsua_process_msg_data( tdata, msg_data);
1517
1518 /* Send the request */
1519 status = pjsip_inv_send_msg( call->inv, tdata);
1520 if (status != PJ_SUCCESS) {
1521 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001522 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001523 return status;
1524 }
1525
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001526 /* Set flag that local put the call on hold */
1527 call->local_hold = PJ_TRUE;
1528
Benny Prijonodc752ca2006-09-22 16:55:42 +00001529 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001530
1531 return PJ_SUCCESS;
1532}
1533
1534
1535/*
1536 * Send re-INVITE (to release hold).
1537 */
1538PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1539 pj_bool_t unhold,
1540 const pjsua_msg_data *msg_data)
1541{
1542 pjmedia_sdp_session *sdp;
1543 pjsip_tx_data *tdata;
1544 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001545 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 pj_status_t status;
1547
1548
1549 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1550 PJ_EINVAL);
1551
Benny Prijonodc752ca2006-09-22 16:55:42 +00001552 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001553 if (status != PJ_SUCCESS)
1554 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555
1556 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1557 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001558 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001559 return PJSIP_ESESSIONSTATE;
1560 }
1561
1562 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001563 if (call->local_hold && !unhold) {
1564 status = create_sdp_of_call_hold(call, &sdp);
1565 } else {
1566 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
1567 NULL, &sdp, NULL);
1568 call->local_hold = PJ_FALSE;
1569 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 if (status != PJ_SUCCESS) {
1571 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1572 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001573 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001574 return status;
1575 }
1576
1577 /* Create re-INVITE with new offer */
1578 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1579 if (status != PJ_SUCCESS) {
1580 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001581 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582 return status;
1583 }
1584
1585 /* Add additional headers etc */
1586 pjsua_process_msg_data( tdata, msg_data);
1587
1588 /* Send the request */
1589 status = pjsip_inv_send_msg( call->inv, tdata);
1590 if (status != PJ_SUCCESS) {
1591 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001592 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593 return status;
1594 }
1595
Benny Prijonodc752ca2006-09-22 16:55:42 +00001596 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597
1598 return PJ_SUCCESS;
1599}
1600
1601
1602/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001603 * Send UPDATE request.
1604 */
1605PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1606 unsigned options,
1607 const pjsua_msg_data *msg_data)
1608{
1609 pjmedia_sdp_session *sdp;
1610 pjsip_tx_data *tdata;
1611 pjsua_call *call;
1612 pjsip_dialog *dlg;
1613 pj_status_t status;
1614
1615 PJ_UNUSED_ARG(options);
1616
1617 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1618 PJ_EINVAL);
1619
1620 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1621 if (status != PJ_SUCCESS)
1622 return status;
1623
Benny Prijonoc08682e2007-10-04 06:17:58 +00001624 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001625 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001626 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001627 if (status != PJ_SUCCESS) {
1628 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1629 status);
1630 pjsip_dlg_dec_lock(dlg);
1631 return status;
1632 }
1633
Benny Prijono224b4e22008-06-19 14:10:28 +00001634 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001635 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1636 if (status != PJ_SUCCESS) {
1637 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1638 pjsip_dlg_dec_lock(dlg);
1639 return status;
1640 }
1641
1642 /* Add additional headers etc */
1643 pjsua_process_msg_data( tdata, msg_data);
1644
1645 /* Send the request */
1646 status = pjsip_inv_send_msg( call->inv, tdata);
1647 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001648 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001649 pjsip_dlg_dec_lock(dlg);
1650 return status;
1651 }
1652
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001653 call->local_hold = PJ_FALSE;
1654
Benny Prijonoc08682e2007-10-04 06:17:58 +00001655 pjsip_dlg_dec_lock(dlg);
1656
1657 return PJ_SUCCESS;
1658}
1659
1660
1661/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662 * Initiate call transfer to the specified address.
1663 */
1664PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1665 const pj_str_t *dest,
1666 const pjsua_msg_data *msg_data)
1667{
1668 pjsip_evsub *sub;
1669 pjsip_tx_data *tdata;
1670 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001672 pjsip_generic_string_hdr *gs_hdr;
1673 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001674 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 pj_status_t status;
1676
1677
1678 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1679 PJ_EINVAL);
1680
Benny Prijonodc752ca2006-09-22 16:55:42 +00001681 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001682 if (status != PJ_SUCCESS)
1683 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001684
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685
Benny Prijonod524e822006-09-22 12:48:18 +00001686 /* Create xfer client subscription. */
1687 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001688 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001689
1690 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001691 if (status != PJ_SUCCESS) {
1692 pjsua_perror(THIS_FILE, "Unable to create xfer", 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
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001697 /* Associate this call with the client subscription */
1698 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1699
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 /*
1701 * Create REFER request.
1702 */
1703 status = pjsip_xfer_initiate(sub, dest, &tdata);
1704 if (status != PJ_SUCCESS) {
1705 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001706 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 return status;
1708 }
1709
Benny Prijono053f5222006-11-11 16:16:04 +00001710 /* Add Referred-By header */
1711 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1712 &dlg->local.info_str);
1713 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1714
1715
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716 /* Add additional headers etc */
1717 pjsua_process_msg_data( tdata, msg_data);
1718
1719 /* Send. */
1720 status = pjsip_xfer_send_request(sub, tdata);
1721 if (status != PJ_SUCCESS) {
1722 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001723 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001724 return status;
1725 }
1726
1727 /* For simplicity (that's what this program is intended to be!),
1728 * leave the original invite session as it is. More advanced application
1729 * may want to hold the INVITE, or terminate the invite, or whatever.
1730 */
1731
Benny Prijonodc752ca2006-09-22 16:55:42 +00001732 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001733
1734 return PJ_SUCCESS;
1735
1736}
1737
1738
1739/*
Benny Prijono053f5222006-11-11 16:16:04 +00001740 * Initiate attended call transfer to the specified address.
1741 */
1742PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1743 pjsua_call_id dest_call_id,
1744 unsigned options,
1745 const pjsua_msg_data *msg_data)
1746{
1747 pjsua_call *dest_call;
1748 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001749 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001750 pj_str_t str_dest;
1751 int len;
1752 pjsip_uri *uri;
1753 pj_status_t status;
1754
1755
1756 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1757 PJ_EINVAL);
1758 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1759 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1760 PJ_EINVAL);
1761
1762 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1763 &dest_call, &dest_dlg);
1764 if (status != PJ_SUCCESS)
1765 return status;
1766
1767 /*
1768 * Create REFER destination URI with Replaces field.
1769 */
1770
1771 /* Make sure we have sufficient buffer's length */
1772 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1773 dest_dlg->call_id->id.slen +
1774 dest_dlg->remote.info->tag.slen +
1775 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001776 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001777
1778 /* Print URI */
1779 str_dest_buf[0] = '<';
1780 str_dest.slen = 1;
1781
Benny Prijonoa1e69682007-05-11 15:14:34 +00001782 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001783 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1784 str_dest_buf+1, sizeof(str_dest_buf)-1);
1785 if (len < 0)
1786 return PJSIP_EURITOOLONG;
1787
1788 str_dest.slen += len;
1789
1790
1791 /* Build the URI */
1792 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1793 sizeof(str_dest_buf) - str_dest.slen,
1794 "?%s"
1795 "Replaces=%.*s"
1796 "%%3Bto-tag%%3D%.*s"
1797 "%%3Bfrom-tag%%3D%.*s>",
1798 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1799 "" : "Require=replaces&"),
1800 (int)dest_dlg->call_id->id.slen,
1801 dest_dlg->call_id->id.ptr,
1802 (int)dest_dlg->remote.info->tag.slen,
1803 dest_dlg->remote.info->tag.ptr,
1804 (int)dest_dlg->local.info->tag.slen,
1805 dest_dlg->local.info->tag.ptr);
1806
1807 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1808 PJSIP_EURITOOLONG);
1809
1810 str_dest.ptr = str_dest_buf;
1811 str_dest.slen += len;
1812
1813 pjsip_dlg_dec_lock(dest_dlg);
1814
1815 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1816}
1817
1818
1819/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 * Send DTMF digits to remote using RFC 2833 payload formats.
1821 */
1822PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1823 const pj_str_t *digits)
1824{
1825 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001826 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001827 pj_status_t status;
1828
1829 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1830 PJ_EINVAL);
1831
Benny Prijonodc752ca2006-09-22 16:55:42 +00001832 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001833 if (status != PJ_SUCCESS)
1834 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836 if (!call->session) {
1837 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001838 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001839 return PJ_EINVALIDOP;
1840 }
1841
1842 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1843
Benny Prijonodc752ca2006-09-22 16:55:42 +00001844 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845
1846 return status;
1847}
1848
1849
1850/**
1851 * Send instant messaging inside INVITE session.
1852 */
1853PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1854 const pj_str_t *mime_type,
1855 const pj_str_t *content,
1856 const pjsua_msg_data *msg_data,
1857 void *user_data)
1858{
1859 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001860 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001861 const pj_str_t mime_text_plain = pj_str("text/plain");
1862 pjsip_media_type ctype;
1863 pjsua_im_data *im_data;
1864 pjsip_tx_data *tdata;
1865 pj_status_t status;
1866
1867
1868 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1869 PJ_EINVAL);
1870
Benny Prijonodc752ca2006-09-22 16:55:42 +00001871 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001872 if (status != PJ_SUCCESS)
1873 return status;
1874
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001875 /* Set default media type if none is specified */
1876 if (mime_type == NULL) {
1877 mime_type = &mime_text_plain;
1878 }
1879
1880 /* Create request message. */
1881 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1882 -1, &tdata);
1883 if (status != PJ_SUCCESS) {
1884 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1885 goto on_return;
1886 }
1887
1888 /* Add accept header. */
1889 pjsip_msg_add_hdr( tdata->msg,
1890 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1891
1892 /* Parse MIME type */
1893 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1894
1895 /* Create "text/plain" message body. */
1896 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1897 &ctype.subtype, content);
1898 if (tdata->msg->body == NULL) {
1899 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1900 pjsip_tx_data_dec_ref(tdata);
1901 goto on_return;
1902 }
1903
1904 /* Add additional headers etc */
1905 pjsua_process_msg_data( tdata, msg_data);
1906
1907 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001908 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001909 im_data->acc_id = call->acc_id;
1910 im_data->call_id = call_id;
1911 im_data->to = call->inv->dlg->remote.info_str;
1912 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1913 im_data->user_data = user_data;
1914
1915
1916 /* Send the request. */
1917 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1918 pjsua_var.mod.id, im_data);
1919 if (status != PJ_SUCCESS) {
1920 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1921 goto on_return;
1922 }
1923
1924on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001925 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001926 return status;
1927}
1928
1929
1930/*
1931 * Send IM typing indication inside INVITE session.
1932 */
1933PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1934 pj_bool_t is_typing,
1935 const pjsua_msg_data*msg_data)
1936{
1937 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001938 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939 pjsip_tx_data *tdata;
1940 pj_status_t status;
1941
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001942 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1943 PJ_EINVAL);
1944
Benny Prijonodc752ca2006-09-22 16:55:42 +00001945 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001946 if (status != PJ_SUCCESS)
1947 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001948
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949 /* Create request message. */
1950 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1951 -1, &tdata);
1952 if (status != PJ_SUCCESS) {
1953 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1954 goto on_return;
1955 }
1956
1957 /* Create "application/im-iscomposing+xml" msg body. */
1958 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1959 NULL, NULL, -1);
1960
1961 /* Add additional headers etc */
1962 pjsua_process_msg_data( tdata, msg_data);
1963
1964 /* Send the request. */
1965 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1966 if (status != PJ_SUCCESS) {
1967 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1968 goto on_return;
1969 }
1970
1971on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001972 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001973 return status;
1974}
1975
1976
1977/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001978 * Send arbitrary request.
1979 */
1980PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1981 const pj_str_t *method_str,
1982 const pjsua_msg_data *msg_data)
1983{
1984 pjsua_call *call;
1985 pjsip_dialog *dlg;
1986 pjsip_method method;
1987 pjsip_tx_data *tdata;
1988 pj_status_t status;
1989
1990 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1991 PJ_EINVAL);
1992
1993 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1994 if (status != PJ_SUCCESS)
1995 return status;
1996
1997 /* Init method */
1998 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1999
2000 /* Create request message. */
2001 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2002 if (status != PJ_SUCCESS) {
2003 pjsua_perror(THIS_FILE, "Unable to create request", status);
2004 goto on_return;
2005 }
2006
2007 /* Add additional headers etc */
2008 pjsua_process_msg_data( tdata, msg_data);
2009
2010 /* Send the request. */
2011 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2012 if (status != PJ_SUCCESS) {
2013 pjsua_perror(THIS_FILE, "Unable to send request", status);
2014 goto on_return;
2015 }
2016
2017on_return:
2018 pjsip_dlg_dec_lock(dlg);
2019 return status;
2020}
2021
2022
2023/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024 * Terminate all calls.
2025 */
2026PJ_DEF(void) pjsua_call_hangup_all(void)
2027{
2028 unsigned i;
2029
2030 PJSUA_LOCK();
2031
2032 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2033 if (pjsua_var.calls[i].inv)
2034 pjsua_call_hangup(i, 0, NULL, NULL);
2035 }
2036
2037 PJSUA_UNLOCK();
2038}
2039
2040
Benny Prijono627cbb42007-09-25 20:48:49 +00002041const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002042{
2043 if (val < 1000) {
2044 pj_ansi_sprintf(buf, "%d", val);
2045 } else if (val < 1000000) {
2046 pj_ansi_sprintf(buf, "%d.%dK",
2047 val / 1000,
2048 (val % 1000) / 100);
2049 } else {
2050 pj_ansi_sprintf(buf, "%d.%02dM",
2051 val / 1000000,
2052 (val % 1000000) / 10000);
2053 }
2054
2055 return buf;
2056}
2057
2058
2059/* Dump media session */
2060static void dump_media_session(const char *indent,
2061 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002062 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063{
2064 unsigned i;
2065 char *p = buf, *end = buf+maxlen;
2066 int len;
2067 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002068 pjmedia_session *session = call->session;
2069 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002071 pjmedia_transport_info_init(&tp_info);
2072
2073 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 pjmedia_session_get_info(session, &info);
2075
2076 for (i=0; i<info.stream_cnt; ++i) {
2077 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002078 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002079 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002080 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002081 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002082 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002083 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084
2085 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002086 // rem_addr will contain actual address of RTP originator, instead of
2087 // remote RTP address specified by stream which is fetched from the SDP.
2088 // Please note that we are assuming only one stream per call.
2089 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2090 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002091 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2092 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002093 sizeof(rem_addr_buf), 3);
2094 } else {
2095 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002096 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002097 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002098
2099 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2100 dir = "sendonly";
2101 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2102 dir = "recvonly";
2103 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2104 dir = "sendrecv";
2105 else
2106 dir = "inactive";
2107
2108
2109 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002110 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002112 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113 info.stream_info[i].fmt.encoding_name.ptr,
2114 info.stream_info[i].fmt.clock_rate / 1000,
2115 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002116 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002117 if (len < 1 || len > end-p) {
2118 *p = '\0';
2119 return;
2120 }
2121
2122 p += len;
2123 *p++ = '\n';
2124 *p = '\0';
2125
2126 if (stat.rx.update_cnt == 0)
2127 strcpy(last_update, "never");
2128 else {
2129 pj_gettimeofday(&now);
2130 PJ_TIME_VAL_SUB(now, stat.rx.update);
2131 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2132 now.sec / 3600,
2133 (now.sec % 3600) / 60,
2134 now.sec % 60,
2135 now.msec);
2136 }
2137
Benny Prijono80019eb2006-08-07 13:22:23 +00002138 pj_gettimeofday(&media_duration);
2139 PJ_TIME_VAL_SUB(media_duration, stat.start);
2140 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2141 media_duration.msec = 1;
2142
Benny Prijono1402a4a2008-01-08 23:41:22 +00002143 /* protect against division by zero */
2144 if (stat.rx.pkt == 0)
2145 stat.rx.pkt = 1;
2146 if (stat.tx.pkt == 0)
2147 stat.tx.pkt = 1;
2148
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002149 len = pj_ansi_snprintf(p, end-p,
2150 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002151 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002152 "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002153 "%s (msec) min avg max last dev\n"
2154 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2155 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 indent, info.stream_info[i].fmt.pt,
2157 last_update,
2158 indent,
2159 good_number(packets, stat.rx.pkt),
2160 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002161 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002162 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2163 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002164 indent,
2165 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002166 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002167 stat.rx.discard,
2168 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002170 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002172 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002173 indent, indent,
2174 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002175 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002176 stat.rx.loss_period.max / 1000.0,
2177 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002178 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002179 indent,
2180 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002181 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002182 stat.rx.jitter.max / 1000.0,
2183 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002184 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 ""
2186 );
2187
2188 if (len < 1 || len > end-p) {
2189 *p = '\0';
2190 return;
2191 }
2192
2193 p += len;
2194 *p++ = '\n';
2195 *p = '\0';
2196
2197 if (stat.tx.update_cnt == 0)
2198 strcpy(last_update, "never");
2199 else {
2200 pj_gettimeofday(&now);
2201 PJ_TIME_VAL_SUB(now, stat.tx.update);
2202 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2203 now.sec / 3600,
2204 (now.sec % 3600) / 60,
2205 now.sec % 60,
2206 now.msec);
2207 }
2208
2209 len = pj_ansi_snprintf(p, end-p,
2210 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002211 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002213 "%s (msec) min avg max last dev \n"
2214 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2215 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 indent,
2217 info.stream_info[i].tx_pt,
2218 info.stream_info[i].param->info.frm_ptime *
2219 info.stream_info[i].param->setting.frm_per_pkt,
2220 last_update,
2221
2222 indent,
2223 good_number(packets, stat.tx.pkt),
2224 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002225 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002226 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2227 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228
2229 indent,
2230 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002231 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002232 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002233 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002234 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002235 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002236
2237 indent, indent,
2238 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002239 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002240 stat.tx.loss_period.max / 1000.0,
2241 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002242 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002243 indent,
2244 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002245 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002246 stat.tx.jitter.max / 1000.0,
2247 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002248 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002249 ""
2250 );
2251
2252 if (len < 1 || len > end-p) {
2253 *p = '\0';
2254 return;
2255 }
2256
2257 p += len;
2258 *p++ = '\n';
2259 *p = '\0';
2260
2261 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002262 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002263 indent,
2264 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002265 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002266 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002267 stat.rtt.last / 1000.0,
2268 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002269 );
2270 if (len < 1 || len > end-p) {
2271 *p = '\0';
2272 return;
2273 }
2274
2275 p += len;
2276 *p++ = '\n';
2277 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002278
2279#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2280# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2281 do { \
2282 if (samples <= 4294) \
2283 usec = samples * 1000000 / clock_rate; \
2284 else { \
2285 usec = samples * 1000 / clock_rate; \
2286 usec *= 1000; \
2287 } \
2288 } while(0)
2289
2290# define PRINT_VOIP_MTC_VAL(s, v) \
2291 if (v == 127) \
2292 sprintf(s, "(na)"); \
2293 else \
2294 sprintf(s, "%d", v)
2295
2296# define VALIDATE_PRINT_BUF() \
2297 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2298 p += len; *p++ = '\n'; *p = '\0'
2299
2300
2301 do {
2302 char loss[16], dup[16];
2303 char jitter[80];
2304 char toh[80];
2305 char plc[16], jba[16], jbr[16];
2306 char signal_lvl[16], noise_lvl[16], rerl[16];
2307 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2308 pjmedia_rtcp_xr_stat xr_stat;
2309 unsigned clock_rate;
2310
2311 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2312 PJ_SUCCESS)
2313 {
2314 break;
2315 }
2316
2317 clock_rate = info.stream_info[i].fmt.clock_rate;
2318
2319 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2320 VALIDATE_PRINT_BUF();
2321
2322 /* Statistics Summary */
2323 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2324 VALIDATE_PRINT_BUF();
2325
2326 if (xr_stat.rx.stat_sum.l)
2327 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2328 else
2329 sprintf(loss, "(na)");
2330
2331 if (xr_stat.rx.stat_sum.d)
2332 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2333 else
2334 sprintf(dup, "(na)");
2335
2336 if (xr_stat.rx.stat_sum.j) {
2337 unsigned jmin, jmax, jmean, jdev;
2338
2339 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2340 clock_rate);
2341 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2342 clock_rate);
2343 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2344 clock_rate);
2345 SAMPLES_TO_USEC(jdev,
2346 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2347 clock_rate);
2348 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2349 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2350 } else
2351 sprintf(jitter, "(report not available)");
2352
2353 if (xr_stat.rx.stat_sum.t) {
2354 sprintf(toh, "%11d %11d %11d %11d",
2355 xr_stat.rx.stat_sum.toh.min,
2356 xr_stat.rx.stat_sum.toh.mean,
2357 xr_stat.rx.stat_sum.toh.max,
2358 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2359 } else
2360 sprintf(toh, "(report not available)");
2361
2362 if (xr_stat.rx.stat_sum.update.sec == 0)
2363 strcpy(last_update, "never");
2364 else {
2365 pj_gettimeofday(&now);
2366 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2367 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2368 now.sec / 3600,
2369 (now.sec % 3600) / 60,
2370 now.sec % 60,
2371 now.msec);
2372 }
2373
2374 len = pj_ansi_snprintf(p, end-p,
2375 "%s RX last update: %s\n"
2376 "%s begin seq=%d, end seq=%d\n"
2377 "%s pkt loss=%s, dup=%s\n"
2378 "%s (msec) min avg max dev\n"
2379 "%s jitter : %s\n"
2380 "%s toh : %s",
2381 indent, last_update,
2382 indent,
2383 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2384 indent, loss, dup,
2385 indent,
2386 indent, jitter,
2387 indent, toh
2388 );
2389 VALIDATE_PRINT_BUF();
2390
2391 if (xr_stat.tx.stat_sum.l)
2392 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2393 else
2394 sprintf(loss, "(na)");
2395
2396 if (xr_stat.tx.stat_sum.d)
2397 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2398 else
2399 sprintf(dup, "(na)");
2400
2401 if (xr_stat.tx.stat_sum.j) {
2402 unsigned jmin, jmax, jmean, jdev;
2403
2404 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2405 clock_rate);
2406 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2407 clock_rate);
2408 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2409 clock_rate);
2410 SAMPLES_TO_USEC(jdev,
2411 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2412 clock_rate);
2413 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2414 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2415 } else
2416 sprintf(jitter, "(report not available)");
2417
2418 if (xr_stat.tx.stat_sum.t) {
2419 sprintf(toh, "%11d %11d %11d %11d",
2420 xr_stat.tx.stat_sum.toh.min,
2421 xr_stat.tx.stat_sum.toh.mean,
2422 xr_stat.tx.stat_sum.toh.max,
2423 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2424 } else
2425 sprintf(toh, "(report not available)");
2426
2427 if (xr_stat.tx.stat_sum.update.sec == 0)
2428 strcpy(last_update, "never");
2429 else {
2430 pj_gettimeofday(&now);
2431 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2432 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2433 now.sec / 3600,
2434 (now.sec % 3600) / 60,
2435 now.sec % 60,
2436 now.msec);
2437 }
2438
2439 len = pj_ansi_snprintf(p, end-p,
2440 "%s TX last update: %s\n"
2441 "%s begin seq=%d, end seq=%d\n"
2442 "%s pkt loss=%s, dup=%s\n"
2443 "%s (msec) min avg max dev\n"
2444 "%s jitter : %s\n"
2445 "%s toh : %s",
2446 indent, last_update,
2447 indent,
2448 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2449 indent, loss, dup,
2450 indent,
2451 indent, jitter,
2452 indent, toh
2453 );
2454 VALIDATE_PRINT_BUF();
2455
2456
2457 /* VoIP Metrics */
2458 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2459 VALIDATE_PRINT_BUF();
2460
2461 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2462 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2463 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2464 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2465 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2466 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2467 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2468
2469 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2470 case PJMEDIA_RTCP_XR_PLC_DIS:
2471 sprintf(plc, "DISABLED");
2472 break;
2473 case PJMEDIA_RTCP_XR_PLC_ENH:
2474 sprintf(plc, "ENHANCED");
2475 break;
2476 case PJMEDIA_RTCP_XR_PLC_STD:
2477 sprintf(plc, "STANDARD");
2478 break;
2479 case PJMEDIA_RTCP_XR_PLC_UNK:
2480 default:
2481 sprintf(plc, "UNKNOWN");
2482 break;
2483 }
2484
2485 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2486 case PJMEDIA_RTCP_XR_JB_FIXED:
2487 sprintf(jba, "FIXED");
2488 break;
2489 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2490 sprintf(jba, "ADAPTIVE");
2491 break;
2492 default:
2493 sprintf(jba, "UNKNOWN");
2494 break;
2495 }
2496
2497 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2498
2499 if (xr_stat.rx.voip_mtc.update.sec == 0)
2500 strcpy(last_update, "never");
2501 else {
2502 pj_gettimeofday(&now);
2503 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2504 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2505 now.sec / 3600,
2506 (now.sec % 3600) / 60,
2507 now.sec % 60,
2508 now.msec);
2509 }
2510
2511 len = pj_ansi_snprintf(p, end-p,
2512 "%s RX last update: %s\n"
2513 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2514 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2515 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2516 "%s delay : round trip=%d%s, end system=%d%s\n"
2517 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2518 "%s quality : R factor=%s, ext R factor=%s\n"
2519 "%s MOS LQ=%s, MOS CQ=%s\n"
2520 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2521 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2522 indent,
2523 last_update,
2524 /* packets */
2525 indent,
2526 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2527 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2528 /* burst */
2529 indent,
2530 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2531 xr_stat.rx.voip_mtc.burst_dur, "ms",
2532 /* gap */
2533 indent,
2534 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2535 xr_stat.rx.voip_mtc.gap_dur, "ms",
2536 /* delay */
2537 indent,
2538 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2539 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2540 /* level */
2541 indent,
2542 signal_lvl, "dB",
2543 noise_lvl, "dB",
2544 rerl, "",
2545 /* quality */
2546 indent,
2547 r_factor, ext_r_factor,
2548 indent,
2549 mos_lq, mos_cq,
2550 /* config */
2551 indent,
2552 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2553 /* JB delay */
2554 indent,
2555 xr_stat.rx.voip_mtc.jb_nom, "ms",
2556 xr_stat.rx.voip_mtc.jb_max, "ms",
2557 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2558 );
2559 VALIDATE_PRINT_BUF();
2560
2561 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2562 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2563 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2564 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2565 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2566 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2567 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2568
2569 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2570 case PJMEDIA_RTCP_XR_PLC_DIS:
2571 sprintf(plc, "DISABLED");
2572 break;
2573 case PJMEDIA_RTCP_XR_PLC_ENH:
2574 sprintf(plc, "ENHANCED");
2575 break;
2576 case PJMEDIA_RTCP_XR_PLC_STD:
2577 sprintf(plc, "STANDARD");
2578 break;
2579 case PJMEDIA_RTCP_XR_PLC_UNK:
2580 default:
2581 sprintf(plc, "unknown");
2582 break;
2583 }
2584
2585 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2586 case PJMEDIA_RTCP_XR_JB_FIXED:
2587 sprintf(jba, "FIXED");
2588 break;
2589 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2590 sprintf(jba, "ADAPTIVE");
2591 break;
2592 default:
2593 sprintf(jba, "unknown");
2594 break;
2595 }
2596
2597 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2598
2599 if (xr_stat.tx.voip_mtc.update.sec == 0)
2600 strcpy(last_update, "never");
2601 else {
2602 pj_gettimeofday(&now);
2603 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2604 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2605 now.sec / 3600,
2606 (now.sec % 3600) / 60,
2607 now.sec % 60,
2608 now.msec);
2609 }
2610
2611 len = pj_ansi_snprintf(p, end-p,
2612 "%s TX last update: %s\n"
2613 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2614 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2615 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2616 "%s delay : round trip=%d%s, end system=%d%s\n"
2617 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2618 "%s quality : R factor=%s, ext R factor=%s\n"
2619 "%s MOS LQ=%s, MOS CQ=%s\n"
2620 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2621 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2622 indent,
2623 last_update,
2624 /* pakcets */
2625 indent,
2626 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2627 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2628 /* burst */
2629 indent,
2630 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2631 xr_stat.tx.voip_mtc.burst_dur, "ms",
2632 /* gap */
2633 indent,
2634 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2635 xr_stat.tx.voip_mtc.gap_dur, "ms",
2636 /* delay */
2637 indent,
2638 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2639 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2640 /* level */
2641 indent,
2642 signal_lvl, "dB",
2643 noise_lvl, "dB",
2644 rerl, "",
2645 /* quality */
2646 indent,
2647 r_factor, ext_r_factor,
2648 indent,
2649 mos_lq, mos_cq,
2650 /* config */
2651 indent,
2652 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2653 /* JB delay */
2654 indent,
2655 xr_stat.tx.voip_mtc.jb_nom, "ms",
2656 xr_stat.tx.voip_mtc.jb_max, "ms",
2657 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2658 );
2659 VALIDATE_PRINT_BUF();
2660
2661
2662 /* RTT delay (by receiver side) */
2663 len = pj_ansi_snprintf(p, end-p,
2664 "%s RTT (from recv) min avg max last dev",
2665 indent);
2666 VALIDATE_PRINT_BUF();
2667 len = pj_ansi_snprintf(p, end-p,
2668 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2669 indent,
2670 xr_stat.rtt.min / 1000.0,
2671 xr_stat.rtt.mean / 1000.0,
2672 xr_stat.rtt.max / 1000.0,
2673 xr_stat.rtt.last / 1000.0,
2674 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2675 );
2676 VALIDATE_PRINT_BUF();
2677 } while(0);
2678#endif
2679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002680 }
2681}
2682
2683
2684/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002685void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002686 int call_id,
2687 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002688{
2689 int len;
2690 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2691 pjsip_dialog *dlg = inv->dlg;
2692 char userinfo[128];
2693
2694 /* Dump invite sesion info. */
2695
2696 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2697 if (len < 1)
2698 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2699 else
2700 userinfo[len] = '\0';
2701
2702 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2703 title,
2704 pjsip_inv_state_name(inv->state),
2705 userinfo);
2706 if (len < 1 || len >= (int)size) {
2707 pj_ansi_strcpy(buf, "<--uri too long-->");
2708 len = 18;
2709 } else
2710 buf[len] = '\0';
2711}
2712
2713
2714/*
2715 * Dump call and media statistics to string.
2716 */
2717PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2718 pj_bool_t with_media,
2719 char *buffer,
2720 unsigned maxlen,
2721 const char *indent)
2722{
2723 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002724 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002725 pj_time_val duration, res_delay, con_delay;
2726 char tmp[128];
2727 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002728 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002729 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002730 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002731
2732 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2733 PJ_EINVAL);
2734
Benny Prijonodc752ca2006-09-22 16:55:42 +00002735 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002736 if (status != PJ_SUCCESS)
2737 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002738
2739 *buffer = '\0';
2740 p = buffer;
2741 end = buffer + maxlen;
2742 len = 0;
2743
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002744 print_call(indent, call_id, tmp, sizeof(tmp));
2745
2746 len = pj_ansi_strlen(tmp);
2747 pj_ansi_strcpy(buffer, tmp);
2748
2749 p += len;
2750 *p++ = '\r';
2751 *p++ = '\n';
2752
2753 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002754 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002755 pj_gettimeofday(&duration);
2756 PJ_TIME_VAL_SUB(duration, call->conn_time);
2757 con_delay = call->conn_time;
2758 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2759 } else {
2760 duration.sec = duration.msec = 0;
2761 con_delay.sec = con_delay.msec = 0;
2762 }
2763
2764 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002765 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002766 res_delay = call->res_time;
2767 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2768 } else {
2769 res_delay.sec = res_delay.msec = 0;
2770 }
2771
2772 /* Print duration */
2773 len = pj_ansi_snprintf(p, end-p,
2774 "%s Call time: %02dh:%02dm:%02ds, "
2775 "1st res in %d ms, conn in %dms",
2776 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002777 (int)(duration.sec / 3600),
2778 (int)((duration.sec % 3600)/60),
2779 (int)(duration.sec % 60),
2780 (int)PJ_TIME_VAL_MSEC(res_delay),
2781 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002782
2783 if (len > 0 && len < end-p) {
2784 p += len;
2785 *p++ = '\n';
2786 *p = '\0';
2787 }
2788
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002789 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002790 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002791 pjmedia_transport_get_info(call->med_tp, &tp_info);
2792 if (tp_info.specific_info_cnt > 0) {
2793 int i;
2794 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2795 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2796 {
2797 pjmedia_srtp_info *srtp_info =
2798 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2799
2800 len = pj_ansi_snprintf(p, end-p,
2801 "%s SRTP status: %s Crypto-suite: %s",
2802 indent,
2803 (srtp_info->active?"Active":"Not active"),
2804 srtp_info->tx_policy.name.ptr);
2805 if (len > 0 && len < end-p) {
2806 p += len;
2807 *p++ = '\n';
2808 *p = '\0';
2809 }
2810 break;
2811 }
2812 }
2813 }
2814
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002815 /* Dump session statistics */
2816 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002817 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002818
Benny Prijonodc752ca2006-09-22 16:55:42 +00002819 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002820
2821 return PJ_SUCCESS;
2822}
2823
2824
2825/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002826 * This callback receives notification from invite session when the
2827 * session state has changed.
2828 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002829static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2830 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002831{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002832 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002833
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002834 PJSUA_LOCK();
2835
Benny Prijonoa1e69682007-05-11 15:14:34 +00002836 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002837
2838 if (!call) {
2839 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002840 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002841 }
2842
Benny Prijonoe21e7842006-04-09 16:46:05 +00002843
2844 /* Get call times */
2845 switch (inv->state) {
2846 case PJSIP_INV_STATE_EARLY:
2847 case PJSIP_INV_STATE_CONNECTING:
2848 if (call->res_time.sec == 0)
2849 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002850 call->last_code = (pjsip_status_code)
2851 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002852 pj_strncpy(&call->last_text,
2853 &e->body.tsx_state.tsx->status_text,
2854 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002855 break;
2856 case PJSIP_INV_STATE_CONFIRMED:
2857 pj_gettimeofday(&call->conn_time);
2858 break;
2859 case PJSIP_INV_STATE_DISCONNECTED:
2860 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002861 if (call->res_time.sec == 0)
2862 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002863 if (e->type == PJSIP_EVENT_TSX_STATE &&
2864 e->body.tsx_state.tsx->status_code > call->last_code)
2865 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002866 call->last_code = (pjsip_status_code)
2867 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002868 pj_strncpy(&call->last_text,
2869 &e->body.tsx_state.tsx->status_text,
2870 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002871 } else {
2872 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2873 pj_strncpy(&call->last_text,
2874 pjsip_get_status_text(call->last_code),
2875 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002876 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002877 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002878 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002879 call->last_code = (pjsip_status_code)
2880 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881 pj_strncpy(&call->last_text,
2882 &e->body.tsx_state.tsx->status_text,
2883 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002884 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002885 }
2886
Benny Prijono26ff9062006-02-21 23:47:00 +00002887 /* If this is an outgoing INVITE that was created because of
2888 * REFER/transfer, send NOTIFY to transferer.
2889 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002890 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002891 int st_code = -1;
2892 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2893
2894
Benny Prijonoa91a0032006-02-26 21:23:45 +00002895 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002896 case PJSIP_INV_STATE_NULL:
2897 case PJSIP_INV_STATE_CALLING:
2898 /* Do nothing */
2899 break;
2900
2901 case PJSIP_INV_STATE_EARLY:
2902 case PJSIP_INV_STATE_CONNECTING:
2903 st_code = e->body.tsx_state.tsx->status_code;
2904 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2905 break;
2906
2907 case PJSIP_INV_STATE_CONFIRMED:
2908 /* When state is confirmed, send the final 200/OK and terminate
2909 * subscription.
2910 */
2911 st_code = e->body.tsx_state.tsx->status_code;
2912 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2913 break;
2914
2915 case PJSIP_INV_STATE_DISCONNECTED:
2916 st_code = e->body.tsx_state.tsx->status_code;
2917 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2918 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002919
Benny Prijono8b1889b2006-06-06 18:40:40 +00002920 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002921 /* Nothing to do. Just to keep gcc from complaining about
2922 * unused enums.
2923 */
2924 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002925 }
2926
2927 if (st_code != -1) {
2928 pjsip_tx_data *tdata;
2929 pj_status_t status;
2930
Benny Prijonoa91a0032006-02-26 21:23:45 +00002931 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002932 ev_state, st_code,
2933 NULL, &tdata);
2934 if (status != PJ_SUCCESS) {
2935 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2936 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002937 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002938 if (status != PJ_SUCCESS) {
2939 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2940 }
2941 }
2942 }
2943 }
2944
Benny Prijono84126ab2006-02-09 09:30:09 +00002945
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002946 if (pjsua_var.ua_cfg.cb.on_call_state)
2947 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002948
2949 /* call->inv may be NULL now */
2950
Benny Prijono84126ab2006-02-09 09:30:09 +00002951 /* Destroy media session when invite session is disconnected. */
2952 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002953
Benny Prijonoa91a0032006-02-26 21:23:45 +00002954 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002955
Benny Prijono275fd682006-03-22 11:59:11 +00002956 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002957 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002958
Benny Prijono105217f2006-03-06 16:25:59 +00002959 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002960 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002961 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002962
2963 /* Reset call */
2964 reset_call(call->index);
2965
Benny Prijono84126ab2006-02-09 09:30:09 +00002966 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002967
2968 PJSUA_UNLOCK();
2969}
2970
2971/*
2972 * This callback is called by invite session framework when UAC session
2973 * has forked.
2974 */
2975static void pjsua_call_on_forked( pjsip_inv_session *inv,
2976 pjsip_event *e)
2977{
2978 PJ_UNUSED_ARG(inv);
2979 PJ_UNUSED_ARG(e);
2980
2981 PJ_TODO(HANDLE_FORKED_DIALOG);
2982}
2983
2984
2985/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002986 * Callback from UA layer when forked dialog response is received.
2987 */
2988pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2989{
2990 if (dlg->uac_has_2xx &&
2991 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
2992 pjsip_rdata_get_tsx(res) == NULL &&
2993 res->msg_info.msg->line.status.code/100 == 2)
2994 {
2995 pjsip_dialog *forked_dlg;
2996 pjsip_tx_data *bye;
2997 pj_status_t status;
2998
2999 /* Create forked dialog */
3000 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3001 if (status != PJ_SUCCESS)
3002 return NULL;
3003
3004 pjsip_dlg_inc_lock(forked_dlg);
3005
3006 /* Disconnect the call */
3007 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3008 -1, &bye);
3009 if (status == PJ_SUCCESS) {
3010 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3011 }
3012
3013 pjsip_dlg_dec_lock(forked_dlg);
3014
3015 if (status != PJ_SUCCESS) {
3016 return NULL;
3017 }
3018
3019 return forked_dlg;
3020
3021 } else {
3022 return dlg;
3023 }
3024}
3025
3026/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003027 * Disconnect call upon error.
3028 */
3029static void call_disconnect( pjsip_inv_session *inv,
3030 int code )
3031{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003032 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003033 pjsip_tx_data *tdata;
3034 pj_status_t status;
3035
Benny Prijono59b3aed2008-01-15 16:54:54 +00003036 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3037
Benny Prijonoa38ada02006-07-02 14:22:35 +00003038 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003039 if (status != PJ_SUCCESS)
3040 return;
3041
3042 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003043 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3044 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3045 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003046 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003047 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003048
Benny Prijono734fc2d2008-03-17 16:05:35 +00003049 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003050 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003051 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003052 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003053 if (status == PJ_SUCCESS) {
3054 pjsip_create_sdp_body(tdata->pool, local_sdp,
3055 &tdata->msg->body);
3056 }
3057 }
3058
3059 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003060}
3061
3062/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003063 * Callback to be called when SDP offer/answer negotiation has just completed
3064 * in the session. This function will start/update media if negotiation
3065 * has succeeded.
3066 */
3067static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3068 pj_status_t status)
3069{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003070 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003071 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003072 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003073
3074 PJSUA_LOCK();
3075
Benny Prijonoa1e69682007-05-11 15:14:34 +00003076 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003077
3078 if (status != PJ_SUCCESS) {
3079
3080 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3081
Benny Prijono2331d202008-06-26 15:46:52 +00003082 /* Do not deinitialize media since this may be a re-INVITE or
3083 * UPDATE (which in this case the media should not get affected
3084 * by the failed re-INVITE/UPDATE). The media will be shutdown
3085 * when call is disconnected anyway.
3086 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003087 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003088 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003089
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003090 /* Disconnect call if we're not in the middle of initializing an
3091 * UAS dialog and if this is not a re-INVITE
3092 */
3093 if (inv->state != PJSIP_INV_STATE_NULL &&
3094 inv->state != PJSIP_INV_STATE_CONFIRMED)
3095 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003096 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003097 }
3098
3099 PJSUA_UNLOCK();
3100 return;
3101 }
3102
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003103
3104 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003105 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003106 if (status != PJ_SUCCESS) {
3107 pjsua_perror(THIS_FILE,
3108 "Unable to retrieve currently active local SDP",
3109 status);
3110 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3111 PJSUA_UNLOCK();
3112 return;
3113 }
3114
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003115 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3116 if (status != PJ_SUCCESS) {
3117 pjsua_perror(THIS_FILE,
3118 "Unable to retrieve currently active remote SDP",
3119 status);
3120 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3121 PJSUA_UNLOCK();
3122 return;
3123 }
3124
Benny Prijono91a6a172007-10-31 08:59:29 +00003125 /* Update remote's NAT type */
3126 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3127 update_remote_nat_type(call, remote_sdp);
3128 }
3129
3130 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003131 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003132 if (status != PJ_SUCCESS) {
3133 pjsua_perror(THIS_FILE, "Unable to create media session",
3134 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003135 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003136 /* No need to deinitialize; media will be shutdown when call
3137 * state is disconnected anyway.
3138 */
3139 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003140 PJSUA_UNLOCK();
3141 return;
3142 }
3143
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003144
3145 /* Call application callback, if any */
3146 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3147 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3148
3149
3150 PJSUA_UNLOCK();
3151}
3152
3153
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003154/* Create SDP for call hold. */
3155static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3156 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003157{
3158 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003159 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003160 pjmedia_sdp_session *sdp;
3161
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003162 /* Use call's pool */
3163 pool = call->inv->pool;
3164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003165 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003166 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3167 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003168 if (status != PJ_SUCCESS) {
3169 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3170 return status;
3171 }
3172
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003173 /* Call-hold is done by set the media direction to 'sendonly'
3174 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3175 * 'inactive' (PJMEDIA_DIR_NONE).
3176 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3177 */
3178 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3179 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003180
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003181 /* Remove existing directions attributes */
3182 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3183 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3184 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3185 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003186
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003187 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3188 /* Add sendonly attribute */
3189 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3190 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3191 } else {
3192 /* Add inactive attribute */
3193 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3194 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3195 }
3196 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003197
3198 *p_answer = sdp;
3199
3200 return status;
3201}
3202
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003203/*
3204 * Called when session received new offer.
3205 */
3206static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3207 const pjmedia_sdp_session *offer)
3208{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003209 pjsua_call *call;
3210 pjmedia_sdp_conn *conn;
3211 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003212 pj_status_t status;
3213
3214 PJSUA_LOCK();
3215
Benny Prijonoa1e69682007-05-11 15:14:34 +00003216 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003217
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003218 conn = offer->media[0]->conn;
3219 if (!conn)
3220 conn = offer->conn;
3221
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003222 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003223 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3224 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003225
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003226 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3227 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003228 if (status != PJ_SUCCESS) {
3229 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3230 PJSUA_UNLOCK();
3231 return;
3232 }
3233
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003234 /* Check if offer's conn address is zero */
3235 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3236 pj_strcmp2(&conn->addr, "0")==0)
3237 {
3238 /* Modify address */
3239 answer->conn->addr = pj_str("0.0.0.0");
3240 }
3241
3242 /* Check if call is on-hold */
3243 if (call->local_hold) {
3244 pjmedia_sdp_attr *attr;
3245
3246 /* Remove existing directions attributes */
3247 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3248 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3249 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3250 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3251
3252 /* Keep call on-hold by setting 'sendonly' attribute.
3253 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3254 */
3255 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3256 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3257 }
3258
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003259 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3260 if (status != PJ_SUCCESS) {
3261 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3262 PJSUA_UNLOCK();
3263 return;
3264 }
3265
3266 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003267}
3268
3269
3270/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003271 * Called to generate new offer.
3272 */
3273static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3274 pjmedia_sdp_session **offer)
3275{
3276 pjsua_call *call;
3277 pj_status_t status;
3278
3279 PJSUA_LOCK();
3280
3281 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3282
3283 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003284 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003285 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003286 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003287 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003288 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003289 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003290 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3291 call->index));
3292
Benny Prijonod8179652008-01-23 20:39:07 +00003293 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003294 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003295 }
3296
3297 if (status != PJ_SUCCESS) {
3298 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3299 PJSUA_UNLOCK();
3300 return;
3301 }
3302
Benny Prijono77998ce2007-06-20 10:03:46 +00003303 PJSUA_UNLOCK();
3304}
3305
3306
3307/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003308 * Callback called by event framework when the xfer subscription state
3309 * has changed.
3310 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003311static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3312{
3313
3314 PJ_UNUSED_ARG(event);
3315
3316 /*
3317 * When subscription is accepted (got 200/OK to REFER), check if
3318 * subscription suppressed.
3319 */
3320 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3321
3322 pjsip_rx_data *rdata;
3323 pjsip_generic_string_hdr *refer_sub;
3324 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3325 pjsua_call *call;
3326
Benny Prijonoa1e69682007-05-11 15:14:34 +00003327 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003328
3329 /* Must be receipt of response message */
3330 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3331 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3332 rdata = event->body.tsx_state.src.rdata;
3333
3334 /* Find Refer-Sub header */
3335 refer_sub = (pjsip_generic_string_hdr*)
3336 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3337 &REFER_SUB, NULL);
3338
3339 /* Check if subscription is suppressed */
3340 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3341 /* Since no subscription is desired, assume that call has been
3342 * transfered successfully.
3343 */
3344 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3345 const pj_str_t ACCEPTED = { "Accepted", 8 };
3346 pj_bool_t cont = PJ_FALSE;
3347 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3348 200,
3349 &ACCEPTED,
3350 PJ_TRUE,
3351 &cont);
3352 }
3353
3354 /* Yes, subscription is suppressed.
3355 * Terminate our subscription now.
3356 */
3357 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3358 "event subcription..."));
3359 pjsip_evsub_terminate(sub, PJ_TRUE);
3360
3361 } else {
3362 /* Notify application about call transfer progress.
3363 * Initially notify with 100/Accepted status.
3364 */
3365 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3366 const pj_str_t ACCEPTED = { "Accepted", 8 };
3367 pj_bool_t cont = PJ_FALSE;
3368 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3369 100,
3370 &ACCEPTED,
3371 PJ_FALSE,
3372 &cont);
3373 }
3374 }
3375 }
3376 /*
3377 * On incoming NOTIFY, notify application about call transfer progress.
3378 */
3379 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3380 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3381 {
3382 pjsua_call *call;
3383 pjsip_msg *msg;
3384 pjsip_msg_body *body;
3385 pjsip_status_line status_line;
3386 pj_bool_t is_last;
3387 pj_bool_t cont;
3388 pj_status_t status;
3389
Benny Prijonoa1e69682007-05-11 15:14:34 +00003390 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003391
3392 /* When subscription is terminated, clear the xfer_sub member of
3393 * the inv_data.
3394 */
3395 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3396 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3397 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3398
3399 }
3400
3401 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3402 /* Application is not interested with call progress status */
3403 return;
3404 }
3405
3406 /* This better be a NOTIFY request */
3407 if (event->type == PJSIP_EVENT_TSX_STATE &&
3408 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3409 {
3410 pjsip_rx_data *rdata;
3411
3412 rdata = event->body.tsx_state.src.rdata;
3413
3414 /* Check if there's body */
3415 msg = rdata->msg_info.msg;
3416 body = msg->body;
3417 if (!body) {
3418 PJ_LOG(4,(THIS_FILE,
3419 "Warning: received NOTIFY without message body"));
3420 return;
3421 }
3422
3423 /* Check for appropriate content */
3424 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3425 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3426 {
3427 PJ_LOG(4,(THIS_FILE,
3428 "Warning: received NOTIFY with non message/sipfrag "
3429 "content"));
3430 return;
3431 }
3432
3433 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003434 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003435 &status_line);
3436 if (status != PJ_SUCCESS) {
3437 PJ_LOG(4,(THIS_FILE,
3438 "Warning: received NOTIFY with invalid "
3439 "message/sipfrag content"));
3440 return;
3441 }
3442
3443 } else {
3444 status_line.code = 500;
3445 status_line.reason = *pjsip_get_status_text(500);
3446 }
3447
3448 /* Notify application */
3449 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3450 cont = !is_last;
3451 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3452 status_line.code,
3453 &status_line.reason,
3454 is_last, &cont);
3455
3456 if (!cont) {
3457 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3458 }
3459 }
3460}
3461
3462
3463/*
3464 * Callback called by event framework when the xfer subscription state
3465 * has changed.
3466 */
3467static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003468{
3469
3470 PJ_UNUSED_ARG(event);
3471
3472 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003473 * When subscription is terminated, clear the xfer_sub member of
3474 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003475 */
3476 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003477 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003478
Benny Prijonoa1e69682007-05-11 15:14:34 +00003479 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003480 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003481 return;
3482
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003483 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003484 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003485
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003486 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003487 }
3488}
3489
3490
3491/*
3492 * Follow transfer (REFER) request.
3493 */
3494static void on_call_transfered( pjsip_inv_session *inv,
3495 pjsip_rx_data *rdata )
3496{
3497 pj_status_t status;
3498 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003499 pjsua_call *existing_call;
3500 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003501 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003502 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003503 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003504 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003505 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003506 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003507 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003508 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003509 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003510 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003511 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003512 pjsip_evsub *sub;
3513
Benny Prijonoa1e69682007-05-11 15:14:34 +00003514 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003515
Benny Prijono26ff9062006-02-21 23:47:00 +00003516 /* Find the Refer-To header */
3517 refer_to = (pjsip_generic_string_hdr*)
3518 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3519
3520 if (refer_to == NULL) {
3521 /* Invalid Request.
3522 * No Refer-To header!
3523 */
3524 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003525 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003526 return;
3527 }
3528
Benny Prijonoc8141a82006-08-20 09:12:19 +00003529 /* Find optional Refer-Sub header */
3530 refer_sub = (pjsip_generic_string_hdr*)
3531 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3532
3533 if (refer_sub) {
3534 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3535 no_refer_sub = PJ_TRUE;
3536 }
3537
Benny Prijono053f5222006-11-11 16:16:04 +00003538 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3539 * request.
3540 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003541 ref_by_hdr = (pjsip_hdr*)
3542 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003543 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003544
Benny Prijono9fc735d2006-05-28 14:58:12 +00003545 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003546 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003547 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3548 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3549 &refer_to->hvalue,
3550 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003551
3552 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003553 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003554 if (code >= 300) {
3555 /* Application rejects call transfer request */
3556 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3557 return;
3558 }
3559
Benny Prijono26ff9062006-02-21 23:47:00 +00003560 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3561 (int)inv->dlg->remote.info_str.slen,
3562 inv->dlg->remote.info_str.ptr,
3563 (int)refer_to->hvalue.slen,
3564 refer_to->hvalue.ptr));
3565
Benny Prijonoc8141a82006-08-20 09:12:19 +00003566 if (no_refer_sub) {
3567 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003568 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003569 */
3570 pjsip_tx_data *tdata;
3571 const pj_str_t str_false = { "false", 5};
3572 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003573
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003574 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3575 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003576 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003577 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003578 status);
3579 return;
3580 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003581
Benny Prijonoc8141a82006-08-20 09:12:19 +00003582 /* Add Refer-Sub header */
3583 hdr = (pjsip_hdr*)
3584 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3585 &str_false);
3586 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003587
Benny Prijono26ff9062006-02-21 23:47:00 +00003588
Benny Prijonoc8141a82006-08-20 09:12:19 +00003589 /* Send answer */
3590 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3591 tdata);
3592 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003593 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003594 status);
3595 return;
3596 }
3597
3598 /* Don't have subscription */
3599 sub = NULL;
3600
3601 } else {
3602 struct pjsip_evsub_user xfer_cb;
3603 pjsip_hdr hdr_list;
3604
3605 /* Init callback */
3606 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003607 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003608
3609 /* Init additional header list to be sent with REFER response */
3610 pj_list_init(&hdr_list);
3611
3612 /* Create transferee event subscription */
3613 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3614 if (status != PJ_SUCCESS) {
3615 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3616 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3617 return;
3618 }
3619
3620 /* If there's Refer-Sub header and the value is "true", send back
3621 * Refer-Sub in the response with value "true" too.
3622 */
3623 if (refer_sub) {
3624 const pj_str_t str_true = { "true", 4 };
3625 pjsip_hdr *hdr;
3626
3627 hdr = (pjsip_hdr*)
3628 pjsip_generic_string_hdr_create(inv->dlg->pool,
3629 &str_refer_sub,
3630 &str_true);
3631 pj_list_push_back(&hdr_list, hdr);
3632
3633 }
3634
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003635 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003636 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3637
3638 /* Create initial NOTIFY request */
3639 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3640 100, NULL, &tdata);
3641 if (status != PJ_SUCCESS) {
3642 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3643 status);
3644 return;
3645 }
3646
3647 /* Send initial NOTIFY request */
3648 status = pjsip_xfer_send_request( sub, tdata);
3649 if (status != PJ_SUCCESS) {
3650 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3651 return;
3652 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003653 }
3654
3655 /* We're cheating here.
3656 * We need to get a null terminated string from a pj_str_t.
3657 * So grab the pointer from the hvalue and NULL terminate it, knowing
3658 * that the NULL position will be occupied by a newline.
3659 */
3660 uri = refer_to->hvalue.ptr;
3661 uri[refer_to->hvalue.slen] = '\0';
3662
Benny Prijono053f5222006-11-11 16:16:04 +00003663 /* Init msg_data */
3664 pjsua_msg_data_init(&msg_data);
3665
3666 /* If Referred-By header is present in the REFER request, copy this
3667 * to the outgoing INVITE request.
3668 */
3669 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003670 pjsip_hdr *dup = (pjsip_hdr*)
3671 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003672 pj_list_push_back(&msg_data.hdr_list, dup);
3673 }
3674
Benny Prijono26ff9062006-02-21 23:47:00 +00003675 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003676 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003677 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003678 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003679 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003680 if (status != PJ_SUCCESS) {
3681
Benny Prijonoc8141a82006-08-20 09:12:19 +00003682 /* Notify xferer about the error (if we have subscription) */
3683 if (sub) {
3684 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3685 500, NULL, &tdata);
3686 if (status != PJ_SUCCESS) {
3687 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3688 status);
3689 return;
3690 }
3691 status = pjsip_xfer_send_request(sub, tdata);
3692 if (status != PJ_SUCCESS) {
3693 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3694 status);
3695 return;
3696 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003697 }
3698 return;
3699 }
3700
Benny Prijonoc8141a82006-08-20 09:12:19 +00003701 if (sub) {
3702 /* Put the server subscription in inv_data.
3703 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3704 * reported back to the server subscription.
3705 */
3706 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003707
Benny Prijonoc8141a82006-08-20 09:12:19 +00003708 /* Put the invite_data in the subscription. */
3709 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3710 &pjsua_var.calls[new_call]);
3711 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003712}
3713
3714
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003715
Benny Prijono26ff9062006-02-21 23:47:00 +00003716/*
3717 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003718 * session. We use this to trap:
3719 * - incoming REFER request.
3720 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003721 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003722static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3723 pjsip_transaction *tsx,
3724 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003725{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003726 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003727
3728 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003729
Benny Prijono2285e7e2008-12-17 14:28:18 +00003730 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3731
3732 if (call == NULL) {
3733 PJSUA_UNLOCK();
3734 return;
3735 }
3736
Benny Prijonofeb69f42007-10-05 09:12:26 +00003737 /* Notify application callback first */
3738 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3739 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3740 }
3741
Benny Prijono26ff9062006-02-21 23:47:00 +00003742 if (tsx->role==PJSIP_ROLE_UAS &&
3743 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003744 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003745 {
3746 /*
3747 * Incoming REFER request.
3748 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003749 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003750
Benny Prijono26ff9062006-02-21 23:47:00 +00003751 }
Benny Prijonob0808372006-03-02 21:18:58 +00003752 else if (tsx->role==PJSIP_ROLE_UAS &&
3753 tsx->state==PJSIP_TSX_STATE_TRYING &&
3754 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3755 {
3756 /*
3757 * Incoming MESSAGE request!
3758 */
3759 pjsip_rx_data *rdata;
3760 pjsip_msg *msg;
3761 pjsip_accept_hdr *accept_hdr;
3762 pj_status_t status;
3763
3764 rdata = e->body.tsx_state.src.rdata;
3765 msg = rdata->msg_info.msg;
3766
3767 /* Request MUST have message body, with Content-Type equal to
3768 * "text/plain".
3769 */
3770 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3771
3772 pjsip_hdr hdr_list;
3773
3774 pj_list_init(&hdr_list);
3775 pj_list_push_back(&hdr_list, accept_hdr);
3776
3777 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3778 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003779 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003780 return;
3781 }
3782
3783 /* Respond with 200 first, so that remote doesn't retransmit in case
3784 * the UI takes too long to process the message.
3785 */
3786 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3787
3788 /* Process MESSAGE request */
3789 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3790 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003791
Benny Prijonob0808372006-03-02 21:18:58 +00003792 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003793 else if (tsx->role == PJSIP_ROLE_UAC &&
3794 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003795 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003796 /* Handle outgoing pager status */
3797 if (tsx->status_code >= 200) {
3798 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003799
Benny Prijonoa1e69682007-05-11 15:14:34 +00003800 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003801 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003802
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003803 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3804 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3805 &im_data->to,
3806 &im_data->body,
3807 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003808 (pjsip_status_code)
3809 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003810 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003811 }
Benny Prijonofccab712006-02-22 22:23:22 +00003812 }
Benny Prijono834aee32006-02-19 01:38:06 +00003813 }
Benny Prijono834aee32006-02-19 01:38:06 +00003814
Benny Prijono26ff9062006-02-21 23:47:00 +00003815
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003816 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003817}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003818
3819
3820/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003821static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3822 const pjsip_uri *target,
3823 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003824{
3825 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003826 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003827
3828 PJSUA_LOCK();
3829
3830 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003831 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3832 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003833 } else {
3834 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3835 "(callback not implemented by application). Disconnecting "
3836 "call.",
3837 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003838 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003839 }
3840
3841 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003842
3843 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003844}
3845