blob: 9e88a3602e0e792dca304124b3ac2ebfa4d33056 [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 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000227 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000228 }
229
230 for (cid=pjsua_var.next_call_id;
231 cid<(int)pjsua_var.ua_cfg.max_calls;
232 ++cid)
233 {
234 if (pjsua_var.calls[cid].inv == NULL) {
235 ++pjsua_var.next_call_id;
236 return cid;
237 }
238 }
239
240 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
241 if (pjsua_var.calls[cid].inv == NULL) {
242 ++pjsua_var.next_call_id;
243 return cid;
244 }
245 }
246
247#else
248 /* Old algorithm */
249 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
250 if (pjsua_var.calls[cid].inv == NULL)
251 return cid;
252 }
253#endif
254
255 return PJSUA_INVALID_ID;
256}
257
Benny Prijonod8179652008-01-23 20:39:07 +0000258/* Get signaling secure level.
259 * Return:
260 * 0: if signaling is not secure
261 * 1: if TLS transport is used for immediate hop
262 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000263 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000264static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000265{
266 const pj_str_t tls = pj_str(";transport=tls");
267 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000268 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000269
270 if (pj_stristr(dst_uri, &sips))
271 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000272
273 if (!pj_list_empty(&acc->route_set)) {
274 pjsip_route_hdr *r = acc->route_set.next;
275 pjsip_uri *uri = r->name_addr.uri;
276 pjsip_sip_uri *sip_uri;
277
278 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
279 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
280 return 1;
281
282 } else {
283 if (pj_stristr(dst_uri, &tls))
284 return 1;
285 }
286
Benny Prijonod8179652008-01-23 20:39:07 +0000287 return 0;
288}
289
Benny 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) {
Benny Prijono617b8602008-04-07 10:10:31 +0000719 const pj_str_t reason = pj_str("Bad SDP");
720 pjsua_perror(THIS_FILE, "Error parsing SDP in incoming INVITE",
721 status);
722 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, &reason,
Benny Prijonod8179652008-01-23 20:39:07 +0000723 NULL, NULL);
724 pjsua_media_channel_deinit(call->index);
725 PJSUA_UNLOCK();
726 return PJ_TRUE;
727 }
Benny Prijono617b8602008-04-07 10:10:31 +0000728
729 /* Do quick checks on SDP before passing it to transports. More elabore
730 * checks will be done in pjsip_inv_verify_request2() below.
731 */
732 if (offer->media_count==0) {
733 const pj_str_t reason = pj_str("Missing media in SDP");
734 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
735 NULL, NULL, NULL);
736 PJSUA_UNLOCK();
737 return PJ_TRUE;
738 }
739
Benny Prijonod8179652008-01-23 20:39:07 +0000740 } else {
741 offer = NULL;
742 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000743
Benny Prijonoc97608e2007-03-23 16:34:20 +0000744 /* Get media capability from media endpoint: */
Benny Prijonod8179652008-01-23 20:39:07 +0000745 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000746 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000747 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000748 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
749 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
750 sip_err_code, NULL,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000751 NULL, NULL);
752 pjsua_media_channel_deinit(call->index);
753 PJSUA_UNLOCK();
754 return PJ_TRUE;
755 }
756
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000757 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000758 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000759 if (pjsua_var.acc[acc_id].cfg.require_100rel)
760 options |= PJSIP_INV_REQUIRE_100REL;
761
Benny Prijonod8179652008-01-23 20:39:07 +0000762 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
763 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000764 if (status != PJ_SUCCESS) {
765
766 /*
767 * No we can't handle the incoming INVITE request.
768 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000769 if (response) {
770 pjsip_response_addr res_addr;
771
772 pjsip_get_response_addr(response->pool, rdata, &res_addr);
773 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
774 NULL, NULL);
775
776 } else {
777
778 /* Respond with 500 (Internal Server Error) */
779 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
780 NULL, NULL);
781 }
782
Benny Prijonoc97608e2007-03-23 16:34:20 +0000783 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000784 PJSUA_UNLOCK();
785 return PJ_TRUE;
786 }
787
788
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000789 /* Get suitable Contact header */
790 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
791 acc_id, rdata);
792 if (status != PJ_SUCCESS) {
793 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
794 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
795 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000796 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000797 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000798 return PJ_TRUE;
799 }
800
Benny Prijono26ff9062006-02-21 23:47:00 +0000801 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000802 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000803 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000804 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000805 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000806 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000807 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000808 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000809 return PJ_TRUE;
810 }
811
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000812 /* Set credentials */
813 if (pjsua_var.acc[acc_id].cred_cnt) {
814 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
815 pjsua_var.acc[acc_id].cred_cnt,
816 pjsua_var.acc[acc_id].cred);
817 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000818
Benny Prijono48ab2b72007-11-08 09:24:30 +0000819 /* Set preference */
820 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
821 &pjsua_var.acc[acc_id].cfg.auth_pref);
822
Benny Prijono26ff9062006-02-21 23:47:00 +0000823 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000824 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000825 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000826 pjsip_hdr hdr_list;
827 pjsip_warning_hdr *w;
828
829 w = pjsip_warning_hdr_create_from_status(dlg->pool,
830 pjsip_endpt_name(pjsua_var.endpt),
831 status);
832 pj_list_init(&hdr_list);
833 pj_list_push_back(&hdr_list, w);
834
835 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
836
837 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000838 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000839 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000840 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000841 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000842 return PJ_TRUE;
843 }
844
Benny Prijonoea9fd392007-11-06 03:41:40 +0000845 /* Update NAT type of remote endpoint, only when there is SDP in
846 * incoming INVITE!
847 */
848 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
849 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
850 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000851 const pjmedia_sdp_session *remote_sdp;
852
853 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
854 update_remote_nat_type(call, remote_sdp);
855 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000856
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000857 /* Create and attach pjsua_var data to the dialog: */
858 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000859
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860 dlg->mod_data[pjsua_var.mod.id] = call;
861 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000862
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000863 /* If account is locked to specific transport, then lock dialog
864 * to this transport too.
865 */
866 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
867 pjsip_tpselector tp_sel;
868
869 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
870 pjsip_dlg_set_transport(dlg, &tp_sel);
871 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000872
Benny Prijono64f851e2006-02-23 13:49:28 +0000873 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000874 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000875 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000876 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000877 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000878 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
879 status);
880
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000881 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
882 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000883 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000884 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000885 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000886
887 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000888 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000889 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000890 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000891 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000892 }
893
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000895
Benny Prijono105217f2006-03-06 16:25:59 +0000896
Benny Prijono053f5222006-11-11 16:16:04 +0000897 /* Check if this request should replace existing call */
898 if (replaced_dlg) {
899 pjsip_inv_session *replaced_inv;
900 struct pjsua_call *replaced_call;
901 pjsip_tx_data *tdata;
902
903 /* Get the invite session in the dialog */
904 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
905
906 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000907 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000908
909 /* Notify application */
910 if (pjsua_var.ua_cfg.cb.on_call_replaced)
911 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
912 call_id);
913
914 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
915 call_id));
916
917 /* Answer the new call with 200 response */
918 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
919 if (status == PJ_SUCCESS)
920 status = pjsip_inv_send_msg(inv, tdata);
921
922 if (status != PJ_SUCCESS)
923 pjsua_perror(THIS_FILE, "Error answering session", status);
924
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000925 /* Note that inv may be invalid if 200/OK has caused error in
926 * starting the media.
927 */
Benny Prijono053f5222006-11-11 16:16:04 +0000928
929 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
930 replaced_call->index));
931
932 /* Disconnect replaced invite session */
933 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
934 &tdata);
935 if (status == PJ_SUCCESS && tdata)
936 status = pjsip_inv_send_msg(replaced_inv, tdata);
937
938 if (status != PJ_SUCCESS)
939 pjsua_perror(THIS_FILE, "Error terminating session", status);
940
941
942 } else {
943
Benny Prijonob5388cf2007-01-04 22:45:08 +0000944 /* Notify application if on_incoming_call() is overriden,
945 * otherwise hangup the call with 480
946 */
947 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000948 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000949 } else {
950 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
951 NULL, NULL);
952 }
Benny Prijono053f5222006-11-11 16:16:04 +0000953 }
954
Benny Prijono8b1889b2006-06-06 18:40:40 +0000955
Benny Prijono26ff9062006-02-21 23:47:00 +0000956 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000957 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000958 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000959}
960
961
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000962
963/*
964 * Check if the specified call has active INVITE session and the INVITE
965 * session has not been disconnected.
966 */
967PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
968{
969 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
970 PJ_EINVAL);
971 return pjsua_var.calls[call_id].inv != NULL &&
972 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
973}
974
975
976/*
977 * Check if call has an active media session.
978 */
979PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
980{
981 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
982 PJ_EINVAL);
983 return pjsua_var.calls[call_id].session != NULL;
984}
985
986
Benny Prijono148c9dd2006-09-19 13:37:53 +0000987/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000988pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000989 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000990 pjsua_call **p_call,
991 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000992{
993 enum { MAX_RETRY=50 };
994 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000995 pjsua_call *call = NULL;
996 pj_bool_t has_pjsua_lock = PJ_FALSE;
997 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000998
999 for (retry=0; retry<MAX_RETRY; ++retry) {
1000
1001 has_pjsua_lock = PJ_FALSE;
1002
1003 status = PJSUA_TRY_LOCK();
1004 if (status != PJ_SUCCESS) {
1005 pj_thread_sleep(retry/10);
1006 continue;
1007 }
1008
1009 has_pjsua_lock = PJ_TRUE;
1010 call = &pjsua_var.calls[call_id];
1011
1012 if (call->inv == NULL) {
1013 PJSUA_UNLOCK();
1014 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1015 return PJSIP_ESESSIONTERMINATED;
1016 }
1017
1018 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1019 if (status != PJ_SUCCESS) {
1020 PJSUA_UNLOCK();
1021 pj_thread_sleep(retry/10);
1022 continue;
1023 }
1024
1025 PJSUA_UNLOCK();
1026
1027 break;
1028 }
1029
1030 if (status != PJ_SUCCESS) {
1031 if (has_pjsua_lock == PJ_FALSE)
1032 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1033 "(possibly system has deadlocked) in %s",
1034 title));
1035 else
1036 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1037 "(possibly system has deadlocked) in %s",
1038 title));
1039 return PJ_ETIMEDOUT;
1040 }
1041
1042 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001043 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001044
1045 return PJ_SUCCESS;
1046}
1047
1048
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001049/*
1050 * Get the conference port identification associated with the call.
1051 */
1052PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1053{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001054 pjsua_call *call;
1055 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001056 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001057 pj_status_t status;
1058
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001059 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1060 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001061
Benny Prijonodc752ca2006-09-22 16:55:42 +00001062 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001063 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001064 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001065
1066 port_id = call->conf_slot;
1067
Benny Prijonodc752ca2006-09-22 16:55:42 +00001068 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001069
1070 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071}
1072
1073
Benny Prijono148c9dd2006-09-19 13:37:53 +00001074
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001075/*
1076 * Obtain detail information about the specified call.
1077 */
1078PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1079 pjsua_call_info *info)
1080{
1081 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001082 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001083 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001084
1085 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1086 PJ_EINVAL);
1087
Benny Prijonoac623b32006-07-03 15:19:31 +00001088 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001089
Benny Prijonodc752ca2006-09-22 16:55:42 +00001090 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001091 if (status != PJ_SUCCESS) {
1092 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001093 }
1094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095 /* id and role */
1096 info->id = call_id;
1097 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001098 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001099
1100 /* local info */
1101 info->local_info.ptr = info->buf_.local_info;
1102 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1103 sizeof(info->buf_.local_info));
1104
1105 /* local contact */
1106 info->local_contact.ptr = info->buf_.local_contact;
1107 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1108 call->inv->dlg->local.contact->uri,
1109 info->local_contact.ptr,
1110 sizeof(info->buf_.local_contact));
1111
1112 /* remote info */
1113 info->remote_info.ptr = info->buf_.remote_info;
1114 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1115 sizeof(info->buf_.remote_info));
1116
1117 /* remote contact */
1118 if (call->inv->dlg->remote.contact) {
1119 int len;
1120 info->remote_contact.ptr = info->buf_.remote_contact;
1121 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1122 call->inv->dlg->remote.contact->uri,
1123 info->remote_contact.ptr,
1124 sizeof(info->buf_.remote_contact));
1125 if (len < 0) len = 0;
1126 info->remote_contact.slen = len;
1127 } else {
1128 info->remote_contact.slen = 0;
1129 }
1130
1131 /* call id */
1132 info->call_id.ptr = info->buf_.call_id;
1133 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1134 sizeof(info->buf_.call_id));
1135
1136 /* state, state_text */
1137 info->state = call->inv->state;
1138 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1139
1140 /* If call is disconnected, set the last_status from the cause code */
1141 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1142 /* last_status, last_status_text */
1143 info->last_status = call->inv->cause;
1144
1145 info->last_status_text.ptr = info->buf_.last_status_text;
1146 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1147 sizeof(info->buf_.last_status_text));
1148 } else {
1149 /* last_status, last_status_text */
1150 info->last_status = call->last_code;
1151
1152 info->last_status_text.ptr = info->buf_.last_status_text;
1153 pj_strncpy(&info->last_status_text, &call->last_text,
1154 sizeof(info->buf_.last_status_text));
1155 }
1156
1157 /* media status and dir */
1158 info->media_status = call->media_st;
1159 info->media_dir = call->media_dir;
1160
1161
1162 /* conference slot number */
1163 info->conf_slot = call->conf_slot;
1164
1165 /* calculate duration */
1166 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1167
1168 info->total_duration = call->dis_time;
1169 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1170
1171 if (call->conn_time.sec) {
1172 info->connect_duration = call->dis_time;
1173 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1174 }
1175
1176 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1177
1178 pj_gettimeofday(&info->total_duration);
1179 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1180
1181 pj_gettimeofday(&info->connect_duration);
1182 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1183
1184 } else {
1185 pj_gettimeofday(&info->total_duration);
1186 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1187 }
1188
Benny Prijonodc752ca2006-09-22 16:55:42 +00001189 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001190
1191 return PJ_SUCCESS;
1192}
1193
1194
1195/*
1196 * Attach application specific data to the call.
1197 */
1198PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1199 void *user_data)
1200{
1201 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1202 PJ_EINVAL);
1203 pjsua_var.calls[call_id].user_data = user_data;
1204
1205 return PJ_SUCCESS;
1206}
1207
1208
1209/*
1210 * Get user data attached to the call.
1211 */
1212PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1213{
1214 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1215 NULL);
1216 return pjsua_var.calls[call_id].user_data;
1217}
1218
1219
1220/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001221 * Get remote's NAT type.
1222 */
1223PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1224 pj_stun_nat_type *p_type)
1225{
1226 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1227 PJ_EINVAL);
1228 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1229
1230 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1231 return PJ_SUCCESS;
1232}
1233
1234
1235/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236 * Send response to incoming INVITE request.
1237 */
1238PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1239 unsigned code,
1240 const pj_str_t *reason,
1241 const pjsua_msg_data *msg_data)
1242{
1243 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001244 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 pjsip_tx_data *tdata;
1246 pj_status_t status;
1247
1248 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1249 PJ_EINVAL);
1250
Benny Prijonodc752ca2006-09-22 16:55:42 +00001251 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001252 if (status != PJ_SUCCESS)
1253 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001254
Benny Prijono2e507c22006-06-23 15:04:11 +00001255 if (call->res_time.sec == 0)
1256 pj_gettimeofday(&call->res_time);
1257
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001258 if (reason && reason->slen == 0)
1259 reason = NULL;
1260
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261 /* Create response message */
1262 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1263 if (status != PJ_SUCCESS) {
1264 pjsua_perror(THIS_FILE, "Error creating response",
1265 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001266 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267 return status;
1268 }
1269
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001270 /* Call might have been disconnected if application is answering with
1271 * 200/OK and the media failed to start.
1272 */
1273 if (call->inv == NULL) {
1274 pjsip_dlg_dec_lock(dlg);
1275 return PJSIP_ESESSIONTERMINATED;
1276 }
1277
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001278 /* Add additional headers etc */
1279 pjsua_process_msg_data( tdata, msg_data);
1280
1281 /* Send the message */
1282 status = pjsip_inv_send_msg(call->inv, tdata);
1283 if (status != PJ_SUCCESS)
1284 pjsua_perror(THIS_FILE, "Error sending response",
1285 status);
1286
Benny Prijonodc752ca2006-09-22 16:55:42 +00001287 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288
1289 return status;
1290}
1291
1292
1293/*
1294 * Hangup call by using method that is appropriate according to the
1295 * call state.
1296 */
1297PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1298 unsigned code,
1299 const pj_str_t *reason,
1300 const pjsua_msg_data *msg_data)
1301{
1302 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001303 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001304 pj_status_t status;
1305 pjsip_tx_data *tdata;
1306
1307
Benny Prijono148c9dd2006-09-19 13:37:53 +00001308 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1309 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1310 call_id));
1311 }
1312
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001313 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1314 PJ_EINVAL);
1315
Benny Prijonodc752ca2006-09-22 16:55:42 +00001316 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001317 if (status != PJ_SUCCESS)
1318 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001319
1320 if (code==0) {
1321 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1322 code = PJSIP_SC_OK;
1323 else if (call->inv->role == PJSIP_ROLE_UAS)
1324 code = PJSIP_SC_DECLINE;
1325 else
1326 code = PJSIP_SC_REQUEST_TERMINATED;
1327 }
1328
1329 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1330 if (status != PJ_SUCCESS) {
1331 pjsua_perror(THIS_FILE,
1332 "Failed to create end session message",
1333 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001334 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335 return status;
1336 }
1337
1338 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1339 * as p_tdata when INVITE transaction has not been answered
1340 * with any provisional responses.
1341 */
1342 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001343 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001344 return PJ_SUCCESS;
1345 }
1346
1347 /* Add additional headers etc */
1348 pjsua_process_msg_data( tdata, msg_data);
1349
1350 /* Send the message */
1351 status = pjsip_inv_send_msg(call->inv, tdata);
1352 if (status != PJ_SUCCESS) {
1353 pjsua_perror(THIS_FILE,
1354 "Failed to send end session message",
1355 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001356 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357 return status;
1358 }
1359
Benny Prijonodc752ca2006-09-22 16:55:42 +00001360 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001361
1362 return PJ_SUCCESS;
1363}
1364
1365
1366/*
1367 * Put the specified call on hold.
1368 */
1369PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1370 const pjsua_msg_data *msg_data)
1371{
1372 pjmedia_sdp_session *sdp;
1373 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001374 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001375 pjsip_tx_data *tdata;
1376 pj_status_t status;
1377
1378 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1379 PJ_EINVAL);
1380
Benny Prijonodc752ca2006-09-22 16:55:42 +00001381 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001382 if (status != PJ_SUCCESS)
1383 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385
1386 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1387 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001388 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389 return PJSIP_ESESSIONSTATE;
1390 }
1391
1392 status = create_inactive_sdp(call, &sdp);
1393 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001394 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 return status;
1396 }
1397
Benny Prijono7129cc72007-11-05 05:54:25 +00001398 update_sdp_version(call, sdp);
1399
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 /* Create re-INVITE with new offer */
1401 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1402 if (status != PJ_SUCCESS) {
1403 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001404 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405 return status;
1406 }
1407
1408 /* Add additional headers etc */
1409 pjsua_process_msg_data( tdata, msg_data);
1410
1411 /* Send the request */
1412 status = pjsip_inv_send_msg( call->inv, tdata);
1413 if (status != PJ_SUCCESS) {
1414 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001415 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001416 return status;
1417 }
1418
Benny Prijonodc752ca2006-09-22 16:55:42 +00001419 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001420
1421 return PJ_SUCCESS;
1422}
1423
1424
1425/*
1426 * Send re-INVITE (to release hold).
1427 */
1428PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1429 pj_bool_t unhold,
1430 const pjsua_msg_data *msg_data)
1431{
1432 pjmedia_sdp_session *sdp;
1433 pjsip_tx_data *tdata;
1434 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001435 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001436 pj_status_t status;
1437
1438
1439 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1440 PJ_EINVAL);
1441
Benny Prijonodc752ca2006-09-22 16:55:42 +00001442 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001443 if (status != PJ_SUCCESS)
1444 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445
1446 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1447 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001448 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001449 return PJSIP_ESESSIONSTATE;
1450 }
1451
Benny Prijonodb844a42008-02-02 17:07:18 +00001452 /* Update call secure level */
1453 call->secure_level = call_get_secure_level(call);
1454
Benny Prijono667952e2007-04-02 19:27:54 +00001455 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001456 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00001457 call->secure_level, NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00001458 if (status != PJ_SUCCESS) {
1459 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1460 pjsip_dlg_dec_lock(dlg);
1461 return PJSIP_ESESSIONSTATE;
1462 }
1463
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001464 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001465 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001466 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonod8179652008-01-23 20:39:07 +00001467 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001468 NULL, &sdp, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 if (status != PJ_SUCCESS) {
1470 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1471 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001472 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 return status;
1474 }
1475
Benny Prijono7129cc72007-11-05 05:54:25 +00001476 update_sdp_version(call, sdp);
1477
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 /* Create re-INVITE with new offer */
1479 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1480 if (status != PJ_SUCCESS) {
1481 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483 return status;
1484 }
1485
1486 /* Add additional headers etc */
1487 pjsua_process_msg_data( tdata, msg_data);
1488
1489 /* Send the request */
1490 status = pjsip_inv_send_msg( call->inv, tdata);
1491 if (status != PJ_SUCCESS) {
1492 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001493 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494 return status;
1495 }
1496
Benny Prijonodc752ca2006-09-22 16:55:42 +00001497 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001498
1499 return PJ_SUCCESS;
1500}
1501
1502
1503/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001504 * Send UPDATE request.
1505 */
1506PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1507 unsigned options,
1508 const pjsua_msg_data *msg_data)
1509{
1510 pjmedia_sdp_session *sdp;
1511 pjsip_tx_data *tdata;
1512 pjsua_call *call;
1513 pjsip_dialog *dlg;
1514 pj_status_t status;
1515
1516 PJ_UNUSED_ARG(options);
1517
1518 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1519 PJ_EINVAL);
1520
1521 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1522 if (status != PJ_SUCCESS)
1523 return status;
1524
Benny Prijonodb844a42008-02-02 17:07:18 +00001525 /* Update call's secure level */
1526 call->secure_level = call_get_secure_level(call);
1527
Benny Prijonoc08682e2007-10-04 06:17:58 +00001528 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001529 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00001530 call->secure_level, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001531 if (status != PJ_SUCCESS) {
1532 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1533 pjsip_dlg_dec_lock(dlg);
1534 return PJSIP_ESESSIONSTATE;
1535 }
1536
1537 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001538 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001539 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001540 if (status != PJ_SUCCESS) {
1541 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1542 status);
1543 pjsip_dlg_dec_lock(dlg);
1544 return status;
1545 }
1546
1547 /* Create re-INVITE with new offer */
1548 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1549 if (status != PJ_SUCCESS) {
1550 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1551 pjsip_dlg_dec_lock(dlg);
1552 return status;
1553 }
1554
1555 /* Add additional headers etc */
1556 pjsua_process_msg_data( tdata, msg_data);
1557
1558 /* Send the request */
1559 status = pjsip_inv_send_msg( call->inv, tdata);
1560 if (status != PJ_SUCCESS) {
1561 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1562 pjsip_dlg_dec_lock(dlg);
1563 return status;
1564 }
1565
1566 pjsip_dlg_dec_lock(dlg);
1567
1568 return PJ_SUCCESS;
1569}
1570
1571
1572/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 * Initiate call transfer to the specified address.
1574 */
1575PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1576 const pj_str_t *dest,
1577 const pjsua_msg_data *msg_data)
1578{
1579 pjsip_evsub *sub;
1580 pjsip_tx_data *tdata;
1581 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001582 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001583 pjsip_generic_string_hdr *gs_hdr;
1584 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001585 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586 pj_status_t status;
1587
1588
1589 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1590 PJ_EINVAL);
1591
Benny Prijonodc752ca2006-09-22 16:55:42 +00001592 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001593 if (status != PJ_SUCCESS)
1594 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596
Benny Prijonod524e822006-09-22 12:48:18 +00001597 /* Create xfer client subscription. */
1598 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001599 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001600
1601 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602 if (status != PJ_SUCCESS) {
1603 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001604 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001605 return status;
1606 }
1607
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001608 /* Associate this call with the client subscription */
1609 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611 /*
1612 * Create REFER request.
1613 */
1614 status = pjsip_xfer_initiate(sub, dest, &tdata);
1615 if (status != PJ_SUCCESS) {
1616 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001617 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 return status;
1619 }
1620
Benny Prijono053f5222006-11-11 16:16:04 +00001621 /* Add Referred-By header */
1622 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1623 &dlg->local.info_str);
1624 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1625
1626
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627 /* Add additional headers etc */
1628 pjsua_process_msg_data( tdata, msg_data);
1629
1630 /* Send. */
1631 status = pjsip_xfer_send_request(sub, tdata);
1632 if (status != PJ_SUCCESS) {
1633 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001634 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635 return status;
1636 }
1637
1638 /* For simplicity (that's what this program is intended to be!),
1639 * leave the original invite session as it is. More advanced application
1640 * may want to hold the INVITE, or terminate the invite, or whatever.
1641 */
1642
Benny Prijonodc752ca2006-09-22 16:55:42 +00001643 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644
1645 return PJ_SUCCESS;
1646
1647}
1648
1649
1650/*
Benny Prijono053f5222006-11-11 16:16:04 +00001651 * Initiate attended call transfer to the specified address.
1652 */
1653PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1654 pjsua_call_id dest_call_id,
1655 unsigned options,
1656 const pjsua_msg_data *msg_data)
1657{
1658 pjsua_call *dest_call;
1659 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001660 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001661 pj_str_t str_dest;
1662 int len;
1663 pjsip_uri *uri;
1664 pj_status_t status;
1665
1666
1667 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1668 PJ_EINVAL);
1669 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1670 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1671 PJ_EINVAL);
1672
1673 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1674 &dest_call, &dest_dlg);
1675 if (status != PJ_SUCCESS)
1676 return status;
1677
1678 /*
1679 * Create REFER destination URI with Replaces field.
1680 */
1681
1682 /* Make sure we have sufficient buffer's length */
1683 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1684 dest_dlg->call_id->id.slen +
1685 dest_dlg->remote.info->tag.slen +
1686 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001687 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001688
1689 /* Print URI */
1690 str_dest_buf[0] = '<';
1691 str_dest.slen = 1;
1692
Benny Prijonoa1e69682007-05-11 15:14:34 +00001693 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001694 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1695 str_dest_buf+1, sizeof(str_dest_buf)-1);
1696 if (len < 0)
1697 return PJSIP_EURITOOLONG;
1698
1699 str_dest.slen += len;
1700
1701
1702 /* Build the URI */
1703 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1704 sizeof(str_dest_buf) - str_dest.slen,
1705 "?%s"
1706 "Replaces=%.*s"
1707 "%%3Bto-tag%%3D%.*s"
1708 "%%3Bfrom-tag%%3D%.*s>",
1709 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1710 "" : "Require=replaces&"),
1711 (int)dest_dlg->call_id->id.slen,
1712 dest_dlg->call_id->id.ptr,
1713 (int)dest_dlg->remote.info->tag.slen,
1714 dest_dlg->remote.info->tag.ptr,
1715 (int)dest_dlg->local.info->tag.slen,
1716 dest_dlg->local.info->tag.ptr);
1717
1718 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1719 PJSIP_EURITOOLONG);
1720
1721 str_dest.ptr = str_dest_buf;
1722 str_dest.slen += len;
1723
1724 pjsip_dlg_dec_lock(dest_dlg);
1725
1726 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1727}
1728
1729
1730/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001731 * Send DTMF digits to remote using RFC 2833 payload formats.
1732 */
1733PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1734 const pj_str_t *digits)
1735{
1736 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001737 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001738 pj_status_t status;
1739
1740 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1741 PJ_EINVAL);
1742
Benny Prijonodc752ca2006-09-22 16:55:42 +00001743 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001744 if (status != PJ_SUCCESS)
1745 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001746
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001747 if (!call->session) {
1748 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001749 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001750 return PJ_EINVALIDOP;
1751 }
1752
1753 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1754
Benny Prijonodc752ca2006-09-22 16:55:42 +00001755 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001756
1757 return status;
1758}
1759
1760
1761/**
1762 * Send instant messaging inside INVITE session.
1763 */
1764PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1765 const pj_str_t *mime_type,
1766 const pj_str_t *content,
1767 const pjsua_msg_data *msg_data,
1768 void *user_data)
1769{
1770 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001771 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 const pj_str_t mime_text_plain = pj_str("text/plain");
1773 pjsip_media_type ctype;
1774 pjsua_im_data *im_data;
1775 pjsip_tx_data *tdata;
1776 pj_status_t status;
1777
1778
1779 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1780 PJ_EINVAL);
1781
Benny Prijonodc752ca2006-09-22 16:55:42 +00001782 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001783 if (status != PJ_SUCCESS)
1784 return status;
1785
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001786 /* Set default media type if none is specified */
1787 if (mime_type == NULL) {
1788 mime_type = &mime_text_plain;
1789 }
1790
1791 /* Create request message. */
1792 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1793 -1, &tdata);
1794 if (status != PJ_SUCCESS) {
1795 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1796 goto on_return;
1797 }
1798
1799 /* Add accept header. */
1800 pjsip_msg_add_hdr( tdata->msg,
1801 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1802
1803 /* Parse MIME type */
1804 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1805
1806 /* Create "text/plain" message body. */
1807 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1808 &ctype.subtype, content);
1809 if (tdata->msg->body == NULL) {
1810 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1811 pjsip_tx_data_dec_ref(tdata);
1812 goto on_return;
1813 }
1814
1815 /* Add additional headers etc */
1816 pjsua_process_msg_data( tdata, msg_data);
1817
1818 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001819 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 im_data->acc_id = call->acc_id;
1821 im_data->call_id = call_id;
1822 im_data->to = call->inv->dlg->remote.info_str;
1823 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1824 im_data->user_data = user_data;
1825
1826
1827 /* Send the request. */
1828 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1829 pjsua_var.mod.id, im_data);
1830 if (status != PJ_SUCCESS) {
1831 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1832 goto on_return;
1833 }
1834
1835on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001836 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001837 return status;
1838}
1839
1840
1841/*
1842 * Send IM typing indication inside INVITE session.
1843 */
1844PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1845 pj_bool_t is_typing,
1846 const pjsua_msg_data*msg_data)
1847{
1848 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001849 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 pjsip_tx_data *tdata;
1851 pj_status_t status;
1852
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001853 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1854 PJ_EINVAL);
1855
Benny Prijonodc752ca2006-09-22 16:55:42 +00001856 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001857 if (status != PJ_SUCCESS)
1858 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001859
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 /* Create request message. */
1861 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1862 -1, &tdata);
1863 if (status != PJ_SUCCESS) {
1864 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1865 goto on_return;
1866 }
1867
1868 /* Create "application/im-iscomposing+xml" msg body. */
1869 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1870 NULL, NULL, -1);
1871
1872 /* Add additional headers etc */
1873 pjsua_process_msg_data( tdata, msg_data);
1874
1875 /* Send the request. */
1876 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1877 if (status != PJ_SUCCESS) {
1878 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1879 goto on_return;
1880 }
1881
1882on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001883 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001884 return status;
1885}
1886
1887
1888/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001889 * Send arbitrary request.
1890 */
1891PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1892 const pj_str_t *method_str,
1893 const pjsua_msg_data *msg_data)
1894{
1895 pjsua_call *call;
1896 pjsip_dialog *dlg;
1897 pjsip_method method;
1898 pjsip_tx_data *tdata;
1899 pj_status_t status;
1900
1901 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1902 PJ_EINVAL);
1903
1904 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1905 if (status != PJ_SUCCESS)
1906 return status;
1907
1908 /* Init method */
1909 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1910
1911 /* Create request message. */
1912 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1913 if (status != PJ_SUCCESS) {
1914 pjsua_perror(THIS_FILE, "Unable to create request", status);
1915 goto on_return;
1916 }
1917
1918 /* Add additional headers etc */
1919 pjsua_process_msg_data( tdata, msg_data);
1920
1921 /* Send the request. */
1922 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1923 if (status != PJ_SUCCESS) {
1924 pjsua_perror(THIS_FILE, "Unable to send request", status);
1925 goto on_return;
1926 }
1927
1928on_return:
1929 pjsip_dlg_dec_lock(dlg);
1930 return status;
1931}
1932
1933
1934/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001935 * Terminate all calls.
1936 */
1937PJ_DEF(void) pjsua_call_hangup_all(void)
1938{
1939 unsigned i;
1940
1941 PJSUA_LOCK();
1942
1943 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1944 if (pjsua_var.calls[i].inv)
1945 pjsua_call_hangup(i, 0, NULL, NULL);
1946 }
1947
1948 PJSUA_UNLOCK();
1949}
1950
1951
Benny Prijono627cbb42007-09-25 20:48:49 +00001952const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001953{
1954 if (val < 1000) {
1955 pj_ansi_sprintf(buf, "%d", val);
1956 } else if (val < 1000000) {
1957 pj_ansi_sprintf(buf, "%d.%dK",
1958 val / 1000,
1959 (val % 1000) / 100);
1960 } else {
1961 pj_ansi_sprintf(buf, "%d.%02dM",
1962 val / 1000000,
1963 (val % 1000000) / 10000);
1964 }
1965
1966 return buf;
1967}
1968
1969
1970/* Dump media session */
1971static void dump_media_session(const char *indent,
1972 char *buf, unsigned maxlen,
1973 pjmedia_session *session)
1974{
1975 unsigned i;
1976 char *p = buf, *end = buf+maxlen;
1977 int len;
1978 pjmedia_session_info info;
1979
1980 pjmedia_session_get_info(session, &info);
1981
1982 for (i=0; i<info.stream_cnt; ++i) {
1983 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00001984 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001985 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001986 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001987 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001988 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001989 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001990
1991 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00001992 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
1993 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001994
1995 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1996 dir = "sendonly";
1997 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1998 dir = "recvonly";
1999 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2000 dir = "sendrecv";
2001 else
2002 dir = "inactive";
2003
2004
2005 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002006 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002007 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002008 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002009 info.stream_info[i].fmt.encoding_name.ptr,
2010 info.stream_info[i].fmt.clock_rate / 1000,
2011 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002012 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002013 if (len < 1 || len > end-p) {
2014 *p = '\0';
2015 return;
2016 }
2017
2018 p += len;
2019 *p++ = '\n';
2020 *p = '\0';
2021
2022 if (stat.rx.update_cnt == 0)
2023 strcpy(last_update, "never");
2024 else {
2025 pj_gettimeofday(&now);
2026 PJ_TIME_VAL_SUB(now, stat.rx.update);
2027 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2028 now.sec / 3600,
2029 (now.sec % 3600) / 60,
2030 now.sec % 60,
2031 now.msec);
2032 }
2033
Benny Prijono80019eb2006-08-07 13:22:23 +00002034 pj_gettimeofday(&media_duration);
2035 PJ_TIME_VAL_SUB(media_duration, stat.start);
2036 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2037 media_duration.msec = 1;
2038
Benny Prijono1402a4a2008-01-08 23:41:22 +00002039 /* protect against division by zero */
2040 if (stat.rx.pkt == 0)
2041 stat.rx.pkt = 1;
2042 if (stat.tx.pkt == 0)
2043 stat.tx.pkt = 1;
2044
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002045 len = pj_ansi_snprintf(p, end-p,
2046 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002047 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2049 "%s (msec) min avg max last\n"
2050 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2051 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2052 indent, info.stream_info[i].fmt.pt,
2053 last_update,
2054 indent,
2055 good_number(packets, stat.rx.pkt),
2056 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002057 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002058 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2059 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 +00002060 indent,
2061 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002062 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002064 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002066 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067 indent, indent,
2068 stat.rx.loss_period.min / 1000.0,
2069 stat.rx.loss_period.avg / 1000.0,
2070 stat.rx.loss_period.max / 1000.0,
2071 stat.rx.loss_period.last / 1000.0,
2072 indent,
2073 stat.rx.jitter.min / 1000.0,
2074 stat.rx.jitter.avg / 1000.0,
2075 stat.rx.jitter.max / 1000.0,
2076 stat.rx.jitter.last / 1000.0,
2077 ""
2078 );
2079
2080 if (len < 1 || len > end-p) {
2081 *p = '\0';
2082 return;
2083 }
2084
2085 p += len;
2086 *p++ = '\n';
2087 *p = '\0';
2088
2089 if (stat.tx.update_cnt == 0)
2090 strcpy(last_update, "never");
2091 else {
2092 pj_gettimeofday(&now);
2093 PJ_TIME_VAL_SUB(now, stat.tx.update);
2094 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2095 now.sec / 3600,
2096 (now.sec % 3600) / 60,
2097 now.sec % 60,
2098 now.msec);
2099 }
2100
2101 len = pj_ansi_snprintf(p, end-p,
2102 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002103 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002104 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2105 "%s (msec) min avg max last\n"
2106 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2107 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2108 indent,
2109 info.stream_info[i].tx_pt,
2110 info.stream_info[i].param->info.frm_ptime *
2111 info.stream_info[i].param->setting.frm_per_pkt,
2112 last_update,
2113
2114 indent,
2115 good_number(packets, stat.tx.pkt),
2116 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002117 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002118 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2119 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002120
2121 indent,
2122 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002123 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002124 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002125 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002126 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002127 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128
2129 indent, indent,
2130 stat.tx.loss_period.min / 1000.0,
2131 stat.tx.loss_period.avg / 1000.0,
2132 stat.tx.loss_period.max / 1000.0,
2133 stat.tx.loss_period.last / 1000.0,
2134 indent,
2135 stat.tx.jitter.min / 1000.0,
2136 stat.tx.jitter.avg / 1000.0,
2137 stat.tx.jitter.max / 1000.0,
2138 stat.tx.jitter.last / 1000.0,
2139 ""
2140 );
2141
2142 if (len < 1 || len > end-p) {
2143 *p = '\0';
2144 return;
2145 }
2146
2147 p += len;
2148 *p++ = '\n';
2149 *p = '\0';
2150
2151 len = pj_ansi_snprintf(p, end-p,
2152 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
2153 indent,
2154 stat.rtt.min / 1000.0,
2155 stat.rtt.avg / 1000.0,
2156 stat.rtt.max / 1000.0,
2157 stat.rtt.last / 1000.0
2158 );
2159 if (len < 1 || len > end-p) {
2160 *p = '\0';
2161 return;
2162 }
2163
2164 p += len;
2165 *p++ = '\n';
2166 *p = '\0';
2167 }
2168}
2169
2170
2171/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002172void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002173 int call_id,
2174 char *buf, pj_size_t size)
2175{
2176 int len;
2177 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2178 pjsip_dialog *dlg = inv->dlg;
2179 char userinfo[128];
2180
2181 /* Dump invite sesion info. */
2182
2183 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2184 if (len < 1)
2185 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2186 else
2187 userinfo[len] = '\0';
2188
2189 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2190 title,
2191 pjsip_inv_state_name(inv->state),
2192 userinfo);
2193 if (len < 1 || len >= (int)size) {
2194 pj_ansi_strcpy(buf, "<--uri too long-->");
2195 len = 18;
2196 } else
2197 buf[len] = '\0';
2198}
2199
2200
2201/*
2202 * Dump call and media statistics to string.
2203 */
2204PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2205 pj_bool_t with_media,
2206 char *buffer,
2207 unsigned maxlen,
2208 const char *indent)
2209{
2210 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002211 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212 pj_time_val duration, res_delay, con_delay;
2213 char tmp[128];
2214 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002215 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002217 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002218
2219 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2220 PJ_EINVAL);
2221
Benny Prijonodc752ca2006-09-22 16:55:42 +00002222 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002223 if (status != PJ_SUCCESS)
2224 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002225
2226 *buffer = '\0';
2227 p = buffer;
2228 end = buffer + maxlen;
2229 len = 0;
2230
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002231 print_call(indent, call_id, tmp, sizeof(tmp));
2232
2233 len = pj_ansi_strlen(tmp);
2234 pj_ansi_strcpy(buffer, tmp);
2235
2236 p += len;
2237 *p++ = '\r';
2238 *p++ = '\n';
2239
2240 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002241 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242 pj_gettimeofday(&duration);
2243 PJ_TIME_VAL_SUB(duration, call->conn_time);
2244 con_delay = call->conn_time;
2245 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2246 } else {
2247 duration.sec = duration.msec = 0;
2248 con_delay.sec = con_delay.msec = 0;
2249 }
2250
2251 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002252 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002253 res_delay = call->res_time;
2254 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2255 } else {
2256 res_delay.sec = res_delay.msec = 0;
2257 }
2258
2259 /* Print duration */
2260 len = pj_ansi_snprintf(p, end-p,
2261 "%s Call time: %02dh:%02dm:%02ds, "
2262 "1st res in %d ms, conn in %dms",
2263 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002264 (int)(duration.sec / 3600),
2265 (int)((duration.sec % 3600)/60),
2266 (int)(duration.sec % 60),
2267 (int)PJ_TIME_VAL_MSEC(res_delay),
2268 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002269
2270 if (len > 0 && len < end-p) {
2271 p += len;
2272 *p++ = '\n';
2273 *p = '\0';
2274 }
2275
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002276 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002277 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002278 pjmedia_transport_get_info(call->med_tp, &tp_info);
2279 if (tp_info.specific_info_cnt > 0) {
2280 int i;
2281 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2282 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2283 {
2284 pjmedia_srtp_info *srtp_info =
2285 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2286
2287 len = pj_ansi_snprintf(p, end-p,
2288 "%s SRTP status: %s Crypto-suite: %s",
2289 indent,
2290 (srtp_info->active?"Active":"Not active"),
2291 srtp_info->tx_policy.name.ptr);
2292 if (len > 0 && len < end-p) {
2293 p += len;
2294 *p++ = '\n';
2295 *p = '\0';
2296 }
2297 break;
2298 }
2299 }
2300 }
2301
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002302 /* Dump session statistics */
2303 if (with_media && call->session)
2304 dump_media_session(indent, p, end-p, call->session);
2305
Benny Prijonodc752ca2006-09-22 16:55:42 +00002306 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002307
2308 return PJ_SUCCESS;
2309}
2310
2311
2312/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002313 * This callback receives notification from invite session when the
2314 * session state has changed.
2315 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002316static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2317 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002318{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002319 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002320
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002321 PJSUA_LOCK();
2322
Benny Prijonoa1e69682007-05-11 15:14:34 +00002323 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324
2325 if (!call) {
2326 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002327 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002328 }
2329
Benny Prijonoe21e7842006-04-09 16:46:05 +00002330
2331 /* Get call times */
2332 switch (inv->state) {
2333 case PJSIP_INV_STATE_EARLY:
2334 case PJSIP_INV_STATE_CONNECTING:
2335 if (call->res_time.sec == 0)
2336 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002337 call->last_code = (pjsip_status_code)
2338 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002339 pj_strncpy(&call->last_text,
2340 &e->body.tsx_state.tsx->status_text,
2341 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002342 break;
2343 case PJSIP_INV_STATE_CONFIRMED:
2344 pj_gettimeofday(&call->conn_time);
2345 break;
2346 case PJSIP_INV_STATE_DISCONNECTED:
2347 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002348 if (call->res_time.sec == 0)
2349 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002350 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002351 call->last_code = (pjsip_status_code)
2352 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002353 pj_strncpy(&call->last_text,
2354 &e->body.tsx_state.tsx->status_text,
2355 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002356 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002357 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002358 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002359 call->last_code = (pjsip_status_code)
2360 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002361 pj_strncpy(&call->last_text,
2362 &e->body.tsx_state.tsx->status_text,
2363 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002364 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002365 }
2366
Benny Prijono26ff9062006-02-21 23:47:00 +00002367 /* If this is an outgoing INVITE that was created because of
2368 * REFER/transfer, send NOTIFY to transferer.
2369 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002370 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002371 int st_code = -1;
2372 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2373
2374
Benny Prijonoa91a0032006-02-26 21:23:45 +00002375 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002376 case PJSIP_INV_STATE_NULL:
2377 case PJSIP_INV_STATE_CALLING:
2378 /* Do nothing */
2379 break;
2380
2381 case PJSIP_INV_STATE_EARLY:
2382 case PJSIP_INV_STATE_CONNECTING:
2383 st_code = e->body.tsx_state.tsx->status_code;
2384 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2385 break;
2386
2387 case PJSIP_INV_STATE_CONFIRMED:
2388 /* When state is confirmed, send the final 200/OK and terminate
2389 * subscription.
2390 */
2391 st_code = e->body.tsx_state.tsx->status_code;
2392 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2393 break;
2394
2395 case PJSIP_INV_STATE_DISCONNECTED:
2396 st_code = e->body.tsx_state.tsx->status_code;
2397 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2398 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002399
Benny Prijono8b1889b2006-06-06 18:40:40 +00002400 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002401 /* Nothing to do. Just to keep gcc from complaining about
2402 * unused enums.
2403 */
2404 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002405 }
2406
2407 if (st_code != -1) {
2408 pjsip_tx_data *tdata;
2409 pj_status_t status;
2410
Benny Prijonoa91a0032006-02-26 21:23:45 +00002411 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002412 ev_state, st_code,
2413 NULL, &tdata);
2414 if (status != PJ_SUCCESS) {
2415 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2416 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002417 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002418 if (status != PJ_SUCCESS) {
2419 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2420 }
2421 }
2422 }
2423 }
2424
Benny Prijono84126ab2006-02-09 09:30:09 +00002425
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002426 if (pjsua_var.ua_cfg.cb.on_call_state)
2427 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002428
2429 /* call->inv may be NULL now */
2430
Benny Prijono84126ab2006-02-09 09:30:09 +00002431 /* Destroy media session when invite session is disconnected. */
2432 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002433
Benny Prijonoa91a0032006-02-26 21:23:45 +00002434 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002435
Benny Prijono275fd682006-03-22 11:59:11 +00002436 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002437 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002438
Benny Prijono105217f2006-03-06 16:25:59 +00002439 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002440 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002441 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002442
2443 /* Reset call */
2444 reset_call(call->index);
2445
Benny Prijono84126ab2006-02-09 09:30:09 +00002446 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002447
2448 PJSUA_UNLOCK();
2449}
2450
2451/*
2452 * This callback is called by invite session framework when UAC session
2453 * has forked.
2454 */
2455static void pjsua_call_on_forked( pjsip_inv_session *inv,
2456 pjsip_event *e)
2457{
2458 PJ_UNUSED_ARG(inv);
2459 PJ_UNUSED_ARG(e);
2460
2461 PJ_TODO(HANDLE_FORKED_DIALOG);
2462}
2463
2464
2465/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002466 * Disconnect call upon error.
2467 */
2468static void call_disconnect( pjsip_inv_session *inv,
2469 int code )
2470{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002471 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002472 pjsip_tx_data *tdata;
2473 pj_status_t status;
2474
Benny Prijono59b3aed2008-01-15 16:54:54 +00002475 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2476
Benny Prijonoa38ada02006-07-02 14:22:35 +00002477 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002478 if (status != PJ_SUCCESS)
2479 return;
2480
2481 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00002482 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
2483 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
2484 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00002485 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002486 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00002487
Benny Prijono734fc2d2008-03-17 16:05:35 +00002488 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002489 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002490 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002491 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002492 if (status == PJ_SUCCESS) {
2493 pjsip_create_sdp_body(tdata->pool, local_sdp,
2494 &tdata->msg->body);
2495 }
2496 }
2497
2498 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002499}
2500
2501/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002502 * Callback to be called when SDP offer/answer negotiation has just completed
2503 * in the session. This function will start/update media if negotiation
2504 * has succeeded.
2505 */
2506static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2507 pj_status_t status)
2508{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002509 pjsua_call *call;
Benny Prijonod8179652008-01-23 20:39:07 +00002510 const pjmedia_sdp_session *c_local;
2511 pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002512 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002513
2514 PJSUA_LOCK();
2515
Benny Prijonoa1e69682007-05-11 15:14:34 +00002516 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002517
2518 if (status != PJ_SUCCESS) {
2519
2520 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2521
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002522 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002523 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002524
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002525 /* Disconnect call if we're not in the middle of initializing an
2526 * UAS dialog and if this is not a re-INVITE
2527 */
2528 if (inv->state != PJSIP_INV_STATE_NULL &&
2529 inv->state != PJSIP_INV_STATE_CONFIRMED)
2530 {
Benny Prijono2dbed822008-02-21 10:08:27 +00002531 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002532 }
2533
2534 PJSUA_UNLOCK();
2535 return;
2536 }
2537
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002538
2539 /* Get local and remote SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00002540 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &c_local);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002541 if (status != PJ_SUCCESS) {
2542 pjsua_perror(THIS_FILE,
2543 "Unable to retrieve currently active local SDP",
2544 status);
2545 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2546 PJSUA_UNLOCK();
2547 return;
2548 }
Benny Prijonod8179652008-01-23 20:39:07 +00002549 local_sdp = (pjmedia_sdp_session*) c_local;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002550
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002551 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2552 if (status != PJ_SUCCESS) {
2553 pjsua_perror(THIS_FILE,
2554 "Unable to retrieve currently active remote SDP",
2555 status);
2556 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2557 PJSUA_UNLOCK();
2558 return;
2559 }
2560
Benny Prijono91a6a172007-10-31 08:59:29 +00002561 /* Update remote's NAT type */
2562 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2563 update_remote_nat_type(call, remote_sdp);
2564 }
2565
2566 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002567 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002568 if (status != PJ_SUCCESS) {
2569 pjsua_perror(THIS_FILE, "Unable to create media session",
2570 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002571 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002572 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002573 PJSUA_UNLOCK();
2574 return;
2575 }
2576
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002577
2578 /* Call application callback, if any */
2579 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2580 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2581
2582
2583 PJSUA_UNLOCK();
2584}
2585
2586
2587/*
2588 * Create inactive SDP for call hold.
2589 */
2590static pj_status_t create_inactive_sdp(pjsua_call *call,
2591 pjmedia_sdp_session **p_answer)
2592{
2593 pj_status_t status;
2594 pjmedia_sdp_conn *conn;
2595 pjmedia_sdp_attr *attr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002596 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002597 pjmedia_sdp_session *sdp;
2598
Benny Prijono617c5bc2007-04-02 19:51:21 +00002599 /* Get media socket info */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002600 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002601 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijono617c5bc2007-04-02 19:51:21 +00002602
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002603 /* Create new offer */
2604 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002605 &tp_info.sock_info, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002606 if (status != PJ_SUCCESS) {
2607 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2608 return status;
2609 }
2610
2611 /* Get SDP media connection line */
2612 conn = sdp->media[0]->conn;
2613 if (!conn)
2614 conn = sdp->conn;
2615
2616 /* Modify address */
2617 conn->addr = pj_str("0.0.0.0");
2618
2619 /* Remove existing directions attributes */
2620 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2621 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2622 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2623 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2624
2625 /* Add inactive attribute */
2626 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2627 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2628
2629 *p_answer = sdp;
2630
2631 return status;
2632}
2633
2634
2635/*
2636 * Called when session received new offer.
2637 */
2638static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2639 const pjmedia_sdp_session *offer)
2640{
2641 const char *remote_state;
2642 pjsua_call *call;
2643 pjmedia_sdp_conn *conn;
2644 pjmedia_sdp_session *answer;
2645 pj_bool_t is_remote_active;
2646 pj_status_t status;
2647
2648 PJSUA_LOCK();
2649
Benny Prijonoa1e69682007-05-11 15:14:34 +00002650 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002651
2652 /*
2653 * See if remote is offering active media (i.e. not on-hold)
2654 */
2655 is_remote_active = PJ_TRUE;
2656
2657 conn = offer->media[0]->conn;
2658 if (!conn)
2659 conn = offer->conn;
2660
2661 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2662 pj_strcmp2(&conn->addr, "0")==0)
2663 {
2664 is_remote_active = PJ_FALSE;
2665
2666 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002667 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2668 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002669 {
2670 is_remote_active = PJ_FALSE;
2671 }
2672
2673 remote_state = (is_remote_active ? "active" : "inactive");
2674
2675 /* Supply candidate answer */
2676 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2677 PJ_LOG(4,(THIS_FILE,
2678 "Call %d: RX new media offer, creating inactive SDP "
2679 "(media in offer is %s)", call->index, remote_state));
2680 status = create_inactive_sdp( call, &answer );
2681 } else {
2682 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2683 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002684
Benny Prijonodb844a42008-02-02 17:07:18 +00002685 /* Update call's secure level */
2686 call->secure_level = call_get_secure_level(call);
2687
Benny Prijono667952e2007-04-02 19:27:54 +00002688 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002689 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
Benny Prijonodb844a42008-02-02 17:07:18 +00002690 call->secure_level, NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00002691 if (status != PJ_SUCCESS) {
2692 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2693 PJSUA_UNLOCK();
2694 return;
2695 }
2696
Benny Prijonod8179652008-01-23 20:39:07 +00002697 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002698 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002699 }
2700
2701 if (status != PJ_SUCCESS) {
2702 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2703 PJSUA_UNLOCK();
2704 return;
2705 }
2706
2707 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2708 if (status != PJ_SUCCESS) {
2709 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2710 PJSUA_UNLOCK();
2711 return;
2712 }
2713
2714 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002715}
2716
2717
2718/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002719 * Called to generate new offer.
2720 */
2721static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2722 pjmedia_sdp_session **offer)
2723{
2724 pjsua_call *call;
2725 pj_status_t status;
2726
2727 PJSUA_LOCK();
2728
2729 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2730
2731 /* See if we've put call on hold. */
2732 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2733 PJ_LOG(4,(THIS_FILE,
2734 "Call %d: call is on-hold locally, creating inactive SDP ",
2735 call->index));
2736 status = create_inactive_sdp( call, offer );
2737 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00002738 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2739 call->index));
2740
Benny Prijonodb844a42008-02-02 17:07:18 +00002741 /* Update call's secure level */
2742 call->secure_level = call_get_secure_level(call);
2743
Benny Prijono77998ce2007-06-20 10:03:46 +00002744 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002745 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijonodb844a42008-02-02 17:07:18 +00002746 call->secure_level, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002747 if (status != PJ_SUCCESS) {
2748 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2749 PJSUA_UNLOCK();
2750 return;
2751 }
2752
Benny Prijonod8179652008-01-23 20:39:07 +00002753 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002754 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002755 }
2756
2757 if (status != PJ_SUCCESS) {
2758 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2759 PJSUA_UNLOCK();
2760 return;
2761 }
2762
Benny Prijono7129cc72007-11-05 05:54:25 +00002763 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002764
2765 PJSUA_UNLOCK();
2766}
2767
2768
2769/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002770 * Callback called by event framework when the xfer subscription state
2771 * has changed.
2772 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002773static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2774{
2775
2776 PJ_UNUSED_ARG(event);
2777
2778 /*
2779 * When subscription is accepted (got 200/OK to REFER), check if
2780 * subscription suppressed.
2781 */
2782 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2783
2784 pjsip_rx_data *rdata;
2785 pjsip_generic_string_hdr *refer_sub;
2786 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2787 pjsua_call *call;
2788
Benny Prijonoa1e69682007-05-11 15:14:34 +00002789 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002790
2791 /* Must be receipt of response message */
2792 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2793 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2794 rdata = event->body.tsx_state.src.rdata;
2795
2796 /* Find Refer-Sub header */
2797 refer_sub = (pjsip_generic_string_hdr*)
2798 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2799 &REFER_SUB, NULL);
2800
2801 /* Check if subscription is suppressed */
2802 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2803 /* Since no subscription is desired, assume that call has been
2804 * transfered successfully.
2805 */
2806 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2807 const pj_str_t ACCEPTED = { "Accepted", 8 };
2808 pj_bool_t cont = PJ_FALSE;
2809 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2810 200,
2811 &ACCEPTED,
2812 PJ_TRUE,
2813 &cont);
2814 }
2815
2816 /* Yes, subscription is suppressed.
2817 * Terminate our subscription now.
2818 */
2819 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2820 "event subcription..."));
2821 pjsip_evsub_terminate(sub, PJ_TRUE);
2822
2823 } else {
2824 /* Notify application about call transfer progress.
2825 * Initially notify with 100/Accepted status.
2826 */
2827 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2828 const pj_str_t ACCEPTED = { "Accepted", 8 };
2829 pj_bool_t cont = PJ_FALSE;
2830 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2831 100,
2832 &ACCEPTED,
2833 PJ_FALSE,
2834 &cont);
2835 }
2836 }
2837 }
2838 /*
2839 * On incoming NOTIFY, notify application about call transfer progress.
2840 */
2841 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2842 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2843 {
2844 pjsua_call *call;
2845 pjsip_msg *msg;
2846 pjsip_msg_body *body;
2847 pjsip_status_line status_line;
2848 pj_bool_t is_last;
2849 pj_bool_t cont;
2850 pj_status_t status;
2851
Benny Prijonoa1e69682007-05-11 15:14:34 +00002852 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002853
2854 /* When subscription is terminated, clear the xfer_sub member of
2855 * the inv_data.
2856 */
2857 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2858 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2859 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2860
2861 }
2862
2863 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2864 /* Application is not interested with call progress status */
2865 return;
2866 }
2867
2868 /* This better be a NOTIFY request */
2869 if (event->type == PJSIP_EVENT_TSX_STATE &&
2870 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2871 {
2872 pjsip_rx_data *rdata;
2873
2874 rdata = event->body.tsx_state.src.rdata;
2875
2876 /* Check if there's body */
2877 msg = rdata->msg_info.msg;
2878 body = msg->body;
2879 if (!body) {
2880 PJ_LOG(4,(THIS_FILE,
2881 "Warning: received NOTIFY without message body"));
2882 return;
2883 }
2884
2885 /* Check for appropriate content */
2886 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2887 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2888 {
2889 PJ_LOG(4,(THIS_FILE,
2890 "Warning: received NOTIFY with non message/sipfrag "
2891 "content"));
2892 return;
2893 }
2894
2895 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002896 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002897 &status_line);
2898 if (status != PJ_SUCCESS) {
2899 PJ_LOG(4,(THIS_FILE,
2900 "Warning: received NOTIFY with invalid "
2901 "message/sipfrag content"));
2902 return;
2903 }
2904
2905 } else {
2906 status_line.code = 500;
2907 status_line.reason = *pjsip_get_status_text(500);
2908 }
2909
2910 /* Notify application */
2911 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2912 cont = !is_last;
2913 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2914 status_line.code,
2915 &status_line.reason,
2916 is_last, &cont);
2917
2918 if (!cont) {
2919 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2920 }
2921 }
2922}
2923
2924
2925/*
2926 * Callback called by event framework when the xfer subscription state
2927 * has changed.
2928 */
2929static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002930{
2931
2932 PJ_UNUSED_ARG(event);
2933
2934 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002935 * When subscription is terminated, clear the xfer_sub member of
2936 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002937 */
2938 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002939 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002940
Benny Prijonoa1e69682007-05-11 15:14:34 +00002941 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002942 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002943 return;
2944
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002945 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002946 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002947
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002948 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002949 }
2950}
2951
2952
2953/*
2954 * Follow transfer (REFER) request.
2955 */
2956static void on_call_transfered( pjsip_inv_session *inv,
2957 pjsip_rx_data *rdata )
2958{
2959 pj_status_t status;
2960 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002961 pjsua_call *existing_call;
2962 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002963 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002964 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002965 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002966 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002967 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002968 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002969 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002970 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002971 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002972 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002973 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002974 pjsip_evsub *sub;
2975
Benny Prijonoa1e69682007-05-11 15:14:34 +00002976 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002977
Benny Prijono26ff9062006-02-21 23:47:00 +00002978 /* Find the Refer-To header */
2979 refer_to = (pjsip_generic_string_hdr*)
2980 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2981
2982 if (refer_to == NULL) {
2983 /* Invalid Request.
2984 * No Refer-To header!
2985 */
2986 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002987 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002988 return;
2989 }
2990
Benny Prijonoc8141a82006-08-20 09:12:19 +00002991 /* Find optional Refer-Sub header */
2992 refer_sub = (pjsip_generic_string_hdr*)
2993 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2994
2995 if (refer_sub) {
2996 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2997 no_refer_sub = PJ_TRUE;
2998 }
2999
Benny Prijono053f5222006-11-11 16:16:04 +00003000 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3001 * request.
3002 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003003 ref_by_hdr = (pjsip_hdr*)
3004 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003005 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003006
Benny Prijono9fc735d2006-05-28 14:58:12 +00003007 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003008 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003009 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3010 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3011 &refer_to->hvalue,
3012 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003013
3014 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003015 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003016 if (code >= 300) {
3017 /* Application rejects call transfer request */
3018 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3019 return;
3020 }
3021
Benny Prijono26ff9062006-02-21 23:47:00 +00003022 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3023 (int)inv->dlg->remote.info_str.slen,
3024 inv->dlg->remote.info_str.ptr,
3025 (int)refer_to->hvalue.slen,
3026 refer_to->hvalue.ptr));
3027
Benny Prijonoc8141a82006-08-20 09:12:19 +00003028 if (no_refer_sub) {
3029 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003030 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003031 */
3032 pjsip_tx_data *tdata;
3033 const pj_str_t str_false = { "false", 5};
3034 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003035
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003036 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3037 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003038 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003039 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003040 status);
3041 return;
3042 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003043
Benny Prijonoc8141a82006-08-20 09:12:19 +00003044 /* Add Refer-Sub header */
3045 hdr = (pjsip_hdr*)
3046 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3047 &str_false);
3048 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003049
Benny Prijono26ff9062006-02-21 23:47:00 +00003050
Benny Prijonoc8141a82006-08-20 09:12:19 +00003051 /* Send answer */
3052 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3053 tdata);
3054 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003055 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003056 status);
3057 return;
3058 }
3059
3060 /* Don't have subscription */
3061 sub = NULL;
3062
3063 } else {
3064 struct pjsip_evsub_user xfer_cb;
3065 pjsip_hdr hdr_list;
3066
3067 /* Init callback */
3068 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003069 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003070
3071 /* Init additional header list to be sent with REFER response */
3072 pj_list_init(&hdr_list);
3073
3074 /* Create transferee event subscription */
3075 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3076 if (status != PJ_SUCCESS) {
3077 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3078 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3079 return;
3080 }
3081
3082 /* If there's Refer-Sub header and the value is "true", send back
3083 * Refer-Sub in the response with value "true" too.
3084 */
3085 if (refer_sub) {
3086 const pj_str_t str_true = { "true", 4 };
3087 pjsip_hdr *hdr;
3088
3089 hdr = (pjsip_hdr*)
3090 pjsip_generic_string_hdr_create(inv->dlg->pool,
3091 &str_refer_sub,
3092 &str_true);
3093 pj_list_push_back(&hdr_list, hdr);
3094
3095 }
3096
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003097 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003098 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3099
3100 /* Create initial NOTIFY request */
3101 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3102 100, NULL, &tdata);
3103 if (status != PJ_SUCCESS) {
3104 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3105 status);
3106 return;
3107 }
3108
3109 /* Send initial NOTIFY request */
3110 status = pjsip_xfer_send_request( sub, tdata);
3111 if (status != PJ_SUCCESS) {
3112 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3113 return;
3114 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003115 }
3116
3117 /* We're cheating here.
3118 * We need to get a null terminated string from a pj_str_t.
3119 * So grab the pointer from the hvalue and NULL terminate it, knowing
3120 * that the NULL position will be occupied by a newline.
3121 */
3122 uri = refer_to->hvalue.ptr;
3123 uri[refer_to->hvalue.slen] = '\0';
3124
Benny Prijono053f5222006-11-11 16:16:04 +00003125 /* Init msg_data */
3126 pjsua_msg_data_init(&msg_data);
3127
3128 /* If Referred-By header is present in the REFER request, copy this
3129 * to the outgoing INVITE request.
3130 */
3131 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003132 pjsip_hdr *dup = (pjsip_hdr*)
3133 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003134 pj_list_push_back(&msg_data.hdr_list, dup);
3135 }
3136
Benny Prijono26ff9062006-02-21 23:47:00 +00003137 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003138 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003139 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003140 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003141 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003142 if (status != PJ_SUCCESS) {
3143
Benny Prijonoc8141a82006-08-20 09:12:19 +00003144 /* Notify xferer about the error (if we have subscription) */
3145 if (sub) {
3146 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3147 500, NULL, &tdata);
3148 if (status != PJ_SUCCESS) {
3149 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3150 status);
3151 return;
3152 }
3153 status = pjsip_xfer_send_request(sub, tdata);
3154 if (status != PJ_SUCCESS) {
3155 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3156 status);
3157 return;
3158 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003159 }
3160 return;
3161 }
3162
Benny Prijonoc8141a82006-08-20 09:12:19 +00003163 if (sub) {
3164 /* Put the server subscription in inv_data.
3165 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3166 * reported back to the server subscription.
3167 */
3168 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003169
Benny Prijonoc8141a82006-08-20 09:12:19 +00003170 /* Put the invite_data in the subscription. */
3171 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3172 &pjsua_var.calls[new_call]);
3173 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003174}
3175
3176
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003177
Benny Prijono26ff9062006-02-21 23:47:00 +00003178/*
3179 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003180 * session. We use this to trap:
3181 * - incoming REFER request.
3182 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003183 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003184static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3185 pjsip_transaction *tsx,
3186 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003187{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003188 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003189
3190 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003191
Benny Prijonofeb69f42007-10-05 09:12:26 +00003192 /* Notify application callback first */
3193 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3194 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3195 }
3196
Benny Prijono26ff9062006-02-21 23:47:00 +00003197 if (tsx->role==PJSIP_ROLE_UAS &&
3198 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003199 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003200 {
3201 /*
3202 * Incoming REFER request.
3203 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003204 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003205
Benny Prijono26ff9062006-02-21 23:47:00 +00003206 }
Benny Prijonob0808372006-03-02 21:18:58 +00003207 else if (tsx->role==PJSIP_ROLE_UAS &&
3208 tsx->state==PJSIP_TSX_STATE_TRYING &&
3209 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3210 {
3211 /*
3212 * Incoming MESSAGE request!
3213 */
3214 pjsip_rx_data *rdata;
3215 pjsip_msg *msg;
3216 pjsip_accept_hdr *accept_hdr;
3217 pj_status_t status;
3218
3219 rdata = e->body.tsx_state.src.rdata;
3220 msg = rdata->msg_info.msg;
3221
3222 /* Request MUST have message body, with Content-Type equal to
3223 * "text/plain".
3224 */
3225 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3226
3227 pjsip_hdr hdr_list;
3228
3229 pj_list_init(&hdr_list);
3230 pj_list_push_back(&hdr_list, accept_hdr);
3231
3232 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3233 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003234 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003235 return;
3236 }
3237
3238 /* Respond with 200 first, so that remote doesn't retransmit in case
3239 * the UI takes too long to process the message.
3240 */
3241 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3242
3243 /* Process MESSAGE request */
3244 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3245 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003246
Benny Prijonob0808372006-03-02 21:18:58 +00003247 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003248 else if (tsx->role == PJSIP_ROLE_UAC &&
3249 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003250 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003251 /* Handle outgoing pager status */
3252 if (tsx->status_code >= 200) {
3253 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003254
Benny Prijonoa1e69682007-05-11 15:14:34 +00003255 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003256 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003257
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003258 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3259 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3260 &im_data->to,
3261 &im_data->body,
3262 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003263 (pjsip_status_code)
3264 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003265 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003266 }
Benny Prijonofccab712006-02-22 22:23:22 +00003267 }
Benny Prijono834aee32006-02-19 01:38:06 +00003268 }
Benny Prijono834aee32006-02-19 01:38:06 +00003269
Benny Prijono26ff9062006-02-21 23:47:00 +00003270
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003271 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003272}