blob: 6cf63f003b152cb0557d0111be9b78fdbd290f27 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 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 Prijono1f7767b2007-10-03 18:28:49 +0000215#define LATE_SDP 0
216
Benny Prijono5773cd62008-01-19 13:01:42 +0000217/* Allocate one call id */
218static pjsua_call_id alloc_call_id(void)
219{
220 pjsua_call_id cid;
221
222#if 1
223 /* New algorithm: round-robin */
224 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
225 pjsua_var.next_call_id < 0)
226 {
227 cid = 0;
228 }
229
230 for (cid=pjsua_var.next_call_id;
231 cid<(int)pjsua_var.ua_cfg.max_calls;
232 ++cid)
233 {
234 if (pjsua_var.calls[cid].inv == NULL) {
235 ++pjsua_var.next_call_id;
236 return cid;
237 }
238 }
239
240 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
241 if (pjsua_var.calls[cid].inv == NULL) {
242 ++pjsua_var.next_call_id;
243 return cid;
244 }
245 }
246
247#else
248 /* Old algorithm */
249 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
250 if (pjsua_var.calls[cid].inv == NULL)
251 return cid;
252 }
253#endif
254
255 return PJSUA_INVALID_ID;
256}
257
Benny Prijonod8179652008-01-23 20:39:07 +0000258/* Get signaling secure level.
259 * Return:
260 * 0: if signaling is not secure
261 * 1: if TLS transport is used for immediate hop
262 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000263 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000264static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000265{
266 const pj_str_t tls = pj_str(";transport=tls");
267 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000268 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000269
270 if (pj_stristr(dst_uri, &sips))
271 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000272
273 if (!pj_list_empty(&acc->route_set)) {
274 pjsip_route_hdr *r = acc->route_set.next;
275 pjsip_uri *uri = r->name_addr.uri;
276 pjsip_sip_uri *sip_uri;
277
278 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
279 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
280 return 1;
281
282 } else {
283 if (pj_stristr(dst_uri, &tls))
284 return 1;
285 }
286
Benny Prijonod8179652008-01-23 20:39:07 +0000287 return 0;
288}
289
Benny Prijonodb844a42008-02-02 17:07:18 +0000290static int call_get_secure_level(pjsua_call *call)
291{
292 if (call->inv->dlg->secure)
293 return 2;
294
295 if (!pj_list_empty(&call->inv->dlg->route_set)) {
296 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
297 pjsip_uri *uri = r->name_addr.uri;
298 pjsip_sip_uri *sip_uri;
299
300 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
301 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
302 return 1;
303
304 } else {
305 pjsip_sip_uri *sip_uri;
306
307 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
308 return 2;
309 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
310 return 0;
311
312 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
313 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
314 return 1;
315 }
316
317 return 0;
318}
319
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
351 acc = &pjsua_var.acc[acc_id];
352 if (!acc->valid) {
353 pjsua_perror(THIS_FILE, "Unable to make call because account "
354 "is not valid", PJ_EINVALIDOP);
355 PJSUA_UNLOCK();
356 return PJ_EINVALIDOP;
357 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000358
Benny Prijonoa91a0032006-02-26 21:23:45 +0000359 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000360 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000361
Benny Prijono5773cd62008-01-19 13:01:42 +0000362 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
364 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000365 return PJ_ETOOMANY;
366 }
367
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 call = &pjsua_var.calls[call_id];
369
Benny Prijono320fa4d2006-12-07 10:09:16 +0000370 /* Verify that destination URI is valid before calling
371 * pjsua_acc_create_uac_contact, or otherwise there
372 * a misleading "Invalid Contact URI" error will be printed
373 * when pjsua_acc_create_uac_contact() fails.
374 */
375 if (1) {
376 pj_pool_t *pool;
377 pjsip_uri *uri;
378 pj_str_t dup;
379
380 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
381 if (!pool) {
382 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
383 PJSUA_UNLOCK();
384 return PJ_ENOMEM;
385 }
386
387 pj_strdup_with_null(pool, &dup, dest_uri);
388 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
389 pj_pool_release(pool);
390
391 if (uri == NULL) {
392 pjsua_perror(THIS_FILE, "Unable to make call",
393 PJSIP_EINVALIDREQURI);
394 PJSUA_UNLOCK();
395 return PJSIP_EINVALIDREQURI;
396 }
397 }
398
Benny Prijono093d3022006-09-24 00:07:11 +0000399 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
400 (int)dest_uri->slen, dest_uri->ptr));
401
Benny Prijonoe21e7842006-04-09 16:46:05 +0000402 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000404
Benny Prijonoe21e7842006-04-09 16:46:05 +0000405 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000406 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000407
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000408 /* Create suitable Contact header */
409 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
410 acc_id, dest_uri);
411 if (status != PJ_SUCCESS) {
412 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
413 PJSUA_UNLOCK();
414 return status;
415 }
416
Benny Prijonoe21e7842006-04-09 16:46:05 +0000417 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000418 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000419 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000421 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000422 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000424 return status;
425 }
426
Benny Prijonodb844a42008-02-02 17:07:18 +0000427 /* Calculate call's secure level */
428 call->secure_level = get_secure_level(acc_id, dest_uri);
429
Benny Prijonoc97608e2007-03-23 16:34:20 +0000430 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000431 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +0000432 call->secure_level, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000433 if (status != PJ_SUCCESS) {
434 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
435 goto on_error;
436 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000437
Benny Prijonoc97608e2007-03-23 16:34:20 +0000438 /* Create SDP offer */
Benny Prijono1f7767b2007-10-03 18:28:49 +0000439#if LATE_SDP
440 offer = NULL;
441#else
Benny Prijono25b2ea12008-01-24 19:20:54 +0000442 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
443 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000444 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000445 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000446 goto on_error;
447 }
Benny Prijono1f7767b2007-10-03 18:28:49 +0000448#endif
Benny Prijono84126ab2006-02-09 09:30:09 +0000449
450 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000451 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000452 if (acc->cfg.require_100rel)
453 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000454
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000455 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000456 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000457 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 goto on_error;
459 }
460
461
462 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000463 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000464 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000465
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 dlg->mod_data[pjsua_var.mod.id] = call;
467 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000468
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000469 /* Attach user data */
470 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000471
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000472 /* If account is locked to specific transport, then lock dialog
473 * to this transport too.
474 */
475 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
476 pjsip_tpselector tp_sel;
477
478 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
479 pjsip_dlg_set_transport(dlg, &tp_sel);
480 }
481
Benny Prijono84126ab2006-02-09 09:30:09 +0000482 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483 if (!pj_list_empty(&acc->route_set))
484 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000485
486
487 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000488 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000489 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000490 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000491 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000492
Benny Prijono48ab2b72007-11-08 09:24:30 +0000493 /* Set authentication preference */
494 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000495
496 /* Create initial INVITE: */
497
498 status = pjsip_inv_invite(inv, &tdata);
499 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000500 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
501 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000502 goto on_error;
503 }
504
505
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000506 /* Add additional headers etc */
507
508 pjsua_process_msg_data( tdata, msg_data);
509
Benny Prijono093d3022006-09-24 00:07:11 +0000510 /* Must increment call counter now */
511 ++pjsua_var.call_cnt;
512
Benny Prijono84126ab2006-02-09 09:30:09 +0000513 /* Send initial INVITE: */
514
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000515 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000516 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000517 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
518 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000519
520 /* Upon failure to send first request, both dialog and invite
521 * session would have been cleared.
522 */
523 inv = NULL;
524 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000525 goto on_error;
526 }
527
Benny Prijono84126ab2006-02-09 09:30:09 +0000528 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000529
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000530 if (p_call_id)
531 *p_call_id = call_id;
532
533 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000534
535 return PJ_SUCCESS;
536
537
538on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000539 if (inv != NULL) {
540 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000541 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000542 pjsip_dlg_terminate(dlg);
543 }
544
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 if (call_id != -1) {
546 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000547 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000548 }
549
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000550 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000551 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000552}
553
554
Benny Prijono91a6a172007-10-31 08:59:29 +0000555/* Get the NAT type information in remote's SDP */
556static void update_remote_nat_type(pjsua_call *call,
557 const pjmedia_sdp_session *sdp)
558{
559 const pjmedia_sdp_attr *xnat;
560
561 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
562 if (xnat) {
563 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
564 } else {
565 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
566 }
567
568 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
569 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
570}
571
572
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000574 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000575 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000576 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000577pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000578{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000579 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000580 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000581 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000582 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
583 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000584 pjsip_tx_data *response = NULL;
585 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000586 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587 int acc_id;
588 pjsua_call *call;
589 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000590 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000591 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000592 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000593
Benny Prijono26ff9062006-02-21 23:47:00 +0000594 /* Don't want to handle anything but INVITE */
595 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
596 return PJ_FALSE;
597
598 /* Don't want to handle anything that's already associated with
599 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000600 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000601 if (dlg || tsx)
602 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000603
Benny Prijono148c9dd2006-09-19 13:37:53 +0000604 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000605
Benny Prijono26ff9062006-02-21 23:47:00 +0000606 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000607 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000608
Benny Prijono5773cd62008-01-19 13:01:42 +0000609 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000610 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000611 PJSIP_SC_BUSY_HERE, NULL,
612 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000613 PJ_LOG(2,(THIS_FILE,
614 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000615 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000616 return PJ_TRUE;
617 }
618
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 /* Clear call descriptor */
620 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000621
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000622 call = &pjsua_var.calls[call_id];
623
624 /* Mark call start time. */
625 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000626
Benny Prijono053f5222006-11-11 16:16:04 +0000627 /* Check INVITE request for Replaces header. If Replaces header is
628 * present, the function will make sure that we can handle the request.
629 */
630 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
631 &response);
632 if (status != PJ_SUCCESS) {
633 /*
634 * Something wrong with the Replaces header.
635 */
636 if (response) {
637 pjsip_response_addr res_addr;
638
639 pjsip_get_response_addr(response->pool, rdata, &res_addr);
640 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
641 NULL, NULL);
642
643 } else {
644
645 /* Respond with 500 (Internal Server Error) */
646 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
647 NULL, NULL);
648 }
649
650 PJSUA_UNLOCK();
651 return PJ_TRUE;
652 }
653
654 /* If this INVITE request contains Replaces header, notify application
655 * about the request so that application can do subsequent checking
656 * if it wants to.
657 */
658 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
659 pjsua_call *replaced_call;
660 int st_code = 200;
661 pj_str_t st_text = { "OK", 2 };
662
663 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000664 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000665
666 /* Notify application */
667 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
668 rdata, &st_code, &st_text);
669
670 /* Must specify final response */
671 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
672
673 /* Check if application rejects this request. */
674 if (st_code >= 300) {
675
676 if (st_text.slen == 2)
677 st_text = *pjsip_get_status_text(st_code);
678
679 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
680 st_code, &st_text, NULL, NULL, NULL);
681 PJSUA_UNLOCK();
682 return PJ_TRUE;
683 }
684 }
685
Benny Prijonod8179652008-01-23 20:39:07 +0000686 /*
687 * Get which account is most likely to be associated with this incoming
688 * call. We need the account to find which contact URI to put for
689 * the call.
690 */
691 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
692
Benny Prijonodb844a42008-02-02 17:07:18 +0000693 /* Get call's secure level */
694 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
695 call->secure_level = 2;
696 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
697 call->secure_level = 1;
698 else
699 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000700
Benny Prijonoc97608e2007-03-23 16:34:20 +0000701 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000702 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
Benny Prijonodb844a42008-02-02 17:07:18 +0000703 call->secure_level, &sip_err_code);
Benny Prijono26ff9062006-02-21 23:47:00 +0000704 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000705 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
706 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
707 sip_err_code, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000708 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000709 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000710 return PJ_TRUE;
711 }
712
Benny Prijonod8179652008-01-23 20:39:07 +0000713 /* Parse SDP from incoming request */
714 if (rdata->msg_info.msg->body) {
715 status = pjmedia_sdp_parse(rdata->tp_info.pool,
716 rdata->msg_info.msg->body->data,
717 rdata->msg_info.msg->body->len, &offer);
718 if (status != PJ_SUCCESS) {
719 pjsua_perror(THIS_FILE, "Error parsing SDP in incoming INVITE", status);
720 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
721 NULL, NULL);
722 pjsua_media_channel_deinit(call->index);
723 PJSUA_UNLOCK();
724 return PJ_TRUE;
725 }
726 } else {
727 offer = NULL;
728 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000729
Benny Prijonoc97608e2007-03-23 16:34:20 +0000730 /* Get media capability from media endpoint: */
Benny Prijonod8179652008-01-23 20:39:07 +0000731 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000732 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000733 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000734 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
735 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
736 sip_err_code, NULL,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000737 NULL, NULL);
738 pjsua_media_channel_deinit(call->index);
739 PJSUA_UNLOCK();
740 return PJ_TRUE;
741 }
742
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000743 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000744 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000745 if (pjsua_var.acc[acc_id].cfg.require_100rel)
746 options |= PJSIP_INV_REQUIRE_100REL;
747
Benny Prijonod8179652008-01-23 20:39:07 +0000748 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
749 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000750 if (status != PJ_SUCCESS) {
751
752 /*
753 * No we can't handle the incoming INVITE request.
754 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000755 if (response) {
756 pjsip_response_addr res_addr;
757
758 pjsip_get_response_addr(response->pool, rdata, &res_addr);
759 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
760 NULL, NULL);
761
762 } else {
763
764 /* Respond with 500 (Internal Server Error) */
765 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
766 NULL, NULL);
767 }
768
Benny Prijonoc97608e2007-03-23 16:34:20 +0000769 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000770 PJSUA_UNLOCK();
771 return PJ_TRUE;
772 }
773
774
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000775 /* Get suitable Contact header */
776 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
777 acc_id, rdata);
778 if (status != PJ_SUCCESS) {
779 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
780 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
781 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000782 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000783 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000784 return PJ_TRUE;
785 }
786
Benny Prijono26ff9062006-02-21 23:47:00 +0000787 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000788 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000789 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000790 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000791 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000792 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000793 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000794 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000795 return PJ_TRUE;
796 }
797
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000798 /* Set credentials */
799 if (pjsua_var.acc[acc_id].cred_cnt) {
800 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
801 pjsua_var.acc[acc_id].cred_cnt,
802 pjsua_var.acc[acc_id].cred);
803 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000804
Benny Prijono48ab2b72007-11-08 09:24:30 +0000805 /* Set preference */
806 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
807 &pjsua_var.acc[acc_id].cfg.auth_pref);
808
Benny Prijono26ff9062006-02-21 23:47:00 +0000809 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000810 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000811 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000812 pjsip_hdr hdr_list;
813 pjsip_warning_hdr *w;
814
815 w = pjsip_warning_hdr_create_from_status(dlg->pool,
816 pjsip_endpt_name(pjsua_var.endpt),
817 status);
818 pj_list_init(&hdr_list);
819 pj_list_push_back(&hdr_list, w);
820
821 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
822
823 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000824 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000825 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000826 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000827 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000828 return PJ_TRUE;
829 }
830
Benny Prijonoea9fd392007-11-06 03:41:40 +0000831 /* Update NAT type of remote endpoint, only when there is SDP in
832 * incoming INVITE!
833 */
834 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
835 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
836 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000837 const pjmedia_sdp_session *remote_sdp;
838
839 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
840 update_remote_nat_type(call, remote_sdp);
841 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000842
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000843 /* Create and attach pjsua_var data to the dialog: */
844 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000845
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000846 dlg->mod_data[pjsua_var.mod.id] = call;
847 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000848
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000849 /* If account is locked to specific transport, then lock dialog
850 * to this transport too.
851 */
852 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
853 pjsip_tpselector tp_sel;
854
855 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
856 pjsip_dlg_set_transport(dlg, &tp_sel);
857 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000858
Benny Prijono64f851e2006-02-23 13:49:28 +0000859 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000860 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000861 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000862 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000863 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000864 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
865 status);
866
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000867 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
868 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000869 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000870 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000871 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000872
873 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000874 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000875 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000876 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000877 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000878 }
879
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000881
Benny Prijono105217f2006-03-06 16:25:59 +0000882
Benny Prijono053f5222006-11-11 16:16:04 +0000883 /* Check if this request should replace existing call */
884 if (replaced_dlg) {
885 pjsip_inv_session *replaced_inv;
886 struct pjsua_call *replaced_call;
887 pjsip_tx_data *tdata;
888
889 /* Get the invite session in the dialog */
890 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
891
892 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000893 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000894
895 /* Notify application */
896 if (pjsua_var.ua_cfg.cb.on_call_replaced)
897 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
898 call_id);
899
900 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
901 call_id));
902
903 /* Answer the new call with 200 response */
904 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
905 if (status == PJ_SUCCESS)
906 status = pjsip_inv_send_msg(inv, tdata);
907
908 if (status != PJ_SUCCESS)
909 pjsua_perror(THIS_FILE, "Error answering session", status);
910
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000911 /* Note that inv may be invalid if 200/OK has caused error in
912 * starting the media.
913 */
Benny Prijono053f5222006-11-11 16:16:04 +0000914
915 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
916 replaced_call->index));
917
918 /* Disconnect replaced invite session */
919 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
920 &tdata);
921 if (status == PJ_SUCCESS && tdata)
922 status = pjsip_inv_send_msg(replaced_inv, tdata);
923
924 if (status != PJ_SUCCESS)
925 pjsua_perror(THIS_FILE, "Error terminating session", status);
926
927
928 } else {
929
Benny Prijonob5388cf2007-01-04 22:45:08 +0000930 /* Notify application if on_incoming_call() is overriden,
931 * otherwise hangup the call with 480
932 */
933 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000934 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000935 } else {
936 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
937 NULL, NULL);
938 }
Benny Prijono053f5222006-11-11 16:16:04 +0000939 }
940
Benny Prijono8b1889b2006-06-06 18:40:40 +0000941
Benny Prijono26ff9062006-02-21 23:47:00 +0000942 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000943 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000944 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000945}
946
947
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000948
949/*
950 * Check if the specified call has active INVITE session and the INVITE
951 * session has not been disconnected.
952 */
953PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
954{
955 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
956 PJ_EINVAL);
957 return pjsua_var.calls[call_id].inv != NULL &&
958 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
959}
960
961
962/*
963 * Check if call has an active media session.
964 */
965PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
966{
967 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
968 PJ_EINVAL);
969 return pjsua_var.calls[call_id].session != NULL;
970}
971
972
Benny Prijono148c9dd2006-09-19 13:37:53 +0000973/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000974pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000975 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000976 pjsua_call **p_call,
977 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000978{
979 enum { MAX_RETRY=50 };
980 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000981 pjsua_call *call = NULL;
982 pj_bool_t has_pjsua_lock = PJ_FALSE;
983 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000984
985 for (retry=0; retry<MAX_RETRY; ++retry) {
986
987 has_pjsua_lock = PJ_FALSE;
988
989 status = PJSUA_TRY_LOCK();
990 if (status != PJ_SUCCESS) {
991 pj_thread_sleep(retry/10);
992 continue;
993 }
994
995 has_pjsua_lock = PJ_TRUE;
996 call = &pjsua_var.calls[call_id];
997
998 if (call->inv == NULL) {
999 PJSUA_UNLOCK();
1000 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1001 return PJSIP_ESESSIONTERMINATED;
1002 }
1003
1004 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1005 if (status != PJ_SUCCESS) {
1006 PJSUA_UNLOCK();
1007 pj_thread_sleep(retry/10);
1008 continue;
1009 }
1010
1011 PJSUA_UNLOCK();
1012
1013 break;
1014 }
1015
1016 if (status != PJ_SUCCESS) {
1017 if (has_pjsua_lock == PJ_FALSE)
1018 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1019 "(possibly system has deadlocked) in %s",
1020 title));
1021 else
1022 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1023 "(possibly system has deadlocked) in %s",
1024 title));
1025 return PJ_ETIMEDOUT;
1026 }
1027
1028 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001029 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001030
1031 return PJ_SUCCESS;
1032}
1033
1034
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001035/*
1036 * Get the conference port identification associated with the call.
1037 */
1038PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1039{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001040 pjsua_call *call;
1041 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001042 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001043 pj_status_t status;
1044
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001045 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1046 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001047
Benny Prijonodc752ca2006-09-22 16:55:42 +00001048 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001049 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001050 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001051
1052 port_id = call->conf_slot;
1053
Benny Prijonodc752ca2006-09-22 16:55:42 +00001054 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001055
1056 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001057}
1058
1059
Benny Prijono148c9dd2006-09-19 13:37:53 +00001060
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061/*
1062 * Obtain detail information about the specified call.
1063 */
1064PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1065 pjsua_call_info *info)
1066{
1067 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001068 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001069 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001070
1071 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1072 PJ_EINVAL);
1073
Benny Prijonoac623b32006-07-03 15:19:31 +00001074 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001075
Benny Prijonodc752ca2006-09-22 16:55:42 +00001076 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001077 if (status != PJ_SUCCESS) {
1078 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001079 }
1080
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001081 /* id and role */
1082 info->id = call_id;
1083 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001084 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001085
1086 /* local info */
1087 info->local_info.ptr = info->buf_.local_info;
1088 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1089 sizeof(info->buf_.local_info));
1090
1091 /* local contact */
1092 info->local_contact.ptr = info->buf_.local_contact;
1093 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1094 call->inv->dlg->local.contact->uri,
1095 info->local_contact.ptr,
1096 sizeof(info->buf_.local_contact));
1097
1098 /* remote info */
1099 info->remote_info.ptr = info->buf_.remote_info;
1100 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1101 sizeof(info->buf_.remote_info));
1102
1103 /* remote contact */
1104 if (call->inv->dlg->remote.contact) {
1105 int len;
1106 info->remote_contact.ptr = info->buf_.remote_contact;
1107 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1108 call->inv->dlg->remote.contact->uri,
1109 info->remote_contact.ptr,
1110 sizeof(info->buf_.remote_contact));
1111 if (len < 0) len = 0;
1112 info->remote_contact.slen = len;
1113 } else {
1114 info->remote_contact.slen = 0;
1115 }
1116
1117 /* call id */
1118 info->call_id.ptr = info->buf_.call_id;
1119 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1120 sizeof(info->buf_.call_id));
1121
1122 /* state, state_text */
1123 info->state = call->inv->state;
1124 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1125
1126 /* If call is disconnected, set the last_status from the cause code */
1127 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1128 /* last_status, last_status_text */
1129 info->last_status = call->inv->cause;
1130
1131 info->last_status_text.ptr = info->buf_.last_status_text;
1132 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1133 sizeof(info->buf_.last_status_text));
1134 } else {
1135 /* last_status, last_status_text */
1136 info->last_status = call->last_code;
1137
1138 info->last_status_text.ptr = info->buf_.last_status_text;
1139 pj_strncpy(&info->last_status_text, &call->last_text,
1140 sizeof(info->buf_.last_status_text));
1141 }
1142
1143 /* media status and dir */
1144 info->media_status = call->media_st;
1145 info->media_dir = call->media_dir;
1146
1147
1148 /* conference slot number */
1149 info->conf_slot = call->conf_slot;
1150
1151 /* calculate duration */
1152 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1153
1154 info->total_duration = call->dis_time;
1155 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1156
1157 if (call->conn_time.sec) {
1158 info->connect_duration = call->dis_time;
1159 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1160 }
1161
1162 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1163
1164 pj_gettimeofday(&info->total_duration);
1165 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1166
1167 pj_gettimeofday(&info->connect_duration);
1168 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1169
1170 } else {
1171 pj_gettimeofday(&info->total_duration);
1172 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1173 }
1174
Benny Prijonodc752ca2006-09-22 16:55:42 +00001175 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001176
1177 return PJ_SUCCESS;
1178}
1179
1180
1181/*
1182 * Attach application specific data to the call.
1183 */
1184PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1185 void *user_data)
1186{
1187 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1188 PJ_EINVAL);
1189 pjsua_var.calls[call_id].user_data = user_data;
1190
1191 return PJ_SUCCESS;
1192}
1193
1194
1195/*
1196 * Get user data attached to the call.
1197 */
1198PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1199{
1200 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1201 NULL);
1202 return pjsua_var.calls[call_id].user_data;
1203}
1204
1205
1206/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001207 * Get remote's NAT type.
1208 */
1209PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1210 pj_stun_nat_type *p_type)
1211{
1212 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1213 PJ_EINVAL);
1214 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1215
1216 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1217 return PJ_SUCCESS;
1218}
1219
1220
1221/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001222 * Send response to incoming INVITE request.
1223 */
1224PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1225 unsigned code,
1226 const pj_str_t *reason,
1227 const pjsua_msg_data *msg_data)
1228{
1229 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001230 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001231 pjsip_tx_data *tdata;
1232 pj_status_t status;
1233
1234 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1235 PJ_EINVAL);
1236
Benny Prijonodc752ca2006-09-22 16:55:42 +00001237 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001238 if (status != PJ_SUCCESS)
1239 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240
Benny Prijono2e507c22006-06-23 15:04:11 +00001241 if (call->res_time.sec == 0)
1242 pj_gettimeofday(&call->res_time);
1243
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001244 if (reason && reason->slen == 0)
1245 reason = NULL;
1246
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001247 /* Create response message */
1248 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1249 if (status != PJ_SUCCESS) {
1250 pjsua_perror(THIS_FILE, "Error creating response",
1251 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001252 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 return status;
1254 }
1255
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001256 /* Call might have been disconnected if application is answering with
1257 * 200/OK and the media failed to start.
1258 */
1259 if (call->inv == NULL) {
1260 pjsip_dlg_dec_lock(dlg);
1261 return PJSIP_ESESSIONTERMINATED;
1262 }
1263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 /* Add additional headers etc */
1265 pjsua_process_msg_data( tdata, msg_data);
1266
1267 /* Send the message */
1268 status = pjsip_inv_send_msg(call->inv, tdata);
1269 if (status != PJ_SUCCESS)
1270 pjsua_perror(THIS_FILE, "Error sending response",
1271 status);
1272
Benny Prijonodc752ca2006-09-22 16:55:42 +00001273 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274
1275 return status;
1276}
1277
1278
1279/*
1280 * Hangup call by using method that is appropriate according to the
1281 * call state.
1282 */
1283PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1284 unsigned code,
1285 const pj_str_t *reason,
1286 const pjsua_msg_data *msg_data)
1287{
1288 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001289 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001290 pj_status_t status;
1291 pjsip_tx_data *tdata;
1292
1293
Benny Prijono148c9dd2006-09-19 13:37:53 +00001294 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1295 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1296 call_id));
1297 }
1298
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001299 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1300 PJ_EINVAL);
1301
Benny Prijonodc752ca2006-09-22 16:55:42 +00001302 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001303 if (status != PJ_SUCCESS)
1304 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001305
1306 if (code==0) {
1307 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1308 code = PJSIP_SC_OK;
1309 else if (call->inv->role == PJSIP_ROLE_UAS)
1310 code = PJSIP_SC_DECLINE;
1311 else
1312 code = PJSIP_SC_REQUEST_TERMINATED;
1313 }
1314
1315 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1316 if (status != PJ_SUCCESS) {
1317 pjsua_perror(THIS_FILE,
1318 "Failed to create end session message",
1319 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001320 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001321 return status;
1322 }
1323
1324 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1325 * as p_tdata when INVITE transaction has not been answered
1326 * with any provisional responses.
1327 */
1328 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001329 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330 return PJ_SUCCESS;
1331 }
1332
1333 /* Add additional headers etc */
1334 pjsua_process_msg_data( tdata, msg_data);
1335
1336 /* Send the message */
1337 status = pjsip_inv_send_msg(call->inv, tdata);
1338 if (status != PJ_SUCCESS) {
1339 pjsua_perror(THIS_FILE,
1340 "Failed to send end session message",
1341 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001342 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001343 return status;
1344 }
1345
Benny Prijonodc752ca2006-09-22 16:55:42 +00001346 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001347
1348 return PJ_SUCCESS;
1349}
1350
1351
1352/*
1353 * Put the specified call on hold.
1354 */
1355PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1356 const pjsua_msg_data *msg_data)
1357{
1358 pjmedia_sdp_session *sdp;
1359 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001360 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001361 pjsip_tx_data *tdata;
1362 pj_status_t status;
1363
1364 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1365 PJ_EINVAL);
1366
Benny Prijonodc752ca2006-09-22 16:55:42 +00001367 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001368 if (status != PJ_SUCCESS)
1369 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001370
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001371
1372 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1373 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001374 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 return PJSIP_ESESSIONSTATE;
1376 }
1377
1378 status = create_inactive_sdp(call, &sdp);
1379 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001380 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 return status;
1382 }
1383
Benny Prijono7129cc72007-11-05 05:54:25 +00001384 update_sdp_version(call, sdp);
1385
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001386 /* Create re-INVITE with new offer */
1387 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1388 if (status != PJ_SUCCESS) {
1389 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001390 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391 return status;
1392 }
1393
1394 /* Add additional headers etc */
1395 pjsua_process_msg_data( tdata, msg_data);
1396
1397 /* Send the request */
1398 status = pjsip_inv_send_msg( call->inv, tdata);
1399 if (status != PJ_SUCCESS) {
1400 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001401 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402 return status;
1403 }
1404
Benny Prijonodc752ca2006-09-22 16:55:42 +00001405 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406
1407 return PJ_SUCCESS;
1408}
1409
1410
1411/*
1412 * Send re-INVITE (to release hold).
1413 */
1414PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1415 pj_bool_t unhold,
1416 const pjsua_msg_data *msg_data)
1417{
1418 pjmedia_sdp_session *sdp;
1419 pjsip_tx_data *tdata;
1420 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001421 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001422 pj_status_t status;
1423
1424
1425 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1426 PJ_EINVAL);
1427
Benny Prijonodc752ca2006-09-22 16:55:42 +00001428 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001429 if (status != PJ_SUCCESS)
1430 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431
1432 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1433 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001434 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001435 return PJSIP_ESESSIONSTATE;
1436 }
1437
Benny Prijonodb844a42008-02-02 17:07:18 +00001438 /* Update call secure level */
1439 call->secure_level = call_get_secure_level(call);
1440
Benny Prijono667952e2007-04-02 19:27:54 +00001441 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001442 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00001443 call->secure_level, NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00001444 if (status != PJ_SUCCESS) {
1445 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1446 pjsip_dlg_dec_lock(dlg);
1447 return PJSIP_ESESSIONSTATE;
1448 }
1449
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001450 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001451 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001452 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonod8179652008-01-23 20:39:07 +00001453 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001454 NULL, &sdp, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001455 if (status != PJ_SUCCESS) {
1456 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1457 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001458 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459 return status;
1460 }
1461
Benny Prijono7129cc72007-11-05 05:54:25 +00001462 update_sdp_version(call, sdp);
1463
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001464 /* Create re-INVITE with new offer */
1465 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1466 if (status != PJ_SUCCESS) {
1467 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001468 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 return status;
1470 }
1471
1472 /* Add additional headers etc */
1473 pjsua_process_msg_data( tdata, msg_data);
1474
1475 /* Send the request */
1476 status = pjsip_inv_send_msg( call->inv, tdata);
1477 if (status != PJ_SUCCESS) {
1478 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001479 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001480 return status;
1481 }
1482
Benny Prijonodc752ca2006-09-22 16:55:42 +00001483 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001484
1485 return PJ_SUCCESS;
1486}
1487
1488
1489/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001490 * Send UPDATE request.
1491 */
1492PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1493 unsigned options,
1494 const pjsua_msg_data *msg_data)
1495{
1496 pjmedia_sdp_session *sdp;
1497 pjsip_tx_data *tdata;
1498 pjsua_call *call;
1499 pjsip_dialog *dlg;
1500 pj_status_t status;
1501
1502 PJ_UNUSED_ARG(options);
1503
1504 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1505 PJ_EINVAL);
1506
1507 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1508 if (status != PJ_SUCCESS)
1509 return status;
1510
Benny Prijonodb844a42008-02-02 17:07:18 +00001511 /* Update call's secure level */
1512 call->secure_level = call_get_secure_level(call);
1513
Benny Prijonoc08682e2007-10-04 06:17:58 +00001514 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001515 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00001516 call->secure_level, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001517 if (status != PJ_SUCCESS) {
1518 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1519 pjsip_dlg_dec_lock(dlg);
1520 return PJSIP_ESESSIONSTATE;
1521 }
1522
1523 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001524 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001525 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001526 if (status != PJ_SUCCESS) {
1527 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1528 status);
1529 pjsip_dlg_dec_lock(dlg);
1530 return status;
1531 }
1532
1533 /* Create re-INVITE with new offer */
1534 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1535 if (status != PJ_SUCCESS) {
1536 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1537 pjsip_dlg_dec_lock(dlg);
1538 return status;
1539 }
1540
1541 /* Add additional headers etc */
1542 pjsua_process_msg_data( tdata, msg_data);
1543
1544 /* Send the request */
1545 status = pjsip_inv_send_msg( call->inv, tdata);
1546 if (status != PJ_SUCCESS) {
1547 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1548 pjsip_dlg_dec_lock(dlg);
1549 return status;
1550 }
1551
1552 pjsip_dlg_dec_lock(dlg);
1553
1554 return PJ_SUCCESS;
1555}
1556
1557
1558/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001559 * Initiate call transfer to the specified address.
1560 */
1561PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1562 const pj_str_t *dest,
1563 const pjsua_msg_data *msg_data)
1564{
1565 pjsip_evsub *sub;
1566 pjsip_tx_data *tdata;
1567 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001568 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001569 pjsip_generic_string_hdr *gs_hdr;
1570 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001571 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 pj_status_t status;
1573
1574
1575 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1576 PJ_EINVAL);
1577
Benny Prijonodc752ca2006-09-22 16:55:42 +00001578 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001579 if (status != PJ_SUCCESS)
1580 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001581
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582
Benny Prijonod524e822006-09-22 12:48:18 +00001583 /* Create xfer client subscription. */
1584 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001585 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001586
1587 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001588 if (status != PJ_SUCCESS) {
1589 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001590 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001591 return status;
1592 }
1593
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001594 /* Associate this call with the client subscription */
1595 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1596
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597 /*
1598 * Create REFER request.
1599 */
1600 status = pjsip_xfer_initiate(sub, dest, &tdata);
1601 if (status != PJ_SUCCESS) {
1602 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001603 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 return status;
1605 }
1606
Benny Prijono053f5222006-11-11 16:16:04 +00001607 /* Add Referred-By header */
1608 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1609 &dlg->local.info_str);
1610 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1611
1612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613 /* Add additional headers etc */
1614 pjsua_process_msg_data( tdata, msg_data);
1615
1616 /* Send. */
1617 status = pjsip_xfer_send_request(sub, tdata);
1618 if (status != PJ_SUCCESS) {
1619 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001620 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001621 return status;
1622 }
1623
1624 /* For simplicity (that's what this program is intended to be!),
1625 * leave the original invite session as it is. More advanced application
1626 * may want to hold the INVITE, or terminate the invite, or whatever.
1627 */
1628
Benny Prijonodc752ca2006-09-22 16:55:42 +00001629 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001630
1631 return PJ_SUCCESS;
1632
1633}
1634
1635
1636/*
Benny Prijono053f5222006-11-11 16:16:04 +00001637 * Initiate attended call transfer to the specified address.
1638 */
1639PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1640 pjsua_call_id dest_call_id,
1641 unsigned options,
1642 const pjsua_msg_data *msg_data)
1643{
1644 pjsua_call *dest_call;
1645 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001646 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001647 pj_str_t str_dest;
1648 int len;
1649 pjsip_uri *uri;
1650 pj_status_t status;
1651
1652
1653 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1654 PJ_EINVAL);
1655 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1656 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1657 PJ_EINVAL);
1658
1659 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1660 &dest_call, &dest_dlg);
1661 if (status != PJ_SUCCESS)
1662 return status;
1663
1664 /*
1665 * Create REFER destination URI with Replaces field.
1666 */
1667
1668 /* Make sure we have sufficient buffer's length */
1669 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1670 dest_dlg->call_id->id.slen +
1671 dest_dlg->remote.info->tag.slen +
1672 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001673 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001674
1675 /* Print URI */
1676 str_dest_buf[0] = '<';
1677 str_dest.slen = 1;
1678
Benny Prijonoa1e69682007-05-11 15:14:34 +00001679 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001680 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1681 str_dest_buf+1, sizeof(str_dest_buf)-1);
1682 if (len < 0)
1683 return PJSIP_EURITOOLONG;
1684
1685 str_dest.slen += len;
1686
1687
1688 /* Build the URI */
1689 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1690 sizeof(str_dest_buf) - str_dest.slen,
1691 "?%s"
1692 "Replaces=%.*s"
1693 "%%3Bto-tag%%3D%.*s"
1694 "%%3Bfrom-tag%%3D%.*s>",
1695 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1696 "" : "Require=replaces&"),
1697 (int)dest_dlg->call_id->id.slen,
1698 dest_dlg->call_id->id.ptr,
1699 (int)dest_dlg->remote.info->tag.slen,
1700 dest_dlg->remote.info->tag.ptr,
1701 (int)dest_dlg->local.info->tag.slen,
1702 dest_dlg->local.info->tag.ptr);
1703
1704 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1705 PJSIP_EURITOOLONG);
1706
1707 str_dest.ptr = str_dest_buf;
1708 str_dest.slen += len;
1709
1710 pjsip_dlg_dec_lock(dest_dlg);
1711
1712 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1713}
1714
1715
1716/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001717 * Send DTMF digits to remote using RFC 2833 payload formats.
1718 */
1719PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1720 const pj_str_t *digits)
1721{
1722 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001723 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001724 pj_status_t status;
1725
1726 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1727 PJ_EINVAL);
1728
Benny Prijonodc752ca2006-09-22 16:55:42 +00001729 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001730 if (status != PJ_SUCCESS)
1731 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001732
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001733 if (!call->session) {
1734 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001735 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 return PJ_EINVALIDOP;
1737 }
1738
1739 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1740
Benny Prijonodc752ca2006-09-22 16:55:42 +00001741 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001742
1743 return status;
1744}
1745
1746
1747/**
1748 * Send instant messaging inside INVITE session.
1749 */
1750PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1751 const pj_str_t *mime_type,
1752 const pj_str_t *content,
1753 const pjsua_msg_data *msg_data,
1754 void *user_data)
1755{
1756 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001757 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001758 const pj_str_t mime_text_plain = pj_str("text/plain");
1759 pjsip_media_type ctype;
1760 pjsua_im_data *im_data;
1761 pjsip_tx_data *tdata;
1762 pj_status_t status;
1763
1764
1765 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1766 PJ_EINVAL);
1767
Benny Prijonodc752ca2006-09-22 16:55:42 +00001768 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001769 if (status != PJ_SUCCESS)
1770 return status;
1771
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 /* Set default media type if none is specified */
1773 if (mime_type == NULL) {
1774 mime_type = &mime_text_plain;
1775 }
1776
1777 /* Create request message. */
1778 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1779 -1, &tdata);
1780 if (status != PJ_SUCCESS) {
1781 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1782 goto on_return;
1783 }
1784
1785 /* Add accept header. */
1786 pjsip_msg_add_hdr( tdata->msg,
1787 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1788
1789 /* Parse MIME type */
1790 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1791
1792 /* Create "text/plain" message body. */
1793 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1794 &ctype.subtype, content);
1795 if (tdata->msg->body == NULL) {
1796 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1797 pjsip_tx_data_dec_ref(tdata);
1798 goto on_return;
1799 }
1800
1801 /* Add additional headers etc */
1802 pjsua_process_msg_data( tdata, msg_data);
1803
1804 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001805 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806 im_data->acc_id = call->acc_id;
1807 im_data->call_id = call_id;
1808 im_data->to = call->inv->dlg->remote.info_str;
1809 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1810 im_data->user_data = user_data;
1811
1812
1813 /* Send the request. */
1814 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1815 pjsua_var.mod.id, im_data);
1816 if (status != PJ_SUCCESS) {
1817 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1818 goto on_return;
1819 }
1820
1821on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001822 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001823 return status;
1824}
1825
1826
1827/*
1828 * Send IM typing indication inside INVITE session.
1829 */
1830PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1831 pj_bool_t is_typing,
1832 const pjsua_msg_data*msg_data)
1833{
1834 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001835 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001836 pjsip_tx_data *tdata;
1837 pj_status_t status;
1838
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001839 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1840 PJ_EINVAL);
1841
Benny Prijonodc752ca2006-09-22 16:55:42 +00001842 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001843 if (status != PJ_SUCCESS)
1844 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001846 /* Create request message. */
1847 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1848 -1, &tdata);
1849 if (status != PJ_SUCCESS) {
1850 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1851 goto on_return;
1852 }
1853
1854 /* Create "application/im-iscomposing+xml" msg body. */
1855 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1856 NULL, NULL, -1);
1857
1858 /* Add additional headers etc */
1859 pjsua_process_msg_data( tdata, msg_data);
1860
1861 /* Send the request. */
1862 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1863 if (status != PJ_SUCCESS) {
1864 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1865 goto on_return;
1866 }
1867
1868on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001869 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870 return status;
1871}
1872
1873
1874/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001875 * Send arbitrary request.
1876 */
1877PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1878 const pj_str_t *method_str,
1879 const pjsua_msg_data *msg_data)
1880{
1881 pjsua_call *call;
1882 pjsip_dialog *dlg;
1883 pjsip_method method;
1884 pjsip_tx_data *tdata;
1885 pj_status_t status;
1886
1887 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1888 PJ_EINVAL);
1889
1890 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1891 if (status != PJ_SUCCESS)
1892 return status;
1893
1894 /* Init method */
1895 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1896
1897 /* Create request message. */
1898 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1899 if (status != PJ_SUCCESS) {
1900 pjsua_perror(THIS_FILE, "Unable to create request", status);
1901 goto on_return;
1902 }
1903
1904 /* Add additional headers etc */
1905 pjsua_process_msg_data( tdata, msg_data);
1906
1907 /* Send the request. */
1908 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1909 if (status != PJ_SUCCESS) {
1910 pjsua_perror(THIS_FILE, "Unable to send request", status);
1911 goto on_return;
1912 }
1913
1914on_return:
1915 pjsip_dlg_dec_lock(dlg);
1916 return status;
1917}
1918
1919
1920/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001921 * Terminate all calls.
1922 */
1923PJ_DEF(void) pjsua_call_hangup_all(void)
1924{
1925 unsigned i;
1926
1927 PJSUA_LOCK();
1928
1929 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1930 if (pjsua_var.calls[i].inv)
1931 pjsua_call_hangup(i, 0, NULL, NULL);
1932 }
1933
1934 PJSUA_UNLOCK();
1935}
1936
1937
Benny Prijono627cbb42007-09-25 20:48:49 +00001938const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939{
1940 if (val < 1000) {
1941 pj_ansi_sprintf(buf, "%d", val);
1942 } else if (val < 1000000) {
1943 pj_ansi_sprintf(buf, "%d.%dK",
1944 val / 1000,
1945 (val % 1000) / 100);
1946 } else {
1947 pj_ansi_sprintf(buf, "%d.%02dM",
1948 val / 1000000,
1949 (val % 1000000) / 10000);
1950 }
1951
1952 return buf;
1953}
1954
1955
1956/* Dump media session */
1957static void dump_media_session(const char *indent,
1958 char *buf, unsigned maxlen,
1959 pjmedia_session *session)
1960{
1961 unsigned i;
1962 char *p = buf, *end = buf+maxlen;
1963 int len;
1964 pjmedia_session_info info;
1965
1966 pjmedia_session_get_info(session, &info);
1967
1968 for (i=0; i<info.stream_cnt; ++i) {
1969 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00001970 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001972 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001973 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001974 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001975 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001976
1977 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00001978 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
1979 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001980
1981 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1982 dir = "sendonly";
1983 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1984 dir = "recvonly";
1985 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1986 dir = "sendrecv";
1987 else
1988 dir = "inactive";
1989
1990
1991 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00001992 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001993 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001994 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001995 info.stream_info[i].fmt.encoding_name.ptr,
1996 info.stream_info[i].fmt.clock_rate / 1000,
1997 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00001998 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001999 if (len < 1 || len > end-p) {
2000 *p = '\0';
2001 return;
2002 }
2003
2004 p += len;
2005 *p++ = '\n';
2006 *p = '\0';
2007
2008 if (stat.rx.update_cnt == 0)
2009 strcpy(last_update, "never");
2010 else {
2011 pj_gettimeofday(&now);
2012 PJ_TIME_VAL_SUB(now, stat.rx.update);
2013 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2014 now.sec / 3600,
2015 (now.sec % 3600) / 60,
2016 now.sec % 60,
2017 now.msec);
2018 }
2019
Benny Prijono80019eb2006-08-07 13:22:23 +00002020 pj_gettimeofday(&media_duration);
2021 PJ_TIME_VAL_SUB(media_duration, stat.start);
2022 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2023 media_duration.msec = 1;
2024
Benny Prijono1402a4a2008-01-08 23:41:22 +00002025 /* protect against division by zero */
2026 if (stat.rx.pkt == 0)
2027 stat.rx.pkt = 1;
2028 if (stat.tx.pkt == 0)
2029 stat.tx.pkt = 1;
2030
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002031 len = pj_ansi_snprintf(p, end-p,
2032 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002033 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002034 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2035 "%s (msec) min avg max last\n"
2036 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2037 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2038 indent, info.stream_info[i].fmt.pt,
2039 last_update,
2040 indent,
2041 good_number(packets, stat.rx.pkt),
2042 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002043 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002044 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2045 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 +00002046 indent,
2047 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002048 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002049 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002050 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002051 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002052 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002053 indent, indent,
2054 stat.rx.loss_period.min / 1000.0,
2055 stat.rx.loss_period.avg / 1000.0,
2056 stat.rx.loss_period.max / 1000.0,
2057 stat.rx.loss_period.last / 1000.0,
2058 indent,
2059 stat.rx.jitter.min / 1000.0,
2060 stat.rx.jitter.avg / 1000.0,
2061 stat.rx.jitter.max / 1000.0,
2062 stat.rx.jitter.last / 1000.0,
2063 ""
2064 );
2065
2066 if (len < 1 || len > end-p) {
2067 *p = '\0';
2068 return;
2069 }
2070
2071 p += len;
2072 *p++ = '\n';
2073 *p = '\0';
2074
2075 if (stat.tx.update_cnt == 0)
2076 strcpy(last_update, "never");
2077 else {
2078 pj_gettimeofday(&now);
2079 PJ_TIME_VAL_SUB(now, stat.tx.update);
2080 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2081 now.sec / 3600,
2082 (now.sec % 3600) / 60,
2083 now.sec % 60,
2084 now.msec);
2085 }
2086
2087 len = pj_ansi_snprintf(p, end-p,
2088 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002089 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002090 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2091 "%s (msec) min avg max last\n"
2092 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2093 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2094 indent,
2095 info.stream_info[i].tx_pt,
2096 info.stream_info[i].param->info.frm_ptime *
2097 info.stream_info[i].param->setting.frm_per_pkt,
2098 last_update,
2099
2100 indent,
2101 good_number(packets, stat.tx.pkt),
2102 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002103 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002104 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2105 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 +00002106
2107 indent,
2108 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002109 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002110 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002111 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002112 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002113 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002114
2115 indent, indent,
2116 stat.tx.loss_period.min / 1000.0,
2117 stat.tx.loss_period.avg / 1000.0,
2118 stat.tx.loss_period.max / 1000.0,
2119 stat.tx.loss_period.last / 1000.0,
2120 indent,
2121 stat.tx.jitter.min / 1000.0,
2122 stat.tx.jitter.avg / 1000.0,
2123 stat.tx.jitter.max / 1000.0,
2124 stat.tx.jitter.last / 1000.0,
2125 ""
2126 );
2127
2128 if (len < 1 || len > end-p) {
2129 *p = '\0';
2130 return;
2131 }
2132
2133 p += len;
2134 *p++ = '\n';
2135 *p = '\0';
2136
2137 len = pj_ansi_snprintf(p, end-p,
2138 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
2139 indent,
2140 stat.rtt.min / 1000.0,
2141 stat.rtt.avg / 1000.0,
2142 stat.rtt.max / 1000.0,
2143 stat.rtt.last / 1000.0
2144 );
2145 if (len < 1 || len > end-p) {
2146 *p = '\0';
2147 return;
2148 }
2149
2150 p += len;
2151 *p++ = '\n';
2152 *p = '\0';
2153 }
2154}
2155
2156
2157/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002158void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002159 int call_id,
2160 char *buf, pj_size_t size)
2161{
2162 int len;
2163 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2164 pjsip_dialog *dlg = inv->dlg;
2165 char userinfo[128];
2166
2167 /* Dump invite sesion info. */
2168
2169 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2170 if (len < 1)
2171 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2172 else
2173 userinfo[len] = '\0';
2174
2175 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2176 title,
2177 pjsip_inv_state_name(inv->state),
2178 userinfo);
2179 if (len < 1 || len >= (int)size) {
2180 pj_ansi_strcpy(buf, "<--uri too long-->");
2181 len = 18;
2182 } else
2183 buf[len] = '\0';
2184}
2185
2186
2187/*
2188 * Dump call and media statistics to string.
2189 */
2190PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2191 pj_bool_t with_media,
2192 char *buffer,
2193 unsigned maxlen,
2194 const char *indent)
2195{
2196 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002197 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002198 pj_time_val duration, res_delay, con_delay;
2199 char tmp[128];
2200 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002201 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002202 int len;
2203
2204 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2205 PJ_EINVAL);
2206
Benny Prijonodc752ca2006-09-22 16:55:42 +00002207 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002208 if (status != PJ_SUCCESS)
2209 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002210
2211 *buffer = '\0';
2212 p = buffer;
2213 end = buffer + maxlen;
2214 len = 0;
2215
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 print_call(indent, call_id, tmp, sizeof(tmp));
2217
2218 len = pj_ansi_strlen(tmp);
2219 pj_ansi_strcpy(buffer, tmp);
2220
2221 p += len;
2222 *p++ = '\r';
2223 *p++ = '\n';
2224
2225 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002226 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002227 pj_gettimeofday(&duration);
2228 PJ_TIME_VAL_SUB(duration, call->conn_time);
2229 con_delay = call->conn_time;
2230 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2231 } else {
2232 duration.sec = duration.msec = 0;
2233 con_delay.sec = con_delay.msec = 0;
2234 }
2235
2236 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002237 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002238 res_delay = call->res_time;
2239 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2240 } else {
2241 res_delay.sec = res_delay.msec = 0;
2242 }
2243
2244 /* Print duration */
2245 len = pj_ansi_snprintf(p, end-p,
2246 "%s Call time: %02dh:%02dm:%02ds, "
2247 "1st res in %d ms, conn in %dms",
2248 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002249 (int)(duration.sec / 3600),
2250 (int)((duration.sec % 3600)/60),
2251 (int)(duration.sec % 60),
2252 (int)PJ_TIME_VAL_MSEC(res_delay),
2253 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002254
2255 if (len > 0 && len < end-p) {
2256 p += len;
2257 *p++ = '\n';
2258 *p = '\0';
2259 }
2260
2261 /* Dump session statistics */
2262 if (with_media && call->session)
2263 dump_media_session(indent, p, end-p, call->session);
2264
Benny Prijonodc752ca2006-09-22 16:55:42 +00002265 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002266
2267 return PJ_SUCCESS;
2268}
2269
2270
2271/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002272 * This callback receives notification from invite session when the
2273 * session state has changed.
2274 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002275static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2276 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002277{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002278 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002280 PJSUA_LOCK();
2281
Benny Prijonoa1e69682007-05-11 15:14:34 +00002282 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002283
2284 if (!call) {
2285 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002286 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002287 }
2288
Benny Prijonoe21e7842006-04-09 16:46:05 +00002289
2290 /* Get call times */
2291 switch (inv->state) {
2292 case PJSIP_INV_STATE_EARLY:
2293 case PJSIP_INV_STATE_CONNECTING:
2294 if (call->res_time.sec == 0)
2295 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002296 call->last_code = (pjsip_status_code)
2297 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002298 pj_strncpy(&call->last_text,
2299 &e->body.tsx_state.tsx->status_text,
2300 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002301 break;
2302 case PJSIP_INV_STATE_CONFIRMED:
2303 pj_gettimeofday(&call->conn_time);
2304 break;
2305 case PJSIP_INV_STATE_DISCONNECTED:
2306 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002307 if (call->res_time.sec == 0)
2308 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002309 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002310 call->last_code = (pjsip_status_code)
2311 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002312 pj_strncpy(&call->last_text,
2313 &e->body.tsx_state.tsx->status_text,
2314 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002315 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002316 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002317 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002318 call->last_code = (pjsip_status_code)
2319 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002320 pj_strncpy(&call->last_text,
2321 &e->body.tsx_state.tsx->status_text,
2322 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002323 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002324 }
2325
Benny Prijono26ff9062006-02-21 23:47:00 +00002326 /* If this is an outgoing INVITE that was created because of
2327 * REFER/transfer, send NOTIFY to transferer.
2328 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002329 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002330 int st_code = -1;
2331 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2332
2333
Benny Prijonoa91a0032006-02-26 21:23:45 +00002334 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002335 case PJSIP_INV_STATE_NULL:
2336 case PJSIP_INV_STATE_CALLING:
2337 /* Do nothing */
2338 break;
2339
2340 case PJSIP_INV_STATE_EARLY:
2341 case PJSIP_INV_STATE_CONNECTING:
2342 st_code = e->body.tsx_state.tsx->status_code;
2343 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2344 break;
2345
2346 case PJSIP_INV_STATE_CONFIRMED:
2347 /* When state is confirmed, send the final 200/OK and terminate
2348 * subscription.
2349 */
2350 st_code = e->body.tsx_state.tsx->status_code;
2351 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2352 break;
2353
2354 case PJSIP_INV_STATE_DISCONNECTED:
2355 st_code = e->body.tsx_state.tsx->status_code;
2356 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2357 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002358
Benny Prijono8b1889b2006-06-06 18:40:40 +00002359 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002360 /* Nothing to do. Just to keep gcc from complaining about
2361 * unused enums.
2362 */
2363 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002364 }
2365
2366 if (st_code != -1) {
2367 pjsip_tx_data *tdata;
2368 pj_status_t status;
2369
Benny Prijonoa91a0032006-02-26 21:23:45 +00002370 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002371 ev_state, st_code,
2372 NULL, &tdata);
2373 if (status != PJ_SUCCESS) {
2374 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2375 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002376 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002377 if (status != PJ_SUCCESS) {
2378 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2379 }
2380 }
2381 }
2382 }
2383
Benny Prijono84126ab2006-02-09 09:30:09 +00002384
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002385 if (pjsua_var.ua_cfg.cb.on_call_state)
2386 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002387
2388 /* call->inv may be NULL now */
2389
Benny Prijono84126ab2006-02-09 09:30:09 +00002390 /* Destroy media session when invite session is disconnected. */
2391 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002392
Benny Prijonoa91a0032006-02-26 21:23:45 +00002393 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002394
Benny Prijono275fd682006-03-22 11:59:11 +00002395 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002396 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002397
Benny Prijono105217f2006-03-06 16:25:59 +00002398 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002399 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002400 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002401
2402 /* Reset call */
2403 reset_call(call->index);
2404
Benny Prijono84126ab2006-02-09 09:30:09 +00002405 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002406
2407 PJSUA_UNLOCK();
2408}
2409
2410/*
2411 * This callback is called by invite session framework when UAC session
2412 * has forked.
2413 */
2414static void pjsua_call_on_forked( pjsip_inv_session *inv,
2415 pjsip_event *e)
2416{
2417 PJ_UNUSED_ARG(inv);
2418 PJ_UNUSED_ARG(e);
2419
2420 PJ_TODO(HANDLE_FORKED_DIALOG);
2421}
2422
2423
2424/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002425 * Disconnect call upon error.
2426 */
2427static void call_disconnect( pjsip_inv_session *inv,
2428 int code )
2429{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002430 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002431 pjsip_tx_data *tdata;
2432 pj_status_t status;
2433
Benny Prijono59b3aed2008-01-15 16:54:54 +00002434 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2435
Benny Prijonoa38ada02006-07-02 14:22:35 +00002436 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002437 if (status != PJ_SUCCESS)
2438 return;
2439
2440 /* Add SDP in 488 status */
2441 if (call && call->med_tp && code==PJSIP_SC_NOT_ACCEPTABLE_HERE) {
2442 pjmedia_sdp_session *local_sdp;
2443 pjmedia_sock_info si;
2444
2445 call->med_tp->op->get_info(call->med_tp, &si);
2446 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
2447 1, &si, &local_sdp);
2448 if (status == PJ_SUCCESS) {
2449 pjsip_create_sdp_body(tdata->pool, local_sdp,
2450 &tdata->msg->body);
2451 }
2452 }
2453
2454 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002455}
2456
2457/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002458 * Callback to be called when SDP offer/answer negotiation has just completed
2459 * in the session. This function will start/update media if negotiation
2460 * has succeeded.
2461 */
2462static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2463 pj_status_t status)
2464{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002465 pjsua_call *call;
Benny Prijonod8179652008-01-23 20:39:07 +00002466 const pjmedia_sdp_session *c_local;
2467 pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002468 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002469
2470 PJSUA_LOCK();
2471
Benny Prijonoa1e69682007-05-11 15:14:34 +00002472 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002473
2474 if (status != PJ_SUCCESS) {
2475
2476 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2477
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002478 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002479 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002480
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002481 /* Disconnect call if we're not in the middle of initializing an
2482 * UAS dialog and if this is not a re-INVITE
2483 */
2484 if (inv->state != PJSIP_INV_STATE_NULL &&
2485 inv->state != PJSIP_INV_STATE_CONFIRMED)
2486 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002487 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002488 }
2489
2490 PJSUA_UNLOCK();
2491 return;
2492 }
2493
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002494
2495 /* Get local and remote SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00002496 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &c_local);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002497 if (status != PJ_SUCCESS) {
2498 pjsua_perror(THIS_FILE,
2499 "Unable to retrieve currently active local SDP",
2500 status);
2501 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2502 PJSUA_UNLOCK();
2503 return;
2504 }
Benny Prijonod8179652008-01-23 20:39:07 +00002505 local_sdp = (pjmedia_sdp_session*) c_local;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002506
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002507 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2508 if (status != PJ_SUCCESS) {
2509 pjsua_perror(THIS_FILE,
2510 "Unable to retrieve currently active remote SDP",
2511 status);
2512 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2513 PJSUA_UNLOCK();
2514 return;
2515 }
2516
Benny Prijono91a6a172007-10-31 08:59:29 +00002517 /* Update remote's NAT type */
2518 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2519 update_remote_nat_type(call, remote_sdp);
2520 }
2521
2522 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002523 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002524 if (status != PJ_SUCCESS) {
2525 pjsua_perror(THIS_FILE, "Unable to create media session",
2526 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002527 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002528 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002529 PJSUA_UNLOCK();
2530 return;
2531 }
2532
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002533
2534 /* Call application callback, if any */
2535 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2536 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2537
2538
2539 PJSUA_UNLOCK();
2540}
2541
2542
2543/*
2544 * Create inactive SDP for call hold.
2545 */
2546static pj_status_t create_inactive_sdp(pjsua_call *call,
2547 pjmedia_sdp_session **p_answer)
2548{
2549 pj_status_t status;
2550 pjmedia_sdp_conn *conn;
2551 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002552 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002553 pjmedia_sdp_session *sdp;
2554
Benny Prijono617c5bc2007-04-02 19:51:21 +00002555 /* Get media socket info */
2556 pjmedia_transport_get_info(call->med_tp, &skinfo);
2557
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002558 /* Create new offer */
2559 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002560 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002561 if (status != PJ_SUCCESS) {
2562 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2563 return status;
2564 }
2565
2566 /* Get SDP media connection line */
2567 conn = sdp->media[0]->conn;
2568 if (!conn)
2569 conn = sdp->conn;
2570
2571 /* Modify address */
2572 conn->addr = pj_str("0.0.0.0");
2573
2574 /* Remove existing directions attributes */
2575 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2576 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2577 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2578 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2579
2580 /* Add inactive attribute */
2581 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2582 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2583
2584 *p_answer = sdp;
2585
2586 return status;
2587}
2588
2589
2590/*
2591 * Called when session received new offer.
2592 */
2593static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2594 const pjmedia_sdp_session *offer)
2595{
2596 const char *remote_state;
2597 pjsua_call *call;
2598 pjmedia_sdp_conn *conn;
2599 pjmedia_sdp_session *answer;
2600 pj_bool_t is_remote_active;
2601 pj_status_t status;
2602
2603 PJSUA_LOCK();
2604
Benny Prijonoa1e69682007-05-11 15:14:34 +00002605 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002606
2607 /*
2608 * See if remote is offering active media (i.e. not on-hold)
2609 */
2610 is_remote_active = PJ_TRUE;
2611
2612 conn = offer->media[0]->conn;
2613 if (!conn)
2614 conn = offer->conn;
2615
2616 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2617 pj_strcmp2(&conn->addr, "0")==0)
2618 {
2619 is_remote_active = PJ_FALSE;
2620
2621 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002622 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2623 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002624 {
2625 is_remote_active = PJ_FALSE;
2626 }
2627
2628 remote_state = (is_remote_active ? "active" : "inactive");
2629
2630 /* Supply candidate answer */
2631 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2632 PJ_LOG(4,(THIS_FILE,
2633 "Call %d: RX new media offer, creating inactive SDP "
2634 "(media in offer is %s)", call->index, remote_state));
2635 status = create_inactive_sdp( call, &answer );
2636 } else {
2637 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2638 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002639
Benny Prijonodb844a42008-02-02 17:07:18 +00002640 /* Update call's secure level */
2641 call->secure_level = call_get_secure_level(call);
2642
Benny Prijono667952e2007-04-02 19:27:54 +00002643 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002644 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
Benny Prijonodb844a42008-02-02 17:07:18 +00002645 call->secure_level, NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00002646 if (status != PJ_SUCCESS) {
2647 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2648 PJSUA_UNLOCK();
2649 return;
2650 }
2651
Benny Prijonod8179652008-01-23 20:39:07 +00002652 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002653 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002654 }
2655
2656 if (status != PJ_SUCCESS) {
2657 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2658 PJSUA_UNLOCK();
2659 return;
2660 }
2661
2662 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2663 if (status != PJ_SUCCESS) {
2664 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2665 PJSUA_UNLOCK();
2666 return;
2667 }
2668
2669 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002670}
2671
2672
2673/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002674 * Called to generate new offer.
2675 */
2676static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2677 pjmedia_sdp_session **offer)
2678{
2679 pjsua_call *call;
2680 pj_status_t status;
2681
2682 PJSUA_LOCK();
2683
2684 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2685
2686 /* See if we've put call on hold. */
2687 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2688 PJ_LOG(4,(THIS_FILE,
2689 "Call %d: call is on-hold locally, creating inactive SDP ",
2690 call->index));
2691 status = create_inactive_sdp( call, offer );
2692 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00002693 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2694 call->index));
2695
Benny Prijonodb844a42008-02-02 17:07:18 +00002696 /* Update call's secure level */
2697 call->secure_level = call_get_secure_level(call);
2698
Benny Prijono77998ce2007-06-20 10:03:46 +00002699 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002700 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00002701 call->secure_level, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002702 if (status != PJ_SUCCESS) {
2703 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2704 PJSUA_UNLOCK();
2705 return;
2706 }
2707
Benny Prijonod8179652008-01-23 20:39:07 +00002708 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002709 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002710 }
2711
2712 if (status != PJ_SUCCESS) {
2713 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2714 PJSUA_UNLOCK();
2715 return;
2716 }
2717
Benny Prijono7129cc72007-11-05 05:54:25 +00002718 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002719
2720 PJSUA_UNLOCK();
2721}
2722
2723
2724/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002725 * Callback called by event framework when the xfer subscription state
2726 * has changed.
2727 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002728static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2729{
2730
2731 PJ_UNUSED_ARG(event);
2732
2733 /*
2734 * When subscription is accepted (got 200/OK to REFER), check if
2735 * subscription suppressed.
2736 */
2737 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2738
2739 pjsip_rx_data *rdata;
2740 pjsip_generic_string_hdr *refer_sub;
2741 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2742 pjsua_call *call;
2743
Benny Prijonoa1e69682007-05-11 15:14:34 +00002744 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002745
2746 /* Must be receipt of response message */
2747 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2748 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2749 rdata = event->body.tsx_state.src.rdata;
2750
2751 /* Find Refer-Sub header */
2752 refer_sub = (pjsip_generic_string_hdr*)
2753 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2754 &REFER_SUB, NULL);
2755
2756 /* Check if subscription is suppressed */
2757 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2758 /* Since no subscription is desired, assume that call has been
2759 * transfered successfully.
2760 */
2761 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2762 const pj_str_t ACCEPTED = { "Accepted", 8 };
2763 pj_bool_t cont = PJ_FALSE;
2764 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2765 200,
2766 &ACCEPTED,
2767 PJ_TRUE,
2768 &cont);
2769 }
2770
2771 /* Yes, subscription is suppressed.
2772 * Terminate our subscription now.
2773 */
2774 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2775 "event subcription..."));
2776 pjsip_evsub_terminate(sub, PJ_TRUE);
2777
2778 } else {
2779 /* Notify application about call transfer progress.
2780 * Initially notify with 100/Accepted status.
2781 */
2782 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2783 const pj_str_t ACCEPTED = { "Accepted", 8 };
2784 pj_bool_t cont = PJ_FALSE;
2785 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2786 100,
2787 &ACCEPTED,
2788 PJ_FALSE,
2789 &cont);
2790 }
2791 }
2792 }
2793 /*
2794 * On incoming NOTIFY, notify application about call transfer progress.
2795 */
2796 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2797 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2798 {
2799 pjsua_call *call;
2800 pjsip_msg *msg;
2801 pjsip_msg_body *body;
2802 pjsip_status_line status_line;
2803 pj_bool_t is_last;
2804 pj_bool_t cont;
2805 pj_status_t status;
2806
Benny Prijonoa1e69682007-05-11 15:14:34 +00002807 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002808
2809 /* When subscription is terminated, clear the xfer_sub member of
2810 * the inv_data.
2811 */
2812 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2813 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2814 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2815
2816 }
2817
2818 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2819 /* Application is not interested with call progress status */
2820 return;
2821 }
2822
2823 /* This better be a NOTIFY request */
2824 if (event->type == PJSIP_EVENT_TSX_STATE &&
2825 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2826 {
2827 pjsip_rx_data *rdata;
2828
2829 rdata = event->body.tsx_state.src.rdata;
2830
2831 /* Check if there's body */
2832 msg = rdata->msg_info.msg;
2833 body = msg->body;
2834 if (!body) {
2835 PJ_LOG(4,(THIS_FILE,
2836 "Warning: received NOTIFY without message body"));
2837 return;
2838 }
2839
2840 /* Check for appropriate content */
2841 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2842 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2843 {
2844 PJ_LOG(4,(THIS_FILE,
2845 "Warning: received NOTIFY with non message/sipfrag "
2846 "content"));
2847 return;
2848 }
2849
2850 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002851 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002852 &status_line);
2853 if (status != PJ_SUCCESS) {
2854 PJ_LOG(4,(THIS_FILE,
2855 "Warning: received NOTIFY with invalid "
2856 "message/sipfrag content"));
2857 return;
2858 }
2859
2860 } else {
2861 status_line.code = 500;
2862 status_line.reason = *pjsip_get_status_text(500);
2863 }
2864
2865 /* Notify application */
2866 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2867 cont = !is_last;
2868 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2869 status_line.code,
2870 &status_line.reason,
2871 is_last, &cont);
2872
2873 if (!cont) {
2874 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2875 }
2876 }
2877}
2878
2879
2880/*
2881 * Callback called by event framework when the xfer subscription state
2882 * has changed.
2883 */
2884static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002885{
2886
2887 PJ_UNUSED_ARG(event);
2888
2889 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002890 * When subscription is terminated, clear the xfer_sub member of
2891 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002892 */
2893 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002894 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002895
Benny Prijonoa1e69682007-05-11 15:14:34 +00002896 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002897 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002898 return;
2899
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002900 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002901 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002902
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002903 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002904 }
2905}
2906
2907
2908/*
2909 * Follow transfer (REFER) request.
2910 */
2911static void on_call_transfered( pjsip_inv_session *inv,
2912 pjsip_rx_data *rdata )
2913{
2914 pj_status_t status;
2915 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002916 pjsua_call *existing_call;
2917 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002918 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002919 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002920 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002921 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002922 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002923 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002924 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002925 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002926 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002927 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002928 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002929 pjsip_evsub *sub;
2930
Benny Prijonoa1e69682007-05-11 15:14:34 +00002931 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002932
Benny Prijono26ff9062006-02-21 23:47:00 +00002933 /* Find the Refer-To header */
2934 refer_to = (pjsip_generic_string_hdr*)
2935 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2936
2937 if (refer_to == NULL) {
2938 /* Invalid Request.
2939 * No Refer-To header!
2940 */
2941 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002942 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002943 return;
2944 }
2945
Benny Prijonoc8141a82006-08-20 09:12:19 +00002946 /* Find optional Refer-Sub header */
2947 refer_sub = (pjsip_generic_string_hdr*)
2948 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2949
2950 if (refer_sub) {
2951 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2952 no_refer_sub = PJ_TRUE;
2953 }
2954
Benny Prijono053f5222006-11-11 16:16:04 +00002955 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2956 * request.
2957 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002958 ref_by_hdr = (pjsip_hdr*)
2959 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002960 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002961
Benny Prijono9fc735d2006-05-28 14:58:12 +00002962 /* Notify callback */
2963 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002964 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2965 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2966 &refer_to->hvalue,
2967 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002968
2969 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002970 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002971 if (code >= 300) {
2972 /* Application rejects call transfer request */
2973 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2974 return;
2975 }
2976
Benny Prijono26ff9062006-02-21 23:47:00 +00002977 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2978 (int)inv->dlg->remote.info_str.slen,
2979 inv->dlg->remote.info_str.ptr,
2980 (int)refer_to->hvalue.slen,
2981 refer_to->hvalue.ptr));
2982
Benny Prijonoc8141a82006-08-20 09:12:19 +00002983 if (no_refer_sub) {
2984 /*
2985 * Always answer with 200.
2986 */
2987 pjsip_tx_data *tdata;
2988 const pj_str_t str_false = { "false", 5};
2989 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002990
Benny Prijonoc8141a82006-08-20 09:12:19 +00002991 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2992 if (status != PJ_SUCCESS) {
2993 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2994 status);
2995 return;
2996 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002997
Benny Prijonoc8141a82006-08-20 09:12:19 +00002998 /* Add Refer-Sub header */
2999 hdr = (pjsip_hdr*)
3000 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3001 &str_false);
3002 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003003
Benny Prijono26ff9062006-02-21 23:47:00 +00003004
Benny Prijonoc8141a82006-08-20 09:12:19 +00003005 /* Send answer */
3006 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3007 tdata);
3008 if (status != PJ_SUCCESS) {
3009 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
3010 status);
3011 return;
3012 }
3013
3014 /* Don't have subscription */
3015 sub = NULL;
3016
3017 } else {
3018 struct pjsip_evsub_user xfer_cb;
3019 pjsip_hdr hdr_list;
3020
3021 /* Init callback */
3022 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003023 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003024
3025 /* Init additional header list to be sent with REFER response */
3026 pj_list_init(&hdr_list);
3027
3028 /* Create transferee event subscription */
3029 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3030 if (status != PJ_SUCCESS) {
3031 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3032 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3033 return;
3034 }
3035
3036 /* If there's Refer-Sub header and the value is "true", send back
3037 * Refer-Sub in the response with value "true" too.
3038 */
3039 if (refer_sub) {
3040 const pj_str_t str_true = { "true", 4 };
3041 pjsip_hdr *hdr;
3042
3043 hdr = (pjsip_hdr*)
3044 pjsip_generic_string_hdr_create(inv->dlg->pool,
3045 &str_refer_sub,
3046 &str_true);
3047 pj_list_push_back(&hdr_list, hdr);
3048
3049 }
3050
3051 /* Accept the REFER request, send 200 (OK). */
3052 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3053
3054 /* Create initial NOTIFY request */
3055 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3056 100, NULL, &tdata);
3057 if (status != PJ_SUCCESS) {
3058 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3059 status);
3060 return;
3061 }
3062
3063 /* Send initial NOTIFY request */
3064 status = pjsip_xfer_send_request( sub, tdata);
3065 if (status != PJ_SUCCESS) {
3066 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3067 return;
3068 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003069 }
3070
3071 /* We're cheating here.
3072 * We need to get a null terminated string from a pj_str_t.
3073 * So grab the pointer from the hvalue and NULL terminate it, knowing
3074 * that the NULL position will be occupied by a newline.
3075 */
3076 uri = refer_to->hvalue.ptr;
3077 uri[refer_to->hvalue.slen] = '\0';
3078
Benny Prijono053f5222006-11-11 16:16:04 +00003079 /* Init msg_data */
3080 pjsua_msg_data_init(&msg_data);
3081
3082 /* If Referred-By header is present in the REFER request, copy this
3083 * to the outgoing INVITE request.
3084 */
3085 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003086 pjsip_hdr *dup = (pjsip_hdr*)
3087 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003088 pj_list_push_back(&msg_data.hdr_list, dup);
3089 }
3090
Benny Prijono26ff9062006-02-21 23:47:00 +00003091 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003092 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003093 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003094 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003095 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003096 if (status != PJ_SUCCESS) {
3097
Benny Prijonoc8141a82006-08-20 09:12:19 +00003098 /* Notify xferer about the error (if we have subscription) */
3099 if (sub) {
3100 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3101 500, NULL, &tdata);
3102 if (status != PJ_SUCCESS) {
3103 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3104 status);
3105 return;
3106 }
3107 status = pjsip_xfer_send_request(sub, tdata);
3108 if (status != PJ_SUCCESS) {
3109 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3110 status);
3111 return;
3112 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003113 }
3114 return;
3115 }
3116
Benny Prijonoc8141a82006-08-20 09:12:19 +00003117 if (sub) {
3118 /* Put the server subscription in inv_data.
3119 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3120 * reported back to the server subscription.
3121 */
3122 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003123
Benny Prijonoc8141a82006-08-20 09:12:19 +00003124 /* Put the invite_data in the subscription. */
3125 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3126 &pjsua_var.calls[new_call]);
3127 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003128}
3129
3130
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003131
Benny Prijono26ff9062006-02-21 23:47:00 +00003132/*
3133 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003134 * session. We use this to trap:
3135 * - incoming REFER request.
3136 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003137 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003138static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3139 pjsip_transaction *tsx,
3140 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003141{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003142 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003143
3144 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003145
Benny Prijonofeb69f42007-10-05 09:12:26 +00003146 /* Notify application callback first */
3147 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3148 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3149 }
3150
Benny Prijono26ff9062006-02-21 23:47:00 +00003151 if (tsx->role==PJSIP_ROLE_UAS &&
3152 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003153 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003154 {
3155 /*
3156 * Incoming REFER request.
3157 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003158 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003159
Benny Prijono26ff9062006-02-21 23:47:00 +00003160 }
Benny Prijonob0808372006-03-02 21:18:58 +00003161 else if (tsx->role==PJSIP_ROLE_UAS &&
3162 tsx->state==PJSIP_TSX_STATE_TRYING &&
3163 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3164 {
3165 /*
3166 * Incoming MESSAGE request!
3167 */
3168 pjsip_rx_data *rdata;
3169 pjsip_msg *msg;
3170 pjsip_accept_hdr *accept_hdr;
3171 pj_status_t status;
3172
3173 rdata = e->body.tsx_state.src.rdata;
3174 msg = rdata->msg_info.msg;
3175
3176 /* Request MUST have message body, with Content-Type equal to
3177 * "text/plain".
3178 */
3179 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3180
3181 pjsip_hdr hdr_list;
3182
3183 pj_list_init(&hdr_list);
3184 pj_list_push_back(&hdr_list, accept_hdr);
3185
3186 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3187 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003188 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003189 return;
3190 }
3191
3192 /* Respond with 200 first, so that remote doesn't retransmit in case
3193 * the UI takes too long to process the message.
3194 */
3195 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3196
3197 /* Process MESSAGE request */
3198 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3199 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003200
Benny Prijonob0808372006-03-02 21:18:58 +00003201 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003202 else if (tsx->role == PJSIP_ROLE_UAC &&
3203 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003204 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003205 /* Handle outgoing pager status */
3206 if (tsx->status_code >= 200) {
3207 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003208
Benny Prijonoa1e69682007-05-11 15:14:34 +00003209 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003210 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003211
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003212 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3213 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3214 &im_data->to,
3215 &im_data->body,
3216 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003217 (pjsip_status_code)
3218 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003219 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003220 }
Benny Prijonofccab712006-02-22 22:23:22 +00003221 }
Benny Prijono834aee32006-02-19 01:38:06 +00003222 }
Benny Prijono834aee32006-02-19 01:38:06 +00003223
Benny Prijono26ff9062006-02-21 23:47:00 +00003224
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003225 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003226}