blob: 5b6b8e660f2e92f280ae5dc2cbf7fe12874edd7b [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
Benny Prijono77998ce2007-06-20 10:03:46 +000053 * Called to generate new offer.
54 */
55static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
56 pjmedia_sdp_session **offer);
57
58/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059 * This callback is called when transaction state has changed in INVITE
60 * session. We use this to trap:
61 * - incoming REFER request.
62 * - incoming MESSAGE request.
63 */
64static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
65 pjsip_transaction *tsx,
66 pjsip_event *e);
67
Benny Prijono5e51a4e2008-11-27 00:06:46 +000068/*
69 * Redirection handler.
70 */
Benny Prijono08a48b82008-11-27 12:42:07 +000071static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
72 const pjsip_uri *target,
73 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000074
Benny Prijonoeebe9af2006-06-13 22:57:13 +000075
Nanang Izzuddin99d69522008-08-04 15:01:38 +000076/* Create SDP for call hold. */
77static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
78 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000079
Benny Prijono7129cc72007-11-05 05:54:25 +000080/* Update SDP version in the offer */
81static void update_sdp_version(pjsua_call *call,
82 pjmedia_sdp_session *sdp)
83{
84 const pjmedia_sdp_session *old_sdp = NULL;
85 pj_status_t status;
86
87 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
88 if (status != PJ_SUCCESS || old_sdp == NULL)
89 return;
90
91 sdp->origin.version = old_sdp->origin.version + 1;
92}
93
94
Benny Prijonod524e822006-09-22 12:48:18 +000095/*
96 * Callback called by event framework when the xfer subscription state
97 * has changed.
98 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000099static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
100static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
101
102/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 * Reset call descriptor.
104 */
105static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000106{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000108
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109 call->index = id;
110 call->inv = NULL;
111 call->user_data = NULL;
112 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +0000113 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000114 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000115 call->rtp_tx_seq = 0;
116 call->rtp_tx_ts = 0;
117 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000118 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000119 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000120 call->conf_slot = PJSUA_INVALID_ID;
121 call->last_text.ptr = call->last_text_buf_;
122 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000123 call->conn_time.sec = 0;
124 call->conn_time.msec = 0;
125 call->res_time.sec = 0;
126 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000127 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000128 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000129 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000130}
131
132
Benny Prijono275fd682006-03-22 11:59:11 +0000133/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000135 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000137{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 pjsip_inv_callback inv_cb;
139 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000140 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000141 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000142
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 /* Init calls array. */
144 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
145 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 /* Copy config */
148 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000149
Benny Prijono91d06b62008-09-20 12:16:56 +0000150 /* Check the route URI's and force loose route if required */
151 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
152 status = normalize_route_uri(pjsua_var.pool,
153 &pjsua_var.ua_cfg.outbound_proxy[i]);
154 if (status != PJ_SUCCESS)
155 return status;
156 }
157
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000158 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000159 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
161 inv_cb.on_new_session = &pjsua_call_on_forked;
162 inv_cb.on_media_update = &pjsua_call_on_media_update;
163 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000164 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000165 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000166 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 /* Initialize invite session module: */
169 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
170 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
171
Benny Prijonoc8141a82006-08-20 09:12:19 +0000172 /* Add "norefersub" in Supported header */
173 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
174 NULL, 1, &str_norefersub);
175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 return status;
177}
178
179
180/*
181 * Start call subsystem.
182 */
183pj_status_t pjsua_call_subsys_start(void)
184{
185 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000186 return PJ_SUCCESS;
187}
188
189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191 * Get maximum number of calls configured in pjsua.
192 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000193PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000194{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000195 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196}
197
198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199/*
200 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000202PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205}
206
207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208/*
209 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
212 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000215
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000220 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
221 if (!pjsua_var.calls[i].inv)
222 continue;
223 ids[c] = i;
224 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000225 }
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000228
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000229 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000230
231 return PJ_SUCCESS;
232}
233
234
Benny Prijono5773cd62008-01-19 13:01:42 +0000235/* Allocate one call id */
236static pjsua_call_id alloc_call_id(void)
237{
238 pjsua_call_id cid;
239
240#if 1
241 /* New algorithm: round-robin */
242 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
243 pjsua_var.next_call_id < 0)
244 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000245 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000246 }
247
248 for (cid=pjsua_var.next_call_id;
249 cid<(int)pjsua_var.ua_cfg.max_calls;
250 ++cid)
251 {
252 if (pjsua_var.calls[cid].inv == NULL) {
253 ++pjsua_var.next_call_id;
254 return cid;
255 }
256 }
257
258 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
259 if (pjsua_var.calls[cid].inv == NULL) {
260 ++pjsua_var.next_call_id;
261 return cid;
262 }
263 }
264
265#else
266 /* Old algorithm */
267 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
268 if (pjsua_var.calls[cid].inv == NULL)
269 return cid;
270 }
271#endif
272
273 return PJSUA_INVALID_ID;
274}
275
Benny Prijonod8179652008-01-23 20:39:07 +0000276/* Get signaling secure level.
277 * Return:
278 * 0: if signaling is not secure
279 * 1: if TLS transport is used for immediate hop
280 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000281 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000282static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000283{
284 const pj_str_t tls = pj_str(";transport=tls");
285 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000286 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000287
288 if (pj_stristr(dst_uri, &sips))
289 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000290
291 if (!pj_list_empty(&acc->route_set)) {
292 pjsip_route_hdr *r = acc->route_set.next;
293 pjsip_uri *uri = r->name_addr.uri;
294 pjsip_sip_uri *sip_uri;
295
296 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
297 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
298 return 1;
299
300 } else {
301 if (pj_stristr(dst_uri, &tls))
302 return 1;
303 }
304
Benny Prijonod8179652008-01-23 20:39:07 +0000305 return 0;
306}
307
Benny Prijono224b4e22008-06-19 14:10:28 +0000308/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000309static int call_get_secure_level(pjsua_call *call)
310{
311 if (call->inv->dlg->secure)
312 return 2;
313
314 if (!pj_list_empty(&call->inv->dlg->route_set)) {
315 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
316 pjsip_uri *uri = r->name_addr.uri;
317 pjsip_sip_uri *sip_uri;
318
319 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
320 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
321 return 1;
322
323 } else {
324 pjsip_sip_uri *sip_uri;
325
326 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
327 return 2;
328 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
329 return 0;
330
331 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
332 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
333 return 1;
334 }
335
336 return 0;
337}
Benny Prijono224b4e22008-06-19 14:10:28 +0000338*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000339
340
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341/*
342 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000343 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
345 const pj_str_t *dest_uri,
346 unsigned options,
347 void *user_data,
348 const pjsua_msg_data *msg_data,
349 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000350{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000351 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000352 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000353 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000354 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000355 pjsua_acc *acc;
356 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000357 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000358 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000359 pjsip_tx_data *tdata;
360 pj_status_t status;
361
Benny Prijono9fc735d2006-05-28 14:58:12 +0000362
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000364 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000365 PJ_EINVAL);
366
Benny Prijono320fa4d2006-12-07 10:09:16 +0000367 /* Check arguments */
368 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
369
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 PJSUA_LOCK();
371
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000372 /* Create sound port if none is instantiated */
373 if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
374 !pjsua_var.no_snd)
375 {
376 pj_status_t status;
377
378 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
379 if (status != PJ_SUCCESS) {
380 PJSUA_UNLOCK();
381 return status;
382 }
383 }
384
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000385 acc = &pjsua_var.acc[acc_id];
386 if (!acc->valid) {
387 pjsua_perror(THIS_FILE, "Unable to make call because account "
388 "is not valid", PJ_EINVALIDOP);
389 PJSUA_UNLOCK();
390 return PJ_EINVALIDOP;
391 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000392
Benny Prijonoa91a0032006-02-26 21:23:45 +0000393 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000394 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000395
Benny Prijono5773cd62008-01-19 13:01:42 +0000396 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000397 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
398 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000399 return PJ_ETOOMANY;
400 }
401
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000402 call = &pjsua_var.calls[call_id];
403
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000404 /* Create temporary pool */
405 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
406
Benny Prijono320fa4d2006-12-07 10:09:16 +0000407 /* Verify that destination URI is valid before calling
408 * pjsua_acc_create_uac_contact, or otherwise there
409 * a misleading "Invalid Contact URI" error will be printed
410 * when pjsua_acc_create_uac_contact() fails.
411 */
412 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 pjsip_uri *uri;
414 pj_str_t dup;
415
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000416 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
417 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000418
419 if (uri == NULL) {
420 pjsua_perror(THIS_FILE, "Unable to make call",
421 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000422 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000423 PJSUA_UNLOCK();
424 return PJSIP_EINVALIDREQURI;
425 }
426 }
427
Benny Prijono093d3022006-09-24 00:07:11 +0000428 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
429 (int)dest_uri->slen, dest_uri->ptr));
430
Benny Prijonoe21e7842006-04-09 16:46:05 +0000431 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000433
Benny Prijonoe21e7842006-04-09 16:46:05 +0000434 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000437 /* Create suitable Contact header unless a Contact header has been
438 * set in the account.
439 */
440 if (acc->contact.slen) {
441 contact = acc->contact;
442 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000443 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000444 acc_id, dest_uri);
445 if (status != PJ_SUCCESS) {
446 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
447 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000448 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000449 PJSUA_UNLOCK();
450 return status;
451 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000452 }
453
Benny Prijonoe21e7842006-04-09 16:46:05 +0000454 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000455 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000456 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000459 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000460 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 return status;
463 }
464
Benny Prijonodb844a42008-02-02 17:07:18 +0000465 /* Calculate call's secure level */
466 call->secure_level = get_secure_level(acc_id, dest_uri);
467
Benny Prijonoc97608e2007-03-23 16:34:20 +0000468 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000469 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000470 call->secure_level, dlg->pool,
471 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000472 if (status != PJ_SUCCESS) {
473 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
474 goto on_error;
475 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000476
Benny Prijono224b4e22008-06-19 14:10:28 +0000477 /* Create offer */
478 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000479 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000480 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000481 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000482 goto on_error;
483 }
484
485 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000486 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000487 if (acc->cfg.require_100rel)
488 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000489
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000490 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000491 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000492 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000493 goto on_error;
494 }
495
496
497 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000498 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000499 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000500
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000501 dlg->mod_data[pjsua_var.mod.id] = call;
502 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 /* Attach user data */
505 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000506
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000507 /* If account is locked to specific transport, then lock dialog
508 * to this transport too.
509 */
510 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
511 pjsip_tpselector tp_sel;
512
513 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
514 pjsip_dlg_set_transport(dlg, &tp_sel);
515 }
516
Benny Prijono84126ab2006-02-09 09:30:09 +0000517 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 if (!pj_list_empty(&acc->route_set))
519 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000520
521
522 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000524 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000525 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000526 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000527
Benny Prijono48ab2b72007-11-08 09:24:30 +0000528 /* Set authentication preference */
529 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000530
531 /* Create initial INVITE: */
532
533 status = pjsip_inv_invite(inv, &tdata);
534 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000535 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
536 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000537 goto on_error;
538 }
539
540
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000541 /* Add additional headers etc */
542
543 pjsua_process_msg_data( tdata, msg_data);
544
Benny Prijono093d3022006-09-24 00:07:11 +0000545 /* Must increment call counter now */
546 ++pjsua_var.call_cnt;
547
Benny Prijono84126ab2006-02-09 09:30:09 +0000548 /* Send initial INVITE: */
549
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000550 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000551 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000552 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
553 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000554
555 /* Upon failure to send first request, both dialog and invite
556 * session would have been cleared.
557 */
558 inv = NULL;
559 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000560 goto on_error;
561 }
562
Benny Prijono84126ab2006-02-09 09:30:09 +0000563 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000564
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000565 if (p_call_id)
566 *p_call_id = call_id;
567
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000568 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000569 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000570
571 return PJ_SUCCESS;
572
573
574on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000575 if (inv != NULL) {
576 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000577 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000578 pjsip_dlg_terminate(dlg);
579 }
580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 if (call_id != -1) {
582 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000583 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000584 }
585
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000586 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000588 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000589}
590
591
Benny Prijono91a6a172007-10-31 08:59:29 +0000592/* Get the NAT type information in remote's SDP */
593static void update_remote_nat_type(pjsua_call *call,
594 const pjmedia_sdp_session *sdp)
595{
596 const pjmedia_sdp_attr *xnat;
597
598 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
599 if (xnat) {
600 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
601 } else {
602 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
603 }
604
605 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
606 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
607}
608
609
Benny Prijonodc39fe82006-05-26 12:17:46 +0000610/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000611 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000612 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000613 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000614pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000615{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000616 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000617 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000618 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000619 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
620 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000621 pjsip_tx_data *response = NULL;
622 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000623 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000624 int acc_id;
625 pjsua_call *call;
626 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000627 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000628 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000629 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000630
Benny Prijono26ff9062006-02-21 23:47:00 +0000631 /* Don't want to handle anything but INVITE */
632 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
633 return PJ_FALSE;
634
635 /* Don't want to handle anything that's already associated with
636 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000637 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000638 if (dlg || tsx)
639 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000640
Benny Prijono148c9dd2006-09-19 13:37:53 +0000641 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000642
Benny Prijono26ff9062006-02-21 23:47:00 +0000643 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000644 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000645
Benny Prijono5773cd62008-01-19 13:01:42 +0000646 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000647 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000648 PJSIP_SC_BUSY_HERE, NULL,
649 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000650 PJ_LOG(2,(THIS_FILE,
651 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000652 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000653 return PJ_TRUE;
654 }
655
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000656 /* Clear call descriptor */
657 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000658
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000659 call = &pjsua_var.calls[call_id];
660
661 /* Mark call start time. */
662 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000663
Benny Prijono053f5222006-11-11 16:16:04 +0000664 /* Check INVITE request for Replaces header. If Replaces header is
665 * present, the function will make sure that we can handle the request.
666 */
667 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
668 &response);
669 if (status != PJ_SUCCESS) {
670 /*
671 * Something wrong with the Replaces header.
672 */
673 if (response) {
674 pjsip_response_addr res_addr;
675
676 pjsip_get_response_addr(response->pool, rdata, &res_addr);
677 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
678 NULL, NULL);
679
680 } else {
681
682 /* Respond with 500 (Internal Server Error) */
683 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
684 NULL, NULL);
685 }
686
687 PJSUA_UNLOCK();
688 return PJ_TRUE;
689 }
690
691 /* If this INVITE request contains Replaces header, notify application
692 * about the request so that application can do subsequent checking
693 * if it wants to.
694 */
695 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
696 pjsua_call *replaced_call;
697 int st_code = 200;
698 pj_str_t st_text = { "OK", 2 };
699
700 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000701 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000702
703 /* Notify application */
704 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
705 rdata, &st_code, &st_text);
706
707 /* Must specify final response */
708 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
709
710 /* Check if application rejects this request. */
711 if (st_code >= 300) {
712
713 if (st_text.slen == 2)
714 st_text = *pjsip_get_status_text(st_code);
715
716 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
717 st_code, &st_text, NULL, NULL, NULL);
718 PJSUA_UNLOCK();
719 return PJ_TRUE;
720 }
721 }
722
Benny Prijonod8179652008-01-23 20:39:07 +0000723 /*
724 * Get which account is most likely to be associated with this incoming
725 * call. We need the account to find which contact URI to put for
726 * the call.
727 */
728 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
729
Benny Prijonodb844a42008-02-02 17:07:18 +0000730 /* Get call's secure level */
731 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
732 call->secure_level = 2;
733 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
734 call->secure_level = 1;
735 else
736 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000737
Benny Prijonod8179652008-01-23 20:39:07 +0000738 /* Parse SDP from incoming request */
739 if (rdata->msg_info.msg->body) {
740 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000741 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000742 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000743 if (status == PJ_SUCCESS) {
744 /* Validate */
745 status = pjmedia_sdp_validate(offer);
746 }
747
Benny Prijonod8179652008-01-23 20:39:07 +0000748 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000749 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000750 pjsip_hdr hdr_list;
751 pjsip_warning_hdr *w;
752
753 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000754 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000755
756 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
757 pjsip_endpt_name(pjsua_var.endpt),
758 status);
759 pj_list_init(&hdr_list);
760 pj_list_push_back(&hdr_list, w);
761
Benny Prijono224b4e22008-06-19 14:10:28 +0000762 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000763 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000764 PJSUA_UNLOCK();
765 return PJ_TRUE;
766 }
Benny Prijono617b8602008-04-07 10:10:31 +0000767
768 /* Do quick checks on SDP before passing it to transports. More elabore
769 * checks will be done in pjsip_inv_verify_request2() below.
770 */
771 if (offer->media_count==0) {
772 const pj_str_t reason = pj_str("Missing media in SDP");
773 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
774 NULL, NULL, NULL);
775 PJSUA_UNLOCK();
776 return PJ_TRUE;
777 }
778
Benny Prijonod8179652008-01-23 20:39:07 +0000779 } else {
780 offer = NULL;
781 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000782
Benny Prijono224b4e22008-06-19 14:10:28 +0000783 /* Init media channel */
784 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
785 call->secure_level,
786 rdata->tp_info.pool, offer,
787 &sip_err_code);
788 if (status != PJ_SUCCESS) {
789 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
790 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
791 sip_err_code, NULL, NULL, NULL, NULL);
792 PJSUA_UNLOCK();
793 return PJ_TRUE;
794 }
795
796 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000797 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000798 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000799 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000800 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000801 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
802 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000803 PJSUA_UNLOCK();
804 return PJ_TRUE;
805 }
806
Benny Prijono224b4e22008-06-19 14:10:28 +0000807
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000808 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000809 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000810 if (pjsua_var.acc[acc_id].cfg.require_100rel)
811 options |= PJSIP_INV_REQUIRE_100REL;
812
Benny Prijonod8179652008-01-23 20:39:07 +0000813 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
814 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000815 if (status != PJ_SUCCESS) {
816
817 /*
818 * No we can't handle the incoming INVITE request.
819 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000820 if (response) {
821 pjsip_response_addr res_addr;
822
823 pjsip_get_response_addr(response->pool, rdata, &res_addr);
824 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
825 NULL, NULL);
826
827 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000828 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000829 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
830 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000831 }
832
Benny Prijonoc97608e2007-03-23 16:34:20 +0000833 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000834 PJSUA_UNLOCK();
835 return PJ_TRUE;
836 }
837
838
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000839 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000840 if (pjsua_var.acc[acc_id].contact.slen) {
841 contact = pjsua_var.acc[acc_id].contact;
842 } else {
843 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
844 acc_id, rdata);
845 if (status != PJ_SUCCESS) {
846 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
847 status);
848 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
849 NULL, NULL);
850 pjsua_media_channel_deinit(call->index);
851 PJSUA_UNLOCK();
852 return PJ_TRUE;
853 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000854 }
855
Benny Prijono26ff9062006-02-21 23:47:00 +0000856 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000857 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000858 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000859 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000861 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000862 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000863 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000864 return PJ_TRUE;
865 }
866
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000867 /* Set credentials */
868 if (pjsua_var.acc[acc_id].cred_cnt) {
869 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
870 pjsua_var.acc[acc_id].cred_cnt,
871 pjsua_var.acc[acc_id].cred);
872 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000873
Benny Prijono48ab2b72007-11-08 09:24:30 +0000874 /* Set preference */
875 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
876 &pjsua_var.acc[acc_id].cfg.auth_pref);
877
Benny Prijono26ff9062006-02-21 23:47:00 +0000878 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000879 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000880 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000881 pjsip_hdr hdr_list;
882 pjsip_warning_hdr *w;
883
884 w = pjsip_warning_hdr_create_from_status(dlg->pool,
885 pjsip_endpt_name(pjsua_var.endpt),
886 status);
887 pj_list_init(&hdr_list);
888 pj_list_push_back(&hdr_list, w);
889
890 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
891
892 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000893 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000894 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000895 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000896 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 return PJ_TRUE;
898 }
899
Benny Prijonoea9fd392007-11-06 03:41:40 +0000900 /* Update NAT type of remote endpoint, only when there is SDP in
901 * incoming INVITE!
902 */
903 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
904 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
905 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000906 const pjmedia_sdp_session *remote_sdp;
907
908 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
909 update_remote_nat_type(call, remote_sdp);
910 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000911
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000912 /* Create and attach pjsua_var data to the dialog: */
913 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000914
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000915 dlg->mod_data[pjsua_var.mod.id] = call;
916 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000917
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000918 /* If account is locked to specific transport, then lock dialog
919 * to this transport too.
920 */
921 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
922 pjsip_tpselector tp_sel;
923
924 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
925 pjsip_dlg_set_transport(dlg, &tp_sel);
926 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000927
Benny Prijono64f851e2006-02-23 13:49:28 +0000928 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000929 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000930 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000931 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000932 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000933 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
934 status);
935
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000936 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
937 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000938 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000939 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000940 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000941
942 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000943 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000944 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000945 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000946 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000947 }
948
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000949 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000950
Benny Prijono105217f2006-03-06 16:25:59 +0000951
Benny Prijono053f5222006-11-11 16:16:04 +0000952 /* Check if this request should replace existing call */
953 if (replaced_dlg) {
954 pjsip_inv_session *replaced_inv;
955 struct pjsua_call *replaced_call;
956 pjsip_tx_data *tdata;
957
958 /* Get the invite session in the dialog */
959 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
960
961 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000962 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000963
964 /* Notify application */
965 if (pjsua_var.ua_cfg.cb.on_call_replaced)
966 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
967 call_id);
968
969 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
970 call_id));
971
972 /* Answer the new call with 200 response */
973 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
974 if (status == PJ_SUCCESS)
975 status = pjsip_inv_send_msg(inv, tdata);
976
977 if (status != PJ_SUCCESS)
978 pjsua_perror(THIS_FILE, "Error answering session", status);
979
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000980 /* Note that inv may be invalid if 200/OK has caused error in
981 * starting the media.
982 */
Benny Prijono053f5222006-11-11 16:16:04 +0000983
984 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
985 replaced_call->index));
986
987 /* Disconnect replaced invite session */
988 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
989 &tdata);
990 if (status == PJ_SUCCESS && tdata)
991 status = pjsip_inv_send_msg(replaced_inv, tdata);
992
993 if (status != PJ_SUCCESS)
994 pjsua_perror(THIS_FILE, "Error terminating session", status);
995
996
997 } else {
998
Benny Prijonob5388cf2007-01-04 22:45:08 +0000999 /* Notify application if on_incoming_call() is overriden,
1000 * otherwise hangup the call with 480
1001 */
1002 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001003 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001004 } else {
1005 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1006 NULL, NULL);
1007 }
Benny Prijono053f5222006-11-11 16:16:04 +00001008 }
1009
Benny Prijono8b1889b2006-06-06 18:40:40 +00001010
Benny Prijono26ff9062006-02-21 23:47:00 +00001011 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001012 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001013 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001014}
1015
1016
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001017
1018/*
1019 * Check if the specified call has active INVITE session and the INVITE
1020 * session has not been disconnected.
1021 */
1022PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1023{
1024 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1025 PJ_EINVAL);
1026 return pjsua_var.calls[call_id].inv != NULL &&
1027 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1028}
1029
1030
1031/*
1032 * Check if call has an active media session.
1033 */
1034PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1035{
1036 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1037 PJ_EINVAL);
1038 return pjsua_var.calls[call_id].session != NULL;
1039}
1040
1041
Benny Prijonocf986c42008-09-02 11:25:07 +00001042/*
1043 * Retrieve the media session associated with this call.
1044 */
1045PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1046{
1047 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1048 NULL);
1049 return pjsua_var.calls[call_id].session;
1050}
1051
1052
1053/*
1054 * Retrieve the media transport instance that is used for this call.
1055 */
1056PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1057{
1058 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1059 NULL);
1060 return pjsua_var.calls[cid].med_tp;
1061}
1062
1063
Benny Prijono148c9dd2006-09-19 13:37:53 +00001064/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001065pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001066 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001067 pjsua_call **p_call,
1068 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001069{
1070 enum { MAX_RETRY=50 };
1071 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001072 pjsua_call *call = NULL;
1073 pj_bool_t has_pjsua_lock = PJ_FALSE;
1074 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001075
1076 for (retry=0; retry<MAX_RETRY; ++retry) {
1077
1078 has_pjsua_lock = PJ_FALSE;
1079
1080 status = PJSUA_TRY_LOCK();
1081 if (status != PJ_SUCCESS) {
1082 pj_thread_sleep(retry/10);
1083 continue;
1084 }
1085
1086 has_pjsua_lock = PJ_TRUE;
1087 call = &pjsua_var.calls[call_id];
1088
1089 if (call->inv == NULL) {
1090 PJSUA_UNLOCK();
1091 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1092 return PJSIP_ESESSIONTERMINATED;
1093 }
1094
1095 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1096 if (status != PJ_SUCCESS) {
1097 PJSUA_UNLOCK();
1098 pj_thread_sleep(retry/10);
1099 continue;
1100 }
1101
1102 PJSUA_UNLOCK();
1103
1104 break;
1105 }
1106
1107 if (status != PJ_SUCCESS) {
1108 if (has_pjsua_lock == PJ_FALSE)
1109 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1110 "(possibly system has deadlocked) in %s",
1111 title));
1112 else
1113 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1114 "(possibly system has deadlocked) in %s",
1115 title));
1116 return PJ_ETIMEDOUT;
1117 }
1118
1119 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001120 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001121
1122 return PJ_SUCCESS;
1123}
1124
1125
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126/*
1127 * Get the conference port identification associated with the call.
1128 */
1129PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1130{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001131 pjsua_call *call;
1132 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001133 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001134 pj_status_t status;
1135
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1137 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001138
Benny Prijonodc752ca2006-09-22 16:55:42 +00001139 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001140 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001141 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001142
1143 port_id = call->conf_slot;
1144
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001146
1147 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001148}
1149
1150
Benny Prijono148c9dd2006-09-19 13:37:53 +00001151
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152/*
1153 * Obtain detail information about the specified call.
1154 */
1155PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1156 pjsua_call_info *info)
1157{
1158 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161
1162 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1163 PJ_EINVAL);
1164
Benny Prijonoac623b32006-07-03 15:19:31 +00001165 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166
Benny Prijonodc752ca2006-09-22 16:55:42 +00001167 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001168 if (status != PJ_SUCCESS) {
1169 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001170 }
1171
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172 /* id and role */
1173 info->id = call_id;
1174 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001175 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001176
1177 /* local info */
1178 info->local_info.ptr = info->buf_.local_info;
1179 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1180 sizeof(info->buf_.local_info));
1181
1182 /* local contact */
1183 info->local_contact.ptr = info->buf_.local_contact;
1184 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1185 call->inv->dlg->local.contact->uri,
1186 info->local_contact.ptr,
1187 sizeof(info->buf_.local_contact));
1188
1189 /* remote info */
1190 info->remote_info.ptr = info->buf_.remote_info;
1191 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1192 sizeof(info->buf_.remote_info));
1193
1194 /* remote contact */
1195 if (call->inv->dlg->remote.contact) {
1196 int len;
1197 info->remote_contact.ptr = info->buf_.remote_contact;
1198 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1199 call->inv->dlg->remote.contact->uri,
1200 info->remote_contact.ptr,
1201 sizeof(info->buf_.remote_contact));
1202 if (len < 0) len = 0;
1203 info->remote_contact.slen = len;
1204 } else {
1205 info->remote_contact.slen = 0;
1206 }
1207
1208 /* call id */
1209 info->call_id.ptr = info->buf_.call_id;
1210 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1211 sizeof(info->buf_.call_id));
1212
1213 /* state, state_text */
1214 info->state = call->inv->state;
1215 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1216
1217 /* If call is disconnected, set the last_status from the cause code */
1218 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1219 /* last_status, last_status_text */
1220 info->last_status = call->inv->cause;
1221
1222 info->last_status_text.ptr = info->buf_.last_status_text;
1223 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1224 sizeof(info->buf_.last_status_text));
1225 } else {
1226 /* last_status, last_status_text */
1227 info->last_status = call->last_code;
1228
1229 info->last_status_text.ptr = info->buf_.last_status_text;
1230 pj_strncpy(&info->last_status_text, &call->last_text,
1231 sizeof(info->buf_.last_status_text));
1232 }
1233
1234 /* media status and dir */
1235 info->media_status = call->media_st;
1236 info->media_dir = call->media_dir;
1237
1238
1239 /* conference slot number */
1240 info->conf_slot = call->conf_slot;
1241
1242 /* calculate duration */
1243 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1244
1245 info->total_duration = call->dis_time;
1246 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1247
1248 if (call->conn_time.sec) {
1249 info->connect_duration = call->dis_time;
1250 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1251 }
1252
1253 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1254
1255 pj_gettimeofday(&info->total_duration);
1256 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1257
1258 pj_gettimeofday(&info->connect_duration);
1259 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1260
1261 } else {
1262 pj_gettimeofday(&info->total_duration);
1263 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1264 }
1265
Benny Prijonodc752ca2006-09-22 16:55:42 +00001266 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267
1268 return PJ_SUCCESS;
1269}
1270
1271
1272/*
1273 * Attach application specific data to the call.
1274 */
1275PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1276 void *user_data)
1277{
1278 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1279 PJ_EINVAL);
1280 pjsua_var.calls[call_id].user_data = user_data;
1281
1282 return PJ_SUCCESS;
1283}
1284
1285
1286/*
1287 * Get user data attached to the call.
1288 */
1289PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1290{
1291 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1292 NULL);
1293 return pjsua_var.calls[call_id].user_data;
1294}
1295
1296
1297/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001298 * Get remote's NAT type.
1299 */
1300PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1301 pj_stun_nat_type *p_type)
1302{
1303 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1304 PJ_EINVAL);
1305 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1306
1307 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1308 return PJ_SUCCESS;
1309}
1310
1311
1312/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001313 * Send response to incoming INVITE request.
1314 */
1315PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1316 unsigned code,
1317 const pj_str_t *reason,
1318 const pjsua_msg_data *msg_data)
1319{
1320 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001321 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 pjsip_tx_data *tdata;
1323 pj_status_t status;
1324
1325 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1326 PJ_EINVAL);
1327
Benny Prijonodc752ca2006-09-22 16:55:42 +00001328 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001329 if (status != PJ_SUCCESS)
1330 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001331
Benny Prijono2e507c22006-06-23 15:04:11 +00001332 if (call->res_time.sec == 0)
1333 pj_gettimeofday(&call->res_time);
1334
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001335 if (reason && reason->slen == 0)
1336 reason = NULL;
1337
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001338 /* Create response message */
1339 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1340 if (status != PJ_SUCCESS) {
1341 pjsua_perror(THIS_FILE, "Error creating response",
1342 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001343 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001344 return status;
1345 }
1346
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001347 /* Call might have been disconnected if application is answering with
1348 * 200/OK and the media failed to start.
1349 */
1350 if (call->inv == NULL) {
1351 pjsip_dlg_dec_lock(dlg);
1352 return PJSIP_ESESSIONTERMINATED;
1353 }
1354
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355 /* Add additional headers etc */
1356 pjsua_process_msg_data( tdata, msg_data);
1357
1358 /* Send the message */
1359 status = pjsip_inv_send_msg(call->inv, tdata);
1360 if (status != PJ_SUCCESS)
1361 pjsua_perror(THIS_FILE, "Error sending response",
1362 status);
1363
Benny Prijonodc752ca2006-09-22 16:55:42 +00001364 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365
1366 return status;
1367}
1368
1369
1370/*
1371 * Hangup call by using method that is appropriate according to the
1372 * call state.
1373 */
1374PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1375 unsigned code,
1376 const pj_str_t *reason,
1377 const pjsua_msg_data *msg_data)
1378{
1379 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001380 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 pj_status_t status;
1382 pjsip_tx_data *tdata;
1383
1384
Benny Prijono148c9dd2006-09-19 13:37:53 +00001385 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1386 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1387 call_id));
1388 }
1389
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001390 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1391 PJ_EINVAL);
1392
Benny Prijonodc752ca2006-09-22 16:55:42 +00001393 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001394 if (status != PJ_SUCCESS)
1395 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396
1397 if (code==0) {
1398 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1399 code = PJSIP_SC_OK;
1400 else if (call->inv->role == PJSIP_ROLE_UAS)
1401 code = PJSIP_SC_DECLINE;
1402 else
1403 code = PJSIP_SC_REQUEST_TERMINATED;
1404 }
1405
1406 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1407 if (status != PJ_SUCCESS) {
1408 pjsua_perror(THIS_FILE,
1409 "Failed to create end session message",
1410 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001411 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412 return status;
1413 }
1414
1415 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1416 * as p_tdata when INVITE transaction has not been answered
1417 * with any provisional responses.
1418 */
1419 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001420 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001421 return PJ_SUCCESS;
1422 }
1423
1424 /* Add additional headers etc */
1425 pjsua_process_msg_data( tdata, msg_data);
1426
1427 /* Send the message */
1428 status = pjsip_inv_send_msg(call->inv, tdata);
1429 if (status != PJ_SUCCESS) {
1430 pjsua_perror(THIS_FILE,
1431 "Failed to send end session message",
1432 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001433 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001434 return status;
1435 }
1436
Benny Prijonodc752ca2006-09-22 16:55:42 +00001437 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438
1439 return PJ_SUCCESS;
1440}
1441
1442
1443/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001444 * Accept or reject redirection.
1445 */
1446PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1447 pjsip_redirect_op cmd)
1448{
1449 pjsua_call *call;
1450 pjsip_dialog *dlg;
1451 pj_status_t status;
1452
1453 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1454 PJ_EINVAL);
1455
1456 status = acquire_call("pjsua_call_process_redirect()", call_id,
1457 &call, &dlg);
1458 if (status != PJ_SUCCESS)
1459 return status;
1460
1461 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1462
1463 pjsip_dlg_dec_lock(dlg);
1464
1465 return status;
1466}
1467
1468
1469/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470 * Put the specified call on hold.
1471 */
1472PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1473 const pjsua_msg_data *msg_data)
1474{
1475 pjmedia_sdp_session *sdp;
1476 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001477 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 pjsip_tx_data *tdata;
1479 pj_status_t status;
1480
1481 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1482 PJ_EINVAL);
1483
Benny Prijonodc752ca2006-09-22 16:55:42 +00001484 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001485 if (status != PJ_SUCCESS)
1486 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001488
1489 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1490 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001491 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001492 return PJSIP_ESESSIONSTATE;
1493 }
1494
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001495 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001497 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001498 return status;
1499 }
1500
Benny Prijono7129cc72007-11-05 05:54:25 +00001501 update_sdp_version(call, sdp);
1502
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001503 /* Create re-INVITE with new offer */
1504 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1505 if (status != PJ_SUCCESS) {
1506 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001507 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001508 return status;
1509 }
1510
1511 /* Add additional headers etc */
1512 pjsua_process_msg_data( tdata, msg_data);
1513
1514 /* Send the request */
1515 status = pjsip_inv_send_msg( call->inv, tdata);
1516 if (status != PJ_SUCCESS) {
1517 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001518 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519 return status;
1520 }
1521
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001522 /* Set flag that local put the call on hold */
1523 call->local_hold = PJ_TRUE;
1524
Benny Prijonodc752ca2006-09-22 16:55:42 +00001525 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001526
1527 return PJ_SUCCESS;
1528}
1529
1530
1531/*
1532 * Send re-INVITE (to release hold).
1533 */
1534PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1535 pj_bool_t unhold,
1536 const pjsua_msg_data *msg_data)
1537{
1538 pjmedia_sdp_session *sdp;
1539 pjsip_tx_data *tdata;
1540 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001541 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001542 pj_status_t status;
1543
1544
1545 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1546 PJ_EINVAL);
1547
Benny Prijonodc752ca2006-09-22 16:55:42 +00001548 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001549 if (status != PJ_SUCCESS)
1550 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001551
1552 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1553 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001554 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555 return PJSIP_ESESSIONSTATE;
1556 }
1557
1558 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001559 if (call->local_hold && !unhold) {
1560 status = create_sdp_of_call_hold(call, &sdp);
1561 } else {
1562 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
1563 NULL, &sdp, NULL);
1564 call->local_hold = PJ_FALSE;
1565 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566 if (status != PJ_SUCCESS) {
1567 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1568 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001569 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 return status;
1571 }
1572
Benny Prijono7129cc72007-11-05 05:54:25 +00001573 update_sdp_version(call, sdp);
1574
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001575 /* Create re-INVITE with new offer */
1576 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1577 if (status != PJ_SUCCESS) {
1578 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001579 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001580 return status;
1581 }
1582
1583 /* Add additional headers etc */
1584 pjsua_process_msg_data( tdata, msg_data);
1585
1586 /* Send the request */
1587 status = pjsip_inv_send_msg( call->inv, tdata);
1588 if (status != PJ_SUCCESS) {
1589 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001590 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001591 return status;
1592 }
1593
Benny Prijonodc752ca2006-09-22 16:55:42 +00001594 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595
1596 return PJ_SUCCESS;
1597}
1598
1599
1600/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001601 * Send UPDATE request.
1602 */
1603PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1604 unsigned options,
1605 const pjsua_msg_data *msg_data)
1606{
1607 pjmedia_sdp_session *sdp;
1608 pjsip_tx_data *tdata;
1609 pjsua_call *call;
1610 pjsip_dialog *dlg;
1611 pj_status_t status;
1612
1613 PJ_UNUSED_ARG(options);
1614
1615 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1616 PJ_EINVAL);
1617
1618 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1619 if (status != PJ_SUCCESS)
1620 return status;
1621
Benny Prijonoc08682e2007-10-04 06:17:58 +00001622 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001623 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001624 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001625 if (status != PJ_SUCCESS) {
1626 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1627 status);
1628 pjsip_dlg_dec_lock(dlg);
1629 return status;
1630 }
1631
Benny Prijono224b4e22008-06-19 14:10:28 +00001632 update_sdp_version(call, sdp);
1633
1634 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001635 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1636 if (status != PJ_SUCCESS) {
1637 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1638 pjsip_dlg_dec_lock(dlg);
1639 return status;
1640 }
1641
1642 /* Add additional headers etc */
1643 pjsua_process_msg_data( tdata, msg_data);
1644
1645 /* Send the request */
1646 status = pjsip_inv_send_msg( call->inv, tdata);
1647 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001648 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001649 pjsip_dlg_dec_lock(dlg);
1650 return status;
1651 }
1652
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001653 call->local_hold = PJ_FALSE;
1654
Benny Prijonoc08682e2007-10-04 06:17:58 +00001655 pjsip_dlg_dec_lock(dlg);
1656
1657 return PJ_SUCCESS;
1658}
1659
1660
1661/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662 * Initiate call transfer to the specified address.
1663 */
1664PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1665 const pj_str_t *dest,
1666 const pjsua_msg_data *msg_data)
1667{
1668 pjsip_evsub *sub;
1669 pjsip_tx_data *tdata;
1670 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001672 pjsip_generic_string_hdr *gs_hdr;
1673 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001674 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 pj_status_t status;
1676
1677
1678 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1679 PJ_EINVAL);
1680
Benny Prijonodc752ca2006-09-22 16:55:42 +00001681 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001682 if (status != PJ_SUCCESS)
1683 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001684
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685
Benny Prijonod524e822006-09-22 12:48:18 +00001686 /* Create xfer client subscription. */
1687 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001688 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001689
1690 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001691 if (status != PJ_SUCCESS) {
1692 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001693 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694 return status;
1695 }
1696
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001697 /* Associate this call with the client subscription */
1698 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1699
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 /*
1701 * Create REFER request.
1702 */
1703 status = pjsip_xfer_initiate(sub, dest, &tdata);
1704 if (status != PJ_SUCCESS) {
1705 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001706 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 return status;
1708 }
1709
Benny Prijono053f5222006-11-11 16:16:04 +00001710 /* Add Referred-By header */
1711 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1712 &dlg->local.info_str);
1713 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1714
1715
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716 /* Add additional headers etc */
1717 pjsua_process_msg_data( tdata, msg_data);
1718
1719 /* Send. */
1720 status = pjsip_xfer_send_request(sub, tdata);
1721 if (status != PJ_SUCCESS) {
1722 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001723 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001724 return status;
1725 }
1726
1727 /* For simplicity (that's what this program is intended to be!),
1728 * leave the original invite session as it is. More advanced application
1729 * may want to hold the INVITE, or terminate the invite, or whatever.
1730 */
1731
Benny Prijonodc752ca2006-09-22 16:55:42 +00001732 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001733
1734 return PJ_SUCCESS;
1735
1736}
1737
1738
1739/*
Benny Prijono053f5222006-11-11 16:16:04 +00001740 * Initiate attended call transfer to the specified address.
1741 */
1742PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1743 pjsua_call_id dest_call_id,
1744 unsigned options,
1745 const pjsua_msg_data *msg_data)
1746{
1747 pjsua_call *dest_call;
1748 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001749 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001750 pj_str_t str_dest;
1751 int len;
1752 pjsip_uri *uri;
1753 pj_status_t status;
1754
1755
1756 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1757 PJ_EINVAL);
1758 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1759 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1760 PJ_EINVAL);
1761
1762 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1763 &dest_call, &dest_dlg);
1764 if (status != PJ_SUCCESS)
1765 return status;
1766
1767 /*
1768 * Create REFER destination URI with Replaces field.
1769 */
1770
1771 /* Make sure we have sufficient buffer's length */
1772 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1773 dest_dlg->call_id->id.slen +
1774 dest_dlg->remote.info->tag.slen +
1775 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001776 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001777
1778 /* Print URI */
1779 str_dest_buf[0] = '<';
1780 str_dest.slen = 1;
1781
Benny Prijonoa1e69682007-05-11 15:14:34 +00001782 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001783 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1784 str_dest_buf+1, sizeof(str_dest_buf)-1);
1785 if (len < 0)
1786 return PJSIP_EURITOOLONG;
1787
1788 str_dest.slen += len;
1789
1790
1791 /* Build the URI */
1792 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1793 sizeof(str_dest_buf) - str_dest.slen,
1794 "?%s"
1795 "Replaces=%.*s"
1796 "%%3Bto-tag%%3D%.*s"
1797 "%%3Bfrom-tag%%3D%.*s>",
1798 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1799 "" : "Require=replaces&"),
1800 (int)dest_dlg->call_id->id.slen,
1801 dest_dlg->call_id->id.ptr,
1802 (int)dest_dlg->remote.info->tag.slen,
1803 dest_dlg->remote.info->tag.ptr,
1804 (int)dest_dlg->local.info->tag.slen,
1805 dest_dlg->local.info->tag.ptr);
1806
1807 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1808 PJSIP_EURITOOLONG);
1809
1810 str_dest.ptr = str_dest_buf;
1811 str_dest.slen += len;
1812
1813 pjsip_dlg_dec_lock(dest_dlg);
1814
1815 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1816}
1817
1818
1819/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 * Send DTMF digits to remote using RFC 2833 payload formats.
1821 */
1822PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1823 const pj_str_t *digits)
1824{
1825 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001826 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001827 pj_status_t status;
1828
1829 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1830 PJ_EINVAL);
1831
Benny Prijonodc752ca2006-09-22 16:55:42 +00001832 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001833 if (status != PJ_SUCCESS)
1834 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836 if (!call->session) {
1837 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001838 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001839 return PJ_EINVALIDOP;
1840 }
1841
1842 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1843
Benny Prijonodc752ca2006-09-22 16:55:42 +00001844 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845
1846 return status;
1847}
1848
1849
1850/**
1851 * Send instant messaging inside INVITE session.
1852 */
1853PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1854 const pj_str_t *mime_type,
1855 const pj_str_t *content,
1856 const pjsua_msg_data *msg_data,
1857 void *user_data)
1858{
1859 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001860 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001861 const pj_str_t mime_text_plain = pj_str("text/plain");
1862 pjsip_media_type ctype;
1863 pjsua_im_data *im_data;
1864 pjsip_tx_data *tdata;
1865 pj_status_t status;
1866
1867
1868 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1869 PJ_EINVAL);
1870
Benny Prijonodc752ca2006-09-22 16:55:42 +00001871 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001872 if (status != PJ_SUCCESS)
1873 return status;
1874
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001875 /* Set default media type if none is specified */
1876 if (mime_type == NULL) {
1877 mime_type = &mime_text_plain;
1878 }
1879
1880 /* Create request message. */
1881 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1882 -1, &tdata);
1883 if (status != PJ_SUCCESS) {
1884 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1885 goto on_return;
1886 }
1887
1888 /* Add accept header. */
1889 pjsip_msg_add_hdr( tdata->msg,
1890 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1891
1892 /* Parse MIME type */
1893 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1894
1895 /* Create "text/plain" message body. */
1896 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1897 &ctype.subtype, content);
1898 if (tdata->msg->body == NULL) {
1899 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1900 pjsip_tx_data_dec_ref(tdata);
1901 goto on_return;
1902 }
1903
1904 /* Add additional headers etc */
1905 pjsua_process_msg_data( tdata, msg_data);
1906
1907 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001908 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001909 im_data->acc_id = call->acc_id;
1910 im_data->call_id = call_id;
1911 im_data->to = call->inv->dlg->remote.info_str;
1912 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1913 im_data->user_data = user_data;
1914
1915
1916 /* Send the request. */
1917 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1918 pjsua_var.mod.id, im_data);
1919 if (status != PJ_SUCCESS) {
1920 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1921 goto on_return;
1922 }
1923
1924on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001925 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001926 return status;
1927}
1928
1929
1930/*
1931 * Send IM typing indication inside INVITE session.
1932 */
1933PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1934 pj_bool_t is_typing,
1935 const pjsua_msg_data*msg_data)
1936{
1937 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001938 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939 pjsip_tx_data *tdata;
1940 pj_status_t status;
1941
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001942 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1943 PJ_EINVAL);
1944
Benny Prijonodc752ca2006-09-22 16:55:42 +00001945 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001946 if (status != PJ_SUCCESS)
1947 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001948
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949 /* Create request message. */
1950 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1951 -1, &tdata);
1952 if (status != PJ_SUCCESS) {
1953 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1954 goto on_return;
1955 }
1956
1957 /* Create "application/im-iscomposing+xml" msg body. */
1958 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1959 NULL, NULL, -1);
1960
1961 /* Add additional headers etc */
1962 pjsua_process_msg_data( tdata, msg_data);
1963
1964 /* Send the request. */
1965 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1966 if (status != PJ_SUCCESS) {
1967 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1968 goto on_return;
1969 }
1970
1971on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001972 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001973 return status;
1974}
1975
1976
1977/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001978 * Send arbitrary request.
1979 */
1980PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1981 const pj_str_t *method_str,
1982 const pjsua_msg_data *msg_data)
1983{
1984 pjsua_call *call;
1985 pjsip_dialog *dlg;
1986 pjsip_method method;
1987 pjsip_tx_data *tdata;
1988 pj_status_t status;
1989
1990 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1991 PJ_EINVAL);
1992
1993 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1994 if (status != PJ_SUCCESS)
1995 return status;
1996
1997 /* Init method */
1998 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1999
2000 /* Create request message. */
2001 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2002 if (status != PJ_SUCCESS) {
2003 pjsua_perror(THIS_FILE, "Unable to create request", status);
2004 goto on_return;
2005 }
2006
2007 /* Add additional headers etc */
2008 pjsua_process_msg_data( tdata, msg_data);
2009
2010 /* Send the request. */
2011 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2012 if (status != PJ_SUCCESS) {
2013 pjsua_perror(THIS_FILE, "Unable to send request", status);
2014 goto on_return;
2015 }
2016
2017on_return:
2018 pjsip_dlg_dec_lock(dlg);
2019 return status;
2020}
2021
2022
2023/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024 * Terminate all calls.
2025 */
2026PJ_DEF(void) pjsua_call_hangup_all(void)
2027{
2028 unsigned i;
2029
2030 PJSUA_LOCK();
2031
2032 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2033 if (pjsua_var.calls[i].inv)
2034 pjsua_call_hangup(i, 0, NULL, NULL);
2035 }
2036
2037 PJSUA_UNLOCK();
2038}
2039
2040
Benny Prijono627cbb42007-09-25 20:48:49 +00002041const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002042{
2043 if (val < 1000) {
2044 pj_ansi_sprintf(buf, "%d", val);
2045 } else if (val < 1000000) {
2046 pj_ansi_sprintf(buf, "%d.%dK",
2047 val / 1000,
2048 (val % 1000) / 100);
2049 } else {
2050 pj_ansi_sprintf(buf, "%d.%02dM",
2051 val / 1000000,
2052 (val % 1000000) / 10000);
2053 }
2054
2055 return buf;
2056}
2057
2058
2059/* Dump media session */
2060static void dump_media_session(const char *indent,
2061 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002062 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063{
2064 unsigned i;
2065 char *p = buf, *end = buf+maxlen;
2066 int len;
2067 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002068 pjmedia_session *session = call->session;
2069 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002071 pjmedia_transport_info_init(&tp_info);
2072
2073 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 pjmedia_session_get_info(session, &info);
2075
2076 for (i=0; i<info.stream_cnt; ++i) {
2077 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002078 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002079 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002080 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002081 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002082 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002083 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084
2085 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002086 // rem_addr will contain actual address of RTP originator, instead of
2087 // remote RTP address specified by stream which is fetched from the SDP.
2088 // Please note that we are assuming only one stream per call.
2089 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2090 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002091 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2092 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002093 sizeof(rem_addr_buf), 3);
2094 } else {
2095 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002096 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002097 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002098
2099 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2100 dir = "sendonly";
2101 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2102 dir = "recvonly";
2103 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2104 dir = "sendrecv";
2105 else
2106 dir = "inactive";
2107
2108
2109 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002110 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002112 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113 info.stream_info[i].fmt.encoding_name.ptr,
2114 info.stream_info[i].fmt.clock_rate / 1000,
2115 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002116 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002117 if (len < 1 || len > end-p) {
2118 *p = '\0';
2119 return;
2120 }
2121
2122 p += len;
2123 *p++ = '\n';
2124 *p = '\0';
2125
2126 if (stat.rx.update_cnt == 0)
2127 strcpy(last_update, "never");
2128 else {
2129 pj_gettimeofday(&now);
2130 PJ_TIME_VAL_SUB(now, stat.rx.update);
2131 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2132 now.sec / 3600,
2133 (now.sec % 3600) / 60,
2134 now.sec % 60,
2135 now.msec);
2136 }
2137
Benny Prijono80019eb2006-08-07 13:22:23 +00002138 pj_gettimeofday(&media_duration);
2139 PJ_TIME_VAL_SUB(media_duration, stat.start);
2140 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2141 media_duration.msec = 1;
2142
Benny Prijono1402a4a2008-01-08 23:41:22 +00002143 /* protect against division by zero */
2144 if (stat.rx.pkt == 0)
2145 stat.rx.pkt = 1;
2146 if (stat.tx.pkt == 0)
2147 stat.tx.pkt = 1;
2148
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002149 len = pj_ansi_snprintf(p, end-p,
2150 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002151 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002152 "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002153 "%s (msec) min avg max last dev\n"
2154 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2155 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 indent, info.stream_info[i].fmt.pt,
2157 last_update,
2158 indent,
2159 good_number(packets, stat.rx.pkt),
2160 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002161 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002162 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2163 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002164 indent,
2165 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002166 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002167 stat.rx.discard,
2168 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002170 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002172 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002173 indent, indent,
2174 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002175 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002176 stat.rx.loss_period.max / 1000.0,
2177 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002178 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002179 indent,
2180 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002181 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002182 stat.rx.jitter.max / 1000.0,
2183 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002184 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 ""
2186 );
2187
2188 if (len < 1 || len > end-p) {
2189 *p = '\0';
2190 return;
2191 }
2192
2193 p += len;
2194 *p++ = '\n';
2195 *p = '\0';
2196
2197 if (stat.tx.update_cnt == 0)
2198 strcpy(last_update, "never");
2199 else {
2200 pj_gettimeofday(&now);
2201 PJ_TIME_VAL_SUB(now, stat.tx.update);
2202 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2203 now.sec / 3600,
2204 (now.sec % 3600) / 60,
2205 now.sec % 60,
2206 now.msec);
2207 }
2208
2209 len = pj_ansi_snprintf(p, end-p,
2210 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002211 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002213 "%s (msec) min avg max last dev \n"
2214 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2215 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 indent,
2217 info.stream_info[i].tx_pt,
2218 info.stream_info[i].param->info.frm_ptime *
2219 info.stream_info[i].param->setting.frm_per_pkt,
2220 last_update,
2221
2222 indent,
2223 good_number(packets, stat.tx.pkt),
2224 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002225 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002226 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2227 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228
2229 indent,
2230 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002231 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002232 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002233 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002234 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002235 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002236
2237 indent, indent,
2238 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002239 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002240 stat.tx.loss_period.max / 1000.0,
2241 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002242 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002243 indent,
2244 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002245 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002246 stat.tx.jitter.max / 1000.0,
2247 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002248 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002249 ""
2250 );
2251
2252 if (len < 1 || len > end-p) {
2253 *p = '\0';
2254 return;
2255 }
2256
2257 p += len;
2258 *p++ = '\n';
2259 *p = '\0';
2260
2261 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002262 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002263 indent,
2264 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002265 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002266 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002267 stat.rtt.last / 1000.0,
2268 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002269 );
2270 if (len < 1 || len > end-p) {
2271 *p = '\0';
2272 return;
2273 }
2274
2275 p += len;
2276 *p++ = '\n';
2277 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002278
2279#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2280# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2281 do { \
2282 if (samples <= 4294) \
2283 usec = samples * 1000000 / clock_rate; \
2284 else { \
2285 usec = samples * 1000 / clock_rate; \
2286 usec *= 1000; \
2287 } \
2288 } while(0)
2289
2290# define PRINT_VOIP_MTC_VAL(s, v) \
2291 if (v == 127) \
2292 sprintf(s, "(na)"); \
2293 else \
2294 sprintf(s, "%d", v)
2295
2296# define VALIDATE_PRINT_BUF() \
2297 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2298 p += len; *p++ = '\n'; *p = '\0'
2299
2300
2301 do {
2302 char loss[16], dup[16];
2303 char jitter[80];
2304 char toh[80];
2305 char plc[16], jba[16], jbr[16];
2306 char signal_lvl[16], noise_lvl[16], rerl[16];
2307 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2308 pjmedia_rtcp_xr_stat xr_stat;
2309 unsigned clock_rate;
2310
2311 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2312 PJ_SUCCESS)
2313 {
2314 break;
2315 }
2316
2317 clock_rate = info.stream_info[i].fmt.clock_rate;
2318
2319 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2320 VALIDATE_PRINT_BUF();
2321
2322 /* Statistics Summary */
2323 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2324 VALIDATE_PRINT_BUF();
2325
2326 if (xr_stat.rx.stat_sum.l)
2327 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2328 else
2329 sprintf(loss, "(na)");
2330
2331 if (xr_stat.rx.stat_sum.d)
2332 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2333 else
2334 sprintf(dup, "(na)");
2335
2336 if (xr_stat.rx.stat_sum.j) {
2337 unsigned jmin, jmax, jmean, jdev;
2338
2339 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2340 clock_rate);
2341 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2342 clock_rate);
2343 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2344 clock_rate);
2345 SAMPLES_TO_USEC(jdev,
2346 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2347 clock_rate);
2348 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2349 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2350 } else
2351 sprintf(jitter, "(report not available)");
2352
2353 if (xr_stat.rx.stat_sum.t) {
2354 sprintf(toh, "%11d %11d %11d %11d",
2355 xr_stat.rx.stat_sum.toh.min,
2356 xr_stat.rx.stat_sum.toh.mean,
2357 xr_stat.rx.stat_sum.toh.max,
2358 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2359 } else
2360 sprintf(toh, "(report not available)");
2361
2362 if (xr_stat.rx.stat_sum.update.sec == 0)
2363 strcpy(last_update, "never");
2364 else {
2365 pj_gettimeofday(&now);
2366 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2367 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2368 now.sec / 3600,
2369 (now.sec % 3600) / 60,
2370 now.sec % 60,
2371 now.msec);
2372 }
2373
2374 len = pj_ansi_snprintf(p, end-p,
2375 "%s RX last update: %s\n"
2376 "%s begin seq=%d, end seq=%d\n"
2377 "%s pkt loss=%s, dup=%s\n"
2378 "%s (msec) min avg max dev\n"
2379 "%s jitter : %s\n"
2380 "%s toh : %s",
2381 indent, last_update,
2382 indent,
2383 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2384 indent, loss, dup,
2385 indent,
2386 indent, jitter,
2387 indent, toh
2388 );
2389 VALIDATE_PRINT_BUF();
2390
2391 if (xr_stat.tx.stat_sum.l)
2392 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2393 else
2394 sprintf(loss, "(na)");
2395
2396 if (xr_stat.tx.stat_sum.d)
2397 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2398 else
2399 sprintf(dup, "(na)");
2400
2401 if (xr_stat.tx.stat_sum.j) {
2402 unsigned jmin, jmax, jmean, jdev;
2403
2404 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2405 clock_rate);
2406 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2407 clock_rate);
2408 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2409 clock_rate);
2410 SAMPLES_TO_USEC(jdev,
2411 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2412 clock_rate);
2413 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2414 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2415 } else
2416 sprintf(jitter, "(report not available)");
2417
2418 if (xr_stat.tx.stat_sum.t) {
2419 sprintf(toh, "%11d %11d %11d %11d",
2420 xr_stat.tx.stat_sum.toh.min,
2421 xr_stat.tx.stat_sum.toh.mean,
2422 xr_stat.tx.stat_sum.toh.max,
2423 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2424 } else
2425 sprintf(toh, "(report not available)");
2426
2427 if (xr_stat.tx.stat_sum.update.sec == 0)
2428 strcpy(last_update, "never");
2429 else {
2430 pj_gettimeofday(&now);
2431 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2432 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2433 now.sec / 3600,
2434 (now.sec % 3600) / 60,
2435 now.sec % 60,
2436 now.msec);
2437 }
2438
2439 len = pj_ansi_snprintf(p, end-p,
2440 "%s TX last update: %s\n"
2441 "%s begin seq=%d, end seq=%d\n"
2442 "%s pkt loss=%s, dup=%s\n"
2443 "%s (msec) min avg max dev\n"
2444 "%s jitter : %s\n"
2445 "%s toh : %s",
2446 indent, last_update,
2447 indent,
2448 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2449 indent, loss, dup,
2450 indent,
2451 indent, jitter,
2452 indent, toh
2453 );
2454 VALIDATE_PRINT_BUF();
2455
2456
2457 /* VoIP Metrics */
2458 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2459 VALIDATE_PRINT_BUF();
2460
2461 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2462 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2463 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2464 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2465 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2466 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2467 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2468
2469 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2470 case PJMEDIA_RTCP_XR_PLC_DIS:
2471 sprintf(plc, "DISABLED");
2472 break;
2473 case PJMEDIA_RTCP_XR_PLC_ENH:
2474 sprintf(plc, "ENHANCED");
2475 break;
2476 case PJMEDIA_RTCP_XR_PLC_STD:
2477 sprintf(plc, "STANDARD");
2478 break;
2479 case PJMEDIA_RTCP_XR_PLC_UNK:
2480 default:
2481 sprintf(plc, "UNKNOWN");
2482 break;
2483 }
2484
2485 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2486 case PJMEDIA_RTCP_XR_JB_FIXED:
2487 sprintf(jba, "FIXED");
2488 break;
2489 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2490 sprintf(jba, "ADAPTIVE");
2491 break;
2492 default:
2493 sprintf(jba, "UNKNOWN");
2494 break;
2495 }
2496
2497 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2498
2499 if (xr_stat.rx.voip_mtc.update.sec == 0)
2500 strcpy(last_update, "never");
2501 else {
2502 pj_gettimeofday(&now);
2503 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2504 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2505 now.sec / 3600,
2506 (now.sec % 3600) / 60,
2507 now.sec % 60,
2508 now.msec);
2509 }
2510
2511 len = pj_ansi_snprintf(p, end-p,
2512 "%s RX last update: %s\n"
2513 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2514 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2515 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2516 "%s delay : round trip=%d%s, end system=%d%s\n"
2517 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2518 "%s quality : R factor=%s, ext R factor=%s\n"
2519 "%s MOS LQ=%s, MOS CQ=%s\n"
2520 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2521 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2522 indent,
2523 last_update,
2524 /* packets */
2525 indent,
2526 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2527 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2528 /* burst */
2529 indent,
2530 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2531 xr_stat.rx.voip_mtc.burst_dur, "ms",
2532 /* gap */
2533 indent,
2534 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2535 xr_stat.rx.voip_mtc.gap_dur, "ms",
2536 /* delay */
2537 indent,
2538 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2539 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2540 /* level */
2541 indent,
2542 signal_lvl, "dB",
2543 noise_lvl, "dB",
2544 rerl, "",
2545 /* quality */
2546 indent,
2547 r_factor, ext_r_factor,
2548 indent,
2549 mos_lq, mos_cq,
2550 /* config */
2551 indent,
2552 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2553 /* JB delay */
2554 indent,
2555 xr_stat.rx.voip_mtc.jb_nom, "ms",
2556 xr_stat.rx.voip_mtc.jb_max, "ms",
2557 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2558 );
2559 VALIDATE_PRINT_BUF();
2560
2561 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2562 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2563 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2564 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2565 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2566 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2567 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2568
2569 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2570 case PJMEDIA_RTCP_XR_PLC_DIS:
2571 sprintf(plc, "DISABLED");
2572 break;
2573 case PJMEDIA_RTCP_XR_PLC_ENH:
2574 sprintf(plc, "ENHANCED");
2575 break;
2576 case PJMEDIA_RTCP_XR_PLC_STD:
2577 sprintf(plc, "STANDARD");
2578 break;
2579 case PJMEDIA_RTCP_XR_PLC_UNK:
2580 default:
2581 sprintf(plc, "unknown");
2582 break;
2583 }
2584
2585 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2586 case PJMEDIA_RTCP_XR_JB_FIXED:
2587 sprintf(jba, "FIXED");
2588 break;
2589 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2590 sprintf(jba, "ADAPTIVE");
2591 break;
2592 default:
2593 sprintf(jba, "unknown");
2594 break;
2595 }
2596
2597 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2598
2599 if (xr_stat.tx.voip_mtc.update.sec == 0)
2600 strcpy(last_update, "never");
2601 else {
2602 pj_gettimeofday(&now);
2603 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2604 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2605 now.sec / 3600,
2606 (now.sec % 3600) / 60,
2607 now.sec % 60,
2608 now.msec);
2609 }
2610
2611 len = pj_ansi_snprintf(p, end-p,
2612 "%s TX last update: %s\n"
2613 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2614 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2615 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2616 "%s delay : round trip=%d%s, end system=%d%s\n"
2617 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2618 "%s quality : R factor=%s, ext R factor=%s\n"
2619 "%s MOS LQ=%s, MOS CQ=%s\n"
2620 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2621 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2622 indent,
2623 last_update,
2624 /* pakcets */
2625 indent,
2626 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2627 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2628 /* burst */
2629 indent,
2630 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2631 xr_stat.tx.voip_mtc.burst_dur, "ms",
2632 /* gap */
2633 indent,
2634 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2635 xr_stat.tx.voip_mtc.gap_dur, "ms",
2636 /* delay */
2637 indent,
2638 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2639 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2640 /* level */
2641 indent,
2642 signal_lvl, "dB",
2643 noise_lvl, "dB",
2644 rerl, "",
2645 /* quality */
2646 indent,
2647 r_factor, ext_r_factor,
2648 indent,
2649 mos_lq, mos_cq,
2650 /* config */
2651 indent,
2652 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2653 /* JB delay */
2654 indent,
2655 xr_stat.tx.voip_mtc.jb_nom, "ms",
2656 xr_stat.tx.voip_mtc.jb_max, "ms",
2657 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2658 );
2659 VALIDATE_PRINT_BUF();
2660
2661
2662 /* RTT delay (by receiver side) */
2663 len = pj_ansi_snprintf(p, end-p,
2664 "%s RTT (from recv) min avg max last dev",
2665 indent);
2666 VALIDATE_PRINT_BUF();
2667 len = pj_ansi_snprintf(p, end-p,
2668 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2669 indent,
2670 xr_stat.rtt.min / 1000.0,
2671 xr_stat.rtt.mean / 1000.0,
2672 xr_stat.rtt.max / 1000.0,
2673 xr_stat.rtt.last / 1000.0,
2674 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2675 );
2676 VALIDATE_PRINT_BUF();
2677 } while(0);
2678#endif
2679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002680 }
2681}
2682
2683
2684/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002685void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002686 int call_id,
2687 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002688{
2689 int len;
2690 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2691 pjsip_dialog *dlg = inv->dlg;
2692 char userinfo[128];
2693
2694 /* Dump invite sesion info. */
2695
2696 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2697 if (len < 1)
2698 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2699 else
2700 userinfo[len] = '\0';
2701
2702 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2703 title,
2704 pjsip_inv_state_name(inv->state),
2705 userinfo);
2706 if (len < 1 || len >= (int)size) {
2707 pj_ansi_strcpy(buf, "<--uri too long-->");
2708 len = 18;
2709 } else
2710 buf[len] = '\0';
2711}
2712
2713
2714/*
2715 * Dump call and media statistics to string.
2716 */
2717PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2718 pj_bool_t with_media,
2719 char *buffer,
2720 unsigned maxlen,
2721 const char *indent)
2722{
2723 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002724 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002725 pj_time_val duration, res_delay, con_delay;
2726 char tmp[128];
2727 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002728 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002729 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002730 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002731
2732 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2733 PJ_EINVAL);
2734
Benny Prijonodc752ca2006-09-22 16:55:42 +00002735 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002736 if (status != PJ_SUCCESS)
2737 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002738
2739 *buffer = '\0';
2740 p = buffer;
2741 end = buffer + maxlen;
2742 len = 0;
2743
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002744 print_call(indent, call_id, tmp, sizeof(tmp));
2745
2746 len = pj_ansi_strlen(tmp);
2747 pj_ansi_strcpy(buffer, tmp);
2748
2749 p += len;
2750 *p++ = '\r';
2751 *p++ = '\n';
2752
2753 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002754 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002755 pj_gettimeofday(&duration);
2756 PJ_TIME_VAL_SUB(duration, call->conn_time);
2757 con_delay = call->conn_time;
2758 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2759 } else {
2760 duration.sec = duration.msec = 0;
2761 con_delay.sec = con_delay.msec = 0;
2762 }
2763
2764 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002765 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002766 res_delay = call->res_time;
2767 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2768 } else {
2769 res_delay.sec = res_delay.msec = 0;
2770 }
2771
2772 /* Print duration */
2773 len = pj_ansi_snprintf(p, end-p,
2774 "%s Call time: %02dh:%02dm:%02ds, "
2775 "1st res in %d ms, conn in %dms",
2776 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002777 (int)(duration.sec / 3600),
2778 (int)((duration.sec % 3600)/60),
2779 (int)(duration.sec % 60),
2780 (int)PJ_TIME_VAL_MSEC(res_delay),
2781 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002782
2783 if (len > 0 && len < end-p) {
2784 p += len;
2785 *p++ = '\n';
2786 *p = '\0';
2787 }
2788
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002789 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002790 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002791 pjmedia_transport_get_info(call->med_tp, &tp_info);
2792 if (tp_info.specific_info_cnt > 0) {
2793 int i;
2794 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2795 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2796 {
2797 pjmedia_srtp_info *srtp_info =
2798 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2799
2800 len = pj_ansi_snprintf(p, end-p,
2801 "%s SRTP status: %s Crypto-suite: %s",
2802 indent,
2803 (srtp_info->active?"Active":"Not active"),
2804 srtp_info->tx_policy.name.ptr);
2805 if (len > 0 && len < end-p) {
2806 p += len;
2807 *p++ = '\n';
2808 *p = '\0';
2809 }
2810 break;
2811 }
2812 }
2813 }
2814
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002815 /* Dump session statistics */
2816 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002817 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002818
Benny Prijonodc752ca2006-09-22 16:55:42 +00002819 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002820
2821 return PJ_SUCCESS;
2822}
2823
2824
2825/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002826 * This callback receives notification from invite session when the
2827 * session state has changed.
2828 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002829static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2830 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002831{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002832 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002833
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002834 PJSUA_LOCK();
2835
Benny Prijonoa1e69682007-05-11 15:14:34 +00002836 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002837
2838 if (!call) {
2839 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002840 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002841 }
2842
Benny Prijonoe21e7842006-04-09 16:46:05 +00002843
2844 /* Get call times */
2845 switch (inv->state) {
2846 case PJSIP_INV_STATE_EARLY:
2847 case PJSIP_INV_STATE_CONNECTING:
2848 if (call->res_time.sec == 0)
2849 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002850 call->last_code = (pjsip_status_code)
2851 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002852 pj_strncpy(&call->last_text,
2853 &e->body.tsx_state.tsx->status_text,
2854 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002855 break;
2856 case PJSIP_INV_STATE_CONFIRMED:
2857 pj_gettimeofday(&call->conn_time);
2858 break;
2859 case PJSIP_INV_STATE_DISCONNECTED:
2860 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002861 if (call->res_time.sec == 0)
2862 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002863 if (e->type == PJSIP_EVENT_TSX_STATE &&
2864 e->body.tsx_state.tsx->status_code > call->last_code)
2865 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002866 call->last_code = (pjsip_status_code)
2867 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002868 pj_strncpy(&call->last_text,
2869 &e->body.tsx_state.tsx->status_text,
2870 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002871 } else {
2872 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2873 pj_strncpy(&call->last_text,
2874 pjsip_get_status_text(call->last_code),
2875 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002876 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002877 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002878 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002879 call->last_code = (pjsip_status_code)
2880 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881 pj_strncpy(&call->last_text,
2882 &e->body.tsx_state.tsx->status_text,
2883 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002884 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002885 }
2886
Benny Prijono26ff9062006-02-21 23:47:00 +00002887 /* If this is an outgoing INVITE that was created because of
2888 * REFER/transfer, send NOTIFY to transferer.
2889 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002890 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002891 int st_code = -1;
2892 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2893
2894
Benny Prijonoa91a0032006-02-26 21:23:45 +00002895 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002896 case PJSIP_INV_STATE_NULL:
2897 case PJSIP_INV_STATE_CALLING:
2898 /* Do nothing */
2899 break;
2900
2901 case PJSIP_INV_STATE_EARLY:
2902 case PJSIP_INV_STATE_CONNECTING:
2903 st_code = e->body.tsx_state.tsx->status_code;
2904 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2905 break;
2906
2907 case PJSIP_INV_STATE_CONFIRMED:
2908 /* When state is confirmed, send the final 200/OK and terminate
2909 * subscription.
2910 */
2911 st_code = e->body.tsx_state.tsx->status_code;
2912 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2913 break;
2914
2915 case PJSIP_INV_STATE_DISCONNECTED:
2916 st_code = e->body.tsx_state.tsx->status_code;
2917 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2918 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002919
Benny Prijono8b1889b2006-06-06 18:40:40 +00002920 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002921 /* Nothing to do. Just to keep gcc from complaining about
2922 * unused enums.
2923 */
2924 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002925 }
2926
2927 if (st_code != -1) {
2928 pjsip_tx_data *tdata;
2929 pj_status_t status;
2930
Benny Prijonoa91a0032006-02-26 21:23:45 +00002931 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002932 ev_state, st_code,
2933 NULL, &tdata);
2934 if (status != PJ_SUCCESS) {
2935 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2936 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002937 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002938 if (status != PJ_SUCCESS) {
2939 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2940 }
2941 }
2942 }
2943 }
2944
Benny Prijono84126ab2006-02-09 09:30:09 +00002945
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002946 if (pjsua_var.ua_cfg.cb.on_call_state)
2947 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002948
2949 /* call->inv may be NULL now */
2950
Benny Prijono84126ab2006-02-09 09:30:09 +00002951 /* Destroy media session when invite session is disconnected. */
2952 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002953
Benny Prijonoa91a0032006-02-26 21:23:45 +00002954 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002955
Benny Prijono275fd682006-03-22 11:59:11 +00002956 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002957 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002958
Benny Prijono105217f2006-03-06 16:25:59 +00002959 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002960 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002961 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002962
2963 /* Reset call */
2964 reset_call(call->index);
2965
Benny Prijono84126ab2006-02-09 09:30:09 +00002966 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002967
2968 PJSUA_UNLOCK();
2969}
2970
2971/*
2972 * This callback is called by invite session framework when UAC session
2973 * has forked.
2974 */
2975static void pjsua_call_on_forked( pjsip_inv_session *inv,
2976 pjsip_event *e)
2977{
2978 PJ_UNUSED_ARG(inv);
2979 PJ_UNUSED_ARG(e);
2980
2981 PJ_TODO(HANDLE_FORKED_DIALOG);
2982}
2983
2984
2985/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00002986 * Callback from UA layer when forked dialog response is received.
2987 */
2988pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
2989{
2990 if (dlg->uac_has_2xx &&
2991 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
2992 pjsip_rdata_get_tsx(res) == NULL &&
2993 res->msg_info.msg->line.status.code/100 == 2)
2994 {
2995 pjsip_dialog *forked_dlg;
2996 pjsip_tx_data *bye;
2997 pj_status_t status;
2998
2999 /* Create forked dialog */
3000 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3001 if (status != PJ_SUCCESS)
3002 return NULL;
3003
3004 pjsip_dlg_inc_lock(forked_dlg);
3005
3006 /* Disconnect the call */
3007 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3008 -1, &bye);
3009 if (status == PJ_SUCCESS) {
3010 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3011 }
3012
3013 pjsip_dlg_dec_lock(forked_dlg);
3014
3015 if (status != PJ_SUCCESS) {
3016 return NULL;
3017 }
3018
3019 return forked_dlg;
3020
3021 } else {
3022 return dlg;
3023 }
3024}
3025
3026/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003027 * Disconnect call upon error.
3028 */
3029static void call_disconnect( pjsip_inv_session *inv,
3030 int code )
3031{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003032 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003033 pjsip_tx_data *tdata;
3034 pj_status_t status;
3035
Benny Prijono59b3aed2008-01-15 16:54:54 +00003036 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3037
Benny Prijonoa38ada02006-07-02 14:22:35 +00003038 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003039 if (status != PJ_SUCCESS)
3040 return;
3041
3042 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003043 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3044 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3045 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003046 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003047 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003048
Benny Prijono734fc2d2008-03-17 16:05:35 +00003049 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003050 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003051 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003052 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003053 if (status == PJ_SUCCESS) {
3054 pjsip_create_sdp_body(tdata->pool, local_sdp,
3055 &tdata->msg->body);
3056 }
3057 }
3058
3059 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003060}
3061
3062/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003063 * Callback to be called when SDP offer/answer negotiation has just completed
3064 * in the session. This function will start/update media if negotiation
3065 * has succeeded.
3066 */
3067static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3068 pj_status_t status)
3069{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003070 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003071 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003072 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003073
3074 PJSUA_LOCK();
3075
Benny Prijonoa1e69682007-05-11 15:14:34 +00003076 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003077
3078 if (status != PJ_SUCCESS) {
3079
3080 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3081
Benny Prijono2331d202008-06-26 15:46:52 +00003082 /* Do not deinitialize media since this may be a re-INVITE or
3083 * UPDATE (which in this case the media should not get affected
3084 * by the failed re-INVITE/UPDATE). The media will be shutdown
3085 * when call is disconnected anyway.
3086 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003087 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003088 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003089
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003090 /* Disconnect call if we're not in the middle of initializing an
3091 * UAS dialog and if this is not a re-INVITE
3092 */
3093 if (inv->state != PJSIP_INV_STATE_NULL &&
3094 inv->state != PJSIP_INV_STATE_CONFIRMED)
3095 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003096 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003097 }
3098
3099 PJSUA_UNLOCK();
3100 return;
3101 }
3102
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003103
3104 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003105 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003106 if (status != PJ_SUCCESS) {
3107 pjsua_perror(THIS_FILE,
3108 "Unable to retrieve currently active local SDP",
3109 status);
3110 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3111 PJSUA_UNLOCK();
3112 return;
3113 }
3114
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003115 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3116 if (status != PJ_SUCCESS) {
3117 pjsua_perror(THIS_FILE,
3118 "Unable to retrieve currently active remote SDP",
3119 status);
3120 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3121 PJSUA_UNLOCK();
3122 return;
3123 }
3124
Benny Prijono91a6a172007-10-31 08:59:29 +00003125 /* Update remote's NAT type */
3126 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3127 update_remote_nat_type(call, remote_sdp);
3128 }
3129
3130 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003131 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003132 if (status != PJ_SUCCESS) {
3133 pjsua_perror(THIS_FILE, "Unable to create media session",
3134 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003135 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003136 /* No need to deinitialize; media will be shutdown when call
3137 * state is disconnected anyway.
3138 */
3139 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003140 PJSUA_UNLOCK();
3141 return;
3142 }
3143
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003144
3145 /* Call application callback, if any */
3146 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3147 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3148
3149
3150 PJSUA_UNLOCK();
3151}
3152
3153
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003154/* Create SDP for call hold. */
3155static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3156 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003157{
3158 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003159 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003160 pjmedia_sdp_session *sdp;
3161
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003162 /* Use call's pool */
3163 pool = call->inv->pool;
3164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003165 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003166 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3167 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003168 if (status != PJ_SUCCESS) {
3169 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3170 return status;
3171 }
3172
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003173 /* Call-hold is done by set the media direction to 'sendonly'
3174 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3175 * 'inactive' (PJMEDIA_DIR_NONE).
3176 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3177 */
3178 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3179 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003180
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003181 /* Remove existing directions attributes */
3182 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3183 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3184 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3185 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003186
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003187 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3188 /* Add sendonly attribute */
3189 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3190 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3191 } else {
3192 /* Add inactive attribute */
3193 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3194 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3195 }
3196 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003197
3198 *p_answer = sdp;
3199
3200 return status;
3201}
3202
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003203/*
3204 * Called when session received new offer.
3205 */
3206static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3207 const pjmedia_sdp_session *offer)
3208{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003209 pjsua_call *call;
3210 pjmedia_sdp_conn *conn;
3211 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003212 pj_status_t status;
3213
3214 PJSUA_LOCK();
3215
Benny Prijonoa1e69682007-05-11 15:14:34 +00003216 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003217
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003218 conn = offer->media[0]->conn;
3219 if (!conn)
3220 conn = offer->conn;
3221
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003222 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003223 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3224 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003225
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003226 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3227 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003228 if (status != PJ_SUCCESS) {
3229 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3230 PJSUA_UNLOCK();
3231 return;
3232 }
3233
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003234 /* Check if offer's conn address is zero */
3235 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3236 pj_strcmp2(&conn->addr, "0")==0)
3237 {
3238 /* Modify address */
3239 answer->conn->addr = pj_str("0.0.0.0");
3240 }
3241
3242 /* Check if call is on-hold */
3243 if (call->local_hold) {
3244 pjmedia_sdp_attr *attr;
3245
3246 /* Remove existing directions attributes */
3247 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3248 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3249 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3250 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3251
3252 /* Keep call on-hold by setting 'sendonly' attribute.
3253 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3254 */
3255 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3256 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3257 }
3258
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003259 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3260 if (status != PJ_SUCCESS) {
3261 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3262 PJSUA_UNLOCK();
3263 return;
3264 }
3265
3266 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003267}
3268
3269
3270/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003271 * Called to generate new offer.
3272 */
3273static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3274 pjmedia_sdp_session **offer)
3275{
3276 pjsua_call *call;
3277 pj_status_t status;
3278
3279 PJSUA_LOCK();
3280
3281 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3282
3283 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003284 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003285 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003286 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003287 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003288 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003289 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003290 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3291 call->index));
3292
Benny Prijonod8179652008-01-23 20:39:07 +00003293 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003294 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003295 }
3296
3297 if (status != PJ_SUCCESS) {
3298 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3299 PJSUA_UNLOCK();
3300 return;
3301 }
3302
Benny Prijono7129cc72007-11-05 05:54:25 +00003303 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00003304
3305 PJSUA_UNLOCK();
3306}
3307
3308
3309/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003310 * Callback called by event framework when the xfer subscription state
3311 * has changed.
3312 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003313static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3314{
3315
3316 PJ_UNUSED_ARG(event);
3317
3318 /*
3319 * When subscription is accepted (got 200/OK to REFER), check if
3320 * subscription suppressed.
3321 */
3322 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3323
3324 pjsip_rx_data *rdata;
3325 pjsip_generic_string_hdr *refer_sub;
3326 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3327 pjsua_call *call;
3328
Benny Prijonoa1e69682007-05-11 15:14:34 +00003329 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003330
3331 /* Must be receipt of response message */
3332 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3333 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3334 rdata = event->body.tsx_state.src.rdata;
3335
3336 /* Find Refer-Sub header */
3337 refer_sub = (pjsip_generic_string_hdr*)
3338 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3339 &REFER_SUB, NULL);
3340
3341 /* Check if subscription is suppressed */
3342 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3343 /* Since no subscription is desired, assume that call has been
3344 * transfered successfully.
3345 */
3346 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3347 const pj_str_t ACCEPTED = { "Accepted", 8 };
3348 pj_bool_t cont = PJ_FALSE;
3349 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3350 200,
3351 &ACCEPTED,
3352 PJ_TRUE,
3353 &cont);
3354 }
3355
3356 /* Yes, subscription is suppressed.
3357 * Terminate our subscription now.
3358 */
3359 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3360 "event subcription..."));
3361 pjsip_evsub_terminate(sub, PJ_TRUE);
3362
3363 } else {
3364 /* Notify application about call transfer progress.
3365 * Initially notify with 100/Accepted status.
3366 */
3367 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3368 const pj_str_t ACCEPTED = { "Accepted", 8 };
3369 pj_bool_t cont = PJ_FALSE;
3370 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3371 100,
3372 &ACCEPTED,
3373 PJ_FALSE,
3374 &cont);
3375 }
3376 }
3377 }
3378 /*
3379 * On incoming NOTIFY, notify application about call transfer progress.
3380 */
3381 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3382 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3383 {
3384 pjsua_call *call;
3385 pjsip_msg *msg;
3386 pjsip_msg_body *body;
3387 pjsip_status_line status_line;
3388 pj_bool_t is_last;
3389 pj_bool_t cont;
3390 pj_status_t status;
3391
Benny Prijonoa1e69682007-05-11 15:14:34 +00003392 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003393
3394 /* When subscription is terminated, clear the xfer_sub member of
3395 * the inv_data.
3396 */
3397 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3398 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3399 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3400
3401 }
3402
3403 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3404 /* Application is not interested with call progress status */
3405 return;
3406 }
3407
3408 /* This better be a NOTIFY request */
3409 if (event->type == PJSIP_EVENT_TSX_STATE &&
3410 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3411 {
3412 pjsip_rx_data *rdata;
3413
3414 rdata = event->body.tsx_state.src.rdata;
3415
3416 /* Check if there's body */
3417 msg = rdata->msg_info.msg;
3418 body = msg->body;
3419 if (!body) {
3420 PJ_LOG(4,(THIS_FILE,
3421 "Warning: received NOTIFY without message body"));
3422 return;
3423 }
3424
3425 /* Check for appropriate content */
3426 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3427 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3428 {
3429 PJ_LOG(4,(THIS_FILE,
3430 "Warning: received NOTIFY with non message/sipfrag "
3431 "content"));
3432 return;
3433 }
3434
3435 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003436 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003437 &status_line);
3438 if (status != PJ_SUCCESS) {
3439 PJ_LOG(4,(THIS_FILE,
3440 "Warning: received NOTIFY with invalid "
3441 "message/sipfrag content"));
3442 return;
3443 }
3444
3445 } else {
3446 status_line.code = 500;
3447 status_line.reason = *pjsip_get_status_text(500);
3448 }
3449
3450 /* Notify application */
3451 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3452 cont = !is_last;
3453 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3454 status_line.code,
3455 &status_line.reason,
3456 is_last, &cont);
3457
3458 if (!cont) {
3459 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3460 }
3461 }
3462}
3463
3464
3465/*
3466 * Callback called by event framework when the xfer subscription state
3467 * has changed.
3468 */
3469static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003470{
3471
3472 PJ_UNUSED_ARG(event);
3473
3474 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003475 * When subscription is terminated, clear the xfer_sub member of
3476 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003477 */
3478 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003479 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003480
Benny Prijonoa1e69682007-05-11 15:14:34 +00003481 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003482 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003483 return;
3484
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003485 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003486 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003487
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003488 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003489 }
3490}
3491
3492
3493/*
3494 * Follow transfer (REFER) request.
3495 */
3496static void on_call_transfered( pjsip_inv_session *inv,
3497 pjsip_rx_data *rdata )
3498{
3499 pj_status_t status;
3500 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003501 pjsua_call *existing_call;
3502 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003503 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003504 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003505 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003506 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003507 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003508 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003509 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003510 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003511 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003512 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003513 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003514 pjsip_evsub *sub;
3515
Benny Prijonoa1e69682007-05-11 15:14:34 +00003516 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003517
Benny Prijono26ff9062006-02-21 23:47:00 +00003518 /* Find the Refer-To header */
3519 refer_to = (pjsip_generic_string_hdr*)
3520 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3521
3522 if (refer_to == NULL) {
3523 /* Invalid Request.
3524 * No Refer-To header!
3525 */
3526 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003527 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003528 return;
3529 }
3530
Benny Prijonoc8141a82006-08-20 09:12:19 +00003531 /* Find optional Refer-Sub header */
3532 refer_sub = (pjsip_generic_string_hdr*)
3533 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3534
3535 if (refer_sub) {
3536 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3537 no_refer_sub = PJ_TRUE;
3538 }
3539
Benny Prijono053f5222006-11-11 16:16:04 +00003540 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3541 * request.
3542 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003543 ref_by_hdr = (pjsip_hdr*)
3544 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003545 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003546
Benny Prijono9fc735d2006-05-28 14:58:12 +00003547 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003548 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003549 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3550 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3551 &refer_to->hvalue,
3552 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003553
3554 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003555 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003556 if (code >= 300) {
3557 /* Application rejects call transfer request */
3558 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3559 return;
3560 }
3561
Benny Prijono26ff9062006-02-21 23:47:00 +00003562 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3563 (int)inv->dlg->remote.info_str.slen,
3564 inv->dlg->remote.info_str.ptr,
3565 (int)refer_to->hvalue.slen,
3566 refer_to->hvalue.ptr));
3567
Benny Prijonoc8141a82006-08-20 09:12:19 +00003568 if (no_refer_sub) {
3569 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003570 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003571 */
3572 pjsip_tx_data *tdata;
3573 const pj_str_t str_false = { "false", 5};
3574 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003575
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003576 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3577 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003578 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003579 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003580 status);
3581 return;
3582 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003583
Benny Prijonoc8141a82006-08-20 09:12:19 +00003584 /* Add Refer-Sub header */
3585 hdr = (pjsip_hdr*)
3586 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3587 &str_false);
3588 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003589
Benny Prijono26ff9062006-02-21 23:47:00 +00003590
Benny Prijonoc8141a82006-08-20 09:12:19 +00003591 /* Send answer */
3592 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3593 tdata);
3594 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003595 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003596 status);
3597 return;
3598 }
3599
3600 /* Don't have subscription */
3601 sub = NULL;
3602
3603 } else {
3604 struct pjsip_evsub_user xfer_cb;
3605 pjsip_hdr hdr_list;
3606
3607 /* Init callback */
3608 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003609 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003610
3611 /* Init additional header list to be sent with REFER response */
3612 pj_list_init(&hdr_list);
3613
3614 /* Create transferee event subscription */
3615 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3616 if (status != PJ_SUCCESS) {
3617 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3618 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3619 return;
3620 }
3621
3622 /* If there's Refer-Sub header and the value is "true", send back
3623 * Refer-Sub in the response with value "true" too.
3624 */
3625 if (refer_sub) {
3626 const pj_str_t str_true = { "true", 4 };
3627 pjsip_hdr *hdr;
3628
3629 hdr = (pjsip_hdr*)
3630 pjsip_generic_string_hdr_create(inv->dlg->pool,
3631 &str_refer_sub,
3632 &str_true);
3633 pj_list_push_back(&hdr_list, hdr);
3634
3635 }
3636
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003637 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003638 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3639
3640 /* Create initial NOTIFY request */
3641 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3642 100, NULL, &tdata);
3643 if (status != PJ_SUCCESS) {
3644 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3645 status);
3646 return;
3647 }
3648
3649 /* Send initial NOTIFY request */
3650 status = pjsip_xfer_send_request( sub, tdata);
3651 if (status != PJ_SUCCESS) {
3652 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3653 return;
3654 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003655 }
3656
3657 /* We're cheating here.
3658 * We need to get a null terminated string from a pj_str_t.
3659 * So grab the pointer from the hvalue and NULL terminate it, knowing
3660 * that the NULL position will be occupied by a newline.
3661 */
3662 uri = refer_to->hvalue.ptr;
3663 uri[refer_to->hvalue.slen] = '\0';
3664
Benny Prijono053f5222006-11-11 16:16:04 +00003665 /* Init msg_data */
3666 pjsua_msg_data_init(&msg_data);
3667
3668 /* If Referred-By header is present in the REFER request, copy this
3669 * to the outgoing INVITE request.
3670 */
3671 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003672 pjsip_hdr *dup = (pjsip_hdr*)
3673 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003674 pj_list_push_back(&msg_data.hdr_list, dup);
3675 }
3676
Benny Prijono26ff9062006-02-21 23:47:00 +00003677 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003678 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003679 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003680 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003681 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003682 if (status != PJ_SUCCESS) {
3683
Benny Prijonoc8141a82006-08-20 09:12:19 +00003684 /* Notify xferer about the error (if we have subscription) */
3685 if (sub) {
3686 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3687 500, NULL, &tdata);
3688 if (status != PJ_SUCCESS) {
3689 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3690 status);
3691 return;
3692 }
3693 status = pjsip_xfer_send_request(sub, tdata);
3694 if (status != PJ_SUCCESS) {
3695 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3696 status);
3697 return;
3698 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003699 }
3700 return;
3701 }
3702
Benny Prijonoc8141a82006-08-20 09:12:19 +00003703 if (sub) {
3704 /* Put the server subscription in inv_data.
3705 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3706 * reported back to the server subscription.
3707 */
3708 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003709
Benny Prijonoc8141a82006-08-20 09:12:19 +00003710 /* Put the invite_data in the subscription. */
3711 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3712 &pjsua_var.calls[new_call]);
3713 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003714}
3715
3716
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003717
Benny Prijono26ff9062006-02-21 23:47:00 +00003718/*
3719 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003720 * session. We use this to trap:
3721 * - incoming REFER request.
3722 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003723 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003724static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3725 pjsip_transaction *tsx,
3726 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003727{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003728 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003729
3730 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003731
Benny Prijonofeb69f42007-10-05 09:12:26 +00003732 /* Notify application callback first */
3733 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3734 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3735 }
3736
Benny Prijono26ff9062006-02-21 23:47:00 +00003737 if (tsx->role==PJSIP_ROLE_UAS &&
3738 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003739 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003740 {
3741 /*
3742 * Incoming REFER request.
3743 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003744 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003745
Benny Prijono26ff9062006-02-21 23:47:00 +00003746 }
Benny Prijonob0808372006-03-02 21:18:58 +00003747 else if (tsx->role==PJSIP_ROLE_UAS &&
3748 tsx->state==PJSIP_TSX_STATE_TRYING &&
3749 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3750 {
3751 /*
3752 * Incoming MESSAGE request!
3753 */
3754 pjsip_rx_data *rdata;
3755 pjsip_msg *msg;
3756 pjsip_accept_hdr *accept_hdr;
3757 pj_status_t status;
3758
3759 rdata = e->body.tsx_state.src.rdata;
3760 msg = rdata->msg_info.msg;
3761
3762 /* Request MUST have message body, with Content-Type equal to
3763 * "text/plain".
3764 */
3765 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3766
3767 pjsip_hdr hdr_list;
3768
3769 pj_list_init(&hdr_list);
3770 pj_list_push_back(&hdr_list, accept_hdr);
3771
3772 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3773 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003774 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003775 return;
3776 }
3777
3778 /* Respond with 200 first, so that remote doesn't retransmit in case
3779 * the UI takes too long to process the message.
3780 */
3781 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3782
3783 /* Process MESSAGE request */
3784 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3785 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003786
Benny Prijonob0808372006-03-02 21:18:58 +00003787 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003788 else if (tsx->role == PJSIP_ROLE_UAC &&
3789 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003790 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003791 /* Handle outgoing pager status */
3792 if (tsx->status_code >= 200) {
3793 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003794
Benny Prijonoa1e69682007-05-11 15:14:34 +00003795 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003796 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003797
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003798 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3799 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3800 &im_data->to,
3801 &im_data->body,
3802 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003803 (pjsip_status_code)
3804 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003805 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003806 }
Benny Prijonofccab712006-02-22 22:23:22 +00003807 }
Benny Prijono834aee32006-02-19 01:38:06 +00003808 }
Benny Prijono834aee32006-02-19 01:38:06 +00003809
Benny Prijono26ff9062006-02-21 23:47:00 +00003810
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003811 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003812}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003813
3814
3815/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003816static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3817 const pjsip_uri *target,
3818 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003819{
3820 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003821 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003822
3823 PJSUA_LOCK();
3824
3825 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003826 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3827 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003828 } else {
3829 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3830 "(callback not implemented by application). Disconnecting "
3831 "call.",
3832 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003833 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003834 }
3835
3836 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003837
3838 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003839}
3840