blob: 71fc8a24a58bebd8e9dc03e0a8548492c16b75d3 [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,
2021 pjmedia_session *session)
2022{
2023 unsigned i;
2024 char *p = buf, *end = buf+maxlen;
2025 int len;
2026 pjmedia_session_info info;
2027
2028 pjmedia_session_get_info(session, &info);
2029
2030 for (i=0; i<info.stream_cnt; ++i) {
2031 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002032 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002033 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002034 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002035 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002036 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002037 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002038
2039 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00002040 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2041 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002042
2043 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2044 dir = "sendonly";
2045 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2046 dir = "recvonly";
2047 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2048 dir = "sendrecv";
2049 else
2050 dir = "inactive";
2051
2052
2053 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002054 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002055 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002056 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002057 info.stream_info[i].fmt.encoding_name.ptr,
2058 info.stream_info[i].fmt.clock_rate / 1000,
2059 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002060 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002061 if (len < 1 || len > end-p) {
2062 *p = '\0';
2063 return;
2064 }
2065
2066 p += len;
2067 *p++ = '\n';
2068 *p = '\0';
2069
2070 if (stat.rx.update_cnt == 0)
2071 strcpy(last_update, "never");
2072 else {
2073 pj_gettimeofday(&now);
2074 PJ_TIME_VAL_SUB(now, stat.rx.update);
2075 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2076 now.sec / 3600,
2077 (now.sec % 3600) / 60,
2078 now.sec % 60,
2079 now.msec);
2080 }
2081
Benny Prijono80019eb2006-08-07 13:22:23 +00002082 pj_gettimeofday(&media_duration);
2083 PJ_TIME_VAL_SUB(media_duration, stat.start);
2084 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2085 media_duration.msec = 1;
2086
Benny Prijono1402a4a2008-01-08 23:41:22 +00002087 /* protect against division by zero */
2088 if (stat.rx.pkt == 0)
2089 stat.rx.pkt = 1;
2090 if (stat.tx.pkt == 0)
2091 stat.tx.pkt = 1;
2092
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002093 len = pj_ansi_snprintf(p, end-p,
2094 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002095 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002097 "%s (msec) min avg max last dev\n"
2098 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2099 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002100 indent, info.stream_info[i].fmt.pt,
2101 last_update,
2102 indent,
2103 good_number(packets, stat.rx.pkt),
2104 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002105 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002106 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2107 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 +00002108 indent,
2109 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002110 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002112 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002114 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115 indent, indent,
2116 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002117 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002118 stat.rx.loss_period.max / 1000.0,
2119 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002120 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002121 indent,
2122 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002123 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002124 stat.rx.jitter.max / 1000.0,
2125 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002126 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002127 ""
2128 );
2129
2130 if (len < 1 || len > end-p) {
2131 *p = '\0';
2132 return;
2133 }
2134
2135 p += len;
2136 *p++ = '\n';
2137 *p = '\0';
2138
2139 if (stat.tx.update_cnt == 0)
2140 strcpy(last_update, "never");
2141 else {
2142 pj_gettimeofday(&now);
2143 PJ_TIME_VAL_SUB(now, stat.tx.update);
2144 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2145 now.sec / 3600,
2146 (now.sec % 3600) / 60,
2147 now.sec % 60,
2148 now.msec);
2149 }
2150
2151 len = pj_ansi_snprintf(p, end-p,
2152 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002153 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002154 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002155 "%s (msec) min avg max last dev \n"
2156 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2157 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002158 indent,
2159 info.stream_info[i].tx_pt,
2160 info.stream_info[i].param->info.frm_ptime *
2161 info.stream_info[i].param->setting.frm_per_pkt,
2162 last_update,
2163
2164 indent,
2165 good_number(packets, stat.tx.pkt),
2166 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002167 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002168 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2169 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 +00002170
2171 indent,
2172 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002173 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002174 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002175 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002176 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002177 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002178
2179 indent, indent,
2180 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002181 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002182 stat.tx.loss_period.max / 1000.0,
2183 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002184 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 indent,
2186 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002187 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002188 stat.tx.jitter.max / 1000.0,
2189 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002190 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191 ""
2192 );
2193
2194 if (len < 1 || len > end-p) {
2195 *p = '\0';
2196 return;
2197 }
2198
2199 p += len;
2200 *p++ = '\n';
2201 *p = '\0';
2202
2203 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002204 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002205 indent,
2206 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002207 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002208 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002209 stat.rtt.last / 1000.0,
2210 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002211 );
2212 if (len < 1 || len > end-p) {
2213 *p = '\0';
2214 return;
2215 }
2216
2217 p += len;
2218 *p++ = '\n';
2219 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002220
2221#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2222# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2223 do { \
2224 if (samples <= 4294) \
2225 usec = samples * 1000000 / clock_rate; \
2226 else { \
2227 usec = samples * 1000 / clock_rate; \
2228 usec *= 1000; \
2229 } \
2230 } while(0)
2231
2232# define PRINT_VOIP_MTC_VAL(s, v) \
2233 if (v == 127) \
2234 sprintf(s, "(na)"); \
2235 else \
2236 sprintf(s, "%d", v)
2237
2238# define VALIDATE_PRINT_BUF() \
2239 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2240 p += len; *p++ = '\n'; *p = '\0'
2241
2242
2243 do {
2244 char loss[16], dup[16];
2245 char jitter[80];
2246 char toh[80];
2247 char plc[16], jba[16], jbr[16];
2248 char signal_lvl[16], noise_lvl[16], rerl[16];
2249 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2250 pjmedia_rtcp_xr_stat xr_stat;
2251 unsigned clock_rate;
2252
2253 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2254 PJ_SUCCESS)
2255 {
2256 break;
2257 }
2258
2259 clock_rate = info.stream_info[i].fmt.clock_rate;
2260
2261 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2262 VALIDATE_PRINT_BUF();
2263
2264 /* Statistics Summary */
2265 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2266 VALIDATE_PRINT_BUF();
2267
2268 if (xr_stat.rx.stat_sum.l)
2269 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2270 else
2271 sprintf(loss, "(na)");
2272
2273 if (xr_stat.rx.stat_sum.d)
2274 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2275 else
2276 sprintf(dup, "(na)");
2277
2278 if (xr_stat.rx.stat_sum.j) {
2279 unsigned jmin, jmax, jmean, jdev;
2280
2281 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2282 clock_rate);
2283 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2284 clock_rate);
2285 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2286 clock_rate);
2287 SAMPLES_TO_USEC(jdev,
2288 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2289 clock_rate);
2290 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2291 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2292 } else
2293 sprintf(jitter, "(report not available)");
2294
2295 if (xr_stat.rx.stat_sum.t) {
2296 sprintf(toh, "%11d %11d %11d %11d",
2297 xr_stat.rx.stat_sum.toh.min,
2298 xr_stat.rx.stat_sum.toh.mean,
2299 xr_stat.rx.stat_sum.toh.max,
2300 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2301 } else
2302 sprintf(toh, "(report not available)");
2303
2304 if (xr_stat.rx.stat_sum.update.sec == 0)
2305 strcpy(last_update, "never");
2306 else {
2307 pj_gettimeofday(&now);
2308 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2309 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2310 now.sec / 3600,
2311 (now.sec % 3600) / 60,
2312 now.sec % 60,
2313 now.msec);
2314 }
2315
2316 len = pj_ansi_snprintf(p, end-p,
2317 "%s RX last update: %s\n"
2318 "%s begin seq=%d, end seq=%d\n"
2319 "%s pkt loss=%s, dup=%s\n"
2320 "%s (msec) min avg max dev\n"
2321 "%s jitter : %s\n"
2322 "%s toh : %s",
2323 indent, last_update,
2324 indent,
2325 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2326 indent, loss, dup,
2327 indent,
2328 indent, jitter,
2329 indent, toh
2330 );
2331 VALIDATE_PRINT_BUF();
2332
2333 if (xr_stat.tx.stat_sum.l)
2334 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2335 else
2336 sprintf(loss, "(na)");
2337
2338 if (xr_stat.tx.stat_sum.d)
2339 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2340 else
2341 sprintf(dup, "(na)");
2342
2343 if (xr_stat.tx.stat_sum.j) {
2344 unsigned jmin, jmax, jmean, jdev;
2345
2346 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2347 clock_rate);
2348 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2349 clock_rate);
2350 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2351 clock_rate);
2352 SAMPLES_TO_USEC(jdev,
2353 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2354 clock_rate);
2355 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2356 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2357 } else
2358 sprintf(jitter, "(report not available)");
2359
2360 if (xr_stat.tx.stat_sum.t) {
2361 sprintf(toh, "%11d %11d %11d %11d",
2362 xr_stat.tx.stat_sum.toh.min,
2363 xr_stat.tx.stat_sum.toh.mean,
2364 xr_stat.tx.stat_sum.toh.max,
2365 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2366 } else
2367 sprintf(toh, "(report not available)");
2368
2369 if (xr_stat.tx.stat_sum.update.sec == 0)
2370 strcpy(last_update, "never");
2371 else {
2372 pj_gettimeofday(&now);
2373 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2374 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2375 now.sec / 3600,
2376 (now.sec % 3600) / 60,
2377 now.sec % 60,
2378 now.msec);
2379 }
2380
2381 len = pj_ansi_snprintf(p, end-p,
2382 "%s TX last update: %s\n"
2383 "%s begin seq=%d, end seq=%d\n"
2384 "%s pkt loss=%s, dup=%s\n"
2385 "%s (msec) min avg max dev\n"
2386 "%s jitter : %s\n"
2387 "%s toh : %s",
2388 indent, last_update,
2389 indent,
2390 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2391 indent, loss, dup,
2392 indent,
2393 indent, jitter,
2394 indent, toh
2395 );
2396 VALIDATE_PRINT_BUF();
2397
2398
2399 /* VoIP Metrics */
2400 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2401 VALIDATE_PRINT_BUF();
2402
2403 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2404 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2405 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2406 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2407 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2408 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2409 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2410
2411 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2412 case PJMEDIA_RTCP_XR_PLC_DIS:
2413 sprintf(plc, "DISABLED");
2414 break;
2415 case PJMEDIA_RTCP_XR_PLC_ENH:
2416 sprintf(plc, "ENHANCED");
2417 break;
2418 case PJMEDIA_RTCP_XR_PLC_STD:
2419 sprintf(plc, "STANDARD");
2420 break;
2421 case PJMEDIA_RTCP_XR_PLC_UNK:
2422 default:
2423 sprintf(plc, "UNKNOWN");
2424 break;
2425 }
2426
2427 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2428 case PJMEDIA_RTCP_XR_JB_FIXED:
2429 sprintf(jba, "FIXED");
2430 break;
2431 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2432 sprintf(jba, "ADAPTIVE");
2433 break;
2434 default:
2435 sprintf(jba, "UNKNOWN");
2436 break;
2437 }
2438
2439 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2440
2441 if (xr_stat.rx.voip_mtc.update.sec == 0)
2442 strcpy(last_update, "never");
2443 else {
2444 pj_gettimeofday(&now);
2445 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2446 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2447 now.sec / 3600,
2448 (now.sec % 3600) / 60,
2449 now.sec % 60,
2450 now.msec);
2451 }
2452
2453 len = pj_ansi_snprintf(p, end-p,
2454 "%s RX last update: %s\n"
2455 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2456 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2457 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2458 "%s delay : round trip=%d%s, end system=%d%s\n"
2459 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2460 "%s quality : R factor=%s, ext R factor=%s\n"
2461 "%s MOS LQ=%s, MOS CQ=%s\n"
2462 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2463 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2464 indent,
2465 last_update,
2466 /* packets */
2467 indent,
2468 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2469 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2470 /* burst */
2471 indent,
2472 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2473 xr_stat.rx.voip_mtc.burst_dur, "ms",
2474 /* gap */
2475 indent,
2476 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2477 xr_stat.rx.voip_mtc.gap_dur, "ms",
2478 /* delay */
2479 indent,
2480 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2481 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2482 /* level */
2483 indent,
2484 signal_lvl, "dB",
2485 noise_lvl, "dB",
2486 rerl, "",
2487 /* quality */
2488 indent,
2489 r_factor, ext_r_factor,
2490 indent,
2491 mos_lq, mos_cq,
2492 /* config */
2493 indent,
2494 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2495 /* JB delay */
2496 indent,
2497 xr_stat.rx.voip_mtc.jb_nom, "ms",
2498 xr_stat.rx.voip_mtc.jb_max, "ms",
2499 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2500 );
2501 VALIDATE_PRINT_BUF();
2502
2503 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2504 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2505 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2506 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2507 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2508 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2509 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2510
2511 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2512 case PJMEDIA_RTCP_XR_PLC_DIS:
2513 sprintf(plc, "DISABLED");
2514 break;
2515 case PJMEDIA_RTCP_XR_PLC_ENH:
2516 sprintf(plc, "ENHANCED");
2517 break;
2518 case PJMEDIA_RTCP_XR_PLC_STD:
2519 sprintf(plc, "STANDARD");
2520 break;
2521 case PJMEDIA_RTCP_XR_PLC_UNK:
2522 default:
2523 sprintf(plc, "unknown");
2524 break;
2525 }
2526
2527 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2528 case PJMEDIA_RTCP_XR_JB_FIXED:
2529 sprintf(jba, "FIXED");
2530 break;
2531 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2532 sprintf(jba, "ADAPTIVE");
2533 break;
2534 default:
2535 sprintf(jba, "unknown");
2536 break;
2537 }
2538
2539 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2540
2541 if (xr_stat.tx.voip_mtc.update.sec == 0)
2542 strcpy(last_update, "never");
2543 else {
2544 pj_gettimeofday(&now);
2545 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2546 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2547 now.sec / 3600,
2548 (now.sec % 3600) / 60,
2549 now.sec % 60,
2550 now.msec);
2551 }
2552
2553 len = pj_ansi_snprintf(p, end-p,
2554 "%s TX last update: %s\n"
2555 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2556 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2557 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2558 "%s delay : round trip=%d%s, end system=%d%s\n"
2559 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2560 "%s quality : R factor=%s, ext R factor=%s\n"
2561 "%s MOS LQ=%s, MOS CQ=%s\n"
2562 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2563 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2564 indent,
2565 last_update,
2566 /* pakcets */
2567 indent,
2568 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2569 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2570 /* burst */
2571 indent,
2572 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2573 xr_stat.tx.voip_mtc.burst_dur, "ms",
2574 /* gap */
2575 indent,
2576 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2577 xr_stat.tx.voip_mtc.gap_dur, "ms",
2578 /* delay */
2579 indent,
2580 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2581 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2582 /* level */
2583 indent,
2584 signal_lvl, "dB",
2585 noise_lvl, "dB",
2586 rerl, "",
2587 /* quality */
2588 indent,
2589 r_factor, ext_r_factor,
2590 indent,
2591 mos_lq, mos_cq,
2592 /* config */
2593 indent,
2594 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2595 /* JB delay */
2596 indent,
2597 xr_stat.tx.voip_mtc.jb_nom, "ms",
2598 xr_stat.tx.voip_mtc.jb_max, "ms",
2599 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2600 );
2601 VALIDATE_PRINT_BUF();
2602
2603
2604 /* RTT delay (by receiver side) */
2605 len = pj_ansi_snprintf(p, end-p,
2606 "%s RTT (from recv) min avg max last dev",
2607 indent);
2608 VALIDATE_PRINT_BUF();
2609 len = pj_ansi_snprintf(p, end-p,
2610 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2611 indent,
2612 xr_stat.rtt.min / 1000.0,
2613 xr_stat.rtt.mean / 1000.0,
2614 xr_stat.rtt.max / 1000.0,
2615 xr_stat.rtt.last / 1000.0,
2616 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2617 );
2618 VALIDATE_PRINT_BUF();
2619 } while(0);
2620#endif
2621
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002622 }
2623}
2624
2625
2626/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002627void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002628 int call_id,
2629 char *buf, pj_size_t size)
2630{
2631 int len;
2632 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2633 pjsip_dialog *dlg = inv->dlg;
2634 char userinfo[128];
2635
2636 /* Dump invite sesion info. */
2637
2638 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2639 if (len < 1)
2640 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2641 else
2642 userinfo[len] = '\0';
2643
2644 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2645 title,
2646 pjsip_inv_state_name(inv->state),
2647 userinfo);
2648 if (len < 1 || len >= (int)size) {
2649 pj_ansi_strcpy(buf, "<--uri too long-->");
2650 len = 18;
2651 } else
2652 buf[len] = '\0';
2653}
2654
2655
2656/*
2657 * Dump call and media statistics to string.
2658 */
2659PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2660 pj_bool_t with_media,
2661 char *buffer,
2662 unsigned maxlen,
2663 const char *indent)
2664{
2665 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002666 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002667 pj_time_val duration, res_delay, con_delay;
2668 char tmp[128];
2669 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002670 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002671 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002672 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002673
2674 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2675 PJ_EINVAL);
2676
Benny Prijonodc752ca2006-09-22 16:55:42 +00002677 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002678 if (status != PJ_SUCCESS)
2679 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002680
2681 *buffer = '\0';
2682 p = buffer;
2683 end = buffer + maxlen;
2684 len = 0;
2685
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002686 print_call(indent, call_id, tmp, sizeof(tmp));
2687
2688 len = pj_ansi_strlen(tmp);
2689 pj_ansi_strcpy(buffer, tmp);
2690
2691 p += len;
2692 *p++ = '\r';
2693 *p++ = '\n';
2694
2695 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002696 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002697 pj_gettimeofday(&duration);
2698 PJ_TIME_VAL_SUB(duration, call->conn_time);
2699 con_delay = call->conn_time;
2700 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2701 } else {
2702 duration.sec = duration.msec = 0;
2703 con_delay.sec = con_delay.msec = 0;
2704 }
2705
2706 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002707 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002708 res_delay = call->res_time;
2709 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2710 } else {
2711 res_delay.sec = res_delay.msec = 0;
2712 }
2713
2714 /* Print duration */
2715 len = pj_ansi_snprintf(p, end-p,
2716 "%s Call time: %02dh:%02dm:%02ds, "
2717 "1st res in %d ms, conn in %dms",
2718 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002719 (int)(duration.sec / 3600),
2720 (int)((duration.sec % 3600)/60),
2721 (int)(duration.sec % 60),
2722 (int)PJ_TIME_VAL_MSEC(res_delay),
2723 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002724
2725 if (len > 0 && len < end-p) {
2726 p += len;
2727 *p++ = '\n';
2728 *p = '\0';
2729 }
2730
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002731 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002732 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002733 pjmedia_transport_get_info(call->med_tp, &tp_info);
2734 if (tp_info.specific_info_cnt > 0) {
2735 int i;
2736 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2737 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2738 {
2739 pjmedia_srtp_info *srtp_info =
2740 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2741
2742 len = pj_ansi_snprintf(p, end-p,
2743 "%s SRTP status: %s Crypto-suite: %s",
2744 indent,
2745 (srtp_info->active?"Active":"Not active"),
2746 srtp_info->tx_policy.name.ptr);
2747 if (len > 0 && len < end-p) {
2748 p += len;
2749 *p++ = '\n';
2750 *p = '\0';
2751 }
2752 break;
2753 }
2754 }
2755 }
2756
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002757 /* Dump session statistics */
2758 if (with_media && call->session)
2759 dump_media_session(indent, p, end-p, call->session);
2760
Benny Prijonodc752ca2006-09-22 16:55:42 +00002761 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002762
2763 return PJ_SUCCESS;
2764}
2765
2766
2767/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002768 * This callback receives notification from invite session when the
2769 * session state has changed.
2770 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002771static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2772 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002773{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002774 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002775
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002776 PJSUA_LOCK();
2777
Benny Prijonoa1e69682007-05-11 15:14:34 +00002778 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002779
2780 if (!call) {
2781 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002782 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002783 }
2784
Benny Prijonoe21e7842006-04-09 16:46:05 +00002785
2786 /* Get call times */
2787 switch (inv->state) {
2788 case PJSIP_INV_STATE_EARLY:
2789 case PJSIP_INV_STATE_CONNECTING:
2790 if (call->res_time.sec == 0)
2791 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002792 call->last_code = (pjsip_status_code)
2793 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002794 pj_strncpy(&call->last_text,
2795 &e->body.tsx_state.tsx->status_text,
2796 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002797 break;
2798 case PJSIP_INV_STATE_CONFIRMED:
2799 pj_gettimeofday(&call->conn_time);
2800 break;
2801 case PJSIP_INV_STATE_DISCONNECTED:
2802 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002803 if (call->res_time.sec == 0)
2804 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002805 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002806 call->last_code = (pjsip_status_code)
2807 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002808 pj_strncpy(&call->last_text,
2809 &e->body.tsx_state.tsx->status_text,
2810 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002811 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002812 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002813 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002814 call->last_code = (pjsip_status_code)
2815 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002816 pj_strncpy(&call->last_text,
2817 &e->body.tsx_state.tsx->status_text,
2818 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002819 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002820 }
2821
Benny Prijono26ff9062006-02-21 23:47:00 +00002822 /* If this is an outgoing INVITE that was created because of
2823 * REFER/transfer, send NOTIFY to transferer.
2824 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002825 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002826 int st_code = -1;
2827 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2828
2829
Benny Prijonoa91a0032006-02-26 21:23:45 +00002830 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002831 case PJSIP_INV_STATE_NULL:
2832 case PJSIP_INV_STATE_CALLING:
2833 /* Do nothing */
2834 break;
2835
2836 case PJSIP_INV_STATE_EARLY:
2837 case PJSIP_INV_STATE_CONNECTING:
2838 st_code = e->body.tsx_state.tsx->status_code;
2839 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2840 break;
2841
2842 case PJSIP_INV_STATE_CONFIRMED:
2843 /* When state is confirmed, send the final 200/OK and terminate
2844 * subscription.
2845 */
2846 st_code = e->body.tsx_state.tsx->status_code;
2847 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2848 break;
2849
2850 case PJSIP_INV_STATE_DISCONNECTED:
2851 st_code = e->body.tsx_state.tsx->status_code;
2852 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2853 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002854
Benny Prijono8b1889b2006-06-06 18:40:40 +00002855 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002856 /* Nothing to do. Just to keep gcc from complaining about
2857 * unused enums.
2858 */
2859 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002860 }
2861
2862 if (st_code != -1) {
2863 pjsip_tx_data *tdata;
2864 pj_status_t status;
2865
Benny Prijonoa91a0032006-02-26 21:23:45 +00002866 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002867 ev_state, st_code,
2868 NULL, &tdata);
2869 if (status != PJ_SUCCESS) {
2870 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2871 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002872 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002873 if (status != PJ_SUCCESS) {
2874 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2875 }
2876 }
2877 }
2878 }
2879
Benny Prijono84126ab2006-02-09 09:30:09 +00002880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881 if (pjsua_var.ua_cfg.cb.on_call_state)
2882 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002883
2884 /* call->inv may be NULL now */
2885
Benny Prijono84126ab2006-02-09 09:30:09 +00002886 /* Destroy media session when invite session is disconnected. */
2887 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002888
Benny Prijonoa91a0032006-02-26 21:23:45 +00002889 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002890
Benny Prijono275fd682006-03-22 11:59:11 +00002891 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002892 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002893
Benny Prijono105217f2006-03-06 16:25:59 +00002894 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002895 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002896 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002897
2898 /* Reset call */
2899 reset_call(call->index);
2900
Benny Prijono84126ab2006-02-09 09:30:09 +00002901 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002902
2903 PJSUA_UNLOCK();
2904}
2905
2906/*
2907 * This callback is called by invite session framework when UAC session
2908 * has forked.
2909 */
2910static void pjsua_call_on_forked( pjsip_inv_session *inv,
2911 pjsip_event *e)
2912{
2913 PJ_UNUSED_ARG(inv);
2914 PJ_UNUSED_ARG(e);
2915
2916 PJ_TODO(HANDLE_FORKED_DIALOG);
2917}
2918
2919
2920/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002921 * Disconnect call upon error.
2922 */
2923static void call_disconnect( pjsip_inv_session *inv,
2924 int code )
2925{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002926 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002927 pjsip_tx_data *tdata;
2928 pj_status_t status;
2929
Benny Prijono59b3aed2008-01-15 16:54:54 +00002930 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2931
Benny Prijonoa38ada02006-07-02 14:22:35 +00002932 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002933 if (status != PJ_SUCCESS)
2934 return;
2935
2936 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00002937 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
2938 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2939 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002940 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002941 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002942
Benny Prijono734fc2d2008-03-17 16:05:35 +00002943 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002944 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002945 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002946 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002947 if (status == PJ_SUCCESS) {
2948 pjsip_create_sdp_body(tdata->pool, local_sdp,
2949 &tdata->msg->body);
2950 }
2951 }
2952
2953 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002954}
2955
2956/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002957 * Callback to be called when SDP offer/answer negotiation has just completed
2958 * in the session. This function will start/update media if negotiation
2959 * has succeeded.
2960 */
2961static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2962 pj_status_t status)
2963{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002964 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002965 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002966 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002967
2968 PJSUA_LOCK();
2969
Benny Prijonoa1e69682007-05-11 15:14:34 +00002970 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002971
2972 if (status != PJ_SUCCESS) {
2973
2974 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2975
Benny Prijono2331d202008-06-26 15:46:52 +00002976 /* Do not deinitialize media since this may be a re-INVITE or
2977 * UPDATE (which in this case the media should not get affected
2978 * by the failed re-INVITE/UPDATE). The media will be shutdown
2979 * when call is disconnected anyway.
2980 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002981 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00002982 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002983
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002984 /* Disconnect call if we're not in the middle of initializing an
2985 * UAS dialog and if this is not a re-INVITE
2986 */
2987 if (inv->state != PJSIP_INV_STATE_NULL &&
2988 inv->state != PJSIP_INV_STATE_CONFIRMED)
2989 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002990 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002991 }
2992
2993 PJSUA_UNLOCK();
2994 return;
2995 }
2996
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002997
2998 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00002999 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003000 if (status != PJ_SUCCESS) {
3001 pjsua_perror(THIS_FILE,
3002 "Unable to retrieve currently active local SDP",
3003 status);
3004 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3005 PJSUA_UNLOCK();
3006 return;
3007 }
3008
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003009 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3010 if (status != PJ_SUCCESS) {
3011 pjsua_perror(THIS_FILE,
3012 "Unable to retrieve currently active remote SDP",
3013 status);
3014 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3015 PJSUA_UNLOCK();
3016 return;
3017 }
3018
Benny Prijono91a6a172007-10-31 08:59:29 +00003019 /* Update remote's NAT type */
3020 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3021 update_remote_nat_type(call, remote_sdp);
3022 }
3023
3024 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003025 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003026 if (status != PJ_SUCCESS) {
3027 pjsua_perror(THIS_FILE, "Unable to create media session",
3028 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003029 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003030 /* No need to deinitialize; media will be shutdown when call
3031 * state is disconnected anyway.
3032 */
3033 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003034 PJSUA_UNLOCK();
3035 return;
3036 }
3037
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003038
3039 /* Call application callback, if any */
3040 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3041 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3042
3043
3044 PJSUA_UNLOCK();
3045}
3046
3047
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003048/* Create SDP for call hold. */
3049static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3050 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003051{
3052 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003053 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003054 pjmedia_sdp_session *sdp;
3055
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003056 /* Use call's pool */
3057 pool = call->inv->pool;
3058
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003059 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003060 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3061 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003062 if (status != PJ_SUCCESS) {
3063 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3064 return status;
3065 }
3066
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003067 /* Call-hold is done by set the media direction to 'sendonly'
3068 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3069 * 'inactive' (PJMEDIA_DIR_NONE).
3070 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3071 */
3072 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3073 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003074
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003075 /* Remove existing directions attributes */
3076 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3077 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3078 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3079 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003080
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003081 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3082 /* Add sendonly attribute */
3083 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3084 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3085 } else {
3086 /* Add inactive attribute */
3087 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3088 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3089 }
3090 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003091
3092 *p_answer = sdp;
3093
3094 return status;
3095}
3096
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003097/*
3098 * Called when session received new offer.
3099 */
3100static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3101 const pjmedia_sdp_session *offer)
3102{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003103 pjsua_call *call;
3104 pjmedia_sdp_conn *conn;
3105 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003106 pj_status_t status;
3107
3108 PJSUA_LOCK();
3109
Benny Prijonoa1e69682007-05-11 15:14:34 +00003110 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003111
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003112 conn = offer->media[0]->conn;
3113 if (!conn)
3114 conn = offer->conn;
3115
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003116 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003117 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3118 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003119
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003120 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3121 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003122 if (status != PJ_SUCCESS) {
3123 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3124 PJSUA_UNLOCK();
3125 return;
3126 }
3127
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003128 /* Check if offer's conn address is zero */
3129 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3130 pj_strcmp2(&conn->addr, "0")==0)
3131 {
3132 /* Modify address */
3133 answer->conn->addr = pj_str("0.0.0.0");
3134 }
3135
3136 /* Check if call is on-hold */
3137 if (call->local_hold) {
3138 pjmedia_sdp_attr *attr;
3139
3140 /* Remove existing directions attributes */
3141 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3142 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3143 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3144 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3145
3146 /* Keep call on-hold by setting 'sendonly' attribute.
3147 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3148 */
3149 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3150 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3151 }
3152
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003153 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3154 if (status != PJ_SUCCESS) {
3155 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3156 PJSUA_UNLOCK();
3157 return;
3158 }
3159
3160 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003161}
3162
3163
3164/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003165 * Called to generate new offer.
3166 */
3167static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3168 pjmedia_sdp_session **offer)
3169{
3170 pjsua_call *call;
3171 pj_status_t status;
3172
3173 PJSUA_LOCK();
3174
3175 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3176
3177 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003178 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003179 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003180 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003181 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003182 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003183 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003184 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3185 call->index));
3186
Benny Prijonod8179652008-01-23 20:39:07 +00003187 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003188 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003189 }
3190
3191 if (status != PJ_SUCCESS) {
3192 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3193 PJSUA_UNLOCK();
3194 return;
3195 }
3196
Benny Prijono7129cc72007-11-05 05:54:25 +00003197 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00003198
3199 PJSUA_UNLOCK();
3200}
3201
3202
3203/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003204 * Callback called by event framework when the xfer subscription state
3205 * has changed.
3206 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003207static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3208{
3209
3210 PJ_UNUSED_ARG(event);
3211
3212 /*
3213 * When subscription is accepted (got 200/OK to REFER), check if
3214 * subscription suppressed.
3215 */
3216 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3217
3218 pjsip_rx_data *rdata;
3219 pjsip_generic_string_hdr *refer_sub;
3220 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3221 pjsua_call *call;
3222
Benny Prijonoa1e69682007-05-11 15:14:34 +00003223 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003224
3225 /* Must be receipt of response message */
3226 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3227 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3228 rdata = event->body.tsx_state.src.rdata;
3229
3230 /* Find Refer-Sub header */
3231 refer_sub = (pjsip_generic_string_hdr*)
3232 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3233 &REFER_SUB, NULL);
3234
3235 /* Check if subscription is suppressed */
3236 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3237 /* Since no subscription is desired, assume that call has been
3238 * transfered successfully.
3239 */
3240 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3241 const pj_str_t ACCEPTED = { "Accepted", 8 };
3242 pj_bool_t cont = PJ_FALSE;
3243 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3244 200,
3245 &ACCEPTED,
3246 PJ_TRUE,
3247 &cont);
3248 }
3249
3250 /* Yes, subscription is suppressed.
3251 * Terminate our subscription now.
3252 */
3253 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3254 "event subcription..."));
3255 pjsip_evsub_terminate(sub, PJ_TRUE);
3256
3257 } else {
3258 /* Notify application about call transfer progress.
3259 * Initially notify with 100/Accepted status.
3260 */
3261 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3262 const pj_str_t ACCEPTED = { "Accepted", 8 };
3263 pj_bool_t cont = PJ_FALSE;
3264 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3265 100,
3266 &ACCEPTED,
3267 PJ_FALSE,
3268 &cont);
3269 }
3270 }
3271 }
3272 /*
3273 * On incoming NOTIFY, notify application about call transfer progress.
3274 */
3275 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3276 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3277 {
3278 pjsua_call *call;
3279 pjsip_msg *msg;
3280 pjsip_msg_body *body;
3281 pjsip_status_line status_line;
3282 pj_bool_t is_last;
3283 pj_bool_t cont;
3284 pj_status_t status;
3285
Benny Prijonoa1e69682007-05-11 15:14:34 +00003286 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003287
3288 /* When subscription is terminated, clear the xfer_sub member of
3289 * the inv_data.
3290 */
3291 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3292 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3293 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3294
3295 }
3296
3297 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3298 /* Application is not interested with call progress status */
3299 return;
3300 }
3301
3302 /* This better be a NOTIFY request */
3303 if (event->type == PJSIP_EVENT_TSX_STATE &&
3304 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3305 {
3306 pjsip_rx_data *rdata;
3307
3308 rdata = event->body.tsx_state.src.rdata;
3309
3310 /* Check if there's body */
3311 msg = rdata->msg_info.msg;
3312 body = msg->body;
3313 if (!body) {
3314 PJ_LOG(4,(THIS_FILE,
3315 "Warning: received NOTIFY without message body"));
3316 return;
3317 }
3318
3319 /* Check for appropriate content */
3320 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3321 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3322 {
3323 PJ_LOG(4,(THIS_FILE,
3324 "Warning: received NOTIFY with non message/sipfrag "
3325 "content"));
3326 return;
3327 }
3328
3329 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003330 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003331 &status_line);
3332 if (status != PJ_SUCCESS) {
3333 PJ_LOG(4,(THIS_FILE,
3334 "Warning: received NOTIFY with invalid "
3335 "message/sipfrag content"));
3336 return;
3337 }
3338
3339 } else {
3340 status_line.code = 500;
3341 status_line.reason = *pjsip_get_status_text(500);
3342 }
3343
3344 /* Notify application */
3345 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3346 cont = !is_last;
3347 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3348 status_line.code,
3349 &status_line.reason,
3350 is_last, &cont);
3351
3352 if (!cont) {
3353 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3354 }
3355 }
3356}
3357
3358
3359/*
3360 * Callback called by event framework when the xfer subscription state
3361 * has changed.
3362 */
3363static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003364{
3365
3366 PJ_UNUSED_ARG(event);
3367
3368 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003369 * When subscription is terminated, clear the xfer_sub member of
3370 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003371 */
3372 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003373 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003374
Benny Prijonoa1e69682007-05-11 15:14:34 +00003375 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003376 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003377 return;
3378
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003379 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003380 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003381
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003382 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003383 }
3384}
3385
3386
3387/*
3388 * Follow transfer (REFER) request.
3389 */
3390static void on_call_transfered( pjsip_inv_session *inv,
3391 pjsip_rx_data *rdata )
3392{
3393 pj_status_t status;
3394 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003395 pjsua_call *existing_call;
3396 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003397 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003398 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003399 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003400 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003401 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003402 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003403 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003404 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003405 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003406 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003407 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003408 pjsip_evsub *sub;
3409
Benny Prijonoa1e69682007-05-11 15:14:34 +00003410 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003411
Benny Prijono26ff9062006-02-21 23:47:00 +00003412 /* Find the Refer-To header */
3413 refer_to = (pjsip_generic_string_hdr*)
3414 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3415
3416 if (refer_to == NULL) {
3417 /* Invalid Request.
3418 * No Refer-To header!
3419 */
3420 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003421 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003422 return;
3423 }
3424
Benny Prijonoc8141a82006-08-20 09:12:19 +00003425 /* Find optional Refer-Sub header */
3426 refer_sub = (pjsip_generic_string_hdr*)
3427 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3428
3429 if (refer_sub) {
3430 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3431 no_refer_sub = PJ_TRUE;
3432 }
3433
Benny Prijono053f5222006-11-11 16:16:04 +00003434 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3435 * request.
3436 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003437 ref_by_hdr = (pjsip_hdr*)
3438 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003439 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003440
Benny Prijono9fc735d2006-05-28 14:58:12 +00003441 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003442 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003443 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3444 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3445 &refer_to->hvalue,
3446 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003447
3448 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003449 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003450 if (code >= 300) {
3451 /* Application rejects call transfer request */
3452 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3453 return;
3454 }
3455
Benny Prijono26ff9062006-02-21 23:47:00 +00003456 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3457 (int)inv->dlg->remote.info_str.slen,
3458 inv->dlg->remote.info_str.ptr,
3459 (int)refer_to->hvalue.slen,
3460 refer_to->hvalue.ptr));
3461
Benny Prijonoc8141a82006-08-20 09:12:19 +00003462 if (no_refer_sub) {
3463 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003464 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003465 */
3466 pjsip_tx_data *tdata;
3467 const pj_str_t str_false = { "false", 5};
3468 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003469
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003470 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3471 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003472 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003473 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003474 status);
3475 return;
3476 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003477
Benny Prijonoc8141a82006-08-20 09:12:19 +00003478 /* Add Refer-Sub header */
3479 hdr = (pjsip_hdr*)
3480 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3481 &str_false);
3482 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003483
Benny Prijono26ff9062006-02-21 23:47:00 +00003484
Benny Prijonoc8141a82006-08-20 09:12:19 +00003485 /* Send answer */
3486 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3487 tdata);
3488 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003489 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003490 status);
3491 return;
3492 }
3493
3494 /* Don't have subscription */
3495 sub = NULL;
3496
3497 } else {
3498 struct pjsip_evsub_user xfer_cb;
3499 pjsip_hdr hdr_list;
3500
3501 /* Init callback */
3502 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003503 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003504
3505 /* Init additional header list to be sent with REFER response */
3506 pj_list_init(&hdr_list);
3507
3508 /* Create transferee event subscription */
3509 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3510 if (status != PJ_SUCCESS) {
3511 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3512 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3513 return;
3514 }
3515
3516 /* If there's Refer-Sub header and the value is "true", send back
3517 * Refer-Sub in the response with value "true" too.
3518 */
3519 if (refer_sub) {
3520 const pj_str_t str_true = { "true", 4 };
3521 pjsip_hdr *hdr;
3522
3523 hdr = (pjsip_hdr*)
3524 pjsip_generic_string_hdr_create(inv->dlg->pool,
3525 &str_refer_sub,
3526 &str_true);
3527 pj_list_push_back(&hdr_list, hdr);
3528
3529 }
3530
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003531 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003532 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3533
3534 /* Create initial NOTIFY request */
3535 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3536 100, NULL, &tdata);
3537 if (status != PJ_SUCCESS) {
3538 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3539 status);
3540 return;
3541 }
3542
3543 /* Send initial NOTIFY request */
3544 status = pjsip_xfer_send_request( sub, tdata);
3545 if (status != PJ_SUCCESS) {
3546 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3547 return;
3548 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003549 }
3550
3551 /* We're cheating here.
3552 * We need to get a null terminated string from a pj_str_t.
3553 * So grab the pointer from the hvalue and NULL terminate it, knowing
3554 * that the NULL position will be occupied by a newline.
3555 */
3556 uri = refer_to->hvalue.ptr;
3557 uri[refer_to->hvalue.slen] = '\0';
3558
Benny Prijono053f5222006-11-11 16:16:04 +00003559 /* Init msg_data */
3560 pjsua_msg_data_init(&msg_data);
3561
3562 /* If Referred-By header is present in the REFER request, copy this
3563 * to the outgoing INVITE request.
3564 */
3565 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003566 pjsip_hdr *dup = (pjsip_hdr*)
3567 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003568 pj_list_push_back(&msg_data.hdr_list, dup);
3569 }
3570
Benny Prijono26ff9062006-02-21 23:47:00 +00003571 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003572 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003573 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003574 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003575 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003576 if (status != PJ_SUCCESS) {
3577
Benny Prijonoc8141a82006-08-20 09:12:19 +00003578 /* Notify xferer about the error (if we have subscription) */
3579 if (sub) {
3580 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3581 500, NULL, &tdata);
3582 if (status != PJ_SUCCESS) {
3583 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3584 status);
3585 return;
3586 }
3587 status = pjsip_xfer_send_request(sub, tdata);
3588 if (status != PJ_SUCCESS) {
3589 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3590 status);
3591 return;
3592 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003593 }
3594 return;
3595 }
3596
Benny Prijonoc8141a82006-08-20 09:12:19 +00003597 if (sub) {
3598 /* Put the server subscription in inv_data.
3599 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3600 * reported back to the server subscription.
3601 */
3602 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003603
Benny Prijonoc8141a82006-08-20 09:12:19 +00003604 /* Put the invite_data in the subscription. */
3605 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3606 &pjsua_var.calls[new_call]);
3607 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003608}
3609
3610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003611
Benny Prijono26ff9062006-02-21 23:47:00 +00003612/*
3613 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003614 * session. We use this to trap:
3615 * - incoming REFER request.
3616 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003617 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003618static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3619 pjsip_transaction *tsx,
3620 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003621{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003622 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003623
3624 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003625
Benny Prijonofeb69f42007-10-05 09:12:26 +00003626 /* Notify application callback first */
3627 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3628 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3629 }
3630
Benny Prijono26ff9062006-02-21 23:47:00 +00003631 if (tsx->role==PJSIP_ROLE_UAS &&
3632 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003633 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003634 {
3635 /*
3636 * Incoming REFER request.
3637 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003638 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003639
Benny Prijono26ff9062006-02-21 23:47:00 +00003640 }
Benny Prijonob0808372006-03-02 21:18:58 +00003641 else if (tsx->role==PJSIP_ROLE_UAS &&
3642 tsx->state==PJSIP_TSX_STATE_TRYING &&
3643 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3644 {
3645 /*
3646 * Incoming MESSAGE request!
3647 */
3648 pjsip_rx_data *rdata;
3649 pjsip_msg *msg;
3650 pjsip_accept_hdr *accept_hdr;
3651 pj_status_t status;
3652
3653 rdata = e->body.tsx_state.src.rdata;
3654 msg = rdata->msg_info.msg;
3655
3656 /* Request MUST have message body, with Content-Type equal to
3657 * "text/plain".
3658 */
3659 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3660
3661 pjsip_hdr hdr_list;
3662
3663 pj_list_init(&hdr_list);
3664 pj_list_push_back(&hdr_list, accept_hdr);
3665
3666 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3667 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003668 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003669 return;
3670 }
3671
3672 /* Respond with 200 first, so that remote doesn't retransmit in case
3673 * the UI takes too long to process the message.
3674 */
3675 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3676
3677 /* Process MESSAGE request */
3678 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3679 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003680
Benny Prijonob0808372006-03-02 21:18:58 +00003681 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003682 else if (tsx->role == PJSIP_ROLE_UAC &&
3683 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003684 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003685 /* Handle outgoing pager status */
3686 if (tsx->status_code >= 200) {
3687 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003688
Benny Prijonoa1e69682007-05-11 15:14:34 +00003689 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003690 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003691
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003692 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3693 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3694 &im_data->to,
3695 &im_data->body,
3696 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003697 (pjsip_status_code)
3698 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003699 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003700 }
Benny Prijonofccab712006-02-22 22:23:22 +00003701 }
Benny Prijono834aee32006-02-19 01:38:06 +00003702 }
Benny Prijono834aee32006-02-19 01:38:06 +00003703
Benny Prijono26ff9062006-02-21 23:47:00 +00003704
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003705 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003706}