blob: 114da50d04b1c0de2e1ab46c8a96a513cf324d98 [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();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000109 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 call->conf_slot = PJSUA_INVALID_ID;
111 call->last_text.ptr = call->last_text_buf_;
112 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000113 call->conn_time.sec = 0;
114 call->conn_time.msec = 0;
115 call->res_time.sec = 0;
116 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000117 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000118 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000119 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000120}
121
122
Benny Prijono275fd682006-03-22 11:59:11 +0000123/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000125 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000127{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 pjsip_inv_callback inv_cb;
129 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000130 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 /* Init calls array. */
134 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
135 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000136
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000137 /* Copy config */
138 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000139
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000141 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000142 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
143 inv_cb.on_new_session = &pjsua_call_on_forked;
144 inv_cb.on_media_update = &pjsua_call_on_media_update;
145 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000146 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000148
Benny Prijono275fd682006-03-22 11:59:11 +0000149
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150 /* Initialize invite session module: */
151 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
152 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
153
Benny Prijonoc8141a82006-08-20 09:12:19 +0000154 /* Add "norefersub" in Supported header */
155 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
156 NULL, 1, &str_norefersub);
157
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000158 return status;
159}
160
161
162/*
163 * Start call subsystem.
164 */
165pj_status_t pjsua_call_subsys_start(void)
166{
167 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000168 return PJ_SUCCESS;
169}
170
171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173 * Get maximum number of calls configured in pjsua.
174 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000175PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000178}
179
180
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181/*
182 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000184PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000187}
188
189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190/*
191 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000192 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
194 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
203 if (!pjsua_var.calls[i].inv)
204 continue;
205 ids[c] = i;
206 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207 }
208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000212
213 return PJ_SUCCESS;
214}
215
216
Benny Prijono5773cd62008-01-19 13:01:42 +0000217/* Allocate one call id */
218static pjsua_call_id alloc_call_id(void)
219{
220 pjsua_call_id cid;
221
222#if 1
223 /* New algorithm: round-robin */
224 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
225 pjsua_var.next_call_id < 0)
226 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000227 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000228 }
229
230 for (cid=pjsua_var.next_call_id;
231 cid<(int)pjsua_var.ua_cfg.max_calls;
232 ++cid)
233 {
234 if (pjsua_var.calls[cid].inv == NULL) {
235 ++pjsua_var.next_call_id;
236 return cid;
237 }
238 }
239
240 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
241 if (pjsua_var.calls[cid].inv == NULL) {
242 ++pjsua_var.next_call_id;
243 return cid;
244 }
245 }
246
247#else
248 /* Old algorithm */
249 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
250 if (pjsua_var.calls[cid].inv == NULL)
251 return cid;
252 }
253#endif
254
255 return PJSUA_INVALID_ID;
256}
257
Benny Prijonod8179652008-01-23 20:39:07 +0000258/* Get signaling secure level.
259 * Return:
260 * 0: if signaling is not secure
261 * 1: if TLS transport is used for immediate hop
262 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000263 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000264static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000265{
266 const pj_str_t tls = pj_str(";transport=tls");
267 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000268 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000269
270 if (pj_stristr(dst_uri, &sips))
271 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000272
273 if (!pj_list_empty(&acc->route_set)) {
274 pjsip_route_hdr *r = acc->route_set.next;
275 pjsip_uri *uri = r->name_addr.uri;
276 pjsip_sip_uri *sip_uri;
277
278 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
279 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
280 return 1;
281
282 } else {
283 if (pj_stristr(dst_uri, &tls))
284 return 1;
285 }
286
Benny Prijonod8179652008-01-23 20:39:07 +0000287 return 0;
288}
289
Benny Prijono224b4e22008-06-19 14:10:28 +0000290/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000291static int call_get_secure_level(pjsua_call *call)
292{
293 if (call->inv->dlg->secure)
294 return 2;
295
296 if (!pj_list_empty(&call->inv->dlg->route_set)) {
297 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
298 pjsip_uri *uri = r->name_addr.uri;
299 pjsip_sip_uri *sip_uri;
300
301 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
302 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
303 return 1;
304
305 } else {
306 pjsip_sip_uri *sip_uri;
307
308 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
309 return 2;
310 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
311 return 0;
312
313 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
314 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
315 return 1;
316 }
317
318 return 0;
319}
Benny Prijono224b4e22008-06-19 14:10:28 +0000320*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000321
322
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000323/*
324 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000325 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
327 const pj_str_t *dest_uri,
328 unsigned options,
329 void *user_data,
330 const pjsua_msg_data *msg_data,
331 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000332{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000333 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000334 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000335 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000336 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 pjsua_acc *acc;
338 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000339 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000340 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000341 pjsip_tx_data *tdata;
342 pj_status_t status;
343
Benny Prijono9fc735d2006-05-28 14:58:12 +0000344
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000345 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000346 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000347 PJ_EINVAL);
348
Benny Prijono320fa4d2006-12-07 10:09:16 +0000349 /* Check arguments */
350 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
351
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352 PJSUA_LOCK();
353
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000354 /* Create sound port if none is instantiated */
355 if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
356 !pjsua_var.no_snd)
357 {
358 pj_status_t status;
359
360 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
361 if (status != PJ_SUCCESS) {
362 PJSUA_UNLOCK();
363 return status;
364 }
365 }
366
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 acc = &pjsua_var.acc[acc_id];
368 if (!acc->valid) {
369 pjsua_perror(THIS_FILE, "Unable to make call because account "
370 "is not valid", PJ_EINVALIDOP);
371 PJSUA_UNLOCK();
372 return PJ_EINVALIDOP;
373 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000374
Benny Prijonoa91a0032006-02-26 21:23:45 +0000375 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000376 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000377
Benny Prijono5773cd62008-01-19 13:01:42 +0000378 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000379 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
380 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000381 return PJ_ETOOMANY;
382 }
383
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 call = &pjsua_var.calls[call_id];
385
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000386 /* Create temporary pool */
387 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
388
Benny Prijono320fa4d2006-12-07 10:09:16 +0000389 /* Verify that destination URI is valid before calling
390 * pjsua_acc_create_uac_contact, or otherwise there
391 * a misleading "Invalid Contact URI" error will be printed
392 * when pjsua_acc_create_uac_contact() fails.
393 */
394 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000395 pjsip_uri *uri;
396 pj_str_t dup;
397
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000398 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
399 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000400
401 if (uri == NULL) {
402 pjsua_perror(THIS_FILE, "Unable to make call",
403 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000404 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000405 PJSUA_UNLOCK();
406 return PJSIP_EINVALIDREQURI;
407 }
408 }
409
Benny Prijono093d3022006-09-24 00:07:11 +0000410 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
411 (int)dest_uri->slen, dest_uri->ptr));
412
Benny Prijonoe21e7842006-04-09 16:46:05 +0000413 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000414 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000415
Benny Prijonoe21e7842006-04-09 16:46:05 +0000416 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000418
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000419 /* Create suitable Contact header unless a Contact header has been
420 * set in the account.
421 */
422 if (acc->contact.slen) {
423 contact = acc->contact;
424 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000425 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000426 acc_id, dest_uri);
427 if (status != PJ_SUCCESS) {
428 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
429 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000430 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000431 PJSUA_UNLOCK();
432 return status;
433 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000434 }
435
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000437 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000438 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000440 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000441 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000442 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000443 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000444 return status;
445 }
446
Benny Prijonodb844a42008-02-02 17:07:18 +0000447 /* Calculate call's secure level */
448 call->secure_level = get_secure_level(acc_id, dest_uri);
449
Benny Prijonoc97608e2007-03-23 16:34:20 +0000450 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000451 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000452 call->secure_level, dlg->pool,
453 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000454 if (status != PJ_SUCCESS) {
455 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
456 goto on_error;
457 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000458
Benny Prijono224b4e22008-06-19 14:10:28 +0000459 /* Create offer */
460 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000461 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000463 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000464 goto on_error;
465 }
466
467 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000468 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000469 if (acc->cfg.require_100rel)
470 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000471
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000472 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000473 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000474 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000475 goto on_error;
476 }
477
478
479 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000480 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000482
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483 dlg->mod_data[pjsua_var.mod.id] = call;
484 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000485
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486 /* Attach user data */
487 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000488
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000489 /* If account is locked to specific transport, then lock dialog
490 * to this transport too.
491 */
492 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
493 pjsip_tpselector tp_sel;
494
495 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
496 pjsip_dlg_set_transport(dlg, &tp_sel);
497 }
498
Benny Prijono84126ab2006-02-09 09:30:09 +0000499 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 if (!pj_list_empty(&acc->route_set))
501 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000502
503
504 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000506 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000508 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000509
Benny Prijono48ab2b72007-11-08 09:24:30 +0000510 /* Set authentication preference */
511 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000512
513 /* Create initial INVITE: */
514
515 status = pjsip_inv_invite(inv, &tdata);
516 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000517 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
518 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000519 goto on_error;
520 }
521
522
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 /* Add additional headers etc */
524
525 pjsua_process_msg_data( tdata, msg_data);
526
Benny Prijono093d3022006-09-24 00:07:11 +0000527 /* Must increment call counter now */
528 ++pjsua_var.call_cnt;
529
Benny Prijono84126ab2006-02-09 09:30:09 +0000530 /* Send initial INVITE: */
531
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000532 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000533 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000534 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
535 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000536
537 /* Upon failure to send first request, both dialog and invite
538 * session would have been cleared.
539 */
540 inv = NULL;
541 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000542 goto on_error;
543 }
544
Benny Prijono84126ab2006-02-09 09:30:09 +0000545 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000546
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 if (p_call_id)
548 *p_call_id = call_id;
549
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000550 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000552
553 return PJ_SUCCESS;
554
555
556on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000557 if (inv != NULL) {
558 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000559 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000560 pjsip_dlg_terminate(dlg);
561 }
562
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000563 if (call_id != -1) {
564 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000565 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000566 }
567
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000568 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000569 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000570 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571}
572
573
Benny Prijono91a6a172007-10-31 08:59:29 +0000574/* Get the NAT type information in remote's SDP */
575static void update_remote_nat_type(pjsua_call *call,
576 const pjmedia_sdp_session *sdp)
577{
578 const pjmedia_sdp_attr *xnat;
579
580 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
581 if (xnat) {
582 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
583 } else {
584 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
585 }
586
587 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
588 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
589}
590
591
Benny Prijonodc39fe82006-05-26 12:17:46 +0000592/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000593 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000595 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000596pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000597{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000598 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000599 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000600 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000601 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
602 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000603 pjsip_tx_data *response = NULL;
604 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000605 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000606 int acc_id;
607 pjsua_call *call;
608 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000609 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000610 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000611 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000612
Benny Prijono26ff9062006-02-21 23:47:00 +0000613 /* Don't want to handle anything but INVITE */
614 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
615 return PJ_FALSE;
616
617 /* Don't want to handle anything that's already associated with
618 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000619 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000620 if (dlg || tsx)
621 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000622
Benny Prijono148c9dd2006-09-19 13:37:53 +0000623 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000624
Benny Prijono26ff9062006-02-21 23:47:00 +0000625 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000626 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000627
Benny Prijono5773cd62008-01-19 13:01:42 +0000628 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000629 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000630 PJSIP_SC_BUSY_HERE, NULL,
631 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000632 PJ_LOG(2,(THIS_FILE,
633 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000634 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000635 return PJ_TRUE;
636 }
637
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000638 /* Clear call descriptor */
639 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000640
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 call = &pjsua_var.calls[call_id];
642
643 /* Mark call start time. */
644 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000645
Benny Prijono053f5222006-11-11 16:16:04 +0000646 /* Check INVITE request for Replaces header. If Replaces header is
647 * present, the function will make sure that we can handle the request.
648 */
649 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
650 &response);
651 if (status != PJ_SUCCESS) {
652 /*
653 * Something wrong with the Replaces header.
654 */
655 if (response) {
656 pjsip_response_addr res_addr;
657
658 pjsip_get_response_addr(response->pool, rdata, &res_addr);
659 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
660 NULL, NULL);
661
662 } else {
663
664 /* Respond with 500 (Internal Server Error) */
665 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
666 NULL, NULL);
667 }
668
669 PJSUA_UNLOCK();
670 return PJ_TRUE;
671 }
672
673 /* If this INVITE request contains Replaces header, notify application
674 * about the request so that application can do subsequent checking
675 * if it wants to.
676 */
677 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
678 pjsua_call *replaced_call;
679 int st_code = 200;
680 pj_str_t st_text = { "OK", 2 };
681
682 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000683 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000684
685 /* Notify application */
686 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
687 rdata, &st_code, &st_text);
688
689 /* Must specify final response */
690 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
691
692 /* Check if application rejects this request. */
693 if (st_code >= 300) {
694
695 if (st_text.slen == 2)
696 st_text = *pjsip_get_status_text(st_code);
697
698 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
699 st_code, &st_text, NULL, NULL, NULL);
700 PJSUA_UNLOCK();
701 return PJ_TRUE;
702 }
703 }
704
Benny Prijonod8179652008-01-23 20:39:07 +0000705 /*
706 * Get which account is most likely to be associated with this incoming
707 * call. We need the account to find which contact URI to put for
708 * the call.
709 */
710 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
711
Benny Prijonodb844a42008-02-02 17:07:18 +0000712 /* Get call's secure level */
713 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
714 call->secure_level = 2;
715 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
716 call->secure_level = 1;
717 else
718 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000719
Benny Prijonod8179652008-01-23 20:39:07 +0000720 /* Parse SDP from incoming request */
721 if (rdata->msg_info.msg->body) {
722 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000723 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000724 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000725 if (status == PJ_SUCCESS) {
726 /* Validate */
727 status = pjmedia_sdp_validate(offer);
728 }
729
Benny Prijonod8179652008-01-23 20:39:07 +0000730 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000731 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000732 pjsip_hdr hdr_list;
733 pjsip_warning_hdr *w;
734
735 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000736 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000737
738 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
739 pjsip_endpt_name(pjsua_var.endpt),
740 status);
741 pj_list_init(&hdr_list);
742 pj_list_push_back(&hdr_list, w);
743
Benny Prijono224b4e22008-06-19 14:10:28 +0000744 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000745 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000746 PJSUA_UNLOCK();
747 return PJ_TRUE;
748 }
Benny Prijono617b8602008-04-07 10:10:31 +0000749
750 /* Do quick checks on SDP before passing it to transports. More elabore
751 * checks will be done in pjsip_inv_verify_request2() below.
752 */
753 if (offer->media_count==0) {
754 const pj_str_t reason = pj_str("Missing media in SDP");
755 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
756 NULL, NULL, NULL);
757 PJSUA_UNLOCK();
758 return PJ_TRUE;
759 }
760
Benny Prijonod8179652008-01-23 20:39:07 +0000761 } else {
762 offer = NULL;
763 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000764
Benny Prijono224b4e22008-06-19 14:10:28 +0000765 /* Init media channel */
766 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
767 call->secure_level,
768 rdata->tp_info.pool, offer,
769 &sip_err_code);
770 if (status != PJ_SUCCESS) {
771 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
772 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
773 sip_err_code, NULL, NULL, NULL, NULL);
774 PJSUA_UNLOCK();
775 return PJ_TRUE;
776 }
777
778 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000779 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000780 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000781 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000782 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000783 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
784 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000785 PJSUA_UNLOCK();
786 return PJ_TRUE;
787 }
788
Benny Prijono224b4e22008-06-19 14:10:28 +0000789
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000790 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000791 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000792 if (pjsua_var.acc[acc_id].cfg.require_100rel)
793 options |= PJSIP_INV_REQUIRE_100REL;
794
Benny Prijonod8179652008-01-23 20:39:07 +0000795 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
796 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000797 if (status != PJ_SUCCESS) {
798
799 /*
800 * No we can't handle the incoming INVITE request.
801 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000802 if (response) {
803 pjsip_response_addr res_addr;
804
805 pjsip_get_response_addr(response->pool, rdata, &res_addr);
806 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
807 NULL, NULL);
808
809 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000810 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000811 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
812 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000813 }
814
Benny Prijonoc97608e2007-03-23 16:34:20 +0000815 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000816 PJSUA_UNLOCK();
817 return PJ_TRUE;
818 }
819
820
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000821 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000822 if (pjsua_var.acc[acc_id].contact.slen) {
823 contact = pjsua_var.acc[acc_id].contact;
824 } else {
825 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
826 acc_id, rdata);
827 if (status != PJ_SUCCESS) {
828 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
829 status);
830 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
831 NULL, NULL);
832 pjsua_media_channel_deinit(call->index);
833 PJSUA_UNLOCK();
834 return PJ_TRUE;
835 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000836 }
837
Benny Prijono26ff9062006-02-21 23:47:00 +0000838 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000839 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000840 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000841 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000842 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000843 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000844 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000845 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000846 return PJ_TRUE;
847 }
848
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000849 /* Set credentials */
850 if (pjsua_var.acc[acc_id].cred_cnt) {
851 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
852 pjsua_var.acc[acc_id].cred_cnt,
853 pjsua_var.acc[acc_id].cred);
854 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000855
Benny Prijono48ab2b72007-11-08 09:24:30 +0000856 /* Set preference */
857 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
858 &pjsua_var.acc[acc_id].cfg.auth_pref);
859
Benny Prijono26ff9062006-02-21 23:47:00 +0000860 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000861 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000862 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000863 pjsip_hdr hdr_list;
864 pjsip_warning_hdr *w;
865
866 w = pjsip_warning_hdr_create_from_status(dlg->pool,
867 pjsip_endpt_name(pjsua_var.endpt),
868 status);
869 pj_list_init(&hdr_list);
870 pj_list_push_back(&hdr_list, w);
871
872 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
873
874 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000875 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000876 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000877 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000878 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000879 return PJ_TRUE;
880 }
881
Benny Prijonoea9fd392007-11-06 03:41:40 +0000882 /* Update NAT type of remote endpoint, only when there is SDP in
883 * incoming INVITE!
884 */
885 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
886 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
887 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000888 const pjmedia_sdp_session *remote_sdp;
889
890 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
891 update_remote_nat_type(call, remote_sdp);
892 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000893
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 /* Create and attach pjsua_var data to the dialog: */
895 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000896
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000897 dlg->mod_data[pjsua_var.mod.id] = call;
898 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000899
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000900 /* If account is locked to specific transport, then lock dialog
901 * to this transport too.
902 */
903 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
904 pjsip_tpselector tp_sel;
905
906 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
907 pjsip_dlg_set_transport(dlg, &tp_sel);
908 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000909
Benny Prijono64f851e2006-02-23 13:49:28 +0000910 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000911 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000912 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000913 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000914 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000915 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
916 status);
917
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000918 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
919 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000920 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000921 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000922 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000923
924 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000925 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000926 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000927 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000928 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000929 }
930
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000931 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000932
Benny Prijono105217f2006-03-06 16:25:59 +0000933
Benny Prijono053f5222006-11-11 16:16:04 +0000934 /* Check if this request should replace existing call */
935 if (replaced_dlg) {
936 pjsip_inv_session *replaced_inv;
937 struct pjsua_call *replaced_call;
938 pjsip_tx_data *tdata;
939
940 /* Get the invite session in the dialog */
941 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
942
943 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000944 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000945
946 /* Notify application */
947 if (pjsua_var.ua_cfg.cb.on_call_replaced)
948 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
949 call_id);
950
951 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
952 call_id));
953
954 /* Answer the new call with 200 response */
955 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
956 if (status == PJ_SUCCESS)
957 status = pjsip_inv_send_msg(inv, tdata);
958
959 if (status != PJ_SUCCESS)
960 pjsua_perror(THIS_FILE, "Error answering session", status);
961
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000962 /* Note that inv may be invalid if 200/OK has caused error in
963 * starting the media.
964 */
Benny Prijono053f5222006-11-11 16:16:04 +0000965
966 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
967 replaced_call->index));
968
969 /* Disconnect replaced invite session */
970 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
971 &tdata);
972 if (status == PJ_SUCCESS && tdata)
973 status = pjsip_inv_send_msg(replaced_inv, tdata);
974
975 if (status != PJ_SUCCESS)
976 pjsua_perror(THIS_FILE, "Error terminating session", status);
977
978
979 } else {
980
Benny Prijonob5388cf2007-01-04 22:45:08 +0000981 /* Notify application if on_incoming_call() is overriden,
982 * otherwise hangup the call with 480
983 */
984 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000985 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000986 } else {
987 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
988 NULL, NULL);
989 }
Benny Prijono053f5222006-11-11 16:16:04 +0000990 }
991
Benny Prijono8b1889b2006-06-06 18:40:40 +0000992
Benny Prijono26ff9062006-02-21 23:47:00 +0000993 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000994 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000995 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000996}
997
998
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000999
1000/*
1001 * Check if the specified call has active INVITE session and the INVITE
1002 * session has not been disconnected.
1003 */
1004PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1005{
1006 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1007 PJ_EINVAL);
1008 return pjsua_var.calls[call_id].inv != NULL &&
1009 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1010}
1011
1012
1013/*
1014 * Check if call has an active media session.
1015 */
1016PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1017{
1018 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1019 PJ_EINVAL);
1020 return pjsua_var.calls[call_id].session != NULL;
1021}
1022
1023
Benny Prijono148c9dd2006-09-19 13:37:53 +00001024/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001025pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001026 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001027 pjsua_call **p_call,
1028 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001029{
1030 enum { MAX_RETRY=50 };
1031 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001032 pjsua_call *call = NULL;
1033 pj_bool_t has_pjsua_lock = PJ_FALSE;
1034 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001035
1036 for (retry=0; retry<MAX_RETRY; ++retry) {
1037
1038 has_pjsua_lock = PJ_FALSE;
1039
1040 status = PJSUA_TRY_LOCK();
1041 if (status != PJ_SUCCESS) {
1042 pj_thread_sleep(retry/10);
1043 continue;
1044 }
1045
1046 has_pjsua_lock = PJ_TRUE;
1047 call = &pjsua_var.calls[call_id];
1048
1049 if (call->inv == NULL) {
1050 PJSUA_UNLOCK();
1051 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1052 return PJSIP_ESESSIONTERMINATED;
1053 }
1054
1055 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1056 if (status != PJ_SUCCESS) {
1057 PJSUA_UNLOCK();
1058 pj_thread_sleep(retry/10);
1059 continue;
1060 }
1061
1062 PJSUA_UNLOCK();
1063
1064 break;
1065 }
1066
1067 if (status != PJ_SUCCESS) {
1068 if (has_pjsua_lock == PJ_FALSE)
1069 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1070 "(possibly system has deadlocked) in %s",
1071 title));
1072 else
1073 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1074 "(possibly system has deadlocked) in %s",
1075 title));
1076 return PJ_ETIMEDOUT;
1077 }
1078
1079 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001080 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001081
1082 return PJ_SUCCESS;
1083}
1084
1085
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001086/*
1087 * Get the conference port identification associated with the call.
1088 */
1089PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1090{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001091 pjsua_call *call;
1092 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001093 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001094 pj_status_t status;
1095
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001096 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1097 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001098
Benny Prijonodc752ca2006-09-22 16:55:42 +00001099 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001100 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001101 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001102
1103 port_id = call->conf_slot;
1104
Benny Prijonodc752ca2006-09-22 16:55:42 +00001105 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001106
1107 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001108}
1109
1110
Benny Prijono148c9dd2006-09-19 13:37:53 +00001111
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001112/*
1113 * Obtain detail information about the specified call.
1114 */
1115PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1116 pjsua_call_info *info)
1117{
1118 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001119 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001120 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001121
1122 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1123 PJ_EINVAL);
1124
Benny Prijonoac623b32006-07-03 15:19:31 +00001125 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126
Benny Prijonodc752ca2006-09-22 16:55:42 +00001127 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001128 if (status != PJ_SUCCESS) {
1129 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130 }
1131
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001132 /* id and role */
1133 info->id = call_id;
1134 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001135 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136
1137 /* local info */
1138 info->local_info.ptr = info->buf_.local_info;
1139 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1140 sizeof(info->buf_.local_info));
1141
1142 /* local contact */
1143 info->local_contact.ptr = info->buf_.local_contact;
1144 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1145 call->inv->dlg->local.contact->uri,
1146 info->local_contact.ptr,
1147 sizeof(info->buf_.local_contact));
1148
1149 /* remote info */
1150 info->remote_info.ptr = info->buf_.remote_info;
1151 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1152 sizeof(info->buf_.remote_info));
1153
1154 /* remote contact */
1155 if (call->inv->dlg->remote.contact) {
1156 int len;
1157 info->remote_contact.ptr = info->buf_.remote_contact;
1158 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1159 call->inv->dlg->remote.contact->uri,
1160 info->remote_contact.ptr,
1161 sizeof(info->buf_.remote_contact));
1162 if (len < 0) len = 0;
1163 info->remote_contact.slen = len;
1164 } else {
1165 info->remote_contact.slen = 0;
1166 }
1167
1168 /* call id */
1169 info->call_id.ptr = info->buf_.call_id;
1170 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1171 sizeof(info->buf_.call_id));
1172
1173 /* state, state_text */
1174 info->state = call->inv->state;
1175 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1176
1177 /* If call is disconnected, set the last_status from the cause code */
1178 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1179 /* last_status, last_status_text */
1180 info->last_status = call->inv->cause;
1181
1182 info->last_status_text.ptr = info->buf_.last_status_text;
1183 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1184 sizeof(info->buf_.last_status_text));
1185 } else {
1186 /* last_status, last_status_text */
1187 info->last_status = call->last_code;
1188
1189 info->last_status_text.ptr = info->buf_.last_status_text;
1190 pj_strncpy(&info->last_status_text, &call->last_text,
1191 sizeof(info->buf_.last_status_text));
1192 }
1193
1194 /* media status and dir */
1195 info->media_status = call->media_st;
1196 info->media_dir = call->media_dir;
1197
1198
1199 /* conference slot number */
1200 info->conf_slot = call->conf_slot;
1201
1202 /* calculate duration */
1203 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1204
1205 info->total_duration = call->dis_time;
1206 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1207
1208 if (call->conn_time.sec) {
1209 info->connect_duration = call->dis_time;
1210 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1211 }
1212
1213 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1214
1215 pj_gettimeofday(&info->total_duration);
1216 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1217
1218 pj_gettimeofday(&info->connect_duration);
1219 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1220
1221 } else {
1222 pj_gettimeofday(&info->total_duration);
1223 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1224 }
1225
Benny Prijonodc752ca2006-09-22 16:55:42 +00001226 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001227
1228 return PJ_SUCCESS;
1229}
1230
1231
1232/*
1233 * Attach application specific data to the call.
1234 */
1235PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1236 void *user_data)
1237{
1238 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1239 PJ_EINVAL);
1240 pjsua_var.calls[call_id].user_data = user_data;
1241
1242 return PJ_SUCCESS;
1243}
1244
1245
1246/*
1247 * Get user data attached to the call.
1248 */
1249PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1250{
1251 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1252 NULL);
1253 return pjsua_var.calls[call_id].user_data;
1254}
1255
1256
1257/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001258 * Get remote's NAT type.
1259 */
1260PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1261 pj_stun_nat_type *p_type)
1262{
1263 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1264 PJ_EINVAL);
1265 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1266
1267 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1268 return PJ_SUCCESS;
1269}
1270
1271
1272/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001273 * Send response to incoming INVITE request.
1274 */
1275PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1276 unsigned code,
1277 const pj_str_t *reason,
1278 const pjsua_msg_data *msg_data)
1279{
1280 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001281 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001282 pjsip_tx_data *tdata;
1283 pj_status_t status;
1284
1285 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1286 PJ_EINVAL);
1287
Benny Prijonodc752ca2006-09-22 16:55:42 +00001288 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001289 if (status != PJ_SUCCESS)
1290 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291
Benny Prijono2e507c22006-06-23 15:04:11 +00001292 if (call->res_time.sec == 0)
1293 pj_gettimeofday(&call->res_time);
1294
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001295 if (reason && reason->slen == 0)
1296 reason = NULL;
1297
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 /* Create response message */
1299 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1300 if (status != PJ_SUCCESS) {
1301 pjsua_perror(THIS_FILE, "Error creating response",
1302 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001303 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001304 return status;
1305 }
1306
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001307 /* Call might have been disconnected if application is answering with
1308 * 200/OK and the media failed to start.
1309 */
1310 if (call->inv == NULL) {
1311 pjsip_dlg_dec_lock(dlg);
1312 return PJSIP_ESESSIONTERMINATED;
1313 }
1314
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001315 /* Add additional headers etc */
1316 pjsua_process_msg_data( tdata, msg_data);
1317
1318 /* Send the message */
1319 status = pjsip_inv_send_msg(call->inv, tdata);
1320 if (status != PJ_SUCCESS)
1321 pjsua_perror(THIS_FILE, "Error sending response",
1322 status);
1323
Benny Prijonodc752ca2006-09-22 16:55:42 +00001324 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001325
1326 return status;
1327}
1328
1329
1330/*
1331 * Hangup call by using method that is appropriate according to the
1332 * call state.
1333 */
1334PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1335 unsigned code,
1336 const pj_str_t *reason,
1337 const pjsua_msg_data *msg_data)
1338{
1339 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001340 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341 pj_status_t status;
1342 pjsip_tx_data *tdata;
1343
1344
Benny Prijono148c9dd2006-09-19 13:37:53 +00001345 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1346 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1347 call_id));
1348 }
1349
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001350 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1351 PJ_EINVAL);
1352
Benny Prijonodc752ca2006-09-22 16:55:42 +00001353 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001354 if (status != PJ_SUCCESS)
1355 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356
1357 if (code==0) {
1358 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1359 code = PJSIP_SC_OK;
1360 else if (call->inv->role == PJSIP_ROLE_UAS)
1361 code = PJSIP_SC_DECLINE;
1362 else
1363 code = PJSIP_SC_REQUEST_TERMINATED;
1364 }
1365
1366 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1367 if (status != PJ_SUCCESS) {
1368 pjsua_perror(THIS_FILE,
1369 "Failed to create end session message",
1370 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001371 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001372 return status;
1373 }
1374
1375 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1376 * as p_tdata when INVITE transaction has not been answered
1377 * with any provisional responses.
1378 */
1379 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001380 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 return PJ_SUCCESS;
1382 }
1383
1384 /* Add additional headers etc */
1385 pjsua_process_msg_data( tdata, msg_data);
1386
1387 /* Send the message */
1388 status = pjsip_inv_send_msg(call->inv, tdata);
1389 if (status != PJ_SUCCESS) {
1390 pjsua_perror(THIS_FILE,
1391 "Failed to send end session message",
1392 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001393 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394 return status;
1395 }
1396
Benny Prijonodc752ca2006-09-22 16:55:42 +00001397 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001398
1399 return PJ_SUCCESS;
1400}
1401
1402
1403/*
1404 * Put the specified call on hold.
1405 */
1406PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1407 const pjsua_msg_data *msg_data)
1408{
1409 pjmedia_sdp_session *sdp;
1410 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001411 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412 pjsip_tx_data *tdata;
1413 pj_status_t status;
1414
1415 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1416 PJ_EINVAL);
1417
Benny Prijonodc752ca2006-09-22 16:55:42 +00001418 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001419 if (status != PJ_SUCCESS)
1420 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001421
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001422
1423 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1424 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001425 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001426 return PJSIP_ESESSIONSTATE;
1427 }
1428
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001429 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001430 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001431 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001432 return status;
1433 }
1434
Benny Prijono7129cc72007-11-05 05:54:25 +00001435 update_sdp_version(call, sdp);
1436
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001437 /* Create re-INVITE with new offer */
1438 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1439 if (status != PJ_SUCCESS) {
1440 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001441 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 return status;
1443 }
1444
1445 /* Add additional headers etc */
1446 pjsua_process_msg_data( tdata, msg_data);
1447
1448 /* Send the request */
1449 status = pjsip_inv_send_msg( call->inv, tdata);
1450 if (status != PJ_SUCCESS) {
1451 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001452 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453 return status;
1454 }
1455
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001456 /* Set flag that local put the call on hold */
1457 call->local_hold = PJ_TRUE;
1458
Benny Prijonodc752ca2006-09-22 16:55:42 +00001459 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001460
1461 return PJ_SUCCESS;
1462}
1463
1464
1465/*
1466 * Send re-INVITE (to release hold).
1467 */
1468PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1469 pj_bool_t unhold,
1470 const pjsua_msg_data *msg_data)
1471{
1472 pjmedia_sdp_session *sdp;
1473 pjsip_tx_data *tdata;
1474 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001475 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001476 pj_status_t status;
1477
1478
1479 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1480 PJ_EINVAL);
1481
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001483 if (status != PJ_SUCCESS)
1484 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001485
1486 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1487 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001488 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001489 return PJSIP_ESESSIONSTATE;
1490 }
1491
1492 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001493 if (call->local_hold && !unhold) {
1494 status = create_sdp_of_call_hold(call, &sdp);
1495 } else {
1496 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
1497 NULL, &sdp, NULL);
1498 call->local_hold = PJ_FALSE;
1499 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500 if (status != PJ_SUCCESS) {
1501 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1502 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001503 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504 return status;
1505 }
1506
Benny Prijono7129cc72007-11-05 05:54:25 +00001507 update_sdp_version(call, sdp);
1508
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 /* Create re-INVITE with new offer */
1510 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1511 if (status != PJ_SUCCESS) {
1512 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001513 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001514 return status;
1515 }
1516
1517 /* Add additional headers etc */
1518 pjsua_process_msg_data( tdata, msg_data);
1519
1520 /* Send the request */
1521 status = pjsip_inv_send_msg( call->inv, tdata);
1522 if (status != PJ_SUCCESS) {
1523 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001524 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525 return status;
1526 }
1527
Benny Prijonodc752ca2006-09-22 16:55:42 +00001528 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001529
1530 return PJ_SUCCESS;
1531}
1532
1533
1534/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001535 * Send UPDATE request.
1536 */
1537PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1538 unsigned options,
1539 const pjsua_msg_data *msg_data)
1540{
1541 pjmedia_sdp_session *sdp;
1542 pjsip_tx_data *tdata;
1543 pjsua_call *call;
1544 pjsip_dialog *dlg;
1545 pj_status_t status;
1546
1547 PJ_UNUSED_ARG(options);
1548
1549 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1550 PJ_EINVAL);
1551
1552 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1553 if (status != PJ_SUCCESS)
1554 return status;
1555
Benny Prijonoc08682e2007-10-04 06:17:58 +00001556 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001557 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001558 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001559 if (status != PJ_SUCCESS) {
1560 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1561 status);
1562 pjsip_dlg_dec_lock(dlg);
1563 return status;
1564 }
1565
Benny Prijono224b4e22008-06-19 14:10:28 +00001566 update_sdp_version(call, sdp);
1567
1568 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001569 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1570 if (status != PJ_SUCCESS) {
1571 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1572 pjsip_dlg_dec_lock(dlg);
1573 return status;
1574 }
1575
1576 /* Add additional headers etc */
1577 pjsua_process_msg_data( tdata, msg_data);
1578
1579 /* Send the request */
1580 status = pjsip_inv_send_msg( call->inv, tdata);
1581 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001582 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001583 pjsip_dlg_dec_lock(dlg);
1584 return status;
1585 }
1586
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001587 call->local_hold = PJ_FALSE;
1588
Benny Prijonoc08682e2007-10-04 06:17:58 +00001589 pjsip_dlg_dec_lock(dlg);
1590
1591 return PJ_SUCCESS;
1592}
1593
1594
1595/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596 * Initiate call transfer to the specified address.
1597 */
1598PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1599 const pj_str_t *dest,
1600 const pjsua_msg_data *msg_data)
1601{
1602 pjsip_evsub *sub;
1603 pjsip_tx_data *tdata;
1604 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001605 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001606 pjsip_generic_string_hdr *gs_hdr;
1607 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001608 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609 pj_status_t status;
1610
1611
1612 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1613 PJ_EINVAL);
1614
Benny Prijonodc752ca2006-09-22 16:55:42 +00001615 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001616 if (status != PJ_SUCCESS)
1617 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619
Benny Prijonod524e822006-09-22 12:48:18 +00001620 /* Create xfer client subscription. */
1621 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001622 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001623
1624 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 if (status != PJ_SUCCESS) {
1626 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001627 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001628 return status;
1629 }
1630
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001631 /* Associate this call with the client subscription */
1632 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1633
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001634 /*
1635 * Create REFER request.
1636 */
1637 status = pjsip_xfer_initiate(sub, dest, &tdata);
1638 if (status != PJ_SUCCESS) {
1639 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001640 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641 return status;
1642 }
1643
Benny Prijono053f5222006-11-11 16:16:04 +00001644 /* Add Referred-By header */
1645 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1646 &dlg->local.info_str);
1647 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1648
1649
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001650 /* Add additional headers etc */
1651 pjsua_process_msg_data( tdata, msg_data);
1652
1653 /* Send. */
1654 status = pjsip_xfer_send_request(sub, tdata);
1655 if (status != PJ_SUCCESS) {
1656 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001657 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001658 return status;
1659 }
1660
1661 /* For simplicity (that's what this program is intended to be!),
1662 * leave the original invite session as it is. More advanced application
1663 * may want to hold the INVITE, or terminate the invite, or whatever.
1664 */
1665
Benny Prijonodc752ca2006-09-22 16:55:42 +00001666 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667
1668 return PJ_SUCCESS;
1669
1670}
1671
1672
1673/*
Benny Prijono053f5222006-11-11 16:16:04 +00001674 * Initiate attended call transfer to the specified address.
1675 */
1676PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1677 pjsua_call_id dest_call_id,
1678 unsigned options,
1679 const pjsua_msg_data *msg_data)
1680{
1681 pjsua_call *dest_call;
1682 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001683 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001684 pj_str_t str_dest;
1685 int len;
1686 pjsip_uri *uri;
1687 pj_status_t status;
1688
1689
1690 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1691 PJ_EINVAL);
1692 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1693 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1694 PJ_EINVAL);
1695
1696 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1697 &dest_call, &dest_dlg);
1698 if (status != PJ_SUCCESS)
1699 return status;
1700
1701 /*
1702 * Create REFER destination URI with Replaces field.
1703 */
1704
1705 /* Make sure we have sufficient buffer's length */
1706 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1707 dest_dlg->call_id->id.slen +
1708 dest_dlg->remote.info->tag.slen +
1709 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001710 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001711
1712 /* Print URI */
1713 str_dest_buf[0] = '<';
1714 str_dest.slen = 1;
1715
Benny Prijonoa1e69682007-05-11 15:14:34 +00001716 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001717 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1718 str_dest_buf+1, sizeof(str_dest_buf)-1);
1719 if (len < 0)
1720 return PJSIP_EURITOOLONG;
1721
1722 str_dest.slen += len;
1723
1724
1725 /* Build the URI */
1726 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1727 sizeof(str_dest_buf) - str_dest.slen,
1728 "?%s"
1729 "Replaces=%.*s"
1730 "%%3Bto-tag%%3D%.*s"
1731 "%%3Bfrom-tag%%3D%.*s>",
1732 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1733 "" : "Require=replaces&"),
1734 (int)dest_dlg->call_id->id.slen,
1735 dest_dlg->call_id->id.ptr,
1736 (int)dest_dlg->remote.info->tag.slen,
1737 dest_dlg->remote.info->tag.ptr,
1738 (int)dest_dlg->local.info->tag.slen,
1739 dest_dlg->local.info->tag.ptr);
1740
1741 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1742 PJSIP_EURITOOLONG);
1743
1744 str_dest.ptr = str_dest_buf;
1745 str_dest.slen += len;
1746
1747 pjsip_dlg_dec_lock(dest_dlg);
1748
1749 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1750}
1751
1752
1753/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001754 * Send DTMF digits to remote using RFC 2833 payload formats.
1755 */
1756PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1757 const pj_str_t *digits)
1758{
1759 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001760 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761 pj_status_t status;
1762
1763 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1764 PJ_EINVAL);
1765
Benny Prijonodc752ca2006-09-22 16:55:42 +00001766 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001767 if (status != PJ_SUCCESS)
1768 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001769
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001770 if (!call->session) {
1771 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001772 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001773 return PJ_EINVALIDOP;
1774 }
1775
1776 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1777
Benny Prijonodc752ca2006-09-22 16:55:42 +00001778 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779
1780 return status;
1781}
1782
1783
1784/**
1785 * Send instant messaging inside INVITE session.
1786 */
1787PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1788 const pj_str_t *mime_type,
1789 const pj_str_t *content,
1790 const pjsua_msg_data *msg_data,
1791 void *user_data)
1792{
1793 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001794 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001795 const pj_str_t mime_text_plain = pj_str("text/plain");
1796 pjsip_media_type ctype;
1797 pjsua_im_data *im_data;
1798 pjsip_tx_data *tdata;
1799 pj_status_t status;
1800
1801
1802 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1803 PJ_EINVAL);
1804
Benny Prijonodc752ca2006-09-22 16:55:42 +00001805 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001806 if (status != PJ_SUCCESS)
1807 return status;
1808
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001809 /* Set default media type if none is specified */
1810 if (mime_type == NULL) {
1811 mime_type = &mime_text_plain;
1812 }
1813
1814 /* Create request message. */
1815 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1816 -1, &tdata);
1817 if (status != PJ_SUCCESS) {
1818 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1819 goto on_return;
1820 }
1821
1822 /* Add accept header. */
1823 pjsip_msg_add_hdr( tdata->msg,
1824 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1825
1826 /* Parse MIME type */
1827 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1828
1829 /* Create "text/plain" message body. */
1830 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1831 &ctype.subtype, content);
1832 if (tdata->msg->body == NULL) {
1833 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1834 pjsip_tx_data_dec_ref(tdata);
1835 goto on_return;
1836 }
1837
1838 /* Add additional headers etc */
1839 pjsua_process_msg_data( tdata, msg_data);
1840
1841 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001842 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001843 im_data->acc_id = call->acc_id;
1844 im_data->call_id = call_id;
1845 im_data->to = call->inv->dlg->remote.info_str;
1846 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1847 im_data->user_data = user_data;
1848
1849
1850 /* Send the request. */
1851 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1852 pjsua_var.mod.id, im_data);
1853 if (status != PJ_SUCCESS) {
1854 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1855 goto on_return;
1856 }
1857
1858on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001859 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 return status;
1861}
1862
1863
1864/*
1865 * Send IM typing indication inside INVITE session.
1866 */
1867PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1868 pj_bool_t is_typing,
1869 const pjsua_msg_data*msg_data)
1870{
1871 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001872 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001873 pjsip_tx_data *tdata;
1874 pj_status_t status;
1875
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001876 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1877 PJ_EINVAL);
1878
Benny Prijonodc752ca2006-09-22 16:55:42 +00001879 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001880 if (status != PJ_SUCCESS)
1881 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001883 /* Create request message. */
1884 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1885 -1, &tdata);
1886 if (status != PJ_SUCCESS) {
1887 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1888 goto on_return;
1889 }
1890
1891 /* Create "application/im-iscomposing+xml" msg body. */
1892 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1893 NULL, NULL, -1);
1894
1895 /* Add additional headers etc */
1896 pjsua_process_msg_data( tdata, msg_data);
1897
1898 /* Send the request. */
1899 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1900 if (status != PJ_SUCCESS) {
1901 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1902 goto on_return;
1903 }
1904
1905on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001906 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001907 return status;
1908}
1909
1910
1911/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001912 * Send arbitrary request.
1913 */
1914PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1915 const pj_str_t *method_str,
1916 const pjsua_msg_data *msg_data)
1917{
1918 pjsua_call *call;
1919 pjsip_dialog *dlg;
1920 pjsip_method method;
1921 pjsip_tx_data *tdata;
1922 pj_status_t status;
1923
1924 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1925 PJ_EINVAL);
1926
1927 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1928 if (status != PJ_SUCCESS)
1929 return status;
1930
1931 /* Init method */
1932 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1933
1934 /* Create request message. */
1935 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1936 if (status != PJ_SUCCESS) {
1937 pjsua_perror(THIS_FILE, "Unable to create request", status);
1938 goto on_return;
1939 }
1940
1941 /* Add additional headers etc */
1942 pjsua_process_msg_data( tdata, msg_data);
1943
1944 /* Send the request. */
1945 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1946 if (status != PJ_SUCCESS) {
1947 pjsua_perror(THIS_FILE, "Unable to send request", status);
1948 goto on_return;
1949 }
1950
1951on_return:
1952 pjsip_dlg_dec_lock(dlg);
1953 return status;
1954}
1955
1956
1957/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001958 * Terminate all calls.
1959 */
1960PJ_DEF(void) pjsua_call_hangup_all(void)
1961{
1962 unsigned i;
1963
1964 PJSUA_LOCK();
1965
1966 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1967 if (pjsua_var.calls[i].inv)
1968 pjsua_call_hangup(i, 0, NULL, NULL);
1969 }
1970
1971 PJSUA_UNLOCK();
1972}
1973
1974
Benny Prijono627cbb42007-09-25 20:48:49 +00001975const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001976{
1977 if (val < 1000) {
1978 pj_ansi_sprintf(buf, "%d", val);
1979 } else if (val < 1000000) {
1980 pj_ansi_sprintf(buf, "%d.%dK",
1981 val / 1000,
1982 (val % 1000) / 100);
1983 } else {
1984 pj_ansi_sprintf(buf, "%d.%02dM",
1985 val / 1000000,
1986 (val % 1000000) / 10000);
1987 }
1988
1989 return buf;
1990}
1991
1992
1993/* Dump media session */
1994static void dump_media_session(const char *indent,
1995 char *buf, unsigned maxlen,
1996 pjmedia_session *session)
1997{
1998 unsigned i;
1999 char *p = buf, *end = buf+maxlen;
2000 int len;
2001 pjmedia_session_info info;
2002
2003 pjmedia_session_get_info(session, &info);
2004
2005 for (i=0; i<info.stream_cnt; ++i) {
2006 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002007 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002008 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002010 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002011 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002012 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002013
2014 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00002015 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2016 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002017
2018 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2019 dir = "sendonly";
2020 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2021 dir = "recvonly";
2022 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2023 dir = "sendrecv";
2024 else
2025 dir = "inactive";
2026
2027
2028 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002029 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002030 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002031 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002032 info.stream_info[i].fmt.encoding_name.ptr,
2033 info.stream_info[i].fmt.clock_rate / 1000,
2034 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002035 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002036 if (len < 1 || len > end-p) {
2037 *p = '\0';
2038 return;
2039 }
2040
2041 p += len;
2042 *p++ = '\n';
2043 *p = '\0';
2044
2045 if (stat.rx.update_cnt == 0)
2046 strcpy(last_update, "never");
2047 else {
2048 pj_gettimeofday(&now);
2049 PJ_TIME_VAL_SUB(now, stat.rx.update);
2050 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2051 now.sec / 3600,
2052 (now.sec % 3600) / 60,
2053 now.sec % 60,
2054 now.msec);
2055 }
2056
Benny Prijono80019eb2006-08-07 13:22:23 +00002057 pj_gettimeofday(&media_duration);
2058 PJ_TIME_VAL_SUB(media_duration, stat.start);
2059 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2060 media_duration.msec = 1;
2061
Benny Prijono1402a4a2008-01-08 23:41:22 +00002062 /* protect against division by zero */
2063 if (stat.rx.pkt == 0)
2064 stat.rx.pkt = 1;
2065 if (stat.tx.pkt == 0)
2066 stat.tx.pkt = 1;
2067
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068 len = pj_ansi_snprintf(p, end-p,
2069 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002070 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002072 "%s (msec) min avg max last dev\n"
2073 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2074 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002075 indent, info.stream_info[i].fmt.pt,
2076 last_update,
2077 indent,
2078 good_number(packets, stat.rx.pkt),
2079 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002080 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002081 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2082 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 +00002083 indent,
2084 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002085 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002086 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002087 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002088 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002089 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002090 indent, indent,
2091 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002092 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002093 stat.rx.loss_period.max / 1000.0,
2094 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002095 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 indent,
2097 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002098 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002099 stat.rx.jitter.max / 1000.0,
2100 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002101 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002102 ""
2103 );
2104
2105 if (len < 1 || len > end-p) {
2106 *p = '\0';
2107 return;
2108 }
2109
2110 p += len;
2111 *p++ = '\n';
2112 *p = '\0';
2113
2114 if (stat.tx.update_cnt == 0)
2115 strcpy(last_update, "never");
2116 else {
2117 pj_gettimeofday(&now);
2118 PJ_TIME_VAL_SUB(now, stat.tx.update);
2119 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2120 now.sec / 3600,
2121 (now.sec % 3600) / 60,
2122 now.sec % 60,
2123 now.msec);
2124 }
2125
2126 len = pj_ansi_snprintf(p, end-p,
2127 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002128 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002129 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002130 "%s (msec) min avg max last dev \n"
2131 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2132 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002133 indent,
2134 info.stream_info[i].tx_pt,
2135 info.stream_info[i].param->info.frm_ptime *
2136 info.stream_info[i].param->setting.frm_per_pkt,
2137 last_update,
2138
2139 indent,
2140 good_number(packets, stat.tx.pkt),
2141 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002142 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002143 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2144 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 +00002145
2146 indent,
2147 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002148 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002149 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002150 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002151 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002152 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002153
2154 indent, indent,
2155 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002156 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002157 stat.tx.loss_period.max / 1000.0,
2158 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002159 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002160 indent,
2161 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002162 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163 stat.tx.jitter.max / 1000.0,
2164 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002165 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002166 ""
2167 );
2168
2169 if (len < 1 || len > end-p) {
2170 *p = '\0';
2171 return;
2172 }
2173
2174 p += len;
2175 *p++ = '\n';
2176 *p = '\0';
2177
2178 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002179 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002180 indent,
2181 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002182 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002184 stat.rtt.last / 1000.0,
2185 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002186 );
2187 if (len < 1 || len > end-p) {
2188 *p = '\0';
2189 return;
2190 }
2191
2192 p += len;
2193 *p++ = '\n';
2194 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002195
2196#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2197# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2198 do { \
2199 if (samples <= 4294) \
2200 usec = samples * 1000000 / clock_rate; \
2201 else { \
2202 usec = samples * 1000 / clock_rate; \
2203 usec *= 1000; \
2204 } \
2205 } while(0)
2206
2207# define PRINT_VOIP_MTC_VAL(s, v) \
2208 if (v == 127) \
2209 sprintf(s, "(na)"); \
2210 else \
2211 sprintf(s, "%d", v)
2212
2213# define VALIDATE_PRINT_BUF() \
2214 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2215 p += len; *p++ = '\n'; *p = '\0'
2216
2217
2218 do {
2219 char loss[16], dup[16];
2220 char jitter[80];
2221 char toh[80];
2222 char plc[16], jba[16], jbr[16];
2223 char signal_lvl[16], noise_lvl[16], rerl[16];
2224 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2225 pjmedia_rtcp_xr_stat xr_stat;
2226 unsigned clock_rate;
2227
2228 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2229 PJ_SUCCESS)
2230 {
2231 break;
2232 }
2233
2234 clock_rate = info.stream_info[i].fmt.clock_rate;
2235
2236 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2237 VALIDATE_PRINT_BUF();
2238
2239 /* Statistics Summary */
2240 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2241 VALIDATE_PRINT_BUF();
2242
2243 if (xr_stat.rx.stat_sum.l)
2244 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2245 else
2246 sprintf(loss, "(na)");
2247
2248 if (xr_stat.rx.stat_sum.d)
2249 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2250 else
2251 sprintf(dup, "(na)");
2252
2253 if (xr_stat.rx.stat_sum.j) {
2254 unsigned jmin, jmax, jmean, jdev;
2255
2256 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2257 clock_rate);
2258 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2259 clock_rate);
2260 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2261 clock_rate);
2262 SAMPLES_TO_USEC(jdev,
2263 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2264 clock_rate);
2265 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2266 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2267 } else
2268 sprintf(jitter, "(report not available)");
2269
2270 if (xr_stat.rx.stat_sum.t) {
2271 sprintf(toh, "%11d %11d %11d %11d",
2272 xr_stat.rx.stat_sum.toh.min,
2273 xr_stat.rx.stat_sum.toh.mean,
2274 xr_stat.rx.stat_sum.toh.max,
2275 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2276 } else
2277 sprintf(toh, "(report not available)");
2278
2279 if (xr_stat.rx.stat_sum.update.sec == 0)
2280 strcpy(last_update, "never");
2281 else {
2282 pj_gettimeofday(&now);
2283 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2284 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2285 now.sec / 3600,
2286 (now.sec % 3600) / 60,
2287 now.sec % 60,
2288 now.msec);
2289 }
2290
2291 len = pj_ansi_snprintf(p, end-p,
2292 "%s RX last update: %s\n"
2293 "%s begin seq=%d, end seq=%d\n"
2294 "%s pkt loss=%s, dup=%s\n"
2295 "%s (msec) min avg max dev\n"
2296 "%s jitter : %s\n"
2297 "%s toh : %s",
2298 indent, last_update,
2299 indent,
2300 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2301 indent, loss, dup,
2302 indent,
2303 indent, jitter,
2304 indent, toh
2305 );
2306 VALIDATE_PRINT_BUF();
2307
2308 if (xr_stat.tx.stat_sum.l)
2309 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2310 else
2311 sprintf(loss, "(na)");
2312
2313 if (xr_stat.tx.stat_sum.d)
2314 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2315 else
2316 sprintf(dup, "(na)");
2317
2318 if (xr_stat.tx.stat_sum.j) {
2319 unsigned jmin, jmax, jmean, jdev;
2320
2321 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2322 clock_rate);
2323 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2324 clock_rate);
2325 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2326 clock_rate);
2327 SAMPLES_TO_USEC(jdev,
2328 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2329 clock_rate);
2330 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2331 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2332 } else
2333 sprintf(jitter, "(report not available)");
2334
2335 if (xr_stat.tx.stat_sum.t) {
2336 sprintf(toh, "%11d %11d %11d %11d",
2337 xr_stat.tx.stat_sum.toh.min,
2338 xr_stat.tx.stat_sum.toh.mean,
2339 xr_stat.tx.stat_sum.toh.max,
2340 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2341 } else
2342 sprintf(toh, "(report not available)");
2343
2344 if (xr_stat.tx.stat_sum.update.sec == 0)
2345 strcpy(last_update, "never");
2346 else {
2347 pj_gettimeofday(&now);
2348 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2349 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2350 now.sec / 3600,
2351 (now.sec % 3600) / 60,
2352 now.sec % 60,
2353 now.msec);
2354 }
2355
2356 len = pj_ansi_snprintf(p, end-p,
2357 "%s TX last update: %s\n"
2358 "%s begin seq=%d, end seq=%d\n"
2359 "%s pkt loss=%s, dup=%s\n"
2360 "%s (msec) min avg max dev\n"
2361 "%s jitter : %s\n"
2362 "%s toh : %s",
2363 indent, last_update,
2364 indent,
2365 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2366 indent, loss, dup,
2367 indent,
2368 indent, jitter,
2369 indent, toh
2370 );
2371 VALIDATE_PRINT_BUF();
2372
2373
2374 /* VoIP Metrics */
2375 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2376 VALIDATE_PRINT_BUF();
2377
2378 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2379 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2380 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2381 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2382 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2383 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2384 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2385
2386 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2387 case PJMEDIA_RTCP_XR_PLC_DIS:
2388 sprintf(plc, "DISABLED");
2389 break;
2390 case PJMEDIA_RTCP_XR_PLC_ENH:
2391 sprintf(plc, "ENHANCED");
2392 break;
2393 case PJMEDIA_RTCP_XR_PLC_STD:
2394 sprintf(plc, "STANDARD");
2395 break;
2396 case PJMEDIA_RTCP_XR_PLC_UNK:
2397 default:
2398 sprintf(plc, "UNKNOWN");
2399 break;
2400 }
2401
2402 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2403 case PJMEDIA_RTCP_XR_JB_FIXED:
2404 sprintf(jba, "FIXED");
2405 break;
2406 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2407 sprintf(jba, "ADAPTIVE");
2408 break;
2409 default:
2410 sprintf(jba, "UNKNOWN");
2411 break;
2412 }
2413
2414 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2415
2416 if (xr_stat.rx.voip_mtc.update.sec == 0)
2417 strcpy(last_update, "never");
2418 else {
2419 pj_gettimeofday(&now);
2420 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2421 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2422 now.sec / 3600,
2423 (now.sec % 3600) / 60,
2424 now.sec % 60,
2425 now.msec);
2426 }
2427
2428 len = pj_ansi_snprintf(p, end-p,
2429 "%s RX last update: %s\n"
2430 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2431 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2432 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2433 "%s delay : round trip=%d%s, end system=%d%s\n"
2434 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2435 "%s quality : R factor=%s, ext R factor=%s\n"
2436 "%s MOS LQ=%s, MOS CQ=%s\n"
2437 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2438 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2439 indent,
2440 last_update,
2441 /* packets */
2442 indent,
2443 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2444 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2445 /* burst */
2446 indent,
2447 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2448 xr_stat.rx.voip_mtc.burst_dur, "ms",
2449 /* gap */
2450 indent,
2451 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2452 xr_stat.rx.voip_mtc.gap_dur, "ms",
2453 /* delay */
2454 indent,
2455 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2456 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2457 /* level */
2458 indent,
2459 signal_lvl, "dB",
2460 noise_lvl, "dB",
2461 rerl, "",
2462 /* quality */
2463 indent,
2464 r_factor, ext_r_factor,
2465 indent,
2466 mos_lq, mos_cq,
2467 /* config */
2468 indent,
2469 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2470 /* JB delay */
2471 indent,
2472 xr_stat.rx.voip_mtc.jb_nom, "ms",
2473 xr_stat.rx.voip_mtc.jb_max, "ms",
2474 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2475 );
2476 VALIDATE_PRINT_BUF();
2477
2478 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2479 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2480 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2481 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2482 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2483 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2484 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2485
2486 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2487 case PJMEDIA_RTCP_XR_PLC_DIS:
2488 sprintf(plc, "DISABLED");
2489 break;
2490 case PJMEDIA_RTCP_XR_PLC_ENH:
2491 sprintf(plc, "ENHANCED");
2492 break;
2493 case PJMEDIA_RTCP_XR_PLC_STD:
2494 sprintf(plc, "STANDARD");
2495 break;
2496 case PJMEDIA_RTCP_XR_PLC_UNK:
2497 default:
2498 sprintf(plc, "unknown");
2499 break;
2500 }
2501
2502 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2503 case PJMEDIA_RTCP_XR_JB_FIXED:
2504 sprintf(jba, "FIXED");
2505 break;
2506 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2507 sprintf(jba, "ADAPTIVE");
2508 break;
2509 default:
2510 sprintf(jba, "unknown");
2511 break;
2512 }
2513
2514 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2515
2516 if (xr_stat.tx.voip_mtc.update.sec == 0)
2517 strcpy(last_update, "never");
2518 else {
2519 pj_gettimeofday(&now);
2520 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2521 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2522 now.sec / 3600,
2523 (now.sec % 3600) / 60,
2524 now.sec % 60,
2525 now.msec);
2526 }
2527
2528 len = pj_ansi_snprintf(p, end-p,
2529 "%s TX last update: %s\n"
2530 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2531 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2532 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2533 "%s delay : round trip=%d%s, end system=%d%s\n"
2534 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2535 "%s quality : R factor=%s, ext R factor=%s\n"
2536 "%s MOS LQ=%s, MOS CQ=%s\n"
2537 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2538 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2539 indent,
2540 last_update,
2541 /* pakcets */
2542 indent,
2543 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2544 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2545 /* burst */
2546 indent,
2547 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2548 xr_stat.tx.voip_mtc.burst_dur, "ms",
2549 /* gap */
2550 indent,
2551 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2552 xr_stat.tx.voip_mtc.gap_dur, "ms",
2553 /* delay */
2554 indent,
2555 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2556 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2557 /* level */
2558 indent,
2559 signal_lvl, "dB",
2560 noise_lvl, "dB",
2561 rerl, "",
2562 /* quality */
2563 indent,
2564 r_factor, ext_r_factor,
2565 indent,
2566 mos_lq, mos_cq,
2567 /* config */
2568 indent,
2569 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2570 /* JB delay */
2571 indent,
2572 xr_stat.tx.voip_mtc.jb_nom, "ms",
2573 xr_stat.tx.voip_mtc.jb_max, "ms",
2574 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2575 );
2576 VALIDATE_PRINT_BUF();
2577
2578
2579 /* RTT delay (by receiver side) */
2580 len = pj_ansi_snprintf(p, end-p,
2581 "%s RTT (from recv) min avg max last dev",
2582 indent);
2583 VALIDATE_PRINT_BUF();
2584 len = pj_ansi_snprintf(p, end-p,
2585 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2586 indent,
2587 xr_stat.rtt.min / 1000.0,
2588 xr_stat.rtt.mean / 1000.0,
2589 xr_stat.rtt.max / 1000.0,
2590 xr_stat.rtt.last / 1000.0,
2591 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2592 );
2593 VALIDATE_PRINT_BUF();
2594 } while(0);
2595#endif
2596
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002597 }
2598}
2599
2600
2601/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002602void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002603 int call_id,
2604 char *buf, pj_size_t size)
2605{
2606 int len;
2607 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2608 pjsip_dialog *dlg = inv->dlg;
2609 char userinfo[128];
2610
2611 /* Dump invite sesion info. */
2612
2613 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2614 if (len < 1)
2615 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2616 else
2617 userinfo[len] = '\0';
2618
2619 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2620 title,
2621 pjsip_inv_state_name(inv->state),
2622 userinfo);
2623 if (len < 1 || len >= (int)size) {
2624 pj_ansi_strcpy(buf, "<--uri too long-->");
2625 len = 18;
2626 } else
2627 buf[len] = '\0';
2628}
2629
2630
2631/*
2632 * Dump call and media statistics to string.
2633 */
2634PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2635 pj_bool_t with_media,
2636 char *buffer,
2637 unsigned maxlen,
2638 const char *indent)
2639{
2640 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002641 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002642 pj_time_val duration, res_delay, con_delay;
2643 char tmp[128];
2644 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002645 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002646 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002647 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002648
2649 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2650 PJ_EINVAL);
2651
Benny Prijonodc752ca2006-09-22 16:55:42 +00002652 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002653 if (status != PJ_SUCCESS)
2654 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002655
2656 *buffer = '\0';
2657 p = buffer;
2658 end = buffer + maxlen;
2659 len = 0;
2660
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002661 print_call(indent, call_id, tmp, sizeof(tmp));
2662
2663 len = pj_ansi_strlen(tmp);
2664 pj_ansi_strcpy(buffer, tmp);
2665
2666 p += len;
2667 *p++ = '\r';
2668 *p++ = '\n';
2669
2670 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002671 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002672 pj_gettimeofday(&duration);
2673 PJ_TIME_VAL_SUB(duration, call->conn_time);
2674 con_delay = call->conn_time;
2675 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2676 } else {
2677 duration.sec = duration.msec = 0;
2678 con_delay.sec = con_delay.msec = 0;
2679 }
2680
2681 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002682 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002683 res_delay = call->res_time;
2684 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2685 } else {
2686 res_delay.sec = res_delay.msec = 0;
2687 }
2688
2689 /* Print duration */
2690 len = pj_ansi_snprintf(p, end-p,
2691 "%s Call time: %02dh:%02dm:%02ds, "
2692 "1st res in %d ms, conn in %dms",
2693 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002694 (int)(duration.sec / 3600),
2695 (int)((duration.sec % 3600)/60),
2696 (int)(duration.sec % 60),
2697 (int)PJ_TIME_VAL_MSEC(res_delay),
2698 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002699
2700 if (len > 0 && len < end-p) {
2701 p += len;
2702 *p++ = '\n';
2703 *p = '\0';
2704 }
2705
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002706 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002707 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002708 pjmedia_transport_get_info(call->med_tp, &tp_info);
2709 if (tp_info.specific_info_cnt > 0) {
2710 int i;
2711 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2712 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2713 {
2714 pjmedia_srtp_info *srtp_info =
2715 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2716
2717 len = pj_ansi_snprintf(p, end-p,
2718 "%s SRTP status: %s Crypto-suite: %s",
2719 indent,
2720 (srtp_info->active?"Active":"Not active"),
2721 srtp_info->tx_policy.name.ptr);
2722 if (len > 0 && len < end-p) {
2723 p += len;
2724 *p++ = '\n';
2725 *p = '\0';
2726 }
2727 break;
2728 }
2729 }
2730 }
2731
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002732 /* Dump session statistics */
2733 if (with_media && call->session)
2734 dump_media_session(indent, p, end-p, call->session);
2735
Benny Prijonodc752ca2006-09-22 16:55:42 +00002736 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002737
2738 return PJ_SUCCESS;
2739}
2740
2741
2742/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002743 * This callback receives notification from invite session when the
2744 * session state has changed.
2745 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002746static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2747 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002748{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002749 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002750
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002751 PJSUA_LOCK();
2752
Benny Prijonoa1e69682007-05-11 15:14:34 +00002753 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002754
2755 if (!call) {
2756 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002757 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002758 }
2759
Benny Prijonoe21e7842006-04-09 16:46:05 +00002760
2761 /* Get call times */
2762 switch (inv->state) {
2763 case PJSIP_INV_STATE_EARLY:
2764 case PJSIP_INV_STATE_CONNECTING:
2765 if (call->res_time.sec == 0)
2766 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002767 call->last_code = (pjsip_status_code)
2768 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002769 pj_strncpy(&call->last_text,
2770 &e->body.tsx_state.tsx->status_text,
2771 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002772 break;
2773 case PJSIP_INV_STATE_CONFIRMED:
2774 pj_gettimeofday(&call->conn_time);
2775 break;
2776 case PJSIP_INV_STATE_DISCONNECTED:
2777 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002778 if (call->res_time.sec == 0)
2779 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002780 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002781 call->last_code = (pjsip_status_code)
2782 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002783 pj_strncpy(&call->last_text,
2784 &e->body.tsx_state.tsx->status_text,
2785 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002786 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002787 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002788 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002789 call->last_code = (pjsip_status_code)
2790 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002791 pj_strncpy(&call->last_text,
2792 &e->body.tsx_state.tsx->status_text,
2793 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002794 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002795 }
2796
Benny Prijono26ff9062006-02-21 23:47:00 +00002797 /* If this is an outgoing INVITE that was created because of
2798 * REFER/transfer, send NOTIFY to transferer.
2799 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002800 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002801 int st_code = -1;
2802 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2803
2804
Benny Prijonoa91a0032006-02-26 21:23:45 +00002805 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002806 case PJSIP_INV_STATE_NULL:
2807 case PJSIP_INV_STATE_CALLING:
2808 /* Do nothing */
2809 break;
2810
2811 case PJSIP_INV_STATE_EARLY:
2812 case PJSIP_INV_STATE_CONNECTING:
2813 st_code = e->body.tsx_state.tsx->status_code;
2814 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2815 break;
2816
2817 case PJSIP_INV_STATE_CONFIRMED:
2818 /* When state is confirmed, send the final 200/OK and terminate
2819 * subscription.
2820 */
2821 st_code = e->body.tsx_state.tsx->status_code;
2822 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2823 break;
2824
2825 case PJSIP_INV_STATE_DISCONNECTED:
2826 st_code = e->body.tsx_state.tsx->status_code;
2827 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2828 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002829
Benny Prijono8b1889b2006-06-06 18:40:40 +00002830 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002831 /* Nothing to do. Just to keep gcc from complaining about
2832 * unused enums.
2833 */
2834 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002835 }
2836
2837 if (st_code != -1) {
2838 pjsip_tx_data *tdata;
2839 pj_status_t status;
2840
Benny Prijonoa91a0032006-02-26 21:23:45 +00002841 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002842 ev_state, st_code,
2843 NULL, &tdata);
2844 if (status != PJ_SUCCESS) {
2845 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2846 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002847 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002848 if (status != PJ_SUCCESS) {
2849 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2850 }
2851 }
2852 }
2853 }
2854
Benny Prijono84126ab2006-02-09 09:30:09 +00002855
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002856 if (pjsua_var.ua_cfg.cb.on_call_state)
2857 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002858
2859 /* call->inv may be NULL now */
2860
Benny Prijono84126ab2006-02-09 09:30:09 +00002861 /* Destroy media session when invite session is disconnected. */
2862 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002863
Benny Prijonoa91a0032006-02-26 21:23:45 +00002864 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002865
Benny Prijono275fd682006-03-22 11:59:11 +00002866 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002867 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002868
Benny Prijono105217f2006-03-06 16:25:59 +00002869 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002870 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002871 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002872
2873 /* Reset call */
2874 reset_call(call->index);
2875
Benny Prijono84126ab2006-02-09 09:30:09 +00002876 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002877
2878 PJSUA_UNLOCK();
2879}
2880
2881/*
2882 * This callback is called by invite session framework when UAC session
2883 * has forked.
2884 */
2885static void pjsua_call_on_forked( pjsip_inv_session *inv,
2886 pjsip_event *e)
2887{
2888 PJ_UNUSED_ARG(inv);
2889 PJ_UNUSED_ARG(e);
2890
2891 PJ_TODO(HANDLE_FORKED_DIALOG);
2892}
2893
2894
2895/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002896 * Disconnect call upon error.
2897 */
2898static void call_disconnect( pjsip_inv_session *inv,
2899 int code )
2900{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002901 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002902 pjsip_tx_data *tdata;
2903 pj_status_t status;
2904
Benny Prijono59b3aed2008-01-15 16:54:54 +00002905 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2906
Benny Prijonoa38ada02006-07-02 14:22:35 +00002907 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002908 if (status != PJ_SUCCESS)
2909 return;
2910
2911 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00002912 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
2913 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2914 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002915 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002916 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002917
Benny Prijono734fc2d2008-03-17 16:05:35 +00002918 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002919 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002920 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002921 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002922 if (status == PJ_SUCCESS) {
2923 pjsip_create_sdp_body(tdata->pool, local_sdp,
2924 &tdata->msg->body);
2925 }
2926 }
2927
2928 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002929}
2930
2931/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002932 * Callback to be called when SDP offer/answer negotiation has just completed
2933 * in the session. This function will start/update media if negotiation
2934 * has succeeded.
2935 */
2936static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2937 pj_status_t status)
2938{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002939 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002940 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002941 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002942
2943 PJSUA_LOCK();
2944
Benny Prijonoa1e69682007-05-11 15:14:34 +00002945 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002946
2947 if (status != PJ_SUCCESS) {
2948
2949 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2950
Benny Prijono2331d202008-06-26 15:46:52 +00002951 /* Do not deinitialize media since this may be a re-INVITE or
2952 * UPDATE (which in this case the media should not get affected
2953 * by the failed re-INVITE/UPDATE). The media will be shutdown
2954 * when call is disconnected anyway.
2955 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002956 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00002957 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002958
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002959 /* Disconnect call if we're not in the middle of initializing an
2960 * UAS dialog and if this is not a re-INVITE
2961 */
2962 if (inv->state != PJSIP_INV_STATE_NULL &&
2963 inv->state != PJSIP_INV_STATE_CONFIRMED)
2964 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002965 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002966 }
2967
2968 PJSUA_UNLOCK();
2969 return;
2970 }
2971
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002972
2973 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00002974 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002975 if (status != PJ_SUCCESS) {
2976 pjsua_perror(THIS_FILE,
2977 "Unable to retrieve currently active local SDP",
2978 status);
2979 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2980 PJSUA_UNLOCK();
2981 return;
2982 }
2983
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002984 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2985 if (status != PJ_SUCCESS) {
2986 pjsua_perror(THIS_FILE,
2987 "Unable to retrieve currently active remote SDP",
2988 status);
2989 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2990 PJSUA_UNLOCK();
2991 return;
2992 }
2993
Benny Prijono91a6a172007-10-31 08:59:29 +00002994 /* Update remote's NAT type */
2995 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2996 update_remote_nat_type(call, remote_sdp);
2997 }
2998
2999 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003000 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003001 if (status != PJ_SUCCESS) {
3002 pjsua_perror(THIS_FILE, "Unable to create media session",
3003 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003004 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003005 /* No need to deinitialize; media will be shutdown when call
3006 * state is disconnected anyway.
3007 */
3008 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003009 PJSUA_UNLOCK();
3010 return;
3011 }
3012
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003013
3014 /* Call application callback, if any */
3015 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3016 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3017
3018
3019 PJSUA_UNLOCK();
3020}
3021
3022
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003023/* Create SDP for call hold. */
3024static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3025 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003026{
3027 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003028 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003029 pjmedia_sdp_session *sdp;
3030
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003031 /* Use call's pool */
3032 pool = call->inv->pool;
3033
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003034 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003035 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3036 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003037 if (status != PJ_SUCCESS) {
3038 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3039 return status;
3040 }
3041
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003042 /* Call-hold is done by set the media direction to 'sendonly'
3043 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3044 * 'inactive' (PJMEDIA_DIR_NONE).
3045 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3046 */
3047 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3048 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003049
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003050 /* Remove existing directions attributes */
3051 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3052 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3053 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3054 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003055
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003056 if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
3057 /* Add sendonly attribute */
3058 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3059 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3060 } else {
3061 /* Add inactive attribute */
3062 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3063 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3064 }
3065 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003066
3067 *p_answer = sdp;
3068
3069 return status;
3070}
3071
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003072/*
3073 * Called when session received new offer.
3074 */
3075static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3076 const pjmedia_sdp_session *offer)
3077{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003078 pjsua_call *call;
3079 pjmedia_sdp_conn *conn;
3080 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003081 pj_status_t status;
3082
3083 PJSUA_LOCK();
3084
Benny Prijonoa1e69682007-05-11 15:14:34 +00003085 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003086
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003087 conn = offer->media[0]->conn;
3088 if (!conn)
3089 conn = offer->conn;
3090
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003091 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003092 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3093 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003094
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003095 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
3096 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003097 if (status != PJ_SUCCESS) {
3098 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3099 PJSUA_UNLOCK();
3100 return;
3101 }
3102
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003103 /* Check if offer's conn address is zero */
3104 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3105 pj_strcmp2(&conn->addr, "0")==0)
3106 {
3107 /* Modify address */
3108 answer->conn->addr = pj_str("0.0.0.0");
3109 }
3110
3111 /* Check if call is on-hold */
3112 if (call->local_hold) {
3113 pjmedia_sdp_attr *attr;
3114
3115 /* Remove existing directions attributes */
3116 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3117 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3118 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3119 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3120
3121 /* Keep call on-hold by setting 'sendonly' attribute.
3122 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3123 */
3124 attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
3125 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3126 }
3127
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003128 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3129 if (status != PJ_SUCCESS) {
3130 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3131 PJSUA_UNLOCK();
3132 return;
3133 }
3134
3135 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003136}
3137
3138
3139/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003140 * Called to generate new offer.
3141 */
3142static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3143 pjmedia_sdp_session **offer)
3144{
3145 pjsua_call *call;
3146 pj_status_t status;
3147
3148 PJSUA_LOCK();
3149
3150 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3151
3152 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003153 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003154 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003155 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003156 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003157 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003158 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003159 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3160 call->index));
3161
Benny Prijonod8179652008-01-23 20:39:07 +00003162 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003163 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003164 }
3165
3166 if (status != PJ_SUCCESS) {
3167 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3168 PJSUA_UNLOCK();
3169 return;
3170 }
3171
Benny Prijono7129cc72007-11-05 05:54:25 +00003172 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00003173
3174 PJSUA_UNLOCK();
3175}
3176
3177
3178/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003179 * Callback called by event framework when the xfer subscription state
3180 * has changed.
3181 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003182static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3183{
3184
3185 PJ_UNUSED_ARG(event);
3186
3187 /*
3188 * When subscription is accepted (got 200/OK to REFER), check if
3189 * subscription suppressed.
3190 */
3191 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3192
3193 pjsip_rx_data *rdata;
3194 pjsip_generic_string_hdr *refer_sub;
3195 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3196 pjsua_call *call;
3197
Benny Prijonoa1e69682007-05-11 15:14:34 +00003198 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003199
3200 /* Must be receipt of response message */
3201 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3202 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3203 rdata = event->body.tsx_state.src.rdata;
3204
3205 /* Find Refer-Sub header */
3206 refer_sub = (pjsip_generic_string_hdr*)
3207 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3208 &REFER_SUB, NULL);
3209
3210 /* Check if subscription is suppressed */
3211 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3212 /* Since no subscription is desired, assume that call has been
3213 * transfered successfully.
3214 */
3215 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3216 const pj_str_t ACCEPTED = { "Accepted", 8 };
3217 pj_bool_t cont = PJ_FALSE;
3218 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3219 200,
3220 &ACCEPTED,
3221 PJ_TRUE,
3222 &cont);
3223 }
3224
3225 /* Yes, subscription is suppressed.
3226 * Terminate our subscription now.
3227 */
3228 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3229 "event subcription..."));
3230 pjsip_evsub_terminate(sub, PJ_TRUE);
3231
3232 } else {
3233 /* Notify application about call transfer progress.
3234 * Initially notify with 100/Accepted status.
3235 */
3236 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3237 const pj_str_t ACCEPTED = { "Accepted", 8 };
3238 pj_bool_t cont = PJ_FALSE;
3239 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3240 100,
3241 &ACCEPTED,
3242 PJ_FALSE,
3243 &cont);
3244 }
3245 }
3246 }
3247 /*
3248 * On incoming NOTIFY, notify application about call transfer progress.
3249 */
3250 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3251 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3252 {
3253 pjsua_call *call;
3254 pjsip_msg *msg;
3255 pjsip_msg_body *body;
3256 pjsip_status_line status_line;
3257 pj_bool_t is_last;
3258 pj_bool_t cont;
3259 pj_status_t status;
3260
Benny Prijonoa1e69682007-05-11 15:14:34 +00003261 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003262
3263 /* When subscription is terminated, clear the xfer_sub member of
3264 * the inv_data.
3265 */
3266 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3267 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3268 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3269
3270 }
3271
3272 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3273 /* Application is not interested with call progress status */
3274 return;
3275 }
3276
3277 /* This better be a NOTIFY request */
3278 if (event->type == PJSIP_EVENT_TSX_STATE &&
3279 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3280 {
3281 pjsip_rx_data *rdata;
3282
3283 rdata = event->body.tsx_state.src.rdata;
3284
3285 /* Check if there's body */
3286 msg = rdata->msg_info.msg;
3287 body = msg->body;
3288 if (!body) {
3289 PJ_LOG(4,(THIS_FILE,
3290 "Warning: received NOTIFY without message body"));
3291 return;
3292 }
3293
3294 /* Check for appropriate content */
3295 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3296 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3297 {
3298 PJ_LOG(4,(THIS_FILE,
3299 "Warning: received NOTIFY with non message/sipfrag "
3300 "content"));
3301 return;
3302 }
3303
3304 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003305 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003306 &status_line);
3307 if (status != PJ_SUCCESS) {
3308 PJ_LOG(4,(THIS_FILE,
3309 "Warning: received NOTIFY with invalid "
3310 "message/sipfrag content"));
3311 return;
3312 }
3313
3314 } else {
3315 status_line.code = 500;
3316 status_line.reason = *pjsip_get_status_text(500);
3317 }
3318
3319 /* Notify application */
3320 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3321 cont = !is_last;
3322 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3323 status_line.code,
3324 &status_line.reason,
3325 is_last, &cont);
3326
3327 if (!cont) {
3328 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3329 }
3330 }
3331}
3332
3333
3334/*
3335 * Callback called by event framework when the xfer subscription state
3336 * has changed.
3337 */
3338static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003339{
3340
3341 PJ_UNUSED_ARG(event);
3342
3343 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003344 * When subscription is terminated, clear the xfer_sub member of
3345 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003346 */
3347 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003348 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003349
Benny Prijonoa1e69682007-05-11 15:14:34 +00003350 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003351 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003352 return;
3353
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003354 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003355 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003356
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003357 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003358 }
3359}
3360
3361
3362/*
3363 * Follow transfer (REFER) request.
3364 */
3365static void on_call_transfered( pjsip_inv_session *inv,
3366 pjsip_rx_data *rdata )
3367{
3368 pj_status_t status;
3369 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003370 pjsua_call *existing_call;
3371 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003372 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003373 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003374 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003375 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003376 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003377 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003378 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003379 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003380 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003381 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003382 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003383 pjsip_evsub *sub;
3384
Benny Prijonoa1e69682007-05-11 15:14:34 +00003385 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003386
Benny Prijono26ff9062006-02-21 23:47:00 +00003387 /* Find the Refer-To header */
3388 refer_to = (pjsip_generic_string_hdr*)
3389 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3390
3391 if (refer_to == NULL) {
3392 /* Invalid Request.
3393 * No Refer-To header!
3394 */
3395 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003396 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003397 return;
3398 }
3399
Benny Prijonoc8141a82006-08-20 09:12:19 +00003400 /* Find optional Refer-Sub header */
3401 refer_sub = (pjsip_generic_string_hdr*)
3402 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3403
3404 if (refer_sub) {
3405 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3406 no_refer_sub = PJ_TRUE;
3407 }
3408
Benny Prijono053f5222006-11-11 16:16:04 +00003409 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3410 * request.
3411 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003412 ref_by_hdr = (pjsip_hdr*)
3413 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003414 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003415
Benny Prijono9fc735d2006-05-28 14:58:12 +00003416 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003417 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003418 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3419 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3420 &refer_to->hvalue,
3421 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003422
3423 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003424 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003425 if (code >= 300) {
3426 /* Application rejects call transfer request */
3427 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3428 return;
3429 }
3430
Benny Prijono26ff9062006-02-21 23:47:00 +00003431 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3432 (int)inv->dlg->remote.info_str.slen,
3433 inv->dlg->remote.info_str.ptr,
3434 (int)refer_to->hvalue.slen,
3435 refer_to->hvalue.ptr));
3436
Benny Prijonoc8141a82006-08-20 09:12:19 +00003437 if (no_refer_sub) {
3438 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003439 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003440 */
3441 pjsip_tx_data *tdata;
3442 const pj_str_t str_false = { "false", 5};
3443 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003444
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003445 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3446 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003447 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003448 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003449 status);
3450 return;
3451 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003452
Benny Prijonoc8141a82006-08-20 09:12:19 +00003453 /* Add Refer-Sub header */
3454 hdr = (pjsip_hdr*)
3455 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3456 &str_false);
3457 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003458
Benny Prijono26ff9062006-02-21 23:47:00 +00003459
Benny Prijonoc8141a82006-08-20 09:12:19 +00003460 /* Send answer */
3461 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3462 tdata);
3463 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003464 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003465 status);
3466 return;
3467 }
3468
3469 /* Don't have subscription */
3470 sub = NULL;
3471
3472 } else {
3473 struct pjsip_evsub_user xfer_cb;
3474 pjsip_hdr hdr_list;
3475
3476 /* Init callback */
3477 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003478 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003479
3480 /* Init additional header list to be sent with REFER response */
3481 pj_list_init(&hdr_list);
3482
3483 /* Create transferee event subscription */
3484 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3485 if (status != PJ_SUCCESS) {
3486 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3487 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3488 return;
3489 }
3490
3491 /* If there's Refer-Sub header and the value is "true", send back
3492 * Refer-Sub in the response with value "true" too.
3493 */
3494 if (refer_sub) {
3495 const pj_str_t str_true = { "true", 4 };
3496 pjsip_hdr *hdr;
3497
3498 hdr = (pjsip_hdr*)
3499 pjsip_generic_string_hdr_create(inv->dlg->pool,
3500 &str_refer_sub,
3501 &str_true);
3502 pj_list_push_back(&hdr_list, hdr);
3503
3504 }
3505
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003506 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003507 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3508
3509 /* Create initial NOTIFY request */
3510 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3511 100, NULL, &tdata);
3512 if (status != PJ_SUCCESS) {
3513 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3514 status);
3515 return;
3516 }
3517
3518 /* Send initial NOTIFY request */
3519 status = pjsip_xfer_send_request( sub, tdata);
3520 if (status != PJ_SUCCESS) {
3521 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3522 return;
3523 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003524 }
3525
3526 /* We're cheating here.
3527 * We need to get a null terminated string from a pj_str_t.
3528 * So grab the pointer from the hvalue and NULL terminate it, knowing
3529 * that the NULL position will be occupied by a newline.
3530 */
3531 uri = refer_to->hvalue.ptr;
3532 uri[refer_to->hvalue.slen] = '\0';
3533
Benny Prijono053f5222006-11-11 16:16:04 +00003534 /* Init msg_data */
3535 pjsua_msg_data_init(&msg_data);
3536
3537 /* If Referred-By header is present in the REFER request, copy this
3538 * to the outgoing INVITE request.
3539 */
3540 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003541 pjsip_hdr *dup = (pjsip_hdr*)
3542 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003543 pj_list_push_back(&msg_data.hdr_list, dup);
3544 }
3545
Benny Prijono26ff9062006-02-21 23:47:00 +00003546 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003547 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003548 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003549 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003550 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003551 if (status != PJ_SUCCESS) {
3552
Benny Prijonoc8141a82006-08-20 09:12:19 +00003553 /* Notify xferer about the error (if we have subscription) */
3554 if (sub) {
3555 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3556 500, NULL, &tdata);
3557 if (status != PJ_SUCCESS) {
3558 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3559 status);
3560 return;
3561 }
3562 status = pjsip_xfer_send_request(sub, tdata);
3563 if (status != PJ_SUCCESS) {
3564 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3565 status);
3566 return;
3567 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003568 }
3569 return;
3570 }
3571
Benny Prijonoc8141a82006-08-20 09:12:19 +00003572 if (sub) {
3573 /* Put the server subscription in inv_data.
3574 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3575 * reported back to the server subscription.
3576 */
3577 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003578
Benny Prijonoc8141a82006-08-20 09:12:19 +00003579 /* Put the invite_data in the subscription. */
3580 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3581 &pjsua_var.calls[new_call]);
3582 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003583}
3584
3585
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003586
Benny Prijono26ff9062006-02-21 23:47:00 +00003587/*
3588 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003589 * session. We use this to trap:
3590 * - incoming REFER request.
3591 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003592 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003593static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3594 pjsip_transaction *tsx,
3595 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003596{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003597 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003598
3599 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003600
Benny Prijonofeb69f42007-10-05 09:12:26 +00003601 /* Notify application callback first */
3602 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3603 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3604 }
3605
Benny Prijono26ff9062006-02-21 23:47:00 +00003606 if (tsx->role==PJSIP_ROLE_UAS &&
3607 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003608 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003609 {
3610 /*
3611 * Incoming REFER request.
3612 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003613 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003614
Benny Prijono26ff9062006-02-21 23:47:00 +00003615 }
Benny Prijonob0808372006-03-02 21:18:58 +00003616 else if (tsx->role==PJSIP_ROLE_UAS &&
3617 tsx->state==PJSIP_TSX_STATE_TRYING &&
3618 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3619 {
3620 /*
3621 * Incoming MESSAGE request!
3622 */
3623 pjsip_rx_data *rdata;
3624 pjsip_msg *msg;
3625 pjsip_accept_hdr *accept_hdr;
3626 pj_status_t status;
3627
3628 rdata = e->body.tsx_state.src.rdata;
3629 msg = rdata->msg_info.msg;
3630
3631 /* Request MUST have message body, with Content-Type equal to
3632 * "text/plain".
3633 */
3634 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3635
3636 pjsip_hdr hdr_list;
3637
3638 pj_list_init(&hdr_list);
3639 pj_list_push_back(&hdr_list, accept_hdr);
3640
3641 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3642 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003643 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003644 return;
3645 }
3646
3647 /* Respond with 200 first, so that remote doesn't retransmit in case
3648 * the UI takes too long to process the message.
3649 */
3650 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3651
3652 /* Process MESSAGE request */
3653 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3654 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003655
Benny Prijonob0808372006-03-02 21:18:58 +00003656 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003657 else if (tsx->role == PJSIP_ROLE_UAC &&
3658 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003659 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003660 /* Handle outgoing pager status */
3661 if (tsx->status_code >= 200) {
3662 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003663
Benny Prijonoa1e69682007-05-11 15:14:34 +00003664 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003665 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003666
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003667 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3668 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3669 &im_data->to,
3670 &im_data->body,
3671 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003672 (pjsip_status_code)
3673 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003674 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003675 }
Benny Prijonofccab712006-02-22 22:23:22 +00003676 }
Benny Prijono834aee32006-02-19 01:38:06 +00003677 }
Benny Prijono834aee32006-02-19 01:38:06 +00003678
Benny Prijono26ff9062006-02-21 23:47:00 +00003679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003680 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003681}