blob: bb1ab31880208f8753068919ca769e96d942e2a0 [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));
Benny Prijono329d6382009-05-29 13:04:03 +00002697 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002698 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;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002904 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2905 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2906 else
2907 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002908 break;
2909
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002910#if 0
2911/* We don't need this, as we've terminated the subscription in
2912 * CONNECTING state.
2913 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002914 case PJSIP_INV_STATE_CONFIRMED:
2915 /* When state is confirmed, send the final 200/OK and terminate
2916 * subscription.
2917 */
2918 st_code = e->body.tsx_state.tsx->status_code;
2919 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2920 break;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002921#endif
Benny Prijono26ff9062006-02-21 23:47:00 +00002922
2923 case PJSIP_INV_STATE_DISCONNECTED:
2924 st_code = e->body.tsx_state.tsx->status_code;
2925 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2926 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002927
Benny Prijono8b1889b2006-06-06 18:40:40 +00002928 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002929 /* Nothing to do. Just to keep gcc from complaining about
2930 * unused enums.
2931 */
2932 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002933 }
2934
2935 if (st_code != -1) {
2936 pjsip_tx_data *tdata;
2937 pj_status_t status;
2938
Benny Prijonoa91a0032006-02-26 21:23:45 +00002939 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002940 ev_state, st_code,
2941 NULL, &tdata);
2942 if (status != PJ_SUCCESS) {
2943 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2944 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002945 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002946 if (status != PJ_SUCCESS) {
2947 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2948 }
2949 }
2950 }
2951 }
2952
Benny Prijono84126ab2006-02-09 09:30:09 +00002953
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002954 if (pjsua_var.ua_cfg.cb.on_call_state)
2955 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002956
2957 /* call->inv may be NULL now */
2958
Benny Prijono84126ab2006-02-09 09:30:09 +00002959 /* Destroy media session when invite session is disconnected. */
2960 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002961
Benny Prijonoa91a0032006-02-26 21:23:45 +00002962 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002963
Benny Prijono275fd682006-03-22 11:59:11 +00002964 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002965 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002966
Benny Prijono105217f2006-03-06 16:25:59 +00002967 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002968 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002969 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002970
2971 /* Reset call */
2972 reset_call(call->index);
2973
Benny Prijono84126ab2006-02-09 09:30:09 +00002974 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002975
2976 PJSUA_UNLOCK();
2977}
2978
2979/*
2980 * This callback is called by invite session framework when UAC session
2981 * has forked.
2982 */
2983static void pjsua_call_on_forked( pjsip_inv_session *inv,
2984 pjsip_event *e)
2985{
2986 PJ_UNUSED_ARG(inv);
2987 PJ_UNUSED_ARG(e);
2988
2989 PJ_TODO(HANDLE_FORKED_DIALOG);
2990}
2991
2992
2993/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002994 * Callback from UA layer when forked dialog response is received.
2995 */
2996pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2997{
2998 if (dlg->uac_has_2xx &&
2999 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3000 pjsip_rdata_get_tsx(res) == NULL &&
3001 res->msg_info.msg->line.status.code/100 == 2)
3002 {
3003 pjsip_dialog *forked_dlg;
3004 pjsip_tx_data *bye;
3005 pj_status_t status;
3006
3007 /* Create forked dialog */
3008 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3009 if (status != PJ_SUCCESS)
3010 return NULL;
3011
3012 pjsip_dlg_inc_lock(forked_dlg);
3013
3014 /* Disconnect the call */
3015 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3016 -1, &bye);
3017 if (status == PJ_SUCCESS) {
3018 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3019 }
3020
3021 pjsip_dlg_dec_lock(forked_dlg);
3022
3023 if (status != PJ_SUCCESS) {
3024 return NULL;
3025 }
3026
3027 return forked_dlg;
3028
3029 } else {
3030 return dlg;
3031 }
3032}
3033
3034/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003035 * Disconnect call upon error.
3036 */
3037static void call_disconnect( pjsip_inv_session *inv,
3038 int code )
3039{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003040 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003041 pjsip_tx_data *tdata;
3042 pj_status_t status;
3043
Benny Prijono59b3aed2008-01-15 16:54:54 +00003044 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3045
Benny Prijonoa38ada02006-07-02 14:22:35 +00003046 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003047 if (status != PJ_SUCCESS)
3048 return;
3049
3050 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003051 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3052 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3053 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003054 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003055 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003056
Benny Prijono734fc2d2008-03-17 16:05:35 +00003057 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003058 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003059 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003060 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003061 if (status == PJ_SUCCESS) {
3062 pjsip_create_sdp_body(tdata->pool, local_sdp,
3063 &tdata->msg->body);
3064 }
3065 }
3066
3067 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003068}
3069
3070/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003071 * Callback to be called when SDP offer/answer negotiation has just completed
3072 * in the session. This function will start/update media if negotiation
3073 * has succeeded.
3074 */
3075static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3076 pj_status_t status)
3077{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003078 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003079 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003080 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003081
3082 PJSUA_LOCK();
3083
Benny Prijonoa1e69682007-05-11 15:14:34 +00003084 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003085
3086 if (status != PJ_SUCCESS) {
3087
3088 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3089
Benny Prijono2331d202008-06-26 15:46:52 +00003090 /* Do not deinitialize media since this may be a re-INVITE or
3091 * UPDATE (which in this case the media should not get affected
3092 * by the failed re-INVITE/UPDATE). The media will be shutdown
3093 * when call is disconnected anyway.
3094 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003095 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003096 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003097
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003098 /* Disconnect call if we're not in the middle of initializing an
3099 * UAS dialog and if this is not a re-INVITE
3100 */
3101 if (inv->state != PJSIP_INV_STATE_NULL &&
3102 inv->state != PJSIP_INV_STATE_CONFIRMED)
3103 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003104 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003105 }
3106
3107 PJSUA_UNLOCK();
3108 return;
3109 }
3110
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003111
3112 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003113 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003114 if (status != PJ_SUCCESS) {
3115 pjsua_perror(THIS_FILE,
3116 "Unable to retrieve currently active local SDP",
3117 status);
3118 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3119 PJSUA_UNLOCK();
3120 return;
3121 }
3122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003123 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3124 if (status != PJ_SUCCESS) {
3125 pjsua_perror(THIS_FILE,
3126 "Unable to retrieve currently active remote SDP",
3127 status);
3128 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3129 PJSUA_UNLOCK();
3130 return;
3131 }
3132
Benny Prijono91a6a172007-10-31 08:59:29 +00003133 /* Update remote's NAT type */
3134 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3135 update_remote_nat_type(call, remote_sdp);
3136 }
3137
3138 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003139 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003140 if (status != PJ_SUCCESS) {
3141 pjsua_perror(THIS_FILE, "Unable to create media session",
3142 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003143 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003144 /* No need to deinitialize; media will be shutdown when call
3145 * state is disconnected anyway.
3146 */
3147 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003148 PJSUA_UNLOCK();
3149 return;
3150 }
3151
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003152
3153 /* Call application callback, if any */
3154 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3155 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3156
3157
3158 PJSUA_UNLOCK();
3159}
3160
3161
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003162/* Create SDP for call hold. */
3163static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3164 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003165{
3166 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003167 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003168 pjmedia_sdp_session *sdp;
3169
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003170 /* Use call's pool */
3171 pool = call->inv->pool;
3172
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003173 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003174 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3175 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003176 if (status != PJ_SUCCESS) {
3177 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3178 return status;
3179 }
3180
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003181 /* Call-hold is done by set the media direction to 'sendonly'
3182 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3183 * 'inactive' (PJMEDIA_DIR_NONE).
3184 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3185 */
3186 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3187 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003188
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003189 /* Remove existing directions attributes */
3190 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3191 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3192 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3193 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003194
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003195 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3196 /* Add sendonly attribute */
3197 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3198 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3199 } else {
3200 /* Add inactive attribute */
3201 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3202 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3203 }
3204 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003205
3206 *p_answer = sdp;
3207
3208 return status;
3209}
3210
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003211/*
3212 * Called when session received new offer.
3213 */
3214static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3215 const pjmedia_sdp_session *offer)
3216{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003217 pjsua_call *call;
3218 pjmedia_sdp_conn *conn;
3219 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003220 pj_status_t status;
3221
3222 PJSUA_LOCK();
3223
Benny Prijonoa1e69682007-05-11 15:14:34 +00003224 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003225
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003226 conn = offer->media[0]->conn;
3227 if (!conn)
3228 conn = offer->conn;
3229
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003230 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003231 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3232 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003233
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003234 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3235 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003236 if (status != PJ_SUCCESS) {
3237 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3238 PJSUA_UNLOCK();
3239 return;
3240 }
3241
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003242 /* Check if offer's conn address is zero */
3243 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3244 pj_strcmp2(&conn->addr, "0")==0)
3245 {
3246 /* Modify address */
3247 answer->conn->addr = pj_str("0.0.0.0");
3248 }
3249
3250 /* Check if call is on-hold */
3251 if (call->local_hold) {
3252 pjmedia_sdp_attr *attr;
3253
3254 /* Remove existing directions attributes */
3255 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3256 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3257 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3258 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3259
3260 /* Keep call on-hold by setting 'sendonly' attribute.
3261 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3262 */
3263 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3264 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3265 }
3266
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003267 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3268 if (status != PJ_SUCCESS) {
3269 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3270 PJSUA_UNLOCK();
3271 return;
3272 }
3273
3274 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003275}
3276
3277
3278/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003279 * Called to generate new offer.
3280 */
3281static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3282 pjmedia_sdp_session **offer)
3283{
3284 pjsua_call *call;
3285 pj_status_t status;
3286
3287 PJSUA_LOCK();
3288
3289 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3290
3291 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003292 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003293 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003294 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003295 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003296 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003297 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003298 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3299 call->index));
3300
Benny Prijonod8179652008-01-23 20:39:07 +00003301 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003302 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003303 }
3304
3305 if (status != PJ_SUCCESS) {
3306 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3307 PJSUA_UNLOCK();
3308 return;
3309 }
3310
Benny Prijono77998ce2007-06-20 10:03:46 +00003311 PJSUA_UNLOCK();
3312}
3313
3314
3315/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003316 * Callback called by event framework when the xfer subscription state
3317 * has changed.
3318 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003319static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3320{
3321
3322 PJ_UNUSED_ARG(event);
3323
3324 /*
3325 * When subscription is accepted (got 200/OK to REFER), check if
3326 * subscription suppressed.
3327 */
3328 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3329
3330 pjsip_rx_data *rdata;
3331 pjsip_generic_string_hdr *refer_sub;
3332 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3333 pjsua_call *call;
3334
Benny Prijonoa1e69682007-05-11 15:14:34 +00003335 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003336
3337 /* Must be receipt of response message */
3338 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3339 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3340 rdata = event->body.tsx_state.src.rdata;
3341
3342 /* Find Refer-Sub header */
3343 refer_sub = (pjsip_generic_string_hdr*)
3344 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3345 &REFER_SUB, NULL);
3346
3347 /* Check if subscription is suppressed */
3348 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3349 /* Since no subscription is desired, assume that call has been
3350 * transfered successfully.
3351 */
3352 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3353 const pj_str_t ACCEPTED = { "Accepted", 8 };
3354 pj_bool_t cont = PJ_FALSE;
3355 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3356 200,
3357 &ACCEPTED,
3358 PJ_TRUE,
3359 &cont);
3360 }
3361
3362 /* Yes, subscription is suppressed.
3363 * Terminate our subscription now.
3364 */
3365 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3366 "event subcription..."));
3367 pjsip_evsub_terminate(sub, PJ_TRUE);
3368
3369 } else {
3370 /* Notify application about call transfer progress.
3371 * Initially notify with 100/Accepted status.
3372 */
3373 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3374 const pj_str_t ACCEPTED = { "Accepted", 8 };
3375 pj_bool_t cont = PJ_FALSE;
3376 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3377 100,
3378 &ACCEPTED,
3379 PJ_FALSE,
3380 &cont);
3381 }
3382 }
3383 }
3384 /*
3385 * On incoming NOTIFY, notify application about call transfer progress.
3386 */
3387 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3388 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3389 {
3390 pjsua_call *call;
3391 pjsip_msg *msg;
3392 pjsip_msg_body *body;
3393 pjsip_status_line status_line;
3394 pj_bool_t is_last;
3395 pj_bool_t cont;
3396 pj_status_t status;
3397
Benny Prijonoa1e69682007-05-11 15:14:34 +00003398 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003399
3400 /* When subscription is terminated, clear the xfer_sub member of
3401 * the inv_data.
3402 */
3403 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3404 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3405 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3406
3407 }
3408
3409 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3410 /* Application is not interested with call progress status */
3411 return;
3412 }
3413
3414 /* This better be a NOTIFY request */
3415 if (event->type == PJSIP_EVENT_TSX_STATE &&
3416 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3417 {
3418 pjsip_rx_data *rdata;
3419
3420 rdata = event->body.tsx_state.src.rdata;
3421
3422 /* Check if there's body */
3423 msg = rdata->msg_info.msg;
3424 body = msg->body;
3425 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003426 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003427 "Warning: received NOTIFY without message body"));
3428 return;
3429 }
3430
3431 /* Check for appropriate content */
3432 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3433 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3434 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003435 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003436 "Warning: received NOTIFY with non message/sipfrag "
3437 "content"));
3438 return;
3439 }
3440
3441 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003442 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003443 &status_line);
3444 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003445 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003446 "Warning: received NOTIFY with invalid "
3447 "message/sipfrag content"));
3448 return;
3449 }
3450
3451 } else {
3452 status_line.code = 500;
3453 status_line.reason = *pjsip_get_status_text(500);
3454 }
3455
3456 /* Notify application */
3457 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3458 cont = !is_last;
3459 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3460 status_line.code,
3461 &status_line.reason,
3462 is_last, &cont);
3463
3464 if (!cont) {
3465 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3466 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003467
3468 /* If the call transfer has completed but the subscription is
3469 * not terminated, terminate it now.
3470 */
3471 if (status_line.code/100 == 2 && !is_last) {
3472 pjsip_tx_data *tdata;
3473
3474 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3475 0, &tdata);
3476 if (status == PJ_SUCCESS)
3477 status = pjsip_evsub_send_request(sub, tdata);
3478 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003479 }
3480}
3481
3482
3483/*
3484 * Callback called by event framework when the xfer subscription state
3485 * has changed.
3486 */
3487static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003488{
3489
3490 PJ_UNUSED_ARG(event);
3491
3492 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003493 * When subscription is terminated, clear the xfer_sub member of
3494 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003495 */
3496 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003497 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003498
Benny Prijonoa1e69682007-05-11 15:14:34 +00003499 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003500 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003501 return;
3502
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003503 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003504 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003505
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003506 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003507 }
3508}
3509
3510
3511/*
3512 * Follow transfer (REFER) request.
3513 */
3514static void on_call_transfered( pjsip_inv_session *inv,
3515 pjsip_rx_data *rdata )
3516{
3517 pj_status_t status;
3518 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003519 pjsua_call *existing_call;
3520 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003521 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003522 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003523 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003524 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003525 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003526 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003527 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003528 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003529 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003530 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003531 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003532 pjsip_evsub *sub;
3533
Benny Prijonoa1e69682007-05-11 15:14:34 +00003534 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003535
Benny Prijono26ff9062006-02-21 23:47:00 +00003536 /* Find the Refer-To header */
3537 refer_to = (pjsip_generic_string_hdr*)
3538 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3539
3540 if (refer_to == NULL) {
3541 /* Invalid Request.
3542 * No Refer-To header!
3543 */
3544 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003545 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003546 return;
3547 }
3548
Benny Prijonoc8141a82006-08-20 09:12:19 +00003549 /* Find optional Refer-Sub header */
3550 refer_sub = (pjsip_generic_string_hdr*)
3551 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3552
3553 if (refer_sub) {
3554 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3555 no_refer_sub = PJ_TRUE;
3556 }
3557
Benny Prijono053f5222006-11-11 16:16:04 +00003558 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3559 * request.
3560 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003561 ref_by_hdr = (pjsip_hdr*)
3562 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003563 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003564
Benny Prijono9fc735d2006-05-28 14:58:12 +00003565 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003566 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003567 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3568 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3569 &refer_to->hvalue,
3570 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003571
3572 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003573 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003574 if (code >= 300) {
3575 /* Application rejects call transfer request */
3576 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3577 return;
3578 }
3579
Benny Prijono26ff9062006-02-21 23:47:00 +00003580 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3581 (int)inv->dlg->remote.info_str.slen,
3582 inv->dlg->remote.info_str.ptr,
3583 (int)refer_to->hvalue.slen,
3584 refer_to->hvalue.ptr));
3585
Benny Prijonoc8141a82006-08-20 09:12:19 +00003586 if (no_refer_sub) {
3587 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003588 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003589 */
3590 pjsip_tx_data *tdata;
3591 const pj_str_t str_false = { "false", 5};
3592 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003593
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003594 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3595 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003596 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003597 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003598 status);
3599 return;
3600 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003601
Benny Prijonoc8141a82006-08-20 09:12:19 +00003602 /* Add Refer-Sub header */
3603 hdr = (pjsip_hdr*)
3604 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3605 &str_false);
3606 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003607
Benny Prijono26ff9062006-02-21 23:47:00 +00003608
Benny Prijonoc8141a82006-08-20 09:12:19 +00003609 /* Send answer */
3610 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3611 tdata);
3612 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003613 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003614 status);
3615 return;
3616 }
3617
3618 /* Don't have subscription */
3619 sub = NULL;
3620
3621 } else {
3622 struct pjsip_evsub_user xfer_cb;
3623 pjsip_hdr hdr_list;
3624
3625 /* Init callback */
3626 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003627 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003628
3629 /* Init additional header list to be sent with REFER response */
3630 pj_list_init(&hdr_list);
3631
3632 /* Create transferee event subscription */
3633 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3634 if (status != PJ_SUCCESS) {
3635 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3636 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3637 return;
3638 }
3639
3640 /* If there's Refer-Sub header and the value is "true", send back
3641 * Refer-Sub in the response with value "true" too.
3642 */
3643 if (refer_sub) {
3644 const pj_str_t str_true = { "true", 4 };
3645 pjsip_hdr *hdr;
3646
3647 hdr = (pjsip_hdr*)
3648 pjsip_generic_string_hdr_create(inv->dlg->pool,
3649 &str_refer_sub,
3650 &str_true);
3651 pj_list_push_back(&hdr_list, hdr);
3652
3653 }
3654
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003655 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003656 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3657
3658 /* Create initial NOTIFY request */
3659 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3660 100, NULL, &tdata);
3661 if (status != PJ_SUCCESS) {
3662 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3663 status);
3664 return;
3665 }
3666
3667 /* Send initial NOTIFY request */
3668 status = pjsip_xfer_send_request( sub, tdata);
3669 if (status != PJ_SUCCESS) {
3670 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3671 return;
3672 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003673 }
3674
3675 /* We're cheating here.
3676 * We need to get a null terminated string from a pj_str_t.
3677 * So grab the pointer from the hvalue and NULL terminate it, knowing
3678 * that the NULL position will be occupied by a newline.
3679 */
3680 uri = refer_to->hvalue.ptr;
3681 uri[refer_to->hvalue.slen] = '\0';
3682
Benny Prijono053f5222006-11-11 16:16:04 +00003683 /* Init msg_data */
3684 pjsua_msg_data_init(&msg_data);
3685
3686 /* If Referred-By header is present in the REFER request, copy this
3687 * to the outgoing INVITE request.
3688 */
3689 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003690 pjsip_hdr *dup = (pjsip_hdr*)
3691 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003692 pj_list_push_back(&msg_data.hdr_list, dup);
3693 }
3694
Benny Prijono26ff9062006-02-21 23:47:00 +00003695 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003696 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003697 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003698 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003699 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003700 if (status != PJ_SUCCESS) {
3701
Benny Prijonoc8141a82006-08-20 09:12:19 +00003702 /* Notify xferer about the error (if we have subscription) */
3703 if (sub) {
3704 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3705 500, NULL, &tdata);
3706 if (status != PJ_SUCCESS) {
3707 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3708 status);
3709 return;
3710 }
3711 status = pjsip_xfer_send_request(sub, tdata);
3712 if (status != PJ_SUCCESS) {
3713 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3714 status);
3715 return;
3716 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003717 }
3718 return;
3719 }
3720
Benny Prijonoc8141a82006-08-20 09:12:19 +00003721 if (sub) {
3722 /* Put the server subscription in inv_data.
3723 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3724 * reported back to the server subscription.
3725 */
3726 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003727
Benny Prijonoc8141a82006-08-20 09:12:19 +00003728 /* Put the invite_data in the subscription. */
3729 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3730 &pjsua_var.calls[new_call]);
3731 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003732}
3733
3734
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003735
Benny Prijono26ff9062006-02-21 23:47:00 +00003736/*
3737 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003738 * session. We use this to trap:
3739 * - incoming REFER request.
3740 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003741 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003742static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3743 pjsip_transaction *tsx,
3744 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003745{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003746 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003747
3748 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003749
Benny Prijono2285e7e2008-12-17 14:28:18 +00003750 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3751
3752 if (call == NULL) {
3753 PJSUA_UNLOCK();
3754 return;
3755 }
3756
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003757 if (call->inv == NULL) {
3758 /* Shouldn't happen. It happens only when we don't terminate the
3759 * server subscription caused by REFER after the call has been
3760 * transfered (and this call has been disconnected), and we
3761 * receive another REFER for this call.
3762 */
3763 PJSUA_UNLOCK();
3764 return;
3765 }
3766
Benny Prijonofeb69f42007-10-05 09:12:26 +00003767 /* Notify application callback first */
3768 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3769 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3770 }
3771
Benny Prijono26ff9062006-02-21 23:47:00 +00003772 if (tsx->role==PJSIP_ROLE_UAS &&
3773 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003774 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003775 {
3776 /*
3777 * Incoming REFER request.
3778 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003779 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003780
Benny Prijono26ff9062006-02-21 23:47:00 +00003781 }
Benny Prijonob0808372006-03-02 21:18:58 +00003782 else if (tsx->role==PJSIP_ROLE_UAS &&
3783 tsx->state==PJSIP_TSX_STATE_TRYING &&
3784 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3785 {
3786 /*
3787 * Incoming MESSAGE request!
3788 */
3789 pjsip_rx_data *rdata;
3790 pjsip_msg *msg;
3791 pjsip_accept_hdr *accept_hdr;
3792 pj_status_t status;
3793
3794 rdata = e->body.tsx_state.src.rdata;
3795 msg = rdata->msg_info.msg;
3796
3797 /* Request MUST have message body, with Content-Type equal to
3798 * "text/plain".
3799 */
3800 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3801
3802 pjsip_hdr hdr_list;
3803
3804 pj_list_init(&hdr_list);
3805 pj_list_push_back(&hdr_list, accept_hdr);
3806
3807 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3808 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003809 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003810 return;
3811 }
3812
3813 /* Respond with 200 first, so that remote doesn't retransmit in case
3814 * the UI takes too long to process the message.
3815 */
3816 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3817
3818 /* Process MESSAGE request */
3819 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3820 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003821
Benny Prijonob0808372006-03-02 21:18:58 +00003822 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003823 else if (tsx->role == PJSIP_ROLE_UAC &&
3824 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003825 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003826 /* Handle outgoing pager status */
3827 if (tsx->status_code >= 200) {
3828 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003829
Benny Prijonoa1e69682007-05-11 15:14:34 +00003830 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003831 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003832
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003833 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3834 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3835 &im_data->to,
3836 &im_data->body,
3837 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003838 (pjsip_status_code)
3839 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003840 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003841 }
Benny Prijonofccab712006-02-22 22:23:22 +00003842 }
Benny Prijono834aee32006-02-19 01:38:06 +00003843 }
Benny Prijono834aee32006-02-19 01:38:06 +00003844
Benny Prijono26ff9062006-02-21 23:47:00 +00003845
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003846 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003847}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003848
3849
3850/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003851static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3852 const pjsip_uri *target,
3853 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003854{
3855 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003856 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003857
3858 PJSUA_LOCK();
3859
3860 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003861 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3862 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003863 } else {
3864 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3865 "(callback not implemented by application). Disconnecting "
3866 "call.",
3867 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003868 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003869 }
3870
3871 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003872
3873 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003874}
3875