blob: 9f3b9fc1b36bf127a18d7de91b5e64f0b6156d4f [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
68
Nanang Izzuddin99d69522008-08-04 15:01:38 +000069/* Create SDP for call hold. */
70static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
71 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000072
Benny Prijono7129cc72007-11-05 05:54:25 +000073/* Update SDP version in the offer */
74static void update_sdp_version(pjsua_call *call,
75 pjmedia_sdp_session *sdp)
76{
77 const pjmedia_sdp_session *old_sdp = NULL;
78 pj_status_t status;
79
80 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
81 if (status != PJ_SUCCESS || old_sdp == NULL)
82 return;
83
84 sdp->origin.version = old_sdp->origin.version + 1;
85}
86
87
Benny Prijonod524e822006-09-22 12:48:18 +000088/*
89 * Callback called by event framework when the xfer subscription state
90 * has changed.
91 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000092static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
93static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
94
95/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000096 * Reset call descriptor.
97 */
98static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000099{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000100 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000101
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000102 call->index = id;
103 call->inv = NULL;
104 call->user_data = NULL;
105 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +0000106 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000107 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000108 call->rtp_tx_seq = 0;
109 call->rtp_tx_ts = 0;
110 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000111 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000112 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000113 call->conf_slot = PJSUA_INVALID_ID;
114 call->last_text.ptr = call->last_text_buf_;
115 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000116 call->conn_time.sec = 0;
117 call->conn_time.msec = 0;
118 call->res_time.sec = 0;
119 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000120 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000121 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000122 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000123}
124
125
Benny Prijono275fd682006-03-22 11:59:11 +0000126/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000128 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000130{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 pjsip_inv_callback inv_cb;
132 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000133 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 /* Init calls array. */
137 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
138 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000139
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 /* Copy config */
141 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000142
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000144 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
146 inv_cb.on_new_session = &pjsua_call_on_forked;
147 inv_cb.on_media_update = &pjsua_call_on_media_update;
148 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000149 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000151
Benny Prijono275fd682006-03-22 11:59:11 +0000152
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 /* Initialize invite session module: */
154 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
155 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
156
Benny Prijonoc8141a82006-08-20 09:12:19 +0000157 /* Add "norefersub" in Supported header */
158 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
159 NULL, 1, &str_norefersub);
160
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161 return status;
162}
163
164
165/*
166 * Start call subsystem.
167 */
168pj_status_t pjsua_call_subsys_start(void)
169{
170 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000171 return PJ_SUCCESS;
172}
173
174
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000175/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176 * Get maximum number of calls configured in pjsua.
177 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000178PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000179{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000180 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181}
182
183
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184/*
185 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000187PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190}
191
192
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193/*
194 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
197 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000202
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000204
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
206 if (!pjsua_var.calls[i].inv)
207 continue;
208 ids[c] = i;
209 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210 }
211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000215
216 return PJ_SUCCESS;
217}
218
219
Benny Prijono5773cd62008-01-19 13:01:42 +0000220/* Allocate one call id */
221static pjsua_call_id alloc_call_id(void)
222{
223 pjsua_call_id cid;
224
225#if 1
226 /* New algorithm: round-robin */
227 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
228 pjsua_var.next_call_id < 0)
229 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000230 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000231 }
232
233 for (cid=pjsua_var.next_call_id;
234 cid<(int)pjsua_var.ua_cfg.max_calls;
235 ++cid)
236 {
237 if (pjsua_var.calls[cid].inv == NULL) {
238 ++pjsua_var.next_call_id;
239 return cid;
240 }
241 }
242
243 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
244 if (pjsua_var.calls[cid].inv == NULL) {
245 ++pjsua_var.next_call_id;
246 return cid;
247 }
248 }
249
250#else
251 /* Old algorithm */
252 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
253 if (pjsua_var.calls[cid].inv == NULL)
254 return cid;
255 }
256#endif
257
258 return PJSUA_INVALID_ID;
259}
260
Benny Prijonod8179652008-01-23 20:39:07 +0000261/* Get signaling secure level.
262 * Return:
263 * 0: if signaling is not secure
264 * 1: if TLS transport is used for immediate hop
265 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000266 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000267static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000268{
269 const pj_str_t tls = pj_str(";transport=tls");
270 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000271 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000272
273 if (pj_stristr(dst_uri, &sips))
274 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000275
276 if (!pj_list_empty(&acc->route_set)) {
277 pjsip_route_hdr *r = acc->route_set.next;
278 pjsip_uri *uri = r->name_addr.uri;
279 pjsip_sip_uri *sip_uri;
280
281 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
282 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
283 return 1;
284
285 } else {
286 if (pj_stristr(dst_uri, &tls))
287 return 1;
288 }
289
Benny Prijonod8179652008-01-23 20:39:07 +0000290 return 0;
291}
292
Benny Prijono224b4e22008-06-19 14:10:28 +0000293/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000294static int call_get_secure_level(pjsua_call *call)
295{
296 if (call->inv->dlg->secure)
297 return 2;
298
299 if (!pj_list_empty(&call->inv->dlg->route_set)) {
300 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
301 pjsip_uri *uri = r->name_addr.uri;
302 pjsip_sip_uri *sip_uri;
303
304 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
305 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
306 return 1;
307
308 } else {
309 pjsip_sip_uri *sip_uri;
310
311 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
312 return 2;
313 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
314 return 0;
315
316 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
317 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
318 return 1;
319 }
320
321 return 0;
322}
Benny Prijono224b4e22008-06-19 14:10:28 +0000323*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000324
325
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326/*
327 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000328 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000329PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
330 const pj_str_t *dest_uri,
331 unsigned options,
332 void *user_data,
333 const pjsua_msg_data *msg_data,
334 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000335{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000336 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000337 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000338 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000339 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000340 pjsua_acc *acc;
341 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000342 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000343 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000344 pjsip_tx_data *tdata;
345 pj_status_t status;
346
Benny Prijono9fc735d2006-05-28 14:58:12 +0000347
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000348 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000349 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000350 PJ_EINVAL);
351
Benny Prijono320fa4d2006-12-07 10:09:16 +0000352 /* Check arguments */
353 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
354
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000355 PJSUA_LOCK();
356
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000357 /* Create sound port if none is instantiated */
358 if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
359 !pjsua_var.no_snd)
360 {
361 pj_status_t status;
362
363 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
364 if (status != PJ_SUCCESS) {
365 PJSUA_UNLOCK();
366 return status;
367 }
368 }
369
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 acc = &pjsua_var.acc[acc_id];
371 if (!acc->valid) {
372 pjsua_perror(THIS_FILE, "Unable to make call because account "
373 "is not valid", PJ_EINVALIDOP);
374 PJSUA_UNLOCK();
375 return PJ_EINVALIDOP;
376 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000377
Benny Prijonoa91a0032006-02-26 21:23:45 +0000378 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000379 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000380
Benny Prijono5773cd62008-01-19 13:01:42 +0000381 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000382 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
383 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000384 return PJ_ETOOMANY;
385 }
386
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 call = &pjsua_var.calls[call_id];
388
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000389 /* Create temporary pool */
390 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
391
Benny Prijono320fa4d2006-12-07 10:09:16 +0000392 /* Verify that destination URI is valid before calling
393 * pjsua_acc_create_uac_contact, or otherwise there
394 * a misleading "Invalid Contact URI" error will be printed
395 * when pjsua_acc_create_uac_contact() fails.
396 */
397 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000398 pjsip_uri *uri;
399 pj_str_t dup;
400
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000401 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
402 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000403
404 if (uri == NULL) {
405 pjsua_perror(THIS_FILE, "Unable to make call",
406 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000407 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000408 PJSUA_UNLOCK();
409 return PJSIP_EINVALIDREQURI;
410 }
411 }
412
Benny Prijono093d3022006-09-24 00:07:11 +0000413 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
414 (int)dest_uri->slen, dest_uri->ptr));
415
Benny Prijonoe21e7842006-04-09 16:46:05 +0000416 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000418
Benny Prijonoe21e7842006-04-09 16:46:05 +0000419 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000421
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000422 /* Create suitable Contact header unless a Contact header has been
423 * set in the account.
424 */
425 if (acc->contact.slen) {
426 contact = acc->contact;
427 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000428 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000429 acc_id, dest_uri);
430 if (status != PJ_SUCCESS) {
431 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
432 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000433 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000434 PJSUA_UNLOCK();
435 return status;
436 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000437 }
438
Benny Prijonoe21e7842006-04-09 16:46:05 +0000439 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000440 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000441 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000443 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000444 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000445 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000447 return status;
448 }
449
Benny Prijonodb844a42008-02-02 17:07:18 +0000450 /* Calculate call's secure level */
451 call->secure_level = get_secure_level(acc_id, dest_uri);
452
Benny Prijonoc97608e2007-03-23 16:34:20 +0000453 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000454 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000455 call->secure_level, dlg->pool,
456 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000457 if (status != PJ_SUCCESS) {
458 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
459 goto on_error;
460 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000461
Benny Prijono224b4e22008-06-19 14:10:28 +0000462 /* Create offer */
463 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000464 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000465 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000466 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000467 goto on_error;
468 }
469
470 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000471 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000472 if (acc->cfg.require_100rel)
473 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000474
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000475 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000476 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000477 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000478 goto on_error;
479 }
480
481
482 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000483 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000485
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486 dlg->mod_data[pjsua_var.mod.id] = call;
487 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000488
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489 /* Attach user data */
490 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000491
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000492 /* If account is locked to specific transport, then lock dialog
493 * to this transport too.
494 */
495 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
496 pjsip_tpselector tp_sel;
497
498 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
499 pjsip_dlg_set_transport(dlg, &tp_sel);
500 }
501
Benny Prijono84126ab2006-02-09 09:30:09 +0000502 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 if (!pj_list_empty(&acc->route_set))
504 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000505
506
507 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000509 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000511 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000512
Benny Prijono48ab2b72007-11-08 09:24:30 +0000513 /* Set authentication preference */
514 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000515
516 /* Create initial INVITE: */
517
518 status = pjsip_inv_invite(inv, &tdata);
519 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000520 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
521 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000522 goto on_error;
523 }
524
525
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 /* Add additional headers etc */
527
528 pjsua_process_msg_data( tdata, msg_data);
529
Benny Prijono093d3022006-09-24 00:07:11 +0000530 /* Must increment call counter now */
531 ++pjsua_var.call_cnt;
532
Benny Prijono84126ab2006-02-09 09:30:09 +0000533 /* Send initial INVITE: */
534
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000535 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000536 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000537 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
538 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000539
540 /* Upon failure to send first request, both dialog and invite
541 * session would have been cleared.
542 */
543 inv = NULL;
544 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000545 goto on_error;
546 }
547
Benny Prijono84126ab2006-02-09 09:30:09 +0000548 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000549
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000550 if (p_call_id)
551 *p_call_id = call_id;
552
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000553 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000554 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000555
556 return PJ_SUCCESS;
557
558
559on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000560 if (inv != NULL) {
561 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000562 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000563 pjsip_dlg_terminate(dlg);
564 }
565
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000566 if (call_id != -1) {
567 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000568 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000569 }
570
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000571 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000572 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000573 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000574}
575
576
Benny Prijono91a6a172007-10-31 08:59:29 +0000577/* Get the NAT type information in remote's SDP */
578static void update_remote_nat_type(pjsua_call *call,
579 const pjmedia_sdp_session *sdp)
580{
581 const pjmedia_sdp_attr *xnat;
582
583 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
584 if (xnat) {
585 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
586 } else {
587 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
588 }
589
590 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
591 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
592}
593
594
Benny Prijonodc39fe82006-05-26 12:17:46 +0000595/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000596 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000597 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000598 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000599pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000600{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000601 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000602 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000603 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000604 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
605 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000606 pjsip_tx_data *response = NULL;
607 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000608 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 int acc_id;
610 pjsua_call *call;
611 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000612 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000613 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000614 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000615
Benny Prijono26ff9062006-02-21 23:47:00 +0000616 /* Don't want to handle anything but INVITE */
617 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
618 return PJ_FALSE;
619
620 /* Don't want to handle anything that's already associated with
621 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000622 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000623 if (dlg || tsx)
624 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000625
Benny Prijono148c9dd2006-09-19 13:37:53 +0000626 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000627
Benny Prijono26ff9062006-02-21 23:47:00 +0000628 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000629 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000630
Benny Prijono5773cd62008-01-19 13:01:42 +0000631 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000633 PJSIP_SC_BUSY_HERE, NULL,
634 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000635 PJ_LOG(2,(THIS_FILE,
636 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000637 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000638 return PJ_TRUE;
639 }
640
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 /* Clear call descriptor */
642 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000643
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 call = &pjsua_var.calls[call_id];
645
646 /* Mark call start time. */
647 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000648
Benny Prijono053f5222006-11-11 16:16:04 +0000649 /* Check INVITE request for Replaces header. If Replaces header is
650 * present, the function will make sure that we can handle the request.
651 */
652 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
653 &response);
654 if (status != PJ_SUCCESS) {
655 /*
656 * Something wrong with the Replaces header.
657 */
658 if (response) {
659 pjsip_response_addr res_addr;
660
661 pjsip_get_response_addr(response->pool, rdata, &res_addr);
662 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
663 NULL, NULL);
664
665 } else {
666
667 /* Respond with 500 (Internal Server Error) */
668 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
669 NULL, NULL);
670 }
671
672 PJSUA_UNLOCK();
673 return PJ_TRUE;
674 }
675
676 /* If this INVITE request contains Replaces header, notify application
677 * about the request so that application can do subsequent checking
678 * if it wants to.
679 */
680 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
681 pjsua_call *replaced_call;
682 int st_code = 200;
683 pj_str_t st_text = { "OK", 2 };
684
685 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000686 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000687
688 /* Notify application */
689 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
690 rdata, &st_code, &st_text);
691
692 /* Must specify final response */
693 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
694
695 /* Check if application rejects this request. */
696 if (st_code >= 300) {
697
698 if (st_text.slen == 2)
699 st_text = *pjsip_get_status_text(st_code);
700
701 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
702 st_code, &st_text, NULL, NULL, NULL);
703 PJSUA_UNLOCK();
704 return PJ_TRUE;
705 }
706 }
707
Benny Prijonod8179652008-01-23 20:39:07 +0000708 /*
709 * Get which account is most likely to be associated with this incoming
710 * call. We need the account to find which contact URI to put for
711 * the call.
712 */
713 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
714
Benny Prijonodb844a42008-02-02 17:07:18 +0000715 /* Get call's secure level */
716 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
717 call->secure_level = 2;
718 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
719 call->secure_level = 1;
720 else
721 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000722
Benny Prijonod8179652008-01-23 20:39:07 +0000723 /* Parse SDP from incoming request */
724 if (rdata->msg_info.msg->body) {
725 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000726 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000727 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000728 if (status == PJ_SUCCESS) {
729 /* Validate */
730 status = pjmedia_sdp_validate(offer);
731 }
732
Benny Prijonod8179652008-01-23 20:39:07 +0000733 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000734 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000735 pjsip_hdr hdr_list;
736 pjsip_warning_hdr *w;
737
738 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000739 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000740
741 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
742 pjsip_endpt_name(pjsua_var.endpt),
743 status);
744 pj_list_init(&hdr_list);
745 pj_list_push_back(&hdr_list, w);
746
Benny Prijono224b4e22008-06-19 14:10:28 +0000747 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000748 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000749 PJSUA_UNLOCK();
750 return PJ_TRUE;
751 }
Benny Prijono617b8602008-04-07 10:10:31 +0000752
753 /* Do quick checks on SDP before passing it to transports. More elabore
754 * checks will be done in pjsip_inv_verify_request2() below.
755 */
756 if (offer->media_count==0) {
757 const pj_str_t reason = pj_str("Missing media in SDP");
758 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
759 NULL, NULL, NULL);
760 PJSUA_UNLOCK();
761 return PJ_TRUE;
762 }
763
Benny Prijonod8179652008-01-23 20:39:07 +0000764 } else {
765 offer = NULL;
766 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000767
Benny Prijono224b4e22008-06-19 14:10:28 +0000768 /* Init media channel */
769 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
770 call->secure_level,
771 rdata->tp_info.pool, offer,
772 &sip_err_code);
773 if (status != PJ_SUCCESS) {
774 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
775 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
776 sip_err_code, NULL, NULL, NULL, NULL);
777 PJSUA_UNLOCK();
778 return PJ_TRUE;
779 }
780
781 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000782 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000783 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000784 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000785 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000786 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
787 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000788 PJSUA_UNLOCK();
789 return PJ_TRUE;
790 }
791
Benny Prijono224b4e22008-06-19 14:10:28 +0000792
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000793 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000794 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000795 if (pjsua_var.acc[acc_id].cfg.require_100rel)
796 options |= PJSIP_INV_REQUIRE_100REL;
797
Benny Prijonod8179652008-01-23 20:39:07 +0000798 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
799 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000800 if (status != PJ_SUCCESS) {
801
802 /*
803 * No we can't handle the incoming INVITE request.
804 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000805 if (response) {
806 pjsip_response_addr res_addr;
807
808 pjsip_get_response_addr(response->pool, rdata, &res_addr);
809 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
810 NULL, NULL);
811
812 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000813 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000814 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
815 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000816 }
817
Benny Prijonoc97608e2007-03-23 16:34:20 +0000818 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000819 PJSUA_UNLOCK();
820 return PJ_TRUE;
821 }
822
823
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000824 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000825 if (pjsua_var.acc[acc_id].contact.slen) {
826 contact = pjsua_var.acc[acc_id].contact;
827 } else {
828 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
829 acc_id, rdata);
830 if (status != PJ_SUCCESS) {
831 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
832 status);
833 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
834 NULL, NULL);
835 pjsua_media_channel_deinit(call->index);
836 PJSUA_UNLOCK();
837 return PJ_TRUE;
838 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000839 }
840
Benny Prijono26ff9062006-02-21 23:47:00 +0000841 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000842 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000843 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000844 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000845 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000846 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000847 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000848 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000849 return PJ_TRUE;
850 }
851
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000852 /* Set credentials */
853 if (pjsua_var.acc[acc_id].cred_cnt) {
854 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
855 pjsua_var.acc[acc_id].cred_cnt,
856 pjsua_var.acc[acc_id].cred);
857 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000858
Benny Prijono48ab2b72007-11-08 09:24:30 +0000859 /* Set preference */
860 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
861 &pjsua_var.acc[acc_id].cfg.auth_pref);
862
Benny Prijono26ff9062006-02-21 23:47:00 +0000863 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000864 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000865 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000866 pjsip_hdr hdr_list;
867 pjsip_warning_hdr *w;
868
869 w = pjsip_warning_hdr_create_from_status(dlg->pool,
870 pjsip_endpt_name(pjsua_var.endpt),
871 status);
872 pj_list_init(&hdr_list);
873 pj_list_push_back(&hdr_list, w);
874
875 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
876
877 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000878 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000879 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000880 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000881 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000882 return PJ_TRUE;
883 }
884
Benny Prijonoea9fd392007-11-06 03:41:40 +0000885 /* Update NAT type of remote endpoint, only when there is SDP in
886 * incoming INVITE!
887 */
888 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
889 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
890 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000891 const pjmedia_sdp_session *remote_sdp;
892
893 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
894 update_remote_nat_type(call, remote_sdp);
895 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000896
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000897 /* Create and attach pjsua_var data to the dialog: */
898 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000899
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000900 dlg->mod_data[pjsua_var.mod.id] = call;
901 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000902
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000903 /* If account is locked to specific transport, then lock dialog
904 * to this transport too.
905 */
906 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
907 pjsip_tpselector tp_sel;
908
909 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
910 pjsip_dlg_set_transport(dlg, &tp_sel);
911 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000912
Benny Prijono64f851e2006-02-23 13:49:28 +0000913 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000914 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000915 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000916 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000917 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000918 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
919 status);
920
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000921 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
922 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000923 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000924 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000925 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000926
927 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000928 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000929 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000930 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000931 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000932 }
933
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000934 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000935
Benny Prijono105217f2006-03-06 16:25:59 +0000936
Benny Prijono053f5222006-11-11 16:16:04 +0000937 /* Check if this request should replace existing call */
938 if (replaced_dlg) {
939 pjsip_inv_session *replaced_inv;
940 struct pjsua_call *replaced_call;
941 pjsip_tx_data *tdata;
942
943 /* Get the invite session in the dialog */
944 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
945
946 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000947 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000948
949 /* Notify application */
950 if (pjsua_var.ua_cfg.cb.on_call_replaced)
951 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
952 call_id);
953
954 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
955 call_id));
956
957 /* Answer the new call with 200 response */
958 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
959 if (status == PJ_SUCCESS)
960 status = pjsip_inv_send_msg(inv, tdata);
961
962 if (status != PJ_SUCCESS)
963 pjsua_perror(THIS_FILE, "Error answering session", status);
964
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000965 /* Note that inv may be invalid if 200/OK has caused error in
966 * starting the media.
967 */
Benny Prijono053f5222006-11-11 16:16:04 +0000968
969 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
970 replaced_call->index));
971
972 /* Disconnect replaced invite session */
973 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
974 &tdata);
975 if (status == PJ_SUCCESS && tdata)
976 status = pjsip_inv_send_msg(replaced_inv, tdata);
977
978 if (status != PJ_SUCCESS)
979 pjsua_perror(THIS_FILE, "Error terminating session", status);
980
981
982 } else {
983
Benny Prijonob5388cf2007-01-04 22:45:08 +0000984 /* Notify application if on_incoming_call() is overriden,
985 * otherwise hangup the call with 480
986 */
987 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000988 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000989 } else {
990 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
991 NULL, NULL);
992 }
Benny Prijono053f5222006-11-11 16:16:04 +0000993 }
994
Benny Prijono8b1889b2006-06-06 18:40:40 +0000995
Benny Prijono26ff9062006-02-21 23:47:00 +0000996 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000997 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000998 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000999}
1000
1001
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001002
1003/*
1004 * Check if the specified call has active INVITE session and the INVITE
1005 * session has not been disconnected.
1006 */
1007PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1008{
1009 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1010 PJ_EINVAL);
1011 return pjsua_var.calls[call_id].inv != NULL &&
1012 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1013}
1014
1015
1016/*
1017 * Check if call has an active media session.
1018 */
1019PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1020{
1021 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1022 PJ_EINVAL);
1023 return pjsua_var.calls[call_id].session != NULL;
1024}
1025
1026
Benny Prijonocf986c42008-09-02 11:25:07 +00001027/*
1028 * Retrieve the media session associated with this call.
1029 */
1030PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1031{
1032 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1033 NULL);
1034 return pjsua_var.calls[call_id].session;
1035}
1036
1037
1038/*
1039 * Retrieve the media transport instance that is used for this call.
1040 */
1041PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1042{
1043 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1044 NULL);
1045 return pjsua_var.calls[cid].med_tp;
1046}
1047
1048
Benny Prijono148c9dd2006-09-19 13:37:53 +00001049/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001050pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001051 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001052 pjsua_call **p_call,
1053 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001054{
1055 enum { MAX_RETRY=50 };
1056 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001057 pjsua_call *call = NULL;
1058 pj_bool_t has_pjsua_lock = PJ_FALSE;
1059 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001060
1061 for (retry=0; retry<MAX_RETRY; ++retry) {
1062
1063 has_pjsua_lock = PJ_FALSE;
1064
1065 status = PJSUA_TRY_LOCK();
1066 if (status != PJ_SUCCESS) {
1067 pj_thread_sleep(retry/10);
1068 continue;
1069 }
1070
1071 has_pjsua_lock = PJ_TRUE;
1072 call = &pjsua_var.calls[call_id];
1073
1074 if (call->inv == NULL) {
1075 PJSUA_UNLOCK();
1076 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1077 return PJSIP_ESESSIONTERMINATED;
1078 }
1079
1080 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1081 if (status != PJ_SUCCESS) {
1082 PJSUA_UNLOCK();
1083 pj_thread_sleep(retry/10);
1084 continue;
1085 }
1086
1087 PJSUA_UNLOCK();
1088
1089 break;
1090 }
1091
1092 if (status != PJ_SUCCESS) {
1093 if (has_pjsua_lock == PJ_FALSE)
1094 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1095 "(possibly system has deadlocked) in %s",
1096 title));
1097 else
1098 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1099 "(possibly system has deadlocked) in %s",
1100 title));
1101 return PJ_ETIMEDOUT;
1102 }
1103
1104 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001105 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001106
1107 return PJ_SUCCESS;
1108}
1109
1110
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001111/*
1112 * Get the conference port identification associated with the call.
1113 */
1114PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1115{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001116 pjsua_call *call;
1117 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001118 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001119 pj_status_t status;
1120
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001121 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1122 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001123
Benny Prijonodc752ca2006-09-22 16:55:42 +00001124 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001125 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001126 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001127
1128 port_id = call->conf_slot;
1129
Benny Prijonodc752ca2006-09-22 16:55:42 +00001130 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001131
1132 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133}
1134
1135
Benny Prijono148c9dd2006-09-19 13:37:53 +00001136
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001137/*
1138 * Obtain detail information about the specified call.
1139 */
1140PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1141 pjsua_call_info *info)
1142{
1143 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001144 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001145 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001146
1147 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1148 PJ_EINVAL);
1149
Benny Prijonoac623b32006-07-03 15:19:31 +00001150 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001151
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001153 if (status != PJ_SUCCESS) {
1154 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155 }
1156
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001157 /* id and role */
1158 info->id = call_id;
1159 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001160 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161
1162 /* local info */
1163 info->local_info.ptr = info->buf_.local_info;
1164 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1165 sizeof(info->buf_.local_info));
1166
1167 /* local contact */
1168 info->local_contact.ptr = info->buf_.local_contact;
1169 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1170 call->inv->dlg->local.contact->uri,
1171 info->local_contact.ptr,
1172 sizeof(info->buf_.local_contact));
1173
1174 /* remote info */
1175 info->remote_info.ptr = info->buf_.remote_info;
1176 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1177 sizeof(info->buf_.remote_info));
1178
1179 /* remote contact */
1180 if (call->inv->dlg->remote.contact) {
1181 int len;
1182 info->remote_contact.ptr = info->buf_.remote_contact;
1183 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1184 call->inv->dlg->remote.contact->uri,
1185 info->remote_contact.ptr,
1186 sizeof(info->buf_.remote_contact));
1187 if (len < 0) len = 0;
1188 info->remote_contact.slen = len;
1189 } else {
1190 info->remote_contact.slen = 0;
1191 }
1192
1193 /* call id */
1194 info->call_id.ptr = info->buf_.call_id;
1195 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1196 sizeof(info->buf_.call_id));
1197
1198 /* state, state_text */
1199 info->state = call->inv->state;
1200 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1201
1202 /* If call is disconnected, set the last_status from the cause code */
1203 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1204 /* last_status, last_status_text */
1205 info->last_status = call->inv->cause;
1206
1207 info->last_status_text.ptr = info->buf_.last_status_text;
1208 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1209 sizeof(info->buf_.last_status_text));
1210 } else {
1211 /* last_status, last_status_text */
1212 info->last_status = call->last_code;
1213
1214 info->last_status_text.ptr = info->buf_.last_status_text;
1215 pj_strncpy(&info->last_status_text, &call->last_text,
1216 sizeof(info->buf_.last_status_text));
1217 }
1218
1219 /* media status and dir */
1220 info->media_status = call->media_st;
1221 info->media_dir = call->media_dir;
1222
1223
1224 /* conference slot number */
1225 info->conf_slot = call->conf_slot;
1226
1227 /* calculate duration */
1228 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1229
1230 info->total_duration = call->dis_time;
1231 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1232
1233 if (call->conn_time.sec) {
1234 info->connect_duration = call->dis_time;
1235 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1236 }
1237
1238 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1239
1240 pj_gettimeofday(&info->total_duration);
1241 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1242
1243 pj_gettimeofday(&info->connect_duration);
1244 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1245
1246 } else {
1247 pj_gettimeofday(&info->total_duration);
1248 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1249 }
1250
Benny Prijonodc752ca2006-09-22 16:55:42 +00001251 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252
1253 return PJ_SUCCESS;
1254}
1255
1256
1257/*
1258 * Attach application specific data to the call.
1259 */
1260PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1261 void *user_data)
1262{
1263 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1264 PJ_EINVAL);
1265 pjsua_var.calls[call_id].user_data = user_data;
1266
1267 return PJ_SUCCESS;
1268}
1269
1270
1271/*
1272 * Get user data attached to the call.
1273 */
1274PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1275{
1276 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1277 NULL);
1278 return pjsua_var.calls[call_id].user_data;
1279}
1280
1281
1282/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001283 * Get remote's NAT type.
1284 */
1285PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1286 pj_stun_nat_type *p_type)
1287{
1288 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1289 PJ_EINVAL);
1290 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1291
1292 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1293 return PJ_SUCCESS;
1294}
1295
1296
1297/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 * Send response to incoming INVITE request.
1299 */
1300PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1301 unsigned code,
1302 const pj_str_t *reason,
1303 const pjsua_msg_data *msg_data)
1304{
1305 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001306 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 pjsip_tx_data *tdata;
1308 pj_status_t status;
1309
1310 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1311 PJ_EINVAL);
1312
Benny Prijonodc752ca2006-09-22 16:55:42 +00001313 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001314 if (status != PJ_SUCCESS)
1315 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316
Benny Prijono2e507c22006-06-23 15:04:11 +00001317 if (call->res_time.sec == 0)
1318 pj_gettimeofday(&call->res_time);
1319
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001320 if (reason && reason->slen == 0)
1321 reason = NULL;
1322
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323 /* Create response message */
1324 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1325 if (status != PJ_SUCCESS) {
1326 pjsua_perror(THIS_FILE, "Error creating response",
1327 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001328 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001329 return status;
1330 }
1331
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001332 /* Call might have been disconnected if application is answering with
1333 * 200/OK and the media failed to start.
1334 */
1335 if (call->inv == NULL) {
1336 pjsip_dlg_dec_lock(dlg);
1337 return PJSIP_ESESSIONTERMINATED;
1338 }
1339
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340 /* Add additional headers etc */
1341 pjsua_process_msg_data( tdata, msg_data);
1342
1343 /* Send the message */
1344 status = pjsip_inv_send_msg(call->inv, tdata);
1345 if (status != PJ_SUCCESS)
1346 pjsua_perror(THIS_FILE, "Error sending response",
1347 status);
1348
Benny Prijonodc752ca2006-09-22 16:55:42 +00001349 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001350
1351 return status;
1352}
1353
1354
1355/*
1356 * Hangup call by using method that is appropriate according to the
1357 * call state.
1358 */
1359PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1360 unsigned code,
1361 const pj_str_t *reason,
1362 const pjsua_msg_data *msg_data)
1363{
1364 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001365 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001366 pj_status_t status;
1367 pjsip_tx_data *tdata;
1368
1369
Benny Prijono148c9dd2006-09-19 13:37:53 +00001370 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1371 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1372 call_id));
1373 }
1374
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1376 PJ_EINVAL);
1377
Benny Prijonodc752ca2006-09-22 16:55:42 +00001378 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001379 if (status != PJ_SUCCESS)
1380 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381
1382 if (code==0) {
1383 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1384 code = PJSIP_SC_OK;
1385 else if (call->inv->role == PJSIP_ROLE_UAS)
1386 code = PJSIP_SC_DECLINE;
1387 else
1388 code = PJSIP_SC_REQUEST_TERMINATED;
1389 }
1390
1391 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1392 if (status != PJ_SUCCESS) {
1393 pjsua_perror(THIS_FILE,
1394 "Failed to create end session message",
1395 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001396 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001397 return status;
1398 }
1399
1400 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1401 * as p_tdata when INVITE transaction has not been answered
1402 * with any provisional responses.
1403 */
1404 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001405 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 return PJ_SUCCESS;
1407 }
1408
1409 /* Add additional headers etc */
1410 pjsua_process_msg_data( tdata, msg_data);
1411
1412 /* Send the message */
1413 status = pjsip_inv_send_msg(call->inv, tdata);
1414 if (status != PJ_SUCCESS) {
1415 pjsua_perror(THIS_FILE,
1416 "Failed to send end session message",
1417 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001418 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001419 return status;
1420 }
1421
Benny Prijonodc752ca2006-09-22 16:55:42 +00001422 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001423
1424 return PJ_SUCCESS;
1425}
1426
1427
1428/*
1429 * Put the specified call on hold.
1430 */
1431PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1432 const pjsua_msg_data *msg_data)
1433{
1434 pjmedia_sdp_session *sdp;
1435 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001436 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001437 pjsip_tx_data *tdata;
1438 pj_status_t status;
1439
1440 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1441 PJ_EINVAL);
1442
Benny Prijonodc752ca2006-09-22 16:55:42 +00001443 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001444 if (status != PJ_SUCCESS)
1445 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001446
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001447
1448 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1449 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001450 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001451 return PJSIP_ESESSIONSTATE;
1452 }
1453
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001454 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001455 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001456 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001457 return status;
1458 }
1459
Benny Prijono7129cc72007-11-05 05:54:25 +00001460 update_sdp_version(call, sdp);
1461
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001462 /* Create re-INVITE with new offer */
1463 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1464 if (status != PJ_SUCCESS) {
1465 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001466 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001467 return status;
1468 }
1469
1470 /* Add additional headers etc */
1471 pjsua_process_msg_data( tdata, msg_data);
1472
1473 /* Send the request */
1474 status = pjsip_inv_send_msg( call->inv, tdata);
1475 if (status != PJ_SUCCESS) {
1476 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001477 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 return status;
1479 }
1480
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001481 /* Set flag that local put the call on hold */
1482 call->local_hold = PJ_TRUE;
1483
Benny Prijonodc752ca2006-09-22 16:55:42 +00001484 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001485
1486 return PJ_SUCCESS;
1487}
1488
1489
1490/*
1491 * Send re-INVITE (to release hold).
1492 */
1493PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1494 pj_bool_t unhold,
1495 const pjsua_msg_data *msg_data)
1496{
1497 pjmedia_sdp_session *sdp;
1498 pjsip_tx_data *tdata;
1499 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001500 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001501 pj_status_t status;
1502
1503
1504 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1505 PJ_EINVAL);
1506
Benny Prijonodc752ca2006-09-22 16:55:42 +00001507 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001508 if (status != PJ_SUCCESS)
1509 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001510
1511 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1512 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001513 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001514 return PJSIP_ESESSIONSTATE;
1515 }
1516
1517 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001518 if (call->local_hold && !unhold) {
1519 status = create_sdp_of_call_hold(call, &sdp);
1520 } else {
1521 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
1522 NULL, &sdp, NULL);
1523 call->local_hold = PJ_FALSE;
1524 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525 if (status != PJ_SUCCESS) {
1526 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1527 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001528 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001529 return status;
1530 }
1531
Benny Prijono7129cc72007-11-05 05:54:25 +00001532 update_sdp_version(call, sdp);
1533
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001534 /* Create re-INVITE with new offer */
1535 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1536 if (status != PJ_SUCCESS) {
1537 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001538 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539 return status;
1540 }
1541
1542 /* Add additional headers etc */
1543 pjsua_process_msg_data( tdata, msg_data);
1544
1545 /* Send the request */
1546 status = pjsip_inv_send_msg( call->inv, tdata);
1547 if (status != PJ_SUCCESS) {
1548 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001549 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001550 return status;
1551 }
1552
Benny Prijonodc752ca2006-09-22 16:55:42 +00001553 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554
1555 return PJ_SUCCESS;
1556}
1557
1558
1559/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001560 * Send UPDATE request.
1561 */
1562PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1563 unsigned options,
1564 const pjsua_msg_data *msg_data)
1565{
1566 pjmedia_sdp_session *sdp;
1567 pjsip_tx_data *tdata;
1568 pjsua_call *call;
1569 pjsip_dialog *dlg;
1570 pj_status_t status;
1571
1572 PJ_UNUSED_ARG(options);
1573
1574 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1575 PJ_EINVAL);
1576
1577 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1578 if (status != PJ_SUCCESS)
1579 return status;
1580
Benny Prijonoc08682e2007-10-04 06:17:58 +00001581 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001582 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001583 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001584 if (status != PJ_SUCCESS) {
1585 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1586 status);
1587 pjsip_dlg_dec_lock(dlg);
1588 return status;
1589 }
1590
Benny Prijono224b4e22008-06-19 14:10:28 +00001591 update_sdp_version(call, sdp);
1592
1593 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001594 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1595 if (status != PJ_SUCCESS) {
1596 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1597 pjsip_dlg_dec_lock(dlg);
1598 return status;
1599 }
1600
1601 /* Add additional headers etc */
1602 pjsua_process_msg_data( tdata, msg_data);
1603
1604 /* Send the request */
1605 status = pjsip_inv_send_msg( call->inv, tdata);
1606 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001607 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001608 pjsip_dlg_dec_lock(dlg);
1609 return status;
1610 }
1611
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001612 call->local_hold = PJ_FALSE;
1613
Benny Prijonoc08682e2007-10-04 06:17:58 +00001614 pjsip_dlg_dec_lock(dlg);
1615
1616 return PJ_SUCCESS;
1617}
1618
1619
1620/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001621 * Initiate call transfer to the specified address.
1622 */
1623PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1624 const pj_str_t *dest,
1625 const pjsua_msg_data *msg_data)
1626{
1627 pjsip_evsub *sub;
1628 pjsip_tx_data *tdata;
1629 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001630 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001631 pjsip_generic_string_hdr *gs_hdr;
1632 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001633 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001634 pj_status_t status;
1635
1636
1637 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1638 PJ_EINVAL);
1639
Benny Prijonodc752ca2006-09-22 16:55:42 +00001640 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001641 if (status != PJ_SUCCESS)
1642 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001643
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644
Benny Prijonod524e822006-09-22 12:48:18 +00001645 /* Create xfer client subscription. */
1646 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001647 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001648
1649 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001650 if (status != PJ_SUCCESS) {
1651 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001652 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 return status;
1654 }
1655
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001656 /* Associate this call with the client subscription */
1657 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1658
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659 /*
1660 * Create REFER request.
1661 */
1662 status = pjsip_xfer_initiate(sub, dest, &tdata);
1663 if (status != PJ_SUCCESS) {
1664 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001665 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001666 return status;
1667 }
1668
Benny Prijono053f5222006-11-11 16:16:04 +00001669 /* Add Referred-By header */
1670 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1671 &dlg->local.info_str);
1672 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1673
1674
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 /* Add additional headers etc */
1676 pjsua_process_msg_data( tdata, msg_data);
1677
1678 /* Send. */
1679 status = pjsip_xfer_send_request(sub, tdata);
1680 if (status != PJ_SUCCESS) {
1681 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001682 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683 return status;
1684 }
1685
1686 /* For simplicity (that's what this program is intended to be!),
1687 * leave the original invite session as it is. More advanced application
1688 * may want to hold the INVITE, or terminate the invite, or whatever.
1689 */
1690
Benny Prijonodc752ca2006-09-22 16:55:42 +00001691 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001692
1693 return PJ_SUCCESS;
1694
1695}
1696
1697
1698/*
Benny Prijono053f5222006-11-11 16:16:04 +00001699 * Initiate attended call transfer to the specified address.
1700 */
1701PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1702 pjsua_call_id dest_call_id,
1703 unsigned options,
1704 const pjsua_msg_data *msg_data)
1705{
1706 pjsua_call *dest_call;
1707 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001708 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001709 pj_str_t str_dest;
1710 int len;
1711 pjsip_uri *uri;
1712 pj_status_t status;
1713
1714
1715 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1716 PJ_EINVAL);
1717 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1718 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1719 PJ_EINVAL);
1720
1721 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1722 &dest_call, &dest_dlg);
1723 if (status != PJ_SUCCESS)
1724 return status;
1725
1726 /*
1727 * Create REFER destination URI with Replaces field.
1728 */
1729
1730 /* Make sure we have sufficient buffer's length */
1731 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1732 dest_dlg->call_id->id.slen +
1733 dest_dlg->remote.info->tag.slen +
1734 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001735 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001736
1737 /* Print URI */
1738 str_dest_buf[0] = '<';
1739 str_dest.slen = 1;
1740
Benny Prijonoa1e69682007-05-11 15:14:34 +00001741 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001742 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1743 str_dest_buf+1, sizeof(str_dest_buf)-1);
1744 if (len < 0)
1745 return PJSIP_EURITOOLONG;
1746
1747 str_dest.slen += len;
1748
1749
1750 /* Build the URI */
1751 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1752 sizeof(str_dest_buf) - str_dest.slen,
1753 "?%s"
1754 "Replaces=%.*s"
1755 "%%3Bto-tag%%3D%.*s"
1756 "%%3Bfrom-tag%%3D%.*s>",
1757 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1758 "" : "Require=replaces&"),
1759 (int)dest_dlg->call_id->id.slen,
1760 dest_dlg->call_id->id.ptr,
1761 (int)dest_dlg->remote.info->tag.slen,
1762 dest_dlg->remote.info->tag.ptr,
1763 (int)dest_dlg->local.info->tag.slen,
1764 dest_dlg->local.info->tag.ptr);
1765
1766 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1767 PJSIP_EURITOOLONG);
1768
1769 str_dest.ptr = str_dest_buf;
1770 str_dest.slen += len;
1771
1772 pjsip_dlg_dec_lock(dest_dlg);
1773
1774 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1775}
1776
1777
1778/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779 * Send DTMF digits to remote using RFC 2833 payload formats.
1780 */
1781PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1782 const pj_str_t *digits)
1783{
1784 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001785 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001786 pj_status_t status;
1787
1788 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1789 PJ_EINVAL);
1790
Benny Prijonodc752ca2006-09-22 16:55:42 +00001791 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001792 if (status != PJ_SUCCESS)
1793 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001794
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001795 if (!call->session) {
1796 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001797 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001798 return PJ_EINVALIDOP;
1799 }
1800
1801 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1802
Benny Prijonodc752ca2006-09-22 16:55:42 +00001803 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001804
1805 return status;
1806}
1807
1808
1809/**
1810 * Send instant messaging inside INVITE session.
1811 */
1812PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1813 const pj_str_t *mime_type,
1814 const pj_str_t *content,
1815 const pjsua_msg_data *msg_data,
1816 void *user_data)
1817{
1818 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001819 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 const pj_str_t mime_text_plain = pj_str("text/plain");
1821 pjsip_media_type ctype;
1822 pjsua_im_data *im_data;
1823 pjsip_tx_data *tdata;
1824 pj_status_t status;
1825
1826
1827 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1828 PJ_EINVAL);
1829
Benny Prijonodc752ca2006-09-22 16:55:42 +00001830 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001831 if (status != PJ_SUCCESS)
1832 return status;
1833
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001834 /* Set default media type if none is specified */
1835 if (mime_type == NULL) {
1836 mime_type = &mime_text_plain;
1837 }
1838
1839 /* Create request message. */
1840 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1841 -1, &tdata);
1842 if (status != PJ_SUCCESS) {
1843 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1844 goto on_return;
1845 }
1846
1847 /* Add accept header. */
1848 pjsip_msg_add_hdr( tdata->msg,
1849 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1850
1851 /* Parse MIME type */
1852 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1853
1854 /* Create "text/plain" message body. */
1855 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1856 &ctype.subtype, content);
1857 if (tdata->msg->body == NULL) {
1858 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1859 pjsip_tx_data_dec_ref(tdata);
1860 goto on_return;
1861 }
1862
1863 /* Add additional headers etc */
1864 pjsua_process_msg_data( tdata, msg_data);
1865
1866 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001867 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001868 im_data->acc_id = call->acc_id;
1869 im_data->call_id = call_id;
1870 im_data->to = call->inv->dlg->remote.info_str;
1871 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1872 im_data->user_data = user_data;
1873
1874
1875 /* Send the request. */
1876 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1877 pjsua_var.mod.id, im_data);
1878 if (status != PJ_SUCCESS) {
1879 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1880 goto on_return;
1881 }
1882
1883on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001884 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001885 return status;
1886}
1887
1888
1889/*
1890 * Send IM typing indication inside INVITE session.
1891 */
1892PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1893 pj_bool_t is_typing,
1894 const pjsua_msg_data*msg_data)
1895{
1896 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001897 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001898 pjsip_tx_data *tdata;
1899 pj_status_t status;
1900
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001901 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1902 PJ_EINVAL);
1903
Benny Prijonodc752ca2006-09-22 16:55:42 +00001904 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001905 if (status != PJ_SUCCESS)
1906 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001907
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001908 /* Create request message. */
1909 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1910 -1, &tdata);
1911 if (status != PJ_SUCCESS) {
1912 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1913 goto on_return;
1914 }
1915
1916 /* Create "application/im-iscomposing+xml" msg body. */
1917 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1918 NULL, NULL, -1);
1919
1920 /* Add additional headers etc */
1921 pjsua_process_msg_data( tdata, msg_data);
1922
1923 /* Send the request. */
1924 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1925 if (status != PJ_SUCCESS) {
1926 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1927 goto on_return;
1928 }
1929
1930on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001931 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001932 return status;
1933}
1934
1935
1936/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001937 * Send arbitrary request.
1938 */
1939PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1940 const pj_str_t *method_str,
1941 const pjsua_msg_data *msg_data)
1942{
1943 pjsua_call *call;
1944 pjsip_dialog *dlg;
1945 pjsip_method method;
1946 pjsip_tx_data *tdata;
1947 pj_status_t status;
1948
1949 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1950 PJ_EINVAL);
1951
1952 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1953 if (status != PJ_SUCCESS)
1954 return status;
1955
1956 /* Init method */
1957 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1958
1959 /* Create request message. */
1960 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1961 if (status != PJ_SUCCESS) {
1962 pjsua_perror(THIS_FILE, "Unable to create request", status);
1963 goto on_return;
1964 }
1965
1966 /* Add additional headers etc */
1967 pjsua_process_msg_data( tdata, msg_data);
1968
1969 /* Send the request. */
1970 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1971 if (status != PJ_SUCCESS) {
1972 pjsua_perror(THIS_FILE, "Unable to send request", status);
1973 goto on_return;
1974 }
1975
1976on_return:
1977 pjsip_dlg_dec_lock(dlg);
1978 return status;
1979}
1980
1981
1982/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 * Terminate all calls.
1984 */
1985PJ_DEF(void) pjsua_call_hangup_all(void)
1986{
1987 unsigned i;
1988
1989 PJSUA_LOCK();
1990
1991 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1992 if (pjsua_var.calls[i].inv)
1993 pjsua_call_hangup(i, 0, NULL, NULL);
1994 }
1995
1996 PJSUA_UNLOCK();
1997}
1998
1999
Benny Prijono627cbb42007-09-25 20:48:49 +00002000const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002001{
2002 if (val < 1000) {
2003 pj_ansi_sprintf(buf, "%d", val);
2004 } else if (val < 1000000) {
2005 pj_ansi_sprintf(buf, "%d.%dK",
2006 val / 1000,
2007 (val % 1000) / 100);
2008 } else {
2009 pj_ansi_sprintf(buf, "%d.%02dM",
2010 val / 1000000,
2011 (val % 1000000) / 10000);
2012 }
2013
2014 return buf;
2015}
2016
2017
2018/* Dump media session */
2019static void dump_media_session(const char *indent,
2020 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002021 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002022{
2023 unsigned i;
2024 char *p = buf, *end = buf+maxlen;
2025 int len;
2026 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002027 pjmedia_session *session = call->session;
2028 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002029
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002030 pjmedia_transport_info_init(&tp_info);
2031
2032 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002033 pjmedia_session_get_info(session, &info);
2034
2035 for (i=0; i<info.stream_cnt; ++i) {
2036 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002037 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002038 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002039 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002040 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002041 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002042 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002043
2044 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002045 // rem_addr will contain actual address of RTP originator, instead of
2046 // remote RTP address specified by stream which is fetched from the SDP.
2047 // Please note that we are assuming only one stream per call.
2048 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2049 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002050 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2051 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002052 sizeof(rem_addr_buf), 3);
2053 } else {
2054 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002055 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002056 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002057
2058 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2059 dir = "sendonly";
2060 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2061 dir = "recvonly";
2062 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2063 dir = "sendrecv";
2064 else
2065 dir = "inactive";
2066
2067
2068 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002069 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002071 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002072 info.stream_info[i].fmt.encoding_name.ptr,
2073 info.stream_info[i].fmt.clock_rate / 1000,
2074 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002075 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002076 if (len < 1 || len > end-p) {
2077 *p = '\0';
2078 return;
2079 }
2080
2081 p += len;
2082 *p++ = '\n';
2083 *p = '\0';
2084
2085 if (stat.rx.update_cnt == 0)
2086 strcpy(last_update, "never");
2087 else {
2088 pj_gettimeofday(&now);
2089 PJ_TIME_VAL_SUB(now, stat.rx.update);
2090 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2091 now.sec / 3600,
2092 (now.sec % 3600) / 60,
2093 now.sec % 60,
2094 now.msec);
2095 }
2096
Benny Prijono80019eb2006-08-07 13:22:23 +00002097 pj_gettimeofday(&media_duration);
2098 PJ_TIME_VAL_SUB(media_duration, stat.start);
2099 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2100 media_duration.msec = 1;
2101
Benny Prijono1402a4a2008-01-08 23:41:22 +00002102 /* protect against division by zero */
2103 if (stat.rx.pkt == 0)
2104 stat.rx.pkt = 1;
2105 if (stat.tx.pkt == 0)
2106 stat.tx.pkt = 1;
2107
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108 len = pj_ansi_snprintf(p, end-p,
2109 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002110 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002111 "%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 +00002112 "%s (msec) min avg max last dev\n"
2113 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2114 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115 indent, info.stream_info[i].fmt.pt,
2116 last_update,
2117 indent,
2118 good_number(packets, stat.rx.pkt),
2119 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002120 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002121 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2122 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 +00002123 indent,
2124 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002125 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002126 stat.rx.discard,
2127 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002129 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002130 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002131 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002132 indent, indent,
2133 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002134 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002135 stat.rx.loss_period.max / 1000.0,
2136 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002137 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002138 indent,
2139 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002140 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141 stat.rx.jitter.max / 1000.0,
2142 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002143 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002144 ""
2145 );
2146
2147 if (len < 1 || len > end-p) {
2148 *p = '\0';
2149 return;
2150 }
2151
2152 p += len;
2153 *p++ = '\n';
2154 *p = '\0';
2155
2156 if (stat.tx.update_cnt == 0)
2157 strcpy(last_update, "never");
2158 else {
2159 pj_gettimeofday(&now);
2160 PJ_TIME_VAL_SUB(now, stat.tx.update);
2161 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2162 now.sec / 3600,
2163 (now.sec % 3600) / 60,
2164 now.sec % 60,
2165 now.msec);
2166 }
2167
2168 len = pj_ansi_snprintf(p, end-p,
2169 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002170 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002172 "%s (msec) min avg max last dev \n"
2173 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2174 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002175 indent,
2176 info.stream_info[i].tx_pt,
2177 info.stream_info[i].param->info.frm_ptime *
2178 info.stream_info[i].param->setting.frm_per_pkt,
2179 last_update,
2180
2181 indent,
2182 good_number(packets, stat.tx.pkt),
2183 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002184 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002185 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2186 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 +00002187
2188 indent,
2189 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002190 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002192 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002193 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002194 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002195
2196 indent, indent,
2197 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002198 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 stat.tx.loss_period.max / 1000.0,
2200 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002201 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002202 indent,
2203 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002204 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002205 stat.tx.jitter.max / 1000.0,
2206 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002207 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002208 ""
2209 );
2210
2211 if (len < 1 || len > end-p) {
2212 *p = '\0';
2213 return;
2214 }
2215
2216 p += len;
2217 *p++ = '\n';
2218 *p = '\0';
2219
2220 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002221 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002222 indent,
2223 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002224 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002225 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002226 stat.rtt.last / 1000.0,
2227 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228 );
2229 if (len < 1 || len > end-p) {
2230 *p = '\0';
2231 return;
2232 }
2233
2234 p += len;
2235 *p++ = '\n';
2236 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002237
2238#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2239# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2240 do { \
2241 if (samples <= 4294) \
2242 usec = samples * 1000000 / clock_rate; \
2243 else { \
2244 usec = samples * 1000 / clock_rate; \
2245 usec *= 1000; \
2246 } \
2247 } while(0)
2248
2249# define PRINT_VOIP_MTC_VAL(s, v) \
2250 if (v == 127) \
2251 sprintf(s, "(na)"); \
2252 else \
2253 sprintf(s, "%d", v)
2254
2255# define VALIDATE_PRINT_BUF() \
2256 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2257 p += len; *p++ = '\n'; *p = '\0'
2258
2259
2260 do {
2261 char loss[16], dup[16];
2262 char jitter[80];
2263 char toh[80];
2264 char plc[16], jba[16], jbr[16];
2265 char signal_lvl[16], noise_lvl[16], rerl[16];
2266 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2267 pjmedia_rtcp_xr_stat xr_stat;
2268 unsigned clock_rate;
2269
2270 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2271 PJ_SUCCESS)
2272 {
2273 break;
2274 }
2275
2276 clock_rate = info.stream_info[i].fmt.clock_rate;
2277
2278 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2279 VALIDATE_PRINT_BUF();
2280
2281 /* Statistics Summary */
2282 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2283 VALIDATE_PRINT_BUF();
2284
2285 if (xr_stat.rx.stat_sum.l)
2286 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2287 else
2288 sprintf(loss, "(na)");
2289
2290 if (xr_stat.rx.stat_sum.d)
2291 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2292 else
2293 sprintf(dup, "(na)");
2294
2295 if (xr_stat.rx.stat_sum.j) {
2296 unsigned jmin, jmax, jmean, jdev;
2297
2298 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2299 clock_rate);
2300 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2301 clock_rate);
2302 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2303 clock_rate);
2304 SAMPLES_TO_USEC(jdev,
2305 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2306 clock_rate);
2307 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2308 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2309 } else
2310 sprintf(jitter, "(report not available)");
2311
2312 if (xr_stat.rx.stat_sum.t) {
2313 sprintf(toh, "%11d %11d %11d %11d",
2314 xr_stat.rx.stat_sum.toh.min,
2315 xr_stat.rx.stat_sum.toh.mean,
2316 xr_stat.rx.stat_sum.toh.max,
2317 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2318 } else
2319 sprintf(toh, "(report not available)");
2320
2321 if (xr_stat.rx.stat_sum.update.sec == 0)
2322 strcpy(last_update, "never");
2323 else {
2324 pj_gettimeofday(&now);
2325 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2326 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2327 now.sec / 3600,
2328 (now.sec % 3600) / 60,
2329 now.sec % 60,
2330 now.msec);
2331 }
2332
2333 len = pj_ansi_snprintf(p, end-p,
2334 "%s RX last update: %s\n"
2335 "%s begin seq=%d, end seq=%d\n"
2336 "%s pkt loss=%s, dup=%s\n"
2337 "%s (msec) min avg max dev\n"
2338 "%s jitter : %s\n"
2339 "%s toh : %s",
2340 indent, last_update,
2341 indent,
2342 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2343 indent, loss, dup,
2344 indent,
2345 indent, jitter,
2346 indent, toh
2347 );
2348 VALIDATE_PRINT_BUF();
2349
2350 if (xr_stat.tx.stat_sum.l)
2351 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2352 else
2353 sprintf(loss, "(na)");
2354
2355 if (xr_stat.tx.stat_sum.d)
2356 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2357 else
2358 sprintf(dup, "(na)");
2359
2360 if (xr_stat.tx.stat_sum.j) {
2361 unsigned jmin, jmax, jmean, jdev;
2362
2363 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2364 clock_rate);
2365 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2366 clock_rate);
2367 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2368 clock_rate);
2369 SAMPLES_TO_USEC(jdev,
2370 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2371 clock_rate);
2372 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2373 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2374 } else
2375 sprintf(jitter, "(report not available)");
2376
2377 if (xr_stat.tx.stat_sum.t) {
2378 sprintf(toh, "%11d %11d %11d %11d",
2379 xr_stat.tx.stat_sum.toh.min,
2380 xr_stat.tx.stat_sum.toh.mean,
2381 xr_stat.tx.stat_sum.toh.max,
2382 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2383 } else
2384 sprintf(toh, "(report not available)");
2385
2386 if (xr_stat.tx.stat_sum.update.sec == 0)
2387 strcpy(last_update, "never");
2388 else {
2389 pj_gettimeofday(&now);
2390 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2391 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2392 now.sec / 3600,
2393 (now.sec % 3600) / 60,
2394 now.sec % 60,
2395 now.msec);
2396 }
2397
2398 len = pj_ansi_snprintf(p, end-p,
2399 "%s TX last update: %s\n"
2400 "%s begin seq=%d, end seq=%d\n"
2401 "%s pkt loss=%s, dup=%s\n"
2402 "%s (msec) min avg max dev\n"
2403 "%s jitter : %s\n"
2404 "%s toh : %s",
2405 indent, last_update,
2406 indent,
2407 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2408 indent, loss, dup,
2409 indent,
2410 indent, jitter,
2411 indent, toh
2412 );
2413 VALIDATE_PRINT_BUF();
2414
2415
2416 /* VoIP Metrics */
2417 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2418 VALIDATE_PRINT_BUF();
2419
2420 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2421 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2422 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2423 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2424 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2425 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2426 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2427
2428 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2429 case PJMEDIA_RTCP_XR_PLC_DIS:
2430 sprintf(plc, "DISABLED");
2431 break;
2432 case PJMEDIA_RTCP_XR_PLC_ENH:
2433 sprintf(plc, "ENHANCED");
2434 break;
2435 case PJMEDIA_RTCP_XR_PLC_STD:
2436 sprintf(plc, "STANDARD");
2437 break;
2438 case PJMEDIA_RTCP_XR_PLC_UNK:
2439 default:
2440 sprintf(plc, "UNKNOWN");
2441 break;
2442 }
2443
2444 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2445 case PJMEDIA_RTCP_XR_JB_FIXED:
2446 sprintf(jba, "FIXED");
2447 break;
2448 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2449 sprintf(jba, "ADAPTIVE");
2450 break;
2451 default:
2452 sprintf(jba, "UNKNOWN");
2453 break;
2454 }
2455
2456 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2457
2458 if (xr_stat.rx.voip_mtc.update.sec == 0)
2459 strcpy(last_update, "never");
2460 else {
2461 pj_gettimeofday(&now);
2462 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2463 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2464 now.sec / 3600,
2465 (now.sec % 3600) / 60,
2466 now.sec % 60,
2467 now.msec);
2468 }
2469
2470 len = pj_ansi_snprintf(p, end-p,
2471 "%s RX last update: %s\n"
2472 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2473 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2474 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2475 "%s delay : round trip=%d%s, end system=%d%s\n"
2476 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2477 "%s quality : R factor=%s, ext R factor=%s\n"
2478 "%s MOS LQ=%s, MOS CQ=%s\n"
2479 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2480 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2481 indent,
2482 last_update,
2483 /* packets */
2484 indent,
2485 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2486 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2487 /* burst */
2488 indent,
2489 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2490 xr_stat.rx.voip_mtc.burst_dur, "ms",
2491 /* gap */
2492 indent,
2493 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2494 xr_stat.rx.voip_mtc.gap_dur, "ms",
2495 /* delay */
2496 indent,
2497 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2498 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2499 /* level */
2500 indent,
2501 signal_lvl, "dB",
2502 noise_lvl, "dB",
2503 rerl, "",
2504 /* quality */
2505 indent,
2506 r_factor, ext_r_factor,
2507 indent,
2508 mos_lq, mos_cq,
2509 /* config */
2510 indent,
2511 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2512 /* JB delay */
2513 indent,
2514 xr_stat.rx.voip_mtc.jb_nom, "ms",
2515 xr_stat.rx.voip_mtc.jb_max, "ms",
2516 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2517 );
2518 VALIDATE_PRINT_BUF();
2519
2520 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2521 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2522 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2523 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2524 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2525 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2526 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2527
2528 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2529 case PJMEDIA_RTCP_XR_PLC_DIS:
2530 sprintf(plc, "DISABLED");
2531 break;
2532 case PJMEDIA_RTCP_XR_PLC_ENH:
2533 sprintf(plc, "ENHANCED");
2534 break;
2535 case PJMEDIA_RTCP_XR_PLC_STD:
2536 sprintf(plc, "STANDARD");
2537 break;
2538 case PJMEDIA_RTCP_XR_PLC_UNK:
2539 default:
2540 sprintf(plc, "unknown");
2541 break;
2542 }
2543
2544 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2545 case PJMEDIA_RTCP_XR_JB_FIXED:
2546 sprintf(jba, "FIXED");
2547 break;
2548 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2549 sprintf(jba, "ADAPTIVE");
2550 break;
2551 default:
2552 sprintf(jba, "unknown");
2553 break;
2554 }
2555
2556 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2557
2558 if (xr_stat.tx.voip_mtc.update.sec == 0)
2559 strcpy(last_update, "never");
2560 else {
2561 pj_gettimeofday(&now);
2562 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2563 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2564 now.sec / 3600,
2565 (now.sec % 3600) / 60,
2566 now.sec % 60,
2567 now.msec);
2568 }
2569
2570 len = pj_ansi_snprintf(p, end-p,
2571 "%s TX last update: %s\n"
2572 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2573 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2574 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2575 "%s delay : round trip=%d%s, end system=%d%s\n"
2576 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2577 "%s quality : R factor=%s, ext R factor=%s\n"
2578 "%s MOS LQ=%s, MOS CQ=%s\n"
2579 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2580 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2581 indent,
2582 last_update,
2583 /* pakcets */
2584 indent,
2585 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2586 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2587 /* burst */
2588 indent,
2589 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2590 xr_stat.tx.voip_mtc.burst_dur, "ms",
2591 /* gap */
2592 indent,
2593 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2594 xr_stat.tx.voip_mtc.gap_dur, "ms",
2595 /* delay */
2596 indent,
2597 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2598 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2599 /* level */
2600 indent,
2601 signal_lvl, "dB",
2602 noise_lvl, "dB",
2603 rerl, "",
2604 /* quality */
2605 indent,
2606 r_factor, ext_r_factor,
2607 indent,
2608 mos_lq, mos_cq,
2609 /* config */
2610 indent,
2611 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2612 /* JB delay */
2613 indent,
2614 xr_stat.tx.voip_mtc.jb_nom, "ms",
2615 xr_stat.tx.voip_mtc.jb_max, "ms",
2616 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2617 );
2618 VALIDATE_PRINT_BUF();
2619
2620
2621 /* RTT delay (by receiver side) */
2622 len = pj_ansi_snprintf(p, end-p,
2623 "%s RTT (from recv) min avg max last dev",
2624 indent);
2625 VALIDATE_PRINT_BUF();
2626 len = pj_ansi_snprintf(p, end-p,
2627 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2628 indent,
2629 xr_stat.rtt.min / 1000.0,
2630 xr_stat.rtt.mean / 1000.0,
2631 xr_stat.rtt.max / 1000.0,
2632 xr_stat.rtt.last / 1000.0,
2633 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2634 );
2635 VALIDATE_PRINT_BUF();
2636 } while(0);
2637#endif
2638
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002639 }
2640}
2641
2642
2643/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002644void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002645 int call_id,
2646 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002647{
2648 int len;
2649 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2650 pjsip_dialog *dlg = inv->dlg;
2651 char userinfo[128];
2652
2653 /* Dump invite sesion info. */
2654
2655 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2656 if (len < 1)
2657 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2658 else
2659 userinfo[len] = '\0';
2660
2661 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2662 title,
2663 pjsip_inv_state_name(inv->state),
2664 userinfo);
2665 if (len < 1 || len >= (int)size) {
2666 pj_ansi_strcpy(buf, "<--uri too long-->");
2667 len = 18;
2668 } else
2669 buf[len] = '\0';
2670}
2671
2672
2673/*
2674 * Dump call and media statistics to string.
2675 */
2676PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2677 pj_bool_t with_media,
2678 char *buffer,
2679 unsigned maxlen,
2680 const char *indent)
2681{
2682 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002683 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002684 pj_time_val duration, res_delay, con_delay;
2685 char tmp[128];
2686 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002687 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002688 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002689 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002690
2691 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2692 PJ_EINVAL);
2693
Benny Prijonodc752ca2006-09-22 16:55:42 +00002694 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002695 if (status != PJ_SUCCESS)
2696 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002697
2698 *buffer = '\0';
2699 p = buffer;
2700 end = buffer + maxlen;
2701 len = 0;
2702
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002703 print_call(indent, call_id, tmp, sizeof(tmp));
2704
2705 len = pj_ansi_strlen(tmp);
2706 pj_ansi_strcpy(buffer, tmp);
2707
2708 p += len;
2709 *p++ = '\r';
2710 *p++ = '\n';
2711
2712 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002713 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002714 pj_gettimeofday(&duration);
2715 PJ_TIME_VAL_SUB(duration, call->conn_time);
2716 con_delay = call->conn_time;
2717 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2718 } else {
2719 duration.sec = duration.msec = 0;
2720 con_delay.sec = con_delay.msec = 0;
2721 }
2722
2723 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002724 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002725 res_delay = call->res_time;
2726 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2727 } else {
2728 res_delay.sec = res_delay.msec = 0;
2729 }
2730
2731 /* Print duration */
2732 len = pj_ansi_snprintf(p, end-p,
2733 "%s Call time: %02dh:%02dm:%02ds, "
2734 "1st res in %d ms, conn in %dms",
2735 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002736 (int)(duration.sec / 3600),
2737 (int)((duration.sec % 3600)/60),
2738 (int)(duration.sec % 60),
2739 (int)PJ_TIME_VAL_MSEC(res_delay),
2740 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002741
2742 if (len > 0 && len < end-p) {
2743 p += len;
2744 *p++ = '\n';
2745 *p = '\0';
2746 }
2747
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002748 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002749 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002750 pjmedia_transport_get_info(call->med_tp, &tp_info);
2751 if (tp_info.specific_info_cnt > 0) {
2752 int i;
2753 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2754 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2755 {
2756 pjmedia_srtp_info *srtp_info =
2757 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2758
2759 len = pj_ansi_snprintf(p, end-p,
2760 "%s SRTP status: %s Crypto-suite: %s",
2761 indent,
2762 (srtp_info->active?"Active":"Not active"),
2763 srtp_info->tx_policy.name.ptr);
2764 if (len > 0 && len < end-p) {
2765 p += len;
2766 *p++ = '\n';
2767 *p = '\0';
2768 }
2769 break;
2770 }
2771 }
2772 }
2773
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002774 /* Dump session statistics */
2775 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002776 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002777
Benny Prijonodc752ca2006-09-22 16:55:42 +00002778 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002779
2780 return PJ_SUCCESS;
2781}
2782
2783
2784/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002785 * This callback receives notification from invite session when the
2786 * session state has changed.
2787 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002788static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2789 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002790{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002791 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002792
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002793 PJSUA_LOCK();
2794
Benny Prijonoa1e69682007-05-11 15:14:34 +00002795 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002796
2797 if (!call) {
2798 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002799 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002800 }
2801
Benny Prijonoe21e7842006-04-09 16:46:05 +00002802
2803 /* Get call times */
2804 switch (inv->state) {
2805 case PJSIP_INV_STATE_EARLY:
2806 case PJSIP_INV_STATE_CONNECTING:
2807 if (call->res_time.sec == 0)
2808 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002809 call->last_code = (pjsip_status_code)
2810 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002811 pj_strncpy(&call->last_text,
2812 &e->body.tsx_state.tsx->status_text,
2813 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002814 break;
2815 case PJSIP_INV_STATE_CONFIRMED:
2816 pj_gettimeofday(&call->conn_time);
2817 break;
2818 case PJSIP_INV_STATE_DISCONNECTED:
2819 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002820 if (call->res_time.sec == 0)
2821 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002822 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002823 call->last_code = (pjsip_status_code)
2824 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002825 pj_strncpy(&call->last_text,
2826 &e->body.tsx_state.tsx->status_text,
2827 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002828 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002829 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002830 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002831 call->last_code = (pjsip_status_code)
2832 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002833 pj_strncpy(&call->last_text,
2834 &e->body.tsx_state.tsx->status_text,
2835 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002836 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002837 }
2838
Benny Prijono26ff9062006-02-21 23:47:00 +00002839 /* If this is an outgoing INVITE that was created because of
2840 * REFER/transfer, send NOTIFY to transferer.
2841 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002842 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002843 int st_code = -1;
2844 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2845
2846
Benny Prijonoa91a0032006-02-26 21:23:45 +00002847 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002848 case PJSIP_INV_STATE_NULL:
2849 case PJSIP_INV_STATE_CALLING:
2850 /* Do nothing */
2851 break;
2852
2853 case PJSIP_INV_STATE_EARLY:
2854 case PJSIP_INV_STATE_CONNECTING:
2855 st_code = e->body.tsx_state.tsx->status_code;
2856 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2857 break;
2858
2859 case PJSIP_INV_STATE_CONFIRMED:
2860 /* When state is confirmed, send the final 200/OK and terminate
2861 * subscription.
2862 */
2863 st_code = e->body.tsx_state.tsx->status_code;
2864 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2865 break;
2866
2867 case PJSIP_INV_STATE_DISCONNECTED:
2868 st_code = e->body.tsx_state.tsx->status_code;
2869 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2870 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002871
Benny Prijono8b1889b2006-06-06 18:40:40 +00002872 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002873 /* Nothing to do. Just to keep gcc from complaining about
2874 * unused enums.
2875 */
2876 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002877 }
2878
2879 if (st_code != -1) {
2880 pjsip_tx_data *tdata;
2881 pj_status_t status;
2882
Benny Prijonoa91a0032006-02-26 21:23:45 +00002883 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002884 ev_state, st_code,
2885 NULL, &tdata);
2886 if (status != PJ_SUCCESS) {
2887 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2888 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002889 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002890 if (status != PJ_SUCCESS) {
2891 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2892 }
2893 }
2894 }
2895 }
2896
Benny Prijono84126ab2006-02-09 09:30:09 +00002897
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002898 if (pjsua_var.ua_cfg.cb.on_call_state)
2899 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002900
2901 /* call->inv may be NULL now */
2902
Benny Prijono84126ab2006-02-09 09:30:09 +00002903 /* Destroy media session when invite session is disconnected. */
2904 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002905
Benny Prijonoa91a0032006-02-26 21:23:45 +00002906 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002907
Benny Prijono275fd682006-03-22 11:59:11 +00002908 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002909 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002910
Benny Prijono105217f2006-03-06 16:25:59 +00002911 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002912 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002913 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002914
2915 /* Reset call */
2916 reset_call(call->index);
2917
Benny Prijono84126ab2006-02-09 09:30:09 +00002918 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002919
2920 PJSUA_UNLOCK();
2921}
2922
2923/*
2924 * This callback is called by invite session framework when UAC session
2925 * has forked.
2926 */
2927static void pjsua_call_on_forked( pjsip_inv_session *inv,
2928 pjsip_event *e)
2929{
2930 PJ_UNUSED_ARG(inv);
2931 PJ_UNUSED_ARG(e);
2932
2933 PJ_TODO(HANDLE_FORKED_DIALOG);
2934}
2935
2936
2937/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002938 * Disconnect call upon error.
2939 */
2940static void call_disconnect( pjsip_inv_session *inv,
2941 int code )
2942{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002943 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002944 pjsip_tx_data *tdata;
2945 pj_status_t status;
2946
Benny Prijono59b3aed2008-01-15 16:54:54 +00002947 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2948
Benny Prijonoa38ada02006-07-02 14:22:35 +00002949 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002950 if (status != PJ_SUCCESS)
2951 return;
2952
2953 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00002954 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
2955 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2956 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002957 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002958 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002959
Benny Prijono734fc2d2008-03-17 16:05:35 +00002960 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002961 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002962 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002963 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002964 if (status == PJ_SUCCESS) {
2965 pjsip_create_sdp_body(tdata->pool, local_sdp,
2966 &tdata->msg->body);
2967 }
2968 }
2969
2970 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002971}
2972
2973/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002974 * Callback to be called when SDP offer/answer negotiation has just completed
2975 * in the session. This function will start/update media if negotiation
2976 * has succeeded.
2977 */
2978static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2979 pj_status_t status)
2980{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002981 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002982 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002983 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002984
2985 PJSUA_LOCK();
2986
Benny Prijonoa1e69682007-05-11 15:14:34 +00002987 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002988
2989 if (status != PJ_SUCCESS) {
2990
2991 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2992
Benny Prijono2331d202008-06-26 15:46:52 +00002993 /* Do not deinitialize media since this may be a re-INVITE or
2994 * UPDATE (which in this case the media should not get affected
2995 * by the failed re-INVITE/UPDATE). The media will be shutdown
2996 * when call is disconnected anyway.
2997 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002998 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00002999 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003000
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003001 /* Disconnect call if we're not in the middle of initializing an
3002 * UAS dialog and if this is not a re-INVITE
3003 */
3004 if (inv->state != PJSIP_INV_STATE_NULL &&
3005 inv->state != PJSIP_INV_STATE_CONFIRMED)
3006 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003007 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003008 }
3009
3010 PJSUA_UNLOCK();
3011 return;
3012 }
3013
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003014
3015 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003016 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003017 if (status != PJ_SUCCESS) {
3018 pjsua_perror(THIS_FILE,
3019 "Unable to retrieve currently active local SDP",
3020 status);
3021 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3022 PJSUA_UNLOCK();
3023 return;
3024 }
3025
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003026 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3027 if (status != PJ_SUCCESS) {
3028 pjsua_perror(THIS_FILE,
3029 "Unable to retrieve currently active remote SDP",
3030 status);
3031 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3032 PJSUA_UNLOCK();
3033 return;
3034 }
3035
Benny Prijono91a6a172007-10-31 08:59:29 +00003036 /* Update remote's NAT type */
3037 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3038 update_remote_nat_type(call, remote_sdp);
3039 }
3040
3041 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003042 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003043 if (status != PJ_SUCCESS) {
3044 pjsua_perror(THIS_FILE, "Unable to create media session",
3045 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003046 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003047 /* No need to deinitialize; media will be shutdown when call
3048 * state is disconnected anyway.
3049 */
3050 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003051 PJSUA_UNLOCK();
3052 return;
3053 }
3054
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003055
3056 /* Call application callback, if any */
3057 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3058 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3059
3060
3061 PJSUA_UNLOCK();
3062}
3063
3064
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003065/* Create SDP for call hold. */
3066static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3067 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003068{
3069 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003070 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003071 pjmedia_sdp_session *sdp;
3072
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003073 /* Use call's pool */
3074 pool = call->inv->pool;
3075
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003076 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003077 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3078 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003079 if (status != PJ_SUCCESS) {
3080 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3081 return status;
3082 }
3083
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003084 /* Call-hold is done by set the media direction to 'sendonly'
3085 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3086 * 'inactive' (PJMEDIA_DIR_NONE).
3087 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3088 */
3089 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3090 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003091
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003092 /* Remove existing directions attributes */
3093 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3094 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3095 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3096 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003097
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003098 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3099 /* Add sendonly attribute */
3100 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3101 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3102 } else {
3103 /* Add inactive attribute */
3104 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3105 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3106 }
3107 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003108
3109 *p_answer = sdp;
3110
3111 return status;
3112}
3113
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003114/*
3115 * Called when session received new offer.
3116 */
3117static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3118 const pjmedia_sdp_session *offer)
3119{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003120 pjsua_call *call;
3121 pjmedia_sdp_conn *conn;
3122 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003123 pj_status_t status;
3124
3125 PJSUA_LOCK();
3126
Benny Prijonoa1e69682007-05-11 15:14:34 +00003127 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003128
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003129 conn = offer->media[0]->conn;
3130 if (!conn)
3131 conn = offer->conn;
3132
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003133 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003134 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3135 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003136
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003137 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3138 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003139 if (status != PJ_SUCCESS) {
3140 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3141 PJSUA_UNLOCK();
3142 return;
3143 }
3144
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003145 /* Check if offer's conn address is zero */
3146 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3147 pj_strcmp2(&conn->addr, "0")==0)
3148 {
3149 /* Modify address */
3150 answer->conn->addr = pj_str("0.0.0.0");
3151 }
3152
3153 /* Check if call is on-hold */
3154 if (call->local_hold) {
3155 pjmedia_sdp_attr *attr;
3156
3157 /* Remove existing directions attributes */
3158 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3159 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3160 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3161 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3162
3163 /* Keep call on-hold by setting 'sendonly' attribute.
3164 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3165 */
3166 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3167 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3168 }
3169
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003170 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3171 if (status != PJ_SUCCESS) {
3172 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3173 PJSUA_UNLOCK();
3174 return;
3175 }
3176
3177 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003178}
3179
3180
3181/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003182 * Called to generate new offer.
3183 */
3184static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3185 pjmedia_sdp_session **offer)
3186{
3187 pjsua_call *call;
3188 pj_status_t status;
3189
3190 PJSUA_LOCK();
3191
3192 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3193
3194 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003195 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003196 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003197 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003198 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003199 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003200 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003201 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3202 call->index));
3203
Benny Prijonod8179652008-01-23 20:39:07 +00003204 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003205 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003206 }
3207
3208 if (status != PJ_SUCCESS) {
3209 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3210 PJSUA_UNLOCK();
3211 return;
3212 }
3213
Benny Prijono7129cc72007-11-05 05:54:25 +00003214 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00003215
3216 PJSUA_UNLOCK();
3217}
3218
3219
3220/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003221 * Callback called by event framework when the xfer subscription state
3222 * has changed.
3223 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003224static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3225{
3226
3227 PJ_UNUSED_ARG(event);
3228
3229 /*
3230 * When subscription is accepted (got 200/OK to REFER), check if
3231 * subscription suppressed.
3232 */
3233 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3234
3235 pjsip_rx_data *rdata;
3236 pjsip_generic_string_hdr *refer_sub;
3237 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3238 pjsua_call *call;
3239
Benny Prijonoa1e69682007-05-11 15:14:34 +00003240 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003241
3242 /* Must be receipt of response message */
3243 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3244 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3245 rdata = event->body.tsx_state.src.rdata;
3246
3247 /* Find Refer-Sub header */
3248 refer_sub = (pjsip_generic_string_hdr*)
3249 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3250 &REFER_SUB, NULL);
3251
3252 /* Check if subscription is suppressed */
3253 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3254 /* Since no subscription is desired, assume that call has been
3255 * transfered successfully.
3256 */
3257 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3258 const pj_str_t ACCEPTED = { "Accepted", 8 };
3259 pj_bool_t cont = PJ_FALSE;
3260 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3261 200,
3262 &ACCEPTED,
3263 PJ_TRUE,
3264 &cont);
3265 }
3266
3267 /* Yes, subscription is suppressed.
3268 * Terminate our subscription now.
3269 */
3270 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3271 "event subcription..."));
3272 pjsip_evsub_terminate(sub, PJ_TRUE);
3273
3274 } else {
3275 /* Notify application about call transfer progress.
3276 * Initially notify with 100/Accepted status.
3277 */
3278 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3279 const pj_str_t ACCEPTED = { "Accepted", 8 };
3280 pj_bool_t cont = PJ_FALSE;
3281 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3282 100,
3283 &ACCEPTED,
3284 PJ_FALSE,
3285 &cont);
3286 }
3287 }
3288 }
3289 /*
3290 * On incoming NOTIFY, notify application about call transfer progress.
3291 */
3292 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3293 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3294 {
3295 pjsua_call *call;
3296 pjsip_msg *msg;
3297 pjsip_msg_body *body;
3298 pjsip_status_line status_line;
3299 pj_bool_t is_last;
3300 pj_bool_t cont;
3301 pj_status_t status;
3302
Benny Prijonoa1e69682007-05-11 15:14:34 +00003303 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003304
3305 /* When subscription is terminated, clear the xfer_sub member of
3306 * the inv_data.
3307 */
3308 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3309 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3310 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3311
3312 }
3313
3314 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3315 /* Application is not interested with call progress status */
3316 return;
3317 }
3318
3319 /* This better be a NOTIFY request */
3320 if (event->type == PJSIP_EVENT_TSX_STATE &&
3321 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3322 {
3323 pjsip_rx_data *rdata;
3324
3325 rdata = event->body.tsx_state.src.rdata;
3326
3327 /* Check if there's body */
3328 msg = rdata->msg_info.msg;
3329 body = msg->body;
3330 if (!body) {
3331 PJ_LOG(4,(THIS_FILE,
3332 "Warning: received NOTIFY without message body"));
3333 return;
3334 }
3335
3336 /* Check for appropriate content */
3337 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3338 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3339 {
3340 PJ_LOG(4,(THIS_FILE,
3341 "Warning: received NOTIFY with non message/sipfrag "
3342 "content"));
3343 return;
3344 }
3345
3346 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003347 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003348 &status_line);
3349 if (status != PJ_SUCCESS) {
3350 PJ_LOG(4,(THIS_FILE,
3351 "Warning: received NOTIFY with invalid "
3352 "message/sipfrag content"));
3353 return;
3354 }
3355
3356 } else {
3357 status_line.code = 500;
3358 status_line.reason = *pjsip_get_status_text(500);
3359 }
3360
3361 /* Notify application */
3362 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3363 cont = !is_last;
3364 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3365 status_line.code,
3366 &status_line.reason,
3367 is_last, &cont);
3368
3369 if (!cont) {
3370 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3371 }
3372 }
3373}
3374
3375
3376/*
3377 * Callback called by event framework when the xfer subscription state
3378 * has changed.
3379 */
3380static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003381{
3382
3383 PJ_UNUSED_ARG(event);
3384
3385 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003386 * When subscription is terminated, clear the xfer_sub member of
3387 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003388 */
3389 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003390 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003391
Benny Prijonoa1e69682007-05-11 15:14:34 +00003392 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003393 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003394 return;
3395
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003396 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003397 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003398
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003399 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003400 }
3401}
3402
3403
3404/*
3405 * Follow transfer (REFER) request.
3406 */
3407static void on_call_transfered( pjsip_inv_session *inv,
3408 pjsip_rx_data *rdata )
3409{
3410 pj_status_t status;
3411 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003412 pjsua_call *existing_call;
3413 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003414 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003415 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003416 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003417 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003418 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003419 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003420 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003421 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003422 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003423 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003424 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003425 pjsip_evsub *sub;
3426
Benny Prijonoa1e69682007-05-11 15:14:34 +00003427 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003428
Benny Prijono26ff9062006-02-21 23:47:00 +00003429 /* Find the Refer-To header */
3430 refer_to = (pjsip_generic_string_hdr*)
3431 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3432
3433 if (refer_to == NULL) {
3434 /* Invalid Request.
3435 * No Refer-To header!
3436 */
3437 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003438 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003439 return;
3440 }
3441
Benny Prijonoc8141a82006-08-20 09:12:19 +00003442 /* Find optional Refer-Sub header */
3443 refer_sub = (pjsip_generic_string_hdr*)
3444 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3445
3446 if (refer_sub) {
3447 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3448 no_refer_sub = PJ_TRUE;
3449 }
3450
Benny Prijono053f5222006-11-11 16:16:04 +00003451 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3452 * request.
3453 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003454 ref_by_hdr = (pjsip_hdr*)
3455 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003456 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003457
Benny Prijono9fc735d2006-05-28 14:58:12 +00003458 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003459 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003460 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3461 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3462 &refer_to->hvalue,
3463 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003464
3465 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003466 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003467 if (code >= 300) {
3468 /* Application rejects call transfer request */
3469 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3470 return;
3471 }
3472
Benny Prijono26ff9062006-02-21 23:47:00 +00003473 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3474 (int)inv->dlg->remote.info_str.slen,
3475 inv->dlg->remote.info_str.ptr,
3476 (int)refer_to->hvalue.slen,
3477 refer_to->hvalue.ptr));
3478
Benny Prijonoc8141a82006-08-20 09:12:19 +00003479 if (no_refer_sub) {
3480 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003481 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003482 */
3483 pjsip_tx_data *tdata;
3484 const pj_str_t str_false = { "false", 5};
3485 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003486
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003487 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3488 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003489 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003490 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003491 status);
3492 return;
3493 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003494
Benny Prijonoc8141a82006-08-20 09:12:19 +00003495 /* Add Refer-Sub header */
3496 hdr = (pjsip_hdr*)
3497 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3498 &str_false);
3499 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003500
Benny Prijono26ff9062006-02-21 23:47:00 +00003501
Benny Prijonoc8141a82006-08-20 09:12:19 +00003502 /* Send answer */
3503 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3504 tdata);
3505 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003506 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003507 status);
3508 return;
3509 }
3510
3511 /* Don't have subscription */
3512 sub = NULL;
3513
3514 } else {
3515 struct pjsip_evsub_user xfer_cb;
3516 pjsip_hdr hdr_list;
3517
3518 /* Init callback */
3519 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003520 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003521
3522 /* Init additional header list to be sent with REFER response */
3523 pj_list_init(&hdr_list);
3524
3525 /* Create transferee event subscription */
3526 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3527 if (status != PJ_SUCCESS) {
3528 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3529 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3530 return;
3531 }
3532
3533 /* If there's Refer-Sub header and the value is "true", send back
3534 * Refer-Sub in the response with value "true" too.
3535 */
3536 if (refer_sub) {
3537 const pj_str_t str_true = { "true", 4 };
3538 pjsip_hdr *hdr;
3539
3540 hdr = (pjsip_hdr*)
3541 pjsip_generic_string_hdr_create(inv->dlg->pool,
3542 &str_refer_sub,
3543 &str_true);
3544 pj_list_push_back(&hdr_list, hdr);
3545
3546 }
3547
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003548 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003549 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3550
3551 /* Create initial NOTIFY request */
3552 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3553 100, NULL, &tdata);
3554 if (status != PJ_SUCCESS) {
3555 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3556 status);
3557 return;
3558 }
3559
3560 /* Send initial NOTIFY request */
3561 status = pjsip_xfer_send_request( sub, tdata);
3562 if (status != PJ_SUCCESS) {
3563 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3564 return;
3565 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003566 }
3567
3568 /* We're cheating here.
3569 * We need to get a null terminated string from a pj_str_t.
3570 * So grab the pointer from the hvalue and NULL terminate it, knowing
3571 * that the NULL position will be occupied by a newline.
3572 */
3573 uri = refer_to->hvalue.ptr;
3574 uri[refer_to->hvalue.slen] = '\0';
3575
Benny Prijono053f5222006-11-11 16:16:04 +00003576 /* Init msg_data */
3577 pjsua_msg_data_init(&msg_data);
3578
3579 /* If Referred-By header is present in the REFER request, copy this
3580 * to the outgoing INVITE request.
3581 */
3582 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003583 pjsip_hdr *dup = (pjsip_hdr*)
3584 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003585 pj_list_push_back(&msg_data.hdr_list, dup);
3586 }
3587
Benny Prijono26ff9062006-02-21 23:47:00 +00003588 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003589 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003590 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003591 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003592 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003593 if (status != PJ_SUCCESS) {
3594
Benny Prijonoc8141a82006-08-20 09:12:19 +00003595 /* Notify xferer about the error (if we have subscription) */
3596 if (sub) {
3597 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3598 500, NULL, &tdata);
3599 if (status != PJ_SUCCESS) {
3600 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3601 status);
3602 return;
3603 }
3604 status = pjsip_xfer_send_request(sub, tdata);
3605 if (status != PJ_SUCCESS) {
3606 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3607 status);
3608 return;
3609 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003610 }
3611 return;
3612 }
3613
Benny Prijonoc8141a82006-08-20 09:12:19 +00003614 if (sub) {
3615 /* Put the server subscription in inv_data.
3616 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3617 * reported back to the server subscription.
3618 */
3619 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003620
Benny Prijonoc8141a82006-08-20 09:12:19 +00003621 /* Put the invite_data in the subscription. */
3622 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3623 &pjsua_var.calls[new_call]);
3624 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003625}
3626
3627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003628
Benny Prijono26ff9062006-02-21 23:47:00 +00003629/*
3630 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003631 * session. We use this to trap:
3632 * - incoming REFER request.
3633 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003634 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003635static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3636 pjsip_transaction *tsx,
3637 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003638{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003639 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003640
3641 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003642
Benny Prijonofeb69f42007-10-05 09:12:26 +00003643 /* Notify application callback first */
3644 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3645 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3646 }
3647
Benny Prijono26ff9062006-02-21 23:47:00 +00003648 if (tsx->role==PJSIP_ROLE_UAS &&
3649 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003650 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003651 {
3652 /*
3653 * Incoming REFER request.
3654 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003655 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003656
Benny Prijono26ff9062006-02-21 23:47:00 +00003657 }
Benny Prijonob0808372006-03-02 21:18:58 +00003658 else if (tsx->role==PJSIP_ROLE_UAS &&
3659 tsx->state==PJSIP_TSX_STATE_TRYING &&
3660 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3661 {
3662 /*
3663 * Incoming MESSAGE request!
3664 */
3665 pjsip_rx_data *rdata;
3666 pjsip_msg *msg;
3667 pjsip_accept_hdr *accept_hdr;
3668 pj_status_t status;
3669
3670 rdata = e->body.tsx_state.src.rdata;
3671 msg = rdata->msg_info.msg;
3672
3673 /* Request MUST have message body, with Content-Type equal to
3674 * "text/plain".
3675 */
3676 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3677
3678 pjsip_hdr hdr_list;
3679
3680 pj_list_init(&hdr_list);
3681 pj_list_push_back(&hdr_list, accept_hdr);
3682
3683 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3684 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003685 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003686 return;
3687 }
3688
3689 /* Respond with 200 first, so that remote doesn't retransmit in case
3690 * the UI takes too long to process the message.
3691 */
3692 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3693
3694 /* Process MESSAGE request */
3695 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3696 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003697
Benny Prijonob0808372006-03-02 21:18:58 +00003698 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003699 else if (tsx->role == PJSIP_ROLE_UAC &&
3700 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003701 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003702 /* Handle outgoing pager status */
3703 if (tsx->status_code >= 200) {
3704 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003705
Benny Prijonoa1e69682007-05-11 15:14:34 +00003706 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003707 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003708
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003709 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3710 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3711 &im_data->to,
3712 &im_data->body,
3713 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003714 (pjsip_status_code)
3715 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003716 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003717 }
Benny Prijonofccab712006-02-22 22:23:22 +00003718 }
Benny Prijono834aee32006-02-19 01:38:06 +00003719 }
Benny Prijono834aee32006-02-19 01:38:06 +00003720
Benny Prijono26ff9062006-02-21 23:47:00 +00003721
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003722 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003723}