blob: 6e5c5638245bed6e2f73369085ef0a3ed1ccf739 [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
Benny Prijonoeebe9af2006-06-13 22:57:13 +000069
70/* Create inactive SDP for call hold. */
71static pj_status_t create_inactive_sdp(pjsua_call *call,
72 pjmedia_sdp_session **p_answer);
73
Benny Prijono7129cc72007-11-05 05:54:25 +000074/* Update SDP version in the offer */
75static void update_sdp_version(pjsua_call *call,
76 pjmedia_sdp_session *sdp)
77{
78 const pjmedia_sdp_session *old_sdp = NULL;
79 pj_status_t status;
80
81 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
82 if (status != PJ_SUCCESS || old_sdp == NULL)
83 return;
84
85 sdp->origin.version = old_sdp->origin.version + 1;
86}
87
88
Benny Prijonod524e822006-09-22 12:48:18 +000089/*
90 * Callback called by event framework when the xfer subscription state
91 * has changed.
92 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000093static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
94static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
95
96/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000097 * Reset call descriptor.
98 */
99static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000100{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 call->index = id;
104 call->inv = NULL;
105 call->user_data = NULL;
106 call->session = NULL;
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;
Benny Prijono105217f2006-03-06 16:25:59 +0000118}
119
120
Benny Prijono275fd682006-03-22 11:59:11 +0000121/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000123 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000125{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 pjsip_inv_callback inv_cb;
127 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000128 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000130
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 /* Init calls array. */
132 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
133 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000134
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 /* Copy config */
136 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000137
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000139 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
141 inv_cb.on_new_session = &pjsua_call_on_forked;
142 inv_cb.on_media_update = &pjsua_call_on_media_update;
143 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000144 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijono275fd682006-03-22 11:59:11 +0000147
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148 /* Initialize invite session module: */
149 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
150 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
151
Benny Prijonoc8141a82006-08-20 09:12:19 +0000152 /* Add "norefersub" in Supported header */
153 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
154 NULL, 1, &str_norefersub);
155
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156 return status;
157}
158
159
160/*
161 * Start call subsystem.
162 */
163pj_status_t pjsua_call_subsys_start(void)
164{
165 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000166 return PJ_SUCCESS;
167}
168
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171 * Get maximum number of calls configured in pjsua.
172 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000173PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000174{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000175 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176}
177
178
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000179/*
180 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000182PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185}
186
187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188/*
189 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
192 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
201 if (!pjsua_var.calls[i].inv)
202 continue;
203 ids[c] = i;
204 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205 }
206
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
211 return PJ_SUCCESS;
212}
213
214
Benny Prijono5773cd62008-01-19 13:01:42 +0000215/* Allocate one call id */
216static pjsua_call_id alloc_call_id(void)
217{
218 pjsua_call_id cid;
219
220#if 1
221 /* New algorithm: round-robin */
222 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
223 pjsua_var.next_call_id < 0)
224 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000225 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000226 }
227
228 for (cid=pjsua_var.next_call_id;
229 cid<(int)pjsua_var.ua_cfg.max_calls;
230 ++cid)
231 {
232 if (pjsua_var.calls[cid].inv == NULL) {
233 ++pjsua_var.next_call_id;
234 return cid;
235 }
236 }
237
238 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
239 if (pjsua_var.calls[cid].inv == NULL) {
240 ++pjsua_var.next_call_id;
241 return cid;
242 }
243 }
244
245#else
246 /* Old algorithm */
247 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
248 if (pjsua_var.calls[cid].inv == NULL)
249 return cid;
250 }
251#endif
252
253 return PJSUA_INVALID_ID;
254}
255
Benny Prijonod8179652008-01-23 20:39:07 +0000256/* Get signaling secure level.
257 * Return:
258 * 0: if signaling is not secure
259 * 1: if TLS transport is used for immediate hop
260 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000261 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000262static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000263{
264 const pj_str_t tls = pj_str(";transport=tls");
265 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000266 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000267
268 if (pj_stristr(dst_uri, &sips))
269 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000270
271 if (!pj_list_empty(&acc->route_set)) {
272 pjsip_route_hdr *r = acc->route_set.next;
273 pjsip_uri *uri = r->name_addr.uri;
274 pjsip_sip_uri *sip_uri;
275
276 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
277 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
278 return 1;
279
280 } else {
281 if (pj_stristr(dst_uri, &tls))
282 return 1;
283 }
284
Benny Prijonod8179652008-01-23 20:39:07 +0000285 return 0;
286}
287
Benny Prijono224b4e22008-06-19 14:10:28 +0000288/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000289static int call_get_secure_level(pjsua_call *call)
290{
291 if (call->inv->dlg->secure)
292 return 2;
293
294 if (!pj_list_empty(&call->inv->dlg->route_set)) {
295 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
296 pjsip_uri *uri = r->name_addr.uri;
297 pjsip_sip_uri *sip_uri;
298
299 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
300 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
301 return 1;
302
303 } else {
304 pjsip_sip_uri *sip_uri;
305
306 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
307 return 2;
308 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
309 return 0;
310
311 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
312 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
313 return 1;
314 }
315
316 return 0;
317}
Benny Prijono224b4e22008-06-19 14:10:28 +0000318*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000319
320
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000321/*
322 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000323 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000324PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
325 const pj_str_t *dest_uri,
326 unsigned options,
327 void *user_data,
328 const pjsua_msg_data *msg_data,
329 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000330{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000331 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000332 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000333 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000334 pjsua_acc *acc;
335 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000336 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000337 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000338 pjsip_tx_data *tdata;
339 pj_status_t status;
340
Benny Prijono9fc735d2006-05-28 14:58:12 +0000341
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000342 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000343 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000344 PJ_EINVAL);
345
Benny Prijono320fa4d2006-12-07 10:09:16 +0000346 /* Check arguments */
347 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 PJSUA_LOCK();
350
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000351 /* Create sound port if none is instantiated */
352 if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
353 !pjsua_var.no_snd)
354 {
355 pj_status_t status;
356
357 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
358 if (status != PJ_SUCCESS) {
359 PJSUA_UNLOCK();
360 return status;
361 }
362 }
363
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000364 acc = &pjsua_var.acc[acc_id];
365 if (!acc->valid) {
366 pjsua_perror(THIS_FILE, "Unable to make call because account "
367 "is not valid", PJ_EINVALIDOP);
368 PJSUA_UNLOCK();
369 return PJ_EINVALIDOP;
370 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000371
Benny Prijonoa91a0032006-02-26 21:23:45 +0000372 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000373 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000374
Benny Prijono5773cd62008-01-19 13:01:42 +0000375 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
377 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000378 return PJ_ETOOMANY;
379 }
380
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000381 call = &pjsua_var.calls[call_id];
382
Benny Prijono320fa4d2006-12-07 10:09:16 +0000383 /* Verify that destination URI is valid before calling
384 * pjsua_acc_create_uac_contact, or otherwise there
385 * a misleading "Invalid Contact URI" error will be printed
386 * when pjsua_acc_create_uac_contact() fails.
387 */
388 if (1) {
389 pj_pool_t *pool;
390 pjsip_uri *uri;
391 pj_str_t dup;
392
393 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
394 if (!pool) {
395 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
396 PJSUA_UNLOCK();
397 return PJ_ENOMEM;
398 }
399
400 pj_strdup_with_null(pool, &dup, dest_uri);
401 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
402 pj_pool_release(pool);
403
404 if (uri == NULL) {
405 pjsua_perror(THIS_FILE, "Unable to make call",
406 PJSIP_EINVALIDREQURI);
407 PJSUA_UNLOCK();
408 return PJSIP_EINVALIDREQURI;
409 }
410 }
411
Benny Prijono093d3022006-09-24 00:07:11 +0000412 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
413 (int)dest_uri->slen, dest_uri->ptr));
414
Benny Prijonoe21e7842006-04-09 16:46:05 +0000415 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000417
Benny Prijonoe21e7842006-04-09 16:46:05 +0000418 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000420
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000421 /* Create suitable Contact header unless a Contact header has been
422 * set in the account.
423 */
424 if (acc->contact.slen) {
425 contact = acc->contact;
426 } else {
427 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
428 acc_id, dest_uri);
429 if (status != PJ_SUCCESS) {
430 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
431 status);
432 PJSUA_UNLOCK();
433 return status;
434 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000435 }
436
Benny Prijonoe21e7842006-04-09 16:46:05 +0000437 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000438 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000439 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000441 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000442 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
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
550 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000551
552 return PJ_SUCCESS;
553
554
555on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000556 if (inv != NULL) {
557 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000558 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000559 pjsip_dlg_terminate(dlg);
560 }
561
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 if (call_id != -1) {
563 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000564 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000565 }
566
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000567 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000568 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000569}
570
571
Benny Prijono91a6a172007-10-31 08:59:29 +0000572/* Get the NAT type information in remote's SDP */
573static void update_remote_nat_type(pjsua_call *call,
574 const pjmedia_sdp_session *sdp)
575{
576 const pjmedia_sdp_attr *xnat;
577
578 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
579 if (xnat) {
580 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
581 } else {
582 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
583 }
584
585 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
586 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
587}
588
589
Benny Prijonodc39fe82006-05-26 12:17:46 +0000590/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000591 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000592 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000593 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000594pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000595{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000596 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000597 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000598 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000599 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
600 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000601 pjsip_tx_data *response = NULL;
602 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000603 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000604 int acc_id;
605 pjsua_call *call;
606 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000607 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000608 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000609 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000610
Benny Prijono26ff9062006-02-21 23:47:00 +0000611 /* Don't want to handle anything but INVITE */
612 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
613 return PJ_FALSE;
614
615 /* Don't want to handle anything that's already associated with
616 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000617 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000618 if (dlg || tsx)
619 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000620
Benny Prijono148c9dd2006-09-19 13:37:53 +0000621 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000622
Benny Prijono26ff9062006-02-21 23:47:00 +0000623 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000624 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000625
Benny Prijono5773cd62008-01-19 13:01:42 +0000626 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000627 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000628 PJSIP_SC_BUSY_HERE, NULL,
629 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000630 PJ_LOG(2,(THIS_FILE,
631 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000632 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 return PJ_TRUE;
634 }
635
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000636 /* Clear call descriptor */
637 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000638
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000639 call = &pjsua_var.calls[call_id];
640
641 /* Mark call start time. */
642 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000643
Benny Prijono053f5222006-11-11 16:16:04 +0000644 /* Check INVITE request for Replaces header. If Replaces header is
645 * present, the function will make sure that we can handle the request.
646 */
647 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
648 &response);
649 if (status != PJ_SUCCESS) {
650 /*
651 * Something wrong with the Replaces header.
652 */
653 if (response) {
654 pjsip_response_addr res_addr;
655
656 pjsip_get_response_addr(response->pool, rdata, &res_addr);
657 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
658 NULL, NULL);
659
660 } else {
661
662 /* Respond with 500 (Internal Server Error) */
663 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
664 NULL, NULL);
665 }
666
667 PJSUA_UNLOCK();
668 return PJ_TRUE;
669 }
670
671 /* If this INVITE request contains Replaces header, notify application
672 * about the request so that application can do subsequent checking
673 * if it wants to.
674 */
675 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
676 pjsua_call *replaced_call;
677 int st_code = 200;
678 pj_str_t st_text = { "OK", 2 };
679
680 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000681 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000682
683 /* Notify application */
684 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
685 rdata, &st_code, &st_text);
686
687 /* Must specify final response */
688 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
689
690 /* Check if application rejects this request. */
691 if (st_code >= 300) {
692
693 if (st_text.slen == 2)
694 st_text = *pjsip_get_status_text(st_code);
695
696 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
697 st_code, &st_text, NULL, NULL, NULL);
698 PJSUA_UNLOCK();
699 return PJ_TRUE;
700 }
701 }
702
Benny Prijonod8179652008-01-23 20:39:07 +0000703 /*
704 * Get which account is most likely to be associated with this incoming
705 * call. We need the account to find which contact URI to put for
706 * the call.
707 */
708 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
709
Benny Prijonodb844a42008-02-02 17:07:18 +0000710 /* Get call's secure level */
711 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
712 call->secure_level = 2;
713 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
714 call->secure_level = 1;
715 else
716 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000717
Benny Prijonod8179652008-01-23 20:39:07 +0000718 /* Parse SDP from incoming request */
719 if (rdata->msg_info.msg->body) {
720 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000721 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000722 rdata->msg_info.msg->body->len, &offer);
723 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000724 const pj_str_t reason = pj_str("Bad SDP");
725 pjsua_perror(THIS_FILE, "Error parsing SDP in incoming INVITE",
726 status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000727 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
728 &reason, NULL, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000729 PJSUA_UNLOCK();
730 return PJ_TRUE;
731 }
Benny Prijono617b8602008-04-07 10:10:31 +0000732
733 /* Do quick checks on SDP before passing it to transports. More elabore
734 * checks will be done in pjsip_inv_verify_request2() below.
735 */
736 if (offer->media_count==0) {
737 const pj_str_t reason = pj_str("Missing media in SDP");
738 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
739 NULL, NULL, NULL);
740 PJSUA_UNLOCK();
741 return PJ_TRUE;
742 }
743
Benny Prijonod8179652008-01-23 20:39:07 +0000744 } else {
745 offer = NULL;
746 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000747
Benny Prijono224b4e22008-06-19 14:10:28 +0000748 /* Init media channel */
749 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
750 call->secure_level,
751 rdata->tp_info.pool, offer,
752 &sip_err_code);
753 if (status != PJ_SUCCESS) {
754 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
755 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
756 sip_err_code, NULL, NULL, NULL, NULL);
757 PJSUA_UNLOCK();
758 return PJ_TRUE;
759 }
760
761 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000762 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000763 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000764 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000765 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000766 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
767 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000768 PJSUA_UNLOCK();
769 return PJ_TRUE;
770 }
771
Benny Prijono224b4e22008-06-19 14:10:28 +0000772
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000773 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000774 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000775 if (pjsua_var.acc[acc_id].cfg.require_100rel)
776 options |= PJSIP_INV_REQUIRE_100REL;
777
Benny Prijonod8179652008-01-23 20:39:07 +0000778 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
779 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000780 if (status != PJ_SUCCESS) {
781
782 /*
783 * No we can't handle the incoming INVITE request.
784 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000785 if (response) {
786 pjsip_response_addr res_addr;
787
788 pjsip_get_response_addr(response->pool, rdata, &res_addr);
789 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
790 NULL, NULL);
791
792 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000793 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000794 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
795 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000796 }
797
Benny Prijonoc97608e2007-03-23 16:34:20 +0000798 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000799 PJSUA_UNLOCK();
800 return PJ_TRUE;
801 }
802
803
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000804 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000805 if (pjsua_var.acc[acc_id].contact.slen) {
806 contact = pjsua_var.acc[acc_id].contact;
807 } else {
808 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
809 acc_id, rdata);
810 if (status != PJ_SUCCESS) {
811 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
812 status);
813 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
814 NULL, NULL);
815 pjsua_media_channel_deinit(call->index);
816 PJSUA_UNLOCK();
817 return PJ_TRUE;
818 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000819 }
820
Benny Prijono26ff9062006-02-21 23:47:00 +0000821 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000822 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000823 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000824 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000825 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000826 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000827 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000828 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000829 return PJ_TRUE;
830 }
831
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000832 /* Set credentials */
833 if (pjsua_var.acc[acc_id].cred_cnt) {
834 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
835 pjsua_var.acc[acc_id].cred_cnt,
836 pjsua_var.acc[acc_id].cred);
837 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000838
Benny Prijono48ab2b72007-11-08 09:24:30 +0000839 /* Set preference */
840 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
841 &pjsua_var.acc[acc_id].cfg.auth_pref);
842
Benny Prijono26ff9062006-02-21 23:47:00 +0000843 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000844 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000845 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000846 pjsip_hdr hdr_list;
847 pjsip_warning_hdr *w;
848
849 w = pjsip_warning_hdr_create_from_status(dlg->pool,
850 pjsip_endpt_name(pjsua_var.endpt),
851 status);
852 pj_list_init(&hdr_list);
853 pj_list_push_back(&hdr_list, w);
854
855 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
856
857 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000858 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000859 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000860 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000861 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000862 return PJ_TRUE;
863 }
864
Benny Prijonoea9fd392007-11-06 03:41:40 +0000865 /* Update NAT type of remote endpoint, only when there is SDP in
866 * incoming INVITE!
867 */
868 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
869 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
870 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000871 const pjmedia_sdp_session *remote_sdp;
872
873 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
874 update_remote_nat_type(call, remote_sdp);
875 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000876
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000877 /* Create and attach pjsua_var data to the dialog: */
878 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000879
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880 dlg->mod_data[pjsua_var.mod.id] = call;
881 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000882
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000883 /* If account is locked to specific transport, then lock dialog
884 * to this transport too.
885 */
886 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
887 pjsip_tpselector tp_sel;
888
889 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
890 pjsip_dlg_set_transport(dlg, &tp_sel);
891 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000892
Benny Prijono64f851e2006-02-23 13:49:28 +0000893 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000894 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000895 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000896 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000898 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
899 status);
900
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000901 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
902 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000903 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000904 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000905 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000906
907 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000908 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000909 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000910 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000911 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000912 }
913
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000914 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000915
Benny Prijono105217f2006-03-06 16:25:59 +0000916
Benny Prijono053f5222006-11-11 16:16:04 +0000917 /* Check if this request should replace existing call */
918 if (replaced_dlg) {
919 pjsip_inv_session *replaced_inv;
920 struct pjsua_call *replaced_call;
921 pjsip_tx_data *tdata;
922
923 /* Get the invite session in the dialog */
924 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
925
926 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000927 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000928
929 /* Notify application */
930 if (pjsua_var.ua_cfg.cb.on_call_replaced)
931 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
932 call_id);
933
934 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
935 call_id));
936
937 /* Answer the new call with 200 response */
938 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
939 if (status == PJ_SUCCESS)
940 status = pjsip_inv_send_msg(inv, tdata);
941
942 if (status != PJ_SUCCESS)
943 pjsua_perror(THIS_FILE, "Error answering session", status);
944
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000945 /* Note that inv may be invalid if 200/OK has caused error in
946 * starting the media.
947 */
Benny Prijono053f5222006-11-11 16:16:04 +0000948
949 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
950 replaced_call->index));
951
952 /* Disconnect replaced invite session */
953 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
954 &tdata);
955 if (status == PJ_SUCCESS && tdata)
956 status = pjsip_inv_send_msg(replaced_inv, tdata);
957
958 if (status != PJ_SUCCESS)
959 pjsua_perror(THIS_FILE, "Error terminating session", status);
960
961
962 } else {
963
Benny Prijonob5388cf2007-01-04 22:45:08 +0000964 /* Notify application if on_incoming_call() is overriden,
965 * otherwise hangup the call with 480
966 */
967 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000968 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000969 } else {
970 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
971 NULL, NULL);
972 }
Benny Prijono053f5222006-11-11 16:16:04 +0000973 }
974
Benny Prijono8b1889b2006-06-06 18:40:40 +0000975
Benny Prijono26ff9062006-02-21 23:47:00 +0000976 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000977 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000978 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000979}
980
981
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000982
983/*
984 * Check if the specified call has active INVITE session and the INVITE
985 * session has not been disconnected.
986 */
987PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
988{
989 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
990 PJ_EINVAL);
991 return pjsua_var.calls[call_id].inv != NULL &&
992 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
993}
994
995
996/*
997 * Check if call has an active media session.
998 */
999PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1000{
1001 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1002 PJ_EINVAL);
1003 return pjsua_var.calls[call_id].session != NULL;
1004}
1005
1006
Benny Prijono148c9dd2006-09-19 13:37:53 +00001007/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001008pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001009 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001010 pjsua_call **p_call,
1011 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001012{
1013 enum { MAX_RETRY=50 };
1014 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001015 pjsua_call *call = NULL;
1016 pj_bool_t has_pjsua_lock = PJ_FALSE;
1017 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001018
1019 for (retry=0; retry<MAX_RETRY; ++retry) {
1020
1021 has_pjsua_lock = PJ_FALSE;
1022
1023 status = PJSUA_TRY_LOCK();
1024 if (status != PJ_SUCCESS) {
1025 pj_thread_sleep(retry/10);
1026 continue;
1027 }
1028
1029 has_pjsua_lock = PJ_TRUE;
1030 call = &pjsua_var.calls[call_id];
1031
1032 if (call->inv == NULL) {
1033 PJSUA_UNLOCK();
1034 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1035 return PJSIP_ESESSIONTERMINATED;
1036 }
1037
1038 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1039 if (status != PJ_SUCCESS) {
1040 PJSUA_UNLOCK();
1041 pj_thread_sleep(retry/10);
1042 continue;
1043 }
1044
1045 PJSUA_UNLOCK();
1046
1047 break;
1048 }
1049
1050 if (status != PJ_SUCCESS) {
1051 if (has_pjsua_lock == PJ_FALSE)
1052 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1053 "(possibly system has deadlocked) in %s",
1054 title));
1055 else
1056 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1057 "(possibly system has deadlocked) in %s",
1058 title));
1059 return PJ_ETIMEDOUT;
1060 }
1061
1062 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001063 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001064
1065 return PJ_SUCCESS;
1066}
1067
1068
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069/*
1070 * Get the conference port identification associated with the call.
1071 */
1072PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1073{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001074 pjsua_call *call;
1075 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001076 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001077 pj_status_t status;
1078
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001079 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1080 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001081
Benny Prijonodc752ca2006-09-22 16:55:42 +00001082 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001083 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001084 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001085
1086 port_id = call->conf_slot;
1087
Benny Prijonodc752ca2006-09-22 16:55:42 +00001088 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001089
1090 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001091}
1092
1093
Benny Prijono148c9dd2006-09-19 13:37:53 +00001094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095/*
1096 * Obtain detail information about the specified call.
1097 */
1098PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1099 pjsua_call_info *info)
1100{
1101 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001102 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001103 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001104
1105 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1106 PJ_EINVAL);
1107
Benny Prijonoac623b32006-07-03 15:19:31 +00001108 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001109
Benny Prijonodc752ca2006-09-22 16:55:42 +00001110 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001111 if (status != PJ_SUCCESS) {
1112 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 }
1114
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001115 /* id and role */
1116 info->id = call_id;
1117 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001118 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001119
1120 /* local info */
1121 info->local_info.ptr = info->buf_.local_info;
1122 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1123 sizeof(info->buf_.local_info));
1124
1125 /* local contact */
1126 info->local_contact.ptr = info->buf_.local_contact;
1127 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1128 call->inv->dlg->local.contact->uri,
1129 info->local_contact.ptr,
1130 sizeof(info->buf_.local_contact));
1131
1132 /* remote info */
1133 info->remote_info.ptr = info->buf_.remote_info;
1134 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1135 sizeof(info->buf_.remote_info));
1136
1137 /* remote contact */
1138 if (call->inv->dlg->remote.contact) {
1139 int len;
1140 info->remote_contact.ptr = info->buf_.remote_contact;
1141 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1142 call->inv->dlg->remote.contact->uri,
1143 info->remote_contact.ptr,
1144 sizeof(info->buf_.remote_contact));
1145 if (len < 0) len = 0;
1146 info->remote_contact.slen = len;
1147 } else {
1148 info->remote_contact.slen = 0;
1149 }
1150
1151 /* call id */
1152 info->call_id.ptr = info->buf_.call_id;
1153 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1154 sizeof(info->buf_.call_id));
1155
1156 /* state, state_text */
1157 info->state = call->inv->state;
1158 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1159
1160 /* If call is disconnected, set the last_status from the cause code */
1161 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1162 /* last_status, last_status_text */
1163 info->last_status = call->inv->cause;
1164
1165 info->last_status_text.ptr = info->buf_.last_status_text;
1166 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1167 sizeof(info->buf_.last_status_text));
1168 } else {
1169 /* last_status, last_status_text */
1170 info->last_status = call->last_code;
1171
1172 info->last_status_text.ptr = info->buf_.last_status_text;
1173 pj_strncpy(&info->last_status_text, &call->last_text,
1174 sizeof(info->buf_.last_status_text));
1175 }
1176
1177 /* media status and dir */
1178 info->media_status = call->media_st;
1179 info->media_dir = call->media_dir;
1180
1181
1182 /* conference slot number */
1183 info->conf_slot = call->conf_slot;
1184
1185 /* calculate duration */
1186 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1187
1188 info->total_duration = call->dis_time;
1189 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1190
1191 if (call->conn_time.sec) {
1192 info->connect_duration = call->dis_time;
1193 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1194 }
1195
1196 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1197
1198 pj_gettimeofday(&info->total_duration);
1199 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1200
1201 pj_gettimeofday(&info->connect_duration);
1202 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1203
1204 } else {
1205 pj_gettimeofday(&info->total_duration);
1206 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1207 }
1208
Benny Prijonodc752ca2006-09-22 16:55:42 +00001209 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001210
1211 return PJ_SUCCESS;
1212}
1213
1214
1215/*
1216 * Attach application specific data to the call.
1217 */
1218PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1219 void *user_data)
1220{
1221 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1222 PJ_EINVAL);
1223 pjsua_var.calls[call_id].user_data = user_data;
1224
1225 return PJ_SUCCESS;
1226}
1227
1228
1229/*
1230 * Get user data attached to the call.
1231 */
1232PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1233{
1234 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1235 NULL);
1236 return pjsua_var.calls[call_id].user_data;
1237}
1238
1239
1240/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001241 * Get remote's NAT type.
1242 */
1243PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1244 pj_stun_nat_type *p_type)
1245{
1246 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1247 PJ_EINVAL);
1248 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1249
1250 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1251 return PJ_SUCCESS;
1252}
1253
1254
1255/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001256 * Send response to incoming INVITE request.
1257 */
1258PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1259 unsigned code,
1260 const pj_str_t *reason,
1261 const pjsua_msg_data *msg_data)
1262{
1263 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001264 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001265 pjsip_tx_data *tdata;
1266 pj_status_t status;
1267
1268 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1269 PJ_EINVAL);
1270
Benny Prijonodc752ca2006-09-22 16:55:42 +00001271 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001272 if (status != PJ_SUCCESS)
1273 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274
Benny Prijono2e507c22006-06-23 15:04:11 +00001275 if (call->res_time.sec == 0)
1276 pj_gettimeofday(&call->res_time);
1277
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001278 if (reason && reason->slen == 0)
1279 reason = NULL;
1280
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001281 /* Create response message */
1282 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1283 if (status != PJ_SUCCESS) {
1284 pjsua_perror(THIS_FILE, "Error creating response",
1285 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001286 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001287 return status;
1288 }
1289
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001290 /* Call might have been disconnected if application is answering with
1291 * 200/OK and the media failed to start.
1292 */
1293 if (call->inv == NULL) {
1294 pjsip_dlg_dec_lock(dlg);
1295 return PJSIP_ESESSIONTERMINATED;
1296 }
1297
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 /* Add additional headers etc */
1299 pjsua_process_msg_data( tdata, msg_data);
1300
1301 /* Send the message */
1302 status = pjsip_inv_send_msg(call->inv, tdata);
1303 if (status != PJ_SUCCESS)
1304 pjsua_perror(THIS_FILE, "Error sending response",
1305 status);
1306
Benny Prijonodc752ca2006-09-22 16:55:42 +00001307 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001308
1309 return status;
1310}
1311
1312
1313/*
1314 * Hangup call by using method that is appropriate according to the
1315 * call state.
1316 */
1317PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1318 unsigned code,
1319 const pj_str_t *reason,
1320 const pjsua_msg_data *msg_data)
1321{
1322 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001323 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001324 pj_status_t status;
1325 pjsip_tx_data *tdata;
1326
1327
Benny Prijono148c9dd2006-09-19 13:37:53 +00001328 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1329 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1330 call_id));
1331 }
1332
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001333 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1334 PJ_EINVAL);
1335
Benny Prijonodc752ca2006-09-22 16:55:42 +00001336 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001337 if (status != PJ_SUCCESS)
1338 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001339
1340 if (code==0) {
1341 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1342 code = PJSIP_SC_OK;
1343 else if (call->inv->role == PJSIP_ROLE_UAS)
1344 code = PJSIP_SC_DECLINE;
1345 else
1346 code = PJSIP_SC_REQUEST_TERMINATED;
1347 }
1348
1349 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1350 if (status != PJ_SUCCESS) {
1351 pjsua_perror(THIS_FILE,
1352 "Failed to create end session message",
1353 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001354 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001355 return status;
1356 }
1357
1358 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1359 * as p_tdata when INVITE transaction has not been answered
1360 * with any provisional responses.
1361 */
1362 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001363 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001364 return PJ_SUCCESS;
1365 }
1366
1367 /* Add additional headers etc */
1368 pjsua_process_msg_data( tdata, msg_data);
1369
1370 /* Send the message */
1371 status = pjsip_inv_send_msg(call->inv, tdata);
1372 if (status != PJ_SUCCESS) {
1373 pjsua_perror(THIS_FILE,
1374 "Failed to send end session message",
1375 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001376 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 return status;
1378 }
1379
Benny Prijonodc752ca2006-09-22 16:55:42 +00001380 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381
1382 return PJ_SUCCESS;
1383}
1384
1385
1386/*
1387 * Put the specified call on hold.
1388 */
1389PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1390 const pjsua_msg_data *msg_data)
1391{
1392 pjmedia_sdp_session *sdp;
1393 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001394 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 pjsip_tx_data *tdata;
1396 pj_status_t status;
1397
1398 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1399 PJ_EINVAL);
1400
Benny Prijonodc752ca2006-09-22 16:55:42 +00001401 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001402 if (status != PJ_SUCCESS)
1403 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405
1406 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1407 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001408 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409 return PJSIP_ESESSIONSTATE;
1410 }
1411
1412 status = create_inactive_sdp(call, &sdp);
1413 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001414 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001415 return status;
1416 }
1417
Benny Prijono7129cc72007-11-05 05:54:25 +00001418 update_sdp_version(call, sdp);
1419
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001420 /* Create re-INVITE with new offer */
1421 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1422 if (status != PJ_SUCCESS) {
1423 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001424 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001425 return status;
1426 }
1427
1428 /* Add additional headers etc */
1429 pjsua_process_msg_data( tdata, msg_data);
1430
1431 /* Send the request */
1432 status = pjsip_inv_send_msg( call->inv, tdata);
1433 if (status != PJ_SUCCESS) {
1434 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001435 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001436 return status;
1437 }
1438
Benny Prijonodc752ca2006-09-22 16:55:42 +00001439 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440
1441 return PJ_SUCCESS;
1442}
1443
1444
1445/*
1446 * Send re-INVITE (to release hold).
1447 */
1448PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1449 pj_bool_t unhold,
1450 const pjsua_msg_data *msg_data)
1451{
1452 pjmedia_sdp_session *sdp;
1453 pjsip_tx_data *tdata;
1454 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001455 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456 pj_status_t status;
1457
1458
1459 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1460 PJ_EINVAL);
1461
Benny Prijonodc752ca2006-09-22 16:55:42 +00001462 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001463 if (status != PJ_SUCCESS)
1464 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001465
1466 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1467 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001468 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 return PJSIP_ESESSIONSTATE;
1470 }
1471
1472 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001473 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001474 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonod8179652008-01-23 20:39:07 +00001475 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001476 NULL, &sdp, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477 if (status != PJ_SUCCESS) {
1478 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1479 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001480 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481 return status;
1482 }
1483
Benny Prijono7129cc72007-11-05 05:54:25 +00001484 update_sdp_version(call, sdp);
1485
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 /* Create re-INVITE with new offer */
1487 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1488 if (status != PJ_SUCCESS) {
1489 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001490 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001491 return status;
1492 }
1493
1494 /* Add additional headers etc */
1495 pjsua_process_msg_data( tdata, msg_data);
1496
1497 /* Send the request */
1498 status = pjsip_inv_send_msg( call->inv, tdata);
1499 if (status != PJ_SUCCESS) {
1500 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001501 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 return status;
1503 }
1504
Benny Prijonodc752ca2006-09-22 16:55:42 +00001505 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506
1507 return PJ_SUCCESS;
1508}
1509
1510
1511/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001512 * Send UPDATE request.
1513 */
1514PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1515 unsigned options,
1516 const pjsua_msg_data *msg_data)
1517{
1518 pjmedia_sdp_session *sdp;
1519 pjsip_tx_data *tdata;
1520 pjsua_call *call;
1521 pjsip_dialog *dlg;
1522 pj_status_t status;
1523
1524 PJ_UNUSED_ARG(options);
1525
1526 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1527 PJ_EINVAL);
1528
1529 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1530 if (status != PJ_SUCCESS)
1531 return status;
1532
Benny Prijonoc08682e2007-10-04 06:17:58 +00001533 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001534 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001535 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001536 if (status != PJ_SUCCESS) {
1537 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1538 status);
1539 pjsip_dlg_dec_lock(dlg);
1540 return status;
1541 }
1542
Benny Prijono224b4e22008-06-19 14:10:28 +00001543 update_sdp_version(call, sdp);
1544
1545 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001546 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1547 if (status != PJ_SUCCESS) {
1548 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1549 pjsip_dlg_dec_lock(dlg);
1550 return status;
1551 }
1552
1553 /* Add additional headers etc */
1554 pjsua_process_msg_data( tdata, msg_data);
1555
1556 /* Send the request */
1557 status = pjsip_inv_send_msg( call->inv, tdata);
1558 if (status != PJ_SUCCESS) {
1559 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1560 pjsip_dlg_dec_lock(dlg);
1561 return status;
1562 }
1563
1564 pjsip_dlg_dec_lock(dlg);
1565
1566 return PJ_SUCCESS;
1567}
1568
1569
1570/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571 * Initiate call transfer to the specified address.
1572 */
1573PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1574 const pj_str_t *dest,
1575 const pjsua_msg_data *msg_data)
1576{
1577 pjsip_evsub *sub;
1578 pjsip_tx_data *tdata;
1579 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001580 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001581 pjsip_generic_string_hdr *gs_hdr;
1582 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001583 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001584 pj_status_t status;
1585
1586
1587 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1588 PJ_EINVAL);
1589
Benny Prijonodc752ca2006-09-22 16:55:42 +00001590 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001591 if (status != PJ_SUCCESS)
1592 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001594
Benny Prijonod524e822006-09-22 12:48:18 +00001595 /* Create xfer client subscription. */
1596 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001597 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001598
1599 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001600 if (status != PJ_SUCCESS) {
1601 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001602 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 return status;
1604 }
1605
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001606 /* Associate this call with the client subscription */
1607 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1608
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609 /*
1610 * Create REFER request.
1611 */
1612 status = pjsip_xfer_initiate(sub, dest, &tdata);
1613 if (status != PJ_SUCCESS) {
1614 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001615 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 return status;
1617 }
1618
Benny Prijono053f5222006-11-11 16:16:04 +00001619 /* Add Referred-By header */
1620 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1621 &dlg->local.info_str);
1622 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1623
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 /* Add additional headers etc */
1626 pjsua_process_msg_data( tdata, msg_data);
1627
1628 /* Send. */
1629 status = pjsip_xfer_send_request(sub, tdata);
1630 if (status != PJ_SUCCESS) {
1631 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001632 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633 return status;
1634 }
1635
1636 /* For simplicity (that's what this program is intended to be!),
1637 * leave the original invite session as it is. More advanced application
1638 * may want to hold the INVITE, or terminate the invite, or whatever.
1639 */
1640
Benny Prijonodc752ca2006-09-22 16:55:42 +00001641 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642
1643 return PJ_SUCCESS;
1644
1645}
1646
1647
1648/*
Benny Prijono053f5222006-11-11 16:16:04 +00001649 * Initiate attended call transfer to the specified address.
1650 */
1651PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1652 pjsua_call_id dest_call_id,
1653 unsigned options,
1654 const pjsua_msg_data *msg_data)
1655{
1656 pjsua_call *dest_call;
1657 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001658 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001659 pj_str_t str_dest;
1660 int len;
1661 pjsip_uri *uri;
1662 pj_status_t status;
1663
1664
1665 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1666 PJ_EINVAL);
1667 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1668 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1669 PJ_EINVAL);
1670
1671 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1672 &dest_call, &dest_dlg);
1673 if (status != PJ_SUCCESS)
1674 return status;
1675
1676 /*
1677 * Create REFER destination URI with Replaces field.
1678 */
1679
1680 /* Make sure we have sufficient buffer's length */
1681 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1682 dest_dlg->call_id->id.slen +
1683 dest_dlg->remote.info->tag.slen +
1684 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001685 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001686
1687 /* Print URI */
1688 str_dest_buf[0] = '<';
1689 str_dest.slen = 1;
1690
Benny Prijonoa1e69682007-05-11 15:14:34 +00001691 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001692 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1693 str_dest_buf+1, sizeof(str_dest_buf)-1);
1694 if (len < 0)
1695 return PJSIP_EURITOOLONG;
1696
1697 str_dest.slen += len;
1698
1699
1700 /* Build the URI */
1701 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1702 sizeof(str_dest_buf) - str_dest.slen,
1703 "?%s"
1704 "Replaces=%.*s"
1705 "%%3Bto-tag%%3D%.*s"
1706 "%%3Bfrom-tag%%3D%.*s>",
1707 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1708 "" : "Require=replaces&"),
1709 (int)dest_dlg->call_id->id.slen,
1710 dest_dlg->call_id->id.ptr,
1711 (int)dest_dlg->remote.info->tag.slen,
1712 dest_dlg->remote.info->tag.ptr,
1713 (int)dest_dlg->local.info->tag.slen,
1714 dest_dlg->local.info->tag.ptr);
1715
1716 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1717 PJSIP_EURITOOLONG);
1718
1719 str_dest.ptr = str_dest_buf;
1720 str_dest.slen += len;
1721
1722 pjsip_dlg_dec_lock(dest_dlg);
1723
1724 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1725}
1726
1727
1728/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729 * Send DTMF digits to remote using RFC 2833 payload formats.
1730 */
1731PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1732 const pj_str_t *digits)
1733{
1734 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001735 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 pj_status_t status;
1737
1738 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1739 PJ_EINVAL);
1740
Benny Prijonodc752ca2006-09-22 16:55:42 +00001741 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001742 if (status != PJ_SUCCESS)
1743 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001744
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001745 if (!call->session) {
1746 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001747 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001748 return PJ_EINVALIDOP;
1749 }
1750
1751 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1752
Benny Prijonodc752ca2006-09-22 16:55:42 +00001753 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001754
1755 return status;
1756}
1757
1758
1759/**
1760 * Send instant messaging inside INVITE session.
1761 */
1762PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1763 const pj_str_t *mime_type,
1764 const pj_str_t *content,
1765 const pjsua_msg_data *msg_data,
1766 void *user_data)
1767{
1768 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001769 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001770 const pj_str_t mime_text_plain = pj_str("text/plain");
1771 pjsip_media_type ctype;
1772 pjsua_im_data *im_data;
1773 pjsip_tx_data *tdata;
1774 pj_status_t status;
1775
1776
1777 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1778 PJ_EINVAL);
1779
Benny Prijonodc752ca2006-09-22 16:55:42 +00001780 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001781 if (status != PJ_SUCCESS)
1782 return status;
1783
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001784 /* Set default media type if none is specified */
1785 if (mime_type == NULL) {
1786 mime_type = &mime_text_plain;
1787 }
1788
1789 /* Create request message. */
1790 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1791 -1, &tdata);
1792 if (status != PJ_SUCCESS) {
1793 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1794 goto on_return;
1795 }
1796
1797 /* Add accept header. */
1798 pjsip_msg_add_hdr( tdata->msg,
1799 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1800
1801 /* Parse MIME type */
1802 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1803
1804 /* Create "text/plain" message body. */
1805 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1806 &ctype.subtype, content);
1807 if (tdata->msg->body == NULL) {
1808 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1809 pjsip_tx_data_dec_ref(tdata);
1810 goto on_return;
1811 }
1812
1813 /* Add additional headers etc */
1814 pjsua_process_msg_data( tdata, msg_data);
1815
1816 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001817 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818 im_data->acc_id = call->acc_id;
1819 im_data->call_id = call_id;
1820 im_data->to = call->inv->dlg->remote.info_str;
1821 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1822 im_data->user_data = user_data;
1823
1824
1825 /* Send the request. */
1826 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1827 pjsua_var.mod.id, im_data);
1828 if (status != PJ_SUCCESS) {
1829 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1830 goto on_return;
1831 }
1832
1833on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001834 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835 return status;
1836}
1837
1838
1839/*
1840 * Send IM typing indication inside INVITE session.
1841 */
1842PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1843 pj_bool_t is_typing,
1844 const pjsua_msg_data*msg_data)
1845{
1846 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001847 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001848 pjsip_tx_data *tdata;
1849 pj_status_t status;
1850
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1852 PJ_EINVAL);
1853
Benny Prijonodc752ca2006-09-22 16:55:42 +00001854 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001855 if (status != PJ_SUCCESS)
1856 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001858 /* Create request message. */
1859 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1860 -1, &tdata);
1861 if (status != PJ_SUCCESS) {
1862 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1863 goto on_return;
1864 }
1865
1866 /* Create "application/im-iscomposing+xml" msg body. */
1867 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1868 NULL, NULL, -1);
1869
1870 /* Add additional headers etc */
1871 pjsua_process_msg_data( tdata, msg_data);
1872
1873 /* Send the request. */
1874 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1875 if (status != PJ_SUCCESS) {
1876 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1877 goto on_return;
1878 }
1879
1880on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001881 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882 return status;
1883}
1884
1885
1886/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001887 * Send arbitrary request.
1888 */
1889PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1890 const pj_str_t *method_str,
1891 const pjsua_msg_data *msg_data)
1892{
1893 pjsua_call *call;
1894 pjsip_dialog *dlg;
1895 pjsip_method method;
1896 pjsip_tx_data *tdata;
1897 pj_status_t status;
1898
1899 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1900 PJ_EINVAL);
1901
1902 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1903 if (status != PJ_SUCCESS)
1904 return status;
1905
1906 /* Init method */
1907 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1908
1909 /* Create request message. */
1910 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1911 if (status != PJ_SUCCESS) {
1912 pjsua_perror(THIS_FILE, "Unable to create request", status);
1913 goto on_return;
1914 }
1915
1916 /* Add additional headers etc */
1917 pjsua_process_msg_data( tdata, msg_data);
1918
1919 /* Send the request. */
1920 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1921 if (status != PJ_SUCCESS) {
1922 pjsua_perror(THIS_FILE, "Unable to send request", status);
1923 goto on_return;
1924 }
1925
1926on_return:
1927 pjsip_dlg_dec_lock(dlg);
1928 return status;
1929}
1930
1931
1932/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001933 * Terminate all calls.
1934 */
1935PJ_DEF(void) pjsua_call_hangup_all(void)
1936{
1937 unsigned i;
1938
1939 PJSUA_LOCK();
1940
1941 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1942 if (pjsua_var.calls[i].inv)
1943 pjsua_call_hangup(i, 0, NULL, NULL);
1944 }
1945
1946 PJSUA_UNLOCK();
1947}
1948
1949
Benny Prijono627cbb42007-09-25 20:48:49 +00001950const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951{
1952 if (val < 1000) {
1953 pj_ansi_sprintf(buf, "%d", val);
1954 } else if (val < 1000000) {
1955 pj_ansi_sprintf(buf, "%d.%dK",
1956 val / 1000,
1957 (val % 1000) / 100);
1958 } else {
1959 pj_ansi_sprintf(buf, "%d.%02dM",
1960 val / 1000000,
1961 (val % 1000000) / 10000);
1962 }
1963
1964 return buf;
1965}
1966
1967
1968/* Dump media session */
1969static void dump_media_session(const char *indent,
1970 char *buf, unsigned maxlen,
1971 pjmedia_session *session)
1972{
1973 unsigned i;
1974 char *p = buf, *end = buf+maxlen;
1975 int len;
1976 pjmedia_session_info info;
1977
1978 pjmedia_session_get_info(session, &info);
1979
1980 for (i=0; i<info.stream_cnt; ++i) {
1981 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00001982 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001984 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001985 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001986 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001987 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001988
1989 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00001990 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
1991 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001992
1993 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1994 dir = "sendonly";
1995 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1996 dir = "recvonly";
1997 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1998 dir = "sendrecv";
1999 else
2000 dir = "inactive";
2001
2002
2003 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002004 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002005 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002006 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002007 info.stream_info[i].fmt.encoding_name.ptr,
2008 info.stream_info[i].fmt.clock_rate / 1000,
2009 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002010 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002011 if (len < 1 || len > end-p) {
2012 *p = '\0';
2013 return;
2014 }
2015
2016 p += len;
2017 *p++ = '\n';
2018 *p = '\0';
2019
2020 if (stat.rx.update_cnt == 0)
2021 strcpy(last_update, "never");
2022 else {
2023 pj_gettimeofday(&now);
2024 PJ_TIME_VAL_SUB(now, stat.rx.update);
2025 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2026 now.sec / 3600,
2027 (now.sec % 3600) / 60,
2028 now.sec % 60,
2029 now.msec);
2030 }
2031
Benny Prijono80019eb2006-08-07 13:22:23 +00002032 pj_gettimeofday(&media_duration);
2033 PJ_TIME_VAL_SUB(media_duration, stat.start);
2034 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2035 media_duration.msec = 1;
2036
Benny Prijono1402a4a2008-01-08 23:41:22 +00002037 /* protect against division by zero */
2038 if (stat.rx.pkt == 0)
2039 stat.rx.pkt = 1;
2040 if (stat.tx.pkt == 0)
2041 stat.tx.pkt = 1;
2042
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002043 len = pj_ansi_snprintf(p, end-p,
2044 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002045 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002046 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002047 "%s (msec) min avg max last dev\n"
2048 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2049 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002050 indent, info.stream_info[i].fmt.pt,
2051 last_update,
2052 indent,
2053 good_number(packets, stat.rx.pkt),
2054 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002055 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002056 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2057 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 +00002058 indent,
2059 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002060 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002061 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002062 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002064 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 indent, indent,
2066 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002067 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068 stat.rx.loss_period.max / 1000.0,
2069 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002070 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071 indent,
2072 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002073 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 stat.rx.jitter.max / 1000.0,
2075 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002076 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002077 ""
2078 );
2079
2080 if (len < 1 || len > end-p) {
2081 *p = '\0';
2082 return;
2083 }
2084
2085 p += len;
2086 *p++ = '\n';
2087 *p = '\0';
2088
2089 if (stat.tx.update_cnt == 0)
2090 strcpy(last_update, "never");
2091 else {
2092 pj_gettimeofday(&now);
2093 PJ_TIME_VAL_SUB(now, stat.tx.update);
2094 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2095 now.sec / 3600,
2096 (now.sec % 3600) / 60,
2097 now.sec % 60,
2098 now.msec);
2099 }
2100
2101 len = pj_ansi_snprintf(p, end-p,
2102 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002103 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002104 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002105 "%s (msec) min avg max last dev \n"
2106 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2107 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108 indent,
2109 info.stream_info[i].tx_pt,
2110 info.stream_info[i].param->info.frm_ptime *
2111 info.stream_info[i].param->setting.frm_per_pkt,
2112 last_update,
2113
2114 indent,
2115 good_number(packets, stat.tx.pkt),
2116 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002117 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002118 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2119 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 +00002120
2121 indent,
2122 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002123 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002124 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002125 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002126 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002127 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128
2129 indent, indent,
2130 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002131 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002132 stat.tx.loss_period.max / 1000.0,
2133 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002134 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002135 indent,
2136 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002137 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002138 stat.tx.jitter.max / 1000.0,
2139 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002140 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141 ""
2142 );
2143
2144 if (len < 1 || len > end-p) {
2145 *p = '\0';
2146 return;
2147 }
2148
2149 p += len;
2150 *p++ = '\n';
2151 *p = '\0';
2152
2153 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002154 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002155 indent,
2156 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002157 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002158 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002159 stat.rtt.last / 1000.0,
2160 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002161 );
2162 if (len < 1 || len > end-p) {
2163 *p = '\0';
2164 return;
2165 }
2166
2167 p += len;
2168 *p++ = '\n';
2169 *p = '\0';
2170 }
2171}
2172
2173
2174/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002175void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002176 int call_id,
2177 char *buf, pj_size_t size)
2178{
2179 int len;
2180 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2181 pjsip_dialog *dlg = inv->dlg;
2182 char userinfo[128];
2183
2184 /* Dump invite sesion info. */
2185
2186 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2187 if (len < 1)
2188 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2189 else
2190 userinfo[len] = '\0';
2191
2192 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2193 title,
2194 pjsip_inv_state_name(inv->state),
2195 userinfo);
2196 if (len < 1 || len >= (int)size) {
2197 pj_ansi_strcpy(buf, "<--uri too long-->");
2198 len = 18;
2199 } else
2200 buf[len] = '\0';
2201}
2202
2203
2204/*
2205 * Dump call and media statistics to string.
2206 */
2207PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2208 pj_bool_t with_media,
2209 char *buffer,
2210 unsigned maxlen,
2211 const char *indent)
2212{
2213 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002214 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002215 pj_time_val duration, res_delay, con_delay;
2216 char tmp[128];
2217 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002218 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002220 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002221
2222 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2223 PJ_EINVAL);
2224
Benny Prijonodc752ca2006-09-22 16:55:42 +00002225 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002226 if (status != PJ_SUCCESS)
2227 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228
2229 *buffer = '\0';
2230 p = buffer;
2231 end = buffer + maxlen;
2232 len = 0;
2233
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002234 print_call(indent, call_id, tmp, sizeof(tmp));
2235
2236 len = pj_ansi_strlen(tmp);
2237 pj_ansi_strcpy(buffer, tmp);
2238
2239 p += len;
2240 *p++ = '\r';
2241 *p++ = '\n';
2242
2243 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002244 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002245 pj_gettimeofday(&duration);
2246 PJ_TIME_VAL_SUB(duration, call->conn_time);
2247 con_delay = call->conn_time;
2248 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2249 } else {
2250 duration.sec = duration.msec = 0;
2251 con_delay.sec = con_delay.msec = 0;
2252 }
2253
2254 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002255 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002256 res_delay = call->res_time;
2257 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2258 } else {
2259 res_delay.sec = res_delay.msec = 0;
2260 }
2261
2262 /* Print duration */
2263 len = pj_ansi_snprintf(p, end-p,
2264 "%s Call time: %02dh:%02dm:%02ds, "
2265 "1st res in %d ms, conn in %dms",
2266 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002267 (int)(duration.sec / 3600),
2268 (int)((duration.sec % 3600)/60),
2269 (int)(duration.sec % 60),
2270 (int)PJ_TIME_VAL_MSEC(res_delay),
2271 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002272
2273 if (len > 0 && len < end-p) {
2274 p += len;
2275 *p++ = '\n';
2276 *p = '\0';
2277 }
2278
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002279 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002280 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002281 pjmedia_transport_get_info(call->med_tp, &tp_info);
2282 if (tp_info.specific_info_cnt > 0) {
2283 int i;
2284 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2285 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2286 {
2287 pjmedia_srtp_info *srtp_info =
2288 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2289
2290 len = pj_ansi_snprintf(p, end-p,
2291 "%s SRTP status: %s Crypto-suite: %s",
2292 indent,
2293 (srtp_info->active?"Active":"Not active"),
2294 srtp_info->tx_policy.name.ptr);
2295 if (len > 0 && len < end-p) {
2296 p += len;
2297 *p++ = '\n';
2298 *p = '\0';
2299 }
2300 break;
2301 }
2302 }
2303 }
2304
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002305 /* Dump session statistics */
2306 if (with_media && call->session)
2307 dump_media_session(indent, p, end-p, call->session);
2308
Benny Prijonodc752ca2006-09-22 16:55:42 +00002309 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002310
2311 return PJ_SUCCESS;
2312}
2313
2314
2315/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002316 * This callback receives notification from invite session when the
2317 * session state has changed.
2318 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002319static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2320 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002321{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002322 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002323
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324 PJSUA_LOCK();
2325
Benny Prijonoa1e69682007-05-11 15:14:34 +00002326 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002327
2328 if (!call) {
2329 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002330 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002331 }
2332
Benny Prijonoe21e7842006-04-09 16:46:05 +00002333
2334 /* Get call times */
2335 switch (inv->state) {
2336 case PJSIP_INV_STATE_EARLY:
2337 case PJSIP_INV_STATE_CONNECTING:
2338 if (call->res_time.sec == 0)
2339 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002340 call->last_code = (pjsip_status_code)
2341 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002342 pj_strncpy(&call->last_text,
2343 &e->body.tsx_state.tsx->status_text,
2344 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002345 break;
2346 case PJSIP_INV_STATE_CONFIRMED:
2347 pj_gettimeofday(&call->conn_time);
2348 break;
2349 case PJSIP_INV_STATE_DISCONNECTED:
2350 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002351 if (call->res_time.sec == 0)
2352 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002353 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002354 call->last_code = (pjsip_status_code)
2355 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002356 pj_strncpy(&call->last_text,
2357 &e->body.tsx_state.tsx->status_text,
2358 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002359 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002360 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002361 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002362 call->last_code = (pjsip_status_code)
2363 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002364 pj_strncpy(&call->last_text,
2365 &e->body.tsx_state.tsx->status_text,
2366 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002367 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002368 }
2369
Benny Prijono26ff9062006-02-21 23:47:00 +00002370 /* If this is an outgoing INVITE that was created because of
2371 * REFER/transfer, send NOTIFY to transferer.
2372 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002373 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002374 int st_code = -1;
2375 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2376
2377
Benny Prijonoa91a0032006-02-26 21:23:45 +00002378 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002379 case PJSIP_INV_STATE_NULL:
2380 case PJSIP_INV_STATE_CALLING:
2381 /* Do nothing */
2382 break;
2383
2384 case PJSIP_INV_STATE_EARLY:
2385 case PJSIP_INV_STATE_CONNECTING:
2386 st_code = e->body.tsx_state.tsx->status_code;
2387 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2388 break;
2389
2390 case PJSIP_INV_STATE_CONFIRMED:
2391 /* When state is confirmed, send the final 200/OK and terminate
2392 * subscription.
2393 */
2394 st_code = e->body.tsx_state.tsx->status_code;
2395 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2396 break;
2397
2398 case PJSIP_INV_STATE_DISCONNECTED:
2399 st_code = e->body.tsx_state.tsx->status_code;
2400 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2401 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002402
Benny Prijono8b1889b2006-06-06 18:40:40 +00002403 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002404 /* Nothing to do. Just to keep gcc from complaining about
2405 * unused enums.
2406 */
2407 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002408 }
2409
2410 if (st_code != -1) {
2411 pjsip_tx_data *tdata;
2412 pj_status_t status;
2413
Benny Prijonoa91a0032006-02-26 21:23:45 +00002414 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002415 ev_state, st_code,
2416 NULL, &tdata);
2417 if (status != PJ_SUCCESS) {
2418 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2419 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002420 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002421 if (status != PJ_SUCCESS) {
2422 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2423 }
2424 }
2425 }
2426 }
2427
Benny Prijono84126ab2006-02-09 09:30:09 +00002428
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002429 if (pjsua_var.ua_cfg.cb.on_call_state)
2430 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002431
2432 /* call->inv may be NULL now */
2433
Benny Prijono84126ab2006-02-09 09:30:09 +00002434 /* Destroy media session when invite session is disconnected. */
2435 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002436
Benny Prijonoa91a0032006-02-26 21:23:45 +00002437 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002438
Benny Prijono275fd682006-03-22 11:59:11 +00002439 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002440 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002441
Benny Prijono105217f2006-03-06 16:25:59 +00002442 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002443 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002444 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002445
2446 /* Reset call */
2447 reset_call(call->index);
2448
Benny Prijono84126ab2006-02-09 09:30:09 +00002449 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002450
2451 PJSUA_UNLOCK();
2452}
2453
2454/*
2455 * This callback is called by invite session framework when UAC session
2456 * has forked.
2457 */
2458static void pjsua_call_on_forked( pjsip_inv_session *inv,
2459 pjsip_event *e)
2460{
2461 PJ_UNUSED_ARG(inv);
2462 PJ_UNUSED_ARG(e);
2463
2464 PJ_TODO(HANDLE_FORKED_DIALOG);
2465}
2466
2467
2468/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002469 * Disconnect call upon error.
2470 */
2471static void call_disconnect( pjsip_inv_session *inv,
2472 int code )
2473{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002474 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002475 pjsip_tx_data *tdata;
2476 pj_status_t status;
2477
Benny Prijono59b3aed2008-01-15 16:54:54 +00002478 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2479
Benny Prijonoa38ada02006-07-02 14:22:35 +00002480 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002481 if (status != PJ_SUCCESS)
2482 return;
2483
2484 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00002485 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
2486 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2487 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002488 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002489 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002490
Benny Prijono734fc2d2008-03-17 16:05:35 +00002491 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002492 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002493 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002494 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002495 if (status == PJ_SUCCESS) {
2496 pjsip_create_sdp_body(tdata->pool, local_sdp,
2497 &tdata->msg->body);
2498 }
2499 }
2500
2501 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002502}
2503
2504/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002505 * Callback to be called when SDP offer/answer negotiation has just completed
2506 * in the session. This function will start/update media if negotiation
2507 * has succeeded.
2508 */
2509static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2510 pj_status_t status)
2511{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002512 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00002513 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002514 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002515
2516 PJSUA_LOCK();
2517
Benny Prijonoa1e69682007-05-11 15:14:34 +00002518 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002519
2520 if (status != PJ_SUCCESS) {
2521
2522 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2523
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002524 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002525 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002526
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002527 /* Disconnect call if we're not in the middle of initializing an
2528 * UAS dialog and if this is not a re-INVITE
2529 */
2530 if (inv->state != PJSIP_INV_STATE_NULL &&
2531 inv->state != PJSIP_INV_STATE_CONFIRMED)
2532 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002533 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002534 }
2535
2536 PJSUA_UNLOCK();
2537 return;
2538 }
2539
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002540
2541 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00002542 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002543 if (status != PJ_SUCCESS) {
2544 pjsua_perror(THIS_FILE,
2545 "Unable to retrieve currently active local SDP",
2546 status);
2547 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2548 PJSUA_UNLOCK();
2549 return;
2550 }
2551
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002552 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2553 if (status != PJ_SUCCESS) {
2554 pjsua_perror(THIS_FILE,
2555 "Unable to retrieve currently active remote SDP",
2556 status);
2557 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2558 PJSUA_UNLOCK();
2559 return;
2560 }
2561
Benny Prijono91a6a172007-10-31 08:59:29 +00002562 /* Update remote's NAT type */
2563 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2564 update_remote_nat_type(call, remote_sdp);
2565 }
2566
2567 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002568 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002569 if (status != PJ_SUCCESS) {
2570 pjsua_perror(THIS_FILE, "Unable to create media session",
2571 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002572 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002573 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002574 PJSUA_UNLOCK();
2575 return;
2576 }
2577
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002578
2579 /* Call application callback, if any */
2580 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2581 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2582
2583
2584 PJSUA_UNLOCK();
2585}
2586
2587
2588/*
2589 * Create inactive SDP for call hold.
2590 */
2591static pj_status_t create_inactive_sdp(pjsua_call *call,
2592 pjmedia_sdp_session **p_answer)
2593{
2594 pj_status_t status;
2595 pjmedia_sdp_conn *conn;
2596 pjmedia_sdp_attr *attr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002597 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002598 pjmedia_sdp_session *sdp;
2599
Benny Prijono617c5bc2007-04-02 19:51:21 +00002600 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002601 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002602 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijono617c5bc2007-04-02 19:51:21 +00002603
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002604 /* Create new offer */
2605 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002606 &tp_info.sock_info, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002607 if (status != PJ_SUCCESS) {
2608 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2609 return status;
2610 }
2611
2612 /* Get SDP media connection line */
2613 conn = sdp->media[0]->conn;
2614 if (!conn)
2615 conn = sdp->conn;
2616
2617 /* Modify address */
2618 conn->addr = pj_str("0.0.0.0");
2619
2620 /* Remove existing directions attributes */
2621 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2622 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2623 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2624 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2625
2626 /* Add inactive attribute */
2627 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2628 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2629
2630 *p_answer = sdp;
2631
2632 return status;
2633}
2634
2635
2636/*
2637 * Called when session received new offer.
2638 */
2639static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2640 const pjmedia_sdp_session *offer)
2641{
2642 const char *remote_state;
2643 pjsua_call *call;
2644 pjmedia_sdp_conn *conn;
2645 pjmedia_sdp_session *answer;
2646 pj_bool_t is_remote_active;
2647 pj_status_t status;
2648
2649 PJSUA_LOCK();
2650
Benny Prijonoa1e69682007-05-11 15:14:34 +00002651 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002652
2653 /*
2654 * See if remote is offering active media (i.e. not on-hold)
2655 */
2656 is_remote_active = PJ_TRUE;
2657
2658 conn = offer->media[0]->conn;
2659 if (!conn)
2660 conn = offer->conn;
2661
2662 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2663 pj_strcmp2(&conn->addr, "0")==0)
2664 {
2665 is_remote_active = PJ_FALSE;
2666
2667 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002668 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2669 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002670 {
2671 is_remote_active = PJ_FALSE;
2672 }
2673
2674 remote_state = (is_remote_active ? "active" : "inactive");
2675
2676 /* Supply candidate answer */
2677 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2678 PJ_LOG(4,(THIS_FILE,
2679 "Call %d: RX new media offer, creating inactive SDP "
2680 "(media in offer is %s)", call->index, remote_state));
2681 status = create_inactive_sdp( call, &answer );
2682 } else {
2683 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2684 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002685
Benny Prijonod8179652008-01-23 20:39:07 +00002686 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002687 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002688 }
2689
2690 if (status != PJ_SUCCESS) {
2691 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2692 PJSUA_UNLOCK();
2693 return;
2694 }
2695
2696 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2697 if (status != PJ_SUCCESS) {
2698 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2699 PJSUA_UNLOCK();
2700 return;
2701 }
2702
2703 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002704}
2705
2706
2707/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002708 * Called to generate new offer.
2709 */
2710static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2711 pjmedia_sdp_session **offer)
2712{
2713 pjsua_call *call;
2714 pj_status_t status;
2715
2716 PJSUA_LOCK();
2717
2718 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2719
2720 /* See if we've put call on hold. */
2721 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2722 PJ_LOG(4,(THIS_FILE,
2723 "Call %d: call is on-hold locally, creating inactive SDP ",
2724 call->index));
2725 status = create_inactive_sdp( call, offer );
2726 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00002727 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2728 call->index));
2729
Benny Prijonod8179652008-01-23 20:39:07 +00002730 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002731 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002732 }
2733
2734 if (status != PJ_SUCCESS) {
2735 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2736 PJSUA_UNLOCK();
2737 return;
2738 }
2739
Benny Prijono7129cc72007-11-05 05:54:25 +00002740 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002741
2742 PJSUA_UNLOCK();
2743}
2744
2745
2746/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002747 * Callback called by event framework when the xfer subscription state
2748 * has changed.
2749 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002750static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2751{
2752
2753 PJ_UNUSED_ARG(event);
2754
2755 /*
2756 * When subscription is accepted (got 200/OK to REFER), check if
2757 * subscription suppressed.
2758 */
2759 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2760
2761 pjsip_rx_data *rdata;
2762 pjsip_generic_string_hdr *refer_sub;
2763 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2764 pjsua_call *call;
2765
Benny Prijonoa1e69682007-05-11 15:14:34 +00002766 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002767
2768 /* Must be receipt of response message */
2769 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2770 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2771 rdata = event->body.tsx_state.src.rdata;
2772
2773 /* Find Refer-Sub header */
2774 refer_sub = (pjsip_generic_string_hdr*)
2775 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2776 &REFER_SUB, NULL);
2777
2778 /* Check if subscription is suppressed */
2779 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2780 /* Since no subscription is desired, assume that call has been
2781 * transfered successfully.
2782 */
2783 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2784 const pj_str_t ACCEPTED = { "Accepted", 8 };
2785 pj_bool_t cont = PJ_FALSE;
2786 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2787 200,
2788 &ACCEPTED,
2789 PJ_TRUE,
2790 &cont);
2791 }
2792
2793 /* Yes, subscription is suppressed.
2794 * Terminate our subscription now.
2795 */
2796 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2797 "event subcription..."));
2798 pjsip_evsub_terminate(sub, PJ_TRUE);
2799
2800 } else {
2801 /* Notify application about call transfer progress.
2802 * Initially notify with 100/Accepted status.
2803 */
2804 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2805 const pj_str_t ACCEPTED = { "Accepted", 8 };
2806 pj_bool_t cont = PJ_FALSE;
2807 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2808 100,
2809 &ACCEPTED,
2810 PJ_FALSE,
2811 &cont);
2812 }
2813 }
2814 }
2815 /*
2816 * On incoming NOTIFY, notify application about call transfer progress.
2817 */
2818 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2819 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2820 {
2821 pjsua_call *call;
2822 pjsip_msg *msg;
2823 pjsip_msg_body *body;
2824 pjsip_status_line status_line;
2825 pj_bool_t is_last;
2826 pj_bool_t cont;
2827 pj_status_t status;
2828
Benny Prijonoa1e69682007-05-11 15:14:34 +00002829 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002830
2831 /* When subscription is terminated, clear the xfer_sub member of
2832 * the inv_data.
2833 */
2834 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2835 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2836 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2837
2838 }
2839
2840 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2841 /* Application is not interested with call progress status */
2842 return;
2843 }
2844
2845 /* This better be a NOTIFY request */
2846 if (event->type == PJSIP_EVENT_TSX_STATE &&
2847 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2848 {
2849 pjsip_rx_data *rdata;
2850
2851 rdata = event->body.tsx_state.src.rdata;
2852
2853 /* Check if there's body */
2854 msg = rdata->msg_info.msg;
2855 body = msg->body;
2856 if (!body) {
2857 PJ_LOG(4,(THIS_FILE,
2858 "Warning: received NOTIFY without message body"));
2859 return;
2860 }
2861
2862 /* Check for appropriate content */
2863 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2864 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2865 {
2866 PJ_LOG(4,(THIS_FILE,
2867 "Warning: received NOTIFY with non message/sipfrag "
2868 "content"));
2869 return;
2870 }
2871
2872 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002873 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002874 &status_line);
2875 if (status != PJ_SUCCESS) {
2876 PJ_LOG(4,(THIS_FILE,
2877 "Warning: received NOTIFY with invalid "
2878 "message/sipfrag content"));
2879 return;
2880 }
2881
2882 } else {
2883 status_line.code = 500;
2884 status_line.reason = *pjsip_get_status_text(500);
2885 }
2886
2887 /* Notify application */
2888 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2889 cont = !is_last;
2890 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2891 status_line.code,
2892 &status_line.reason,
2893 is_last, &cont);
2894
2895 if (!cont) {
2896 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2897 }
2898 }
2899}
2900
2901
2902/*
2903 * Callback called by event framework when the xfer subscription state
2904 * has changed.
2905 */
2906static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002907{
2908
2909 PJ_UNUSED_ARG(event);
2910
2911 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002912 * When subscription is terminated, clear the xfer_sub member of
2913 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002914 */
2915 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002916 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002917
Benny Prijonoa1e69682007-05-11 15:14:34 +00002918 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002919 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002920 return;
2921
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002922 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002923 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002924
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002925 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002926 }
2927}
2928
2929
2930/*
2931 * Follow transfer (REFER) request.
2932 */
2933static void on_call_transfered( pjsip_inv_session *inv,
2934 pjsip_rx_data *rdata )
2935{
2936 pj_status_t status;
2937 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002938 pjsua_call *existing_call;
2939 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002940 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002941 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002942 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002943 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002944 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002945 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002946 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002947 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002948 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002949 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002950 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002951 pjsip_evsub *sub;
2952
Benny Prijonoa1e69682007-05-11 15:14:34 +00002953 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002954
Benny Prijono26ff9062006-02-21 23:47:00 +00002955 /* Find the Refer-To header */
2956 refer_to = (pjsip_generic_string_hdr*)
2957 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2958
2959 if (refer_to == NULL) {
2960 /* Invalid Request.
2961 * No Refer-To header!
2962 */
2963 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002964 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002965 return;
2966 }
2967
Benny Prijonoc8141a82006-08-20 09:12:19 +00002968 /* Find optional Refer-Sub header */
2969 refer_sub = (pjsip_generic_string_hdr*)
2970 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2971
2972 if (refer_sub) {
2973 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2974 no_refer_sub = PJ_TRUE;
2975 }
2976
Benny Prijono053f5222006-11-11 16:16:04 +00002977 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2978 * request.
2979 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002980 ref_by_hdr = (pjsip_hdr*)
2981 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002982 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002983
Benny Prijono9fc735d2006-05-28 14:58:12 +00002984 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00002985 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002986 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2987 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2988 &refer_to->hvalue,
2989 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002990
2991 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00002992 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002993 if (code >= 300) {
2994 /* Application rejects call transfer request */
2995 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2996 return;
2997 }
2998
Benny Prijono26ff9062006-02-21 23:47:00 +00002999 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3000 (int)inv->dlg->remote.info_str.slen,
3001 inv->dlg->remote.info_str.ptr,
3002 (int)refer_to->hvalue.slen,
3003 refer_to->hvalue.ptr));
3004
Benny Prijonoc8141a82006-08-20 09:12:19 +00003005 if (no_refer_sub) {
3006 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003007 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003008 */
3009 pjsip_tx_data *tdata;
3010 const pj_str_t str_false = { "false", 5};
3011 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003012
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003013 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3014 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003015 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003016 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003017 status);
3018 return;
3019 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003020
Benny Prijonoc8141a82006-08-20 09:12:19 +00003021 /* Add Refer-Sub header */
3022 hdr = (pjsip_hdr*)
3023 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3024 &str_false);
3025 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003026
Benny Prijono26ff9062006-02-21 23:47:00 +00003027
Benny Prijonoc8141a82006-08-20 09:12:19 +00003028 /* Send answer */
3029 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3030 tdata);
3031 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003032 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003033 status);
3034 return;
3035 }
3036
3037 /* Don't have subscription */
3038 sub = NULL;
3039
3040 } else {
3041 struct pjsip_evsub_user xfer_cb;
3042 pjsip_hdr hdr_list;
3043
3044 /* Init callback */
3045 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003046 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003047
3048 /* Init additional header list to be sent with REFER response */
3049 pj_list_init(&hdr_list);
3050
3051 /* Create transferee event subscription */
3052 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3053 if (status != PJ_SUCCESS) {
3054 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3055 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3056 return;
3057 }
3058
3059 /* If there's Refer-Sub header and the value is "true", send back
3060 * Refer-Sub in the response with value "true" too.
3061 */
3062 if (refer_sub) {
3063 const pj_str_t str_true = { "true", 4 };
3064 pjsip_hdr *hdr;
3065
3066 hdr = (pjsip_hdr*)
3067 pjsip_generic_string_hdr_create(inv->dlg->pool,
3068 &str_refer_sub,
3069 &str_true);
3070 pj_list_push_back(&hdr_list, hdr);
3071
3072 }
3073
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003074 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003075 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3076
3077 /* Create initial NOTIFY request */
3078 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3079 100, NULL, &tdata);
3080 if (status != PJ_SUCCESS) {
3081 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3082 status);
3083 return;
3084 }
3085
3086 /* Send initial NOTIFY request */
3087 status = pjsip_xfer_send_request( sub, tdata);
3088 if (status != PJ_SUCCESS) {
3089 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3090 return;
3091 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003092 }
3093
3094 /* We're cheating here.
3095 * We need to get a null terminated string from a pj_str_t.
3096 * So grab the pointer from the hvalue and NULL terminate it, knowing
3097 * that the NULL position will be occupied by a newline.
3098 */
3099 uri = refer_to->hvalue.ptr;
3100 uri[refer_to->hvalue.slen] = '\0';
3101
Benny Prijono053f5222006-11-11 16:16:04 +00003102 /* Init msg_data */
3103 pjsua_msg_data_init(&msg_data);
3104
3105 /* If Referred-By header is present in the REFER request, copy this
3106 * to the outgoing INVITE request.
3107 */
3108 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003109 pjsip_hdr *dup = (pjsip_hdr*)
3110 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003111 pj_list_push_back(&msg_data.hdr_list, dup);
3112 }
3113
Benny Prijono26ff9062006-02-21 23:47:00 +00003114 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003115 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003116 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003117 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003118 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003119 if (status != PJ_SUCCESS) {
3120
Benny Prijonoc8141a82006-08-20 09:12:19 +00003121 /* Notify xferer about the error (if we have subscription) */
3122 if (sub) {
3123 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3124 500, NULL, &tdata);
3125 if (status != PJ_SUCCESS) {
3126 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3127 status);
3128 return;
3129 }
3130 status = pjsip_xfer_send_request(sub, tdata);
3131 if (status != PJ_SUCCESS) {
3132 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3133 status);
3134 return;
3135 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003136 }
3137 return;
3138 }
3139
Benny Prijonoc8141a82006-08-20 09:12:19 +00003140 if (sub) {
3141 /* Put the server subscription in inv_data.
3142 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3143 * reported back to the server subscription.
3144 */
3145 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003146
Benny Prijonoc8141a82006-08-20 09:12:19 +00003147 /* Put the invite_data in the subscription. */
3148 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3149 &pjsua_var.calls[new_call]);
3150 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003151}
3152
3153
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003154
Benny Prijono26ff9062006-02-21 23:47:00 +00003155/*
3156 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003157 * session. We use this to trap:
3158 * - incoming REFER request.
3159 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003160 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003161static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3162 pjsip_transaction *tsx,
3163 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003164{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003165 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003166
3167 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003168
Benny Prijonofeb69f42007-10-05 09:12:26 +00003169 /* Notify application callback first */
3170 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3171 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3172 }
3173
Benny Prijono26ff9062006-02-21 23:47:00 +00003174 if (tsx->role==PJSIP_ROLE_UAS &&
3175 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003176 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003177 {
3178 /*
3179 * Incoming REFER request.
3180 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003181 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003182
Benny Prijono26ff9062006-02-21 23:47:00 +00003183 }
Benny Prijonob0808372006-03-02 21:18:58 +00003184 else if (tsx->role==PJSIP_ROLE_UAS &&
3185 tsx->state==PJSIP_TSX_STATE_TRYING &&
3186 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3187 {
3188 /*
3189 * Incoming MESSAGE request!
3190 */
3191 pjsip_rx_data *rdata;
3192 pjsip_msg *msg;
3193 pjsip_accept_hdr *accept_hdr;
3194 pj_status_t status;
3195
3196 rdata = e->body.tsx_state.src.rdata;
3197 msg = rdata->msg_info.msg;
3198
3199 /* Request MUST have message body, with Content-Type equal to
3200 * "text/plain".
3201 */
3202 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3203
3204 pjsip_hdr hdr_list;
3205
3206 pj_list_init(&hdr_list);
3207 pj_list_push_back(&hdr_list, accept_hdr);
3208
3209 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3210 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003211 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003212 return;
3213 }
3214
3215 /* Respond with 200 first, so that remote doesn't retransmit in case
3216 * the UI takes too long to process the message.
3217 */
3218 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3219
3220 /* Process MESSAGE request */
3221 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3222 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003223
Benny Prijonob0808372006-03-02 21:18:58 +00003224 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003225 else if (tsx->role == PJSIP_ROLE_UAC &&
3226 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003227 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003228 /* Handle outgoing pager status */
3229 if (tsx->status_code >= 200) {
3230 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003231
Benny Prijonoa1e69682007-05-11 15:14:34 +00003232 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003233 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003234
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003235 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3236 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3237 &im_data->to,
3238 &im_data->body,
3239 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003240 (pjsip_status_code)
3241 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003242 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003243 }
Benny Prijonofccab712006-02-22 22:23:22 +00003244 }
Benny Prijono834aee32006-02-19 01:38:06 +00003245 }
Benny Prijono834aee32006-02-19 01:38:06 +00003246
Benny Prijono26ff9062006-02-21 23:47:00 +00003247
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003248 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003249}