blob: 8638a8068f2c76902c5b9442e8e8186f8cc9fb46 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
Benny Prijono77998ce2007-06-20 10:03:46 +000053 * Called to generate new offer.
54 */
55static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
56 pjmedia_sdp_session **offer);
57
58/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059 * This callback is called when transaction state has changed in INVITE
60 * session. We use this to trap:
61 * - incoming REFER request.
62 * - incoming MESSAGE request.
63 */
64static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
65 pjsip_transaction *tsx,
66 pjsip_event *e);
67
68
Benny Prijonoeebe9af2006-06-13 22:57:13 +000069
70/* Create inactive SDP for call hold. */
71static pj_status_t create_inactive_sdp(pjsua_call *call,
72 pjmedia_sdp_session **p_answer);
73
Benny Prijono7129cc72007-11-05 05:54:25 +000074/* Update SDP version in the offer */
75static void update_sdp_version(pjsua_call *call,
76 pjmedia_sdp_session *sdp)
77{
78 const pjmedia_sdp_session *old_sdp = NULL;
79 pj_status_t status;
80
81 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
82 if (status != PJ_SUCCESS || old_sdp == NULL)
83 return;
84
85 sdp->origin.version = old_sdp->origin.version + 1;
86}
87
88
Benny Prijonod524e822006-09-22 12:48:18 +000089/*
90 * Callback called by event framework when the xfer subscription state
91 * has changed.
92 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000093static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
94static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
95
96/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000097 * Reset call descriptor.
98 */
99static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000100{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 call->index = id;
104 call->inv = NULL;
105 call->user_data = NULL;
106 call->session = NULL;
Benny Prijono8147f402007-11-21 14:50:07 +0000107 call->ssrc = pj_rand();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000109 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 call->conf_slot = PJSUA_INVALID_ID;
111 call->last_text.ptr = call->last_text_buf_;
112 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000113 call->conn_time.sec = 0;
114 call->conn_time.msec = 0;
115 call->res_time.sec = 0;
116 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000117 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Benny Prijono105217f2006-03-06 16:25:59 +0000118}
119
120
Benny Prijono275fd682006-03-22 11:59:11 +0000121/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000123 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000125{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 pjsip_inv_callback inv_cb;
127 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000128 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000130
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 /* Init calls array. */
132 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
133 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000134
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 /* Copy config */
136 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000137
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000139 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
141 inv_cb.on_new_session = &pjsua_call_on_forked;
142 inv_cb.on_media_update = &pjsua_call_on_media_update;
143 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000144 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijono275fd682006-03-22 11:59:11 +0000147
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148 /* Initialize invite session module: */
149 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
150 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
151
Benny Prijonoc8141a82006-08-20 09:12:19 +0000152 /* Add "norefersub" in Supported header */
153 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
154 NULL, 1, &str_norefersub);
155
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156 return status;
157}
158
159
160/*
161 * Start call subsystem.
162 */
163pj_status_t pjsua_call_subsys_start(void)
164{
165 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000166 return PJ_SUCCESS;
167}
168
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171 * Get maximum number of calls configured in pjsua.
172 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000173PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000174{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000175 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176}
177
178
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000179/*
180 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000182PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185}
186
187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188/*
189 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
192 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
201 if (!pjsua_var.calls[i].inv)
202 continue;
203 ids[c] = i;
204 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205 }
206
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
211 return PJ_SUCCESS;
212}
213
214
Benny Prijono1f7767b2007-10-03 18:28:49 +0000215#define LATE_SDP 0
216
Benny Prijono5773cd62008-01-19 13:01:42 +0000217/* Allocate one call id */
218static pjsua_call_id alloc_call_id(void)
219{
220 pjsua_call_id cid;
221
222#if 1
223 /* New algorithm: round-robin */
224 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
225 pjsua_var.next_call_id < 0)
226 {
227 cid = 0;
228 }
229
230 for (cid=pjsua_var.next_call_id;
231 cid<(int)pjsua_var.ua_cfg.max_calls;
232 ++cid)
233 {
234 if (pjsua_var.calls[cid].inv == NULL) {
235 ++pjsua_var.next_call_id;
236 return cid;
237 }
238 }
239
240 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
241 if (pjsua_var.calls[cid].inv == NULL) {
242 ++pjsua_var.next_call_id;
243 return cid;
244 }
245 }
246
247#else
248 /* Old algorithm */
249 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
250 if (pjsua_var.calls[cid].inv == NULL)
251 return cid;
252 }
253#endif
254
255 return PJSUA_INVALID_ID;
256}
257
Benny Prijonod8179652008-01-23 20:39:07 +0000258/* Get signaling secure level.
259 * Return:
260 * 0: if signaling is not secure
261 * 1: if TLS transport is used for immediate hop
262 * 2: if end-to-end signaling is secure.
263 *
264 * NOTE:
265 * THIS IS WRONG. It should take into account the route-set.
266 */
267static int get_secure_level(const pj_str_t *dst_uri)
268{
269 const pj_str_t tls = pj_str(";transport=tls");
270 const pj_str_t sips = pj_str("sips:");
271
272 PJ_TODO(Fix_get_secure_level);
273
274 if (pj_stristr(dst_uri, &sips))
275 return 2;
276 if (pj_stristr(dst_uri, &tls))
277 return 1;
278 return 0;
279}
280
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281/*
282 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000283 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000284PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
285 const pj_str_t *dest_uri,
286 unsigned options,
287 void *user_data,
288 const pjsua_msg_data *msg_data,
289 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000290{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000291 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000292 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000293 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000294 pjsua_acc *acc;
295 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000296 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000297 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000298 pjsip_tx_data *tdata;
299 pj_status_t status;
300
Benny Prijono9fc735d2006-05-28 14:58:12 +0000301
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000303 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000304 PJ_EINVAL);
305
Benny Prijono320fa4d2006-12-07 10:09:16 +0000306 /* Check arguments */
307 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
308
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000309 PJSUA_LOCK();
310
311 acc = &pjsua_var.acc[acc_id];
312 if (!acc->valid) {
313 pjsua_perror(THIS_FILE, "Unable to make call because account "
314 "is not valid", PJ_EINVALIDOP);
315 PJSUA_UNLOCK();
316 return PJ_EINVALIDOP;
317 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000318
Benny Prijonoa91a0032006-02-26 21:23:45 +0000319 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000320 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000321
Benny Prijono5773cd62008-01-19 13:01:42 +0000322 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000323 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
324 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000325 return PJ_ETOOMANY;
326 }
327
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 call = &pjsua_var.calls[call_id];
329
Benny Prijono320fa4d2006-12-07 10:09:16 +0000330 /* Verify that destination URI is valid before calling
331 * pjsua_acc_create_uac_contact, or otherwise there
332 * a misleading "Invalid Contact URI" error will be printed
333 * when pjsua_acc_create_uac_contact() fails.
334 */
335 if (1) {
336 pj_pool_t *pool;
337 pjsip_uri *uri;
338 pj_str_t dup;
339
340 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
341 if (!pool) {
342 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
343 PJSUA_UNLOCK();
344 return PJ_ENOMEM;
345 }
346
347 pj_strdup_with_null(pool, &dup, dest_uri);
348 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
349 pj_pool_release(pool);
350
351 if (uri == NULL) {
352 pjsua_perror(THIS_FILE, "Unable to make call",
353 PJSIP_EINVALIDREQURI);
354 PJSUA_UNLOCK();
355 return PJSIP_EINVALIDREQURI;
356 }
357 }
358
Benny Prijono093d3022006-09-24 00:07:11 +0000359 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
360 (int)dest_uri->slen, dest_uri->ptr));
361
Benny Prijonoe21e7842006-04-09 16:46:05 +0000362 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000364
Benny Prijonoe21e7842006-04-09 16:46:05 +0000365 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000366 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000367
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000368 /* Create suitable Contact header */
369 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
370 acc_id, dest_uri);
371 if (status != PJ_SUCCESS) {
372 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
373 PJSUA_UNLOCK();
374 return status;
375 }
376
Benny Prijonoe21e7842006-04-09 16:46:05 +0000377 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000378 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000379 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000380 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000381 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000382 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000384 return status;
385 }
386
Benny Prijonoc97608e2007-03-23 16:34:20 +0000387 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000388 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000389 get_secure_level(dest_uri), NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000390 if (status != PJ_SUCCESS) {
391 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
392 goto on_error;
393 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000394
Benny Prijonoc97608e2007-03-23 16:34:20 +0000395 /* Create SDP offer */
Benny Prijono1f7767b2007-10-03 18:28:49 +0000396#if LATE_SDP
397 offer = NULL;
398#else
Benny Prijono25b2ea12008-01-24 19:20:54 +0000399 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
400 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000401 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000402 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000403 goto on_error;
404 }
Benny Prijono1f7767b2007-10-03 18:28:49 +0000405#endif
Benny Prijono84126ab2006-02-09 09:30:09 +0000406
407 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000408 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000409 if (acc->cfg.require_100rel)
410 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000411
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000412 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000413 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000414 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000415 goto on_error;
416 }
417
418
419 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000420 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000421 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000422
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 dlg->mod_data[pjsua_var.mod.id] = call;
424 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000425
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426 /* Attach user data */
427 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000428
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000429 /* If account is locked to specific transport, then lock dialog
430 * to this transport too.
431 */
432 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
433 pjsip_tpselector tp_sel;
434
435 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
436 pjsip_dlg_set_transport(dlg, &tp_sel);
437 }
438
Benny Prijono84126ab2006-02-09 09:30:09 +0000439 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 if (!pj_list_empty(&acc->route_set))
441 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000442
443
444 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000446 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000448 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000449
Benny Prijono48ab2b72007-11-08 09:24:30 +0000450 /* Set authentication preference */
451 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000452
453 /* Create initial INVITE: */
454
455 status = pjsip_inv_invite(inv, &tdata);
456 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000457 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
458 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000459 goto on_error;
460 }
461
462
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463 /* Add additional headers etc */
464
465 pjsua_process_msg_data( tdata, msg_data);
466
Benny Prijono093d3022006-09-24 00:07:11 +0000467 /* Must increment call counter now */
468 ++pjsua_var.call_cnt;
469
Benny Prijono84126ab2006-02-09 09:30:09 +0000470 /* Send initial INVITE: */
471
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000472 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000473 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000474 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
475 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000476
477 /* Upon failure to send first request, both dialog and invite
478 * session would have been cleared.
479 */
480 inv = NULL;
481 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000482 goto on_error;
483 }
484
Benny Prijono84126ab2006-02-09 09:30:09 +0000485 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000486
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000487 if (p_call_id)
488 *p_call_id = call_id;
489
490 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000491
492 return PJ_SUCCESS;
493
494
495on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000496 if (inv != NULL) {
497 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000498 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000499 pjsip_dlg_terminate(dlg);
500 }
501
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000502 if (call_id != -1) {
503 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000504 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000505 }
506
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000508 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000509}
510
511
Benny Prijono91a6a172007-10-31 08:59:29 +0000512/* Get the NAT type information in remote's SDP */
513static void update_remote_nat_type(pjsua_call *call,
514 const pjmedia_sdp_session *sdp)
515{
516 const pjmedia_sdp_attr *xnat;
517
518 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
519 if (xnat) {
520 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
521 } else {
522 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
523 }
524
525 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
526 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
527}
528
529
Benny Prijonodc39fe82006-05-26 12:17:46 +0000530/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000531 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000532 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000533 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000534pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000535{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000536 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000537 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000538 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000539 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
540 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000541 pjsip_tx_data *response = NULL;
542 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000543 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 int acc_id;
545 pjsua_call *call;
546 int call_id = -1;
Benny Prijono25b2ea12008-01-24 19:20:54 +0000547 int secure_level, sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000548 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000549 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000550
Benny Prijono26ff9062006-02-21 23:47:00 +0000551 /* Don't want to handle anything but INVITE */
552 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
553 return PJ_FALSE;
554
555 /* Don't want to handle anything that's already associated with
556 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000557 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000558 if (dlg || tsx)
559 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000560
Benny Prijono148c9dd2006-09-19 13:37:53 +0000561 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000562
Benny Prijono26ff9062006-02-21 23:47:00 +0000563 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000564 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000565
Benny Prijono5773cd62008-01-19 13:01:42 +0000566 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000567 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000568 PJSIP_SC_BUSY_HERE, NULL,
569 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000570 PJ_LOG(2,(THIS_FILE,
571 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000572 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000573 return PJ_TRUE;
574 }
575
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000576 /* Clear call descriptor */
577 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000578
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579 call = &pjsua_var.calls[call_id];
580
581 /* Mark call start time. */
582 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000583
Benny Prijono053f5222006-11-11 16:16:04 +0000584 /* Check INVITE request for Replaces header. If Replaces header is
585 * present, the function will make sure that we can handle the request.
586 */
587 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
588 &response);
589 if (status != PJ_SUCCESS) {
590 /*
591 * Something wrong with the Replaces header.
592 */
593 if (response) {
594 pjsip_response_addr res_addr;
595
596 pjsip_get_response_addr(response->pool, rdata, &res_addr);
597 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
598 NULL, NULL);
599
600 } else {
601
602 /* Respond with 500 (Internal Server Error) */
603 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
604 NULL, NULL);
605 }
606
607 PJSUA_UNLOCK();
608 return PJ_TRUE;
609 }
610
611 /* If this INVITE request contains Replaces header, notify application
612 * about the request so that application can do subsequent checking
613 * if it wants to.
614 */
615 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
616 pjsua_call *replaced_call;
617 int st_code = 200;
618 pj_str_t st_text = { "OK", 2 };
619
620 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000621 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000622
623 /* Notify application */
624 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
625 rdata, &st_code, &st_text);
626
627 /* Must specify final response */
628 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
629
630 /* Check if application rejects this request. */
631 if (st_code >= 300) {
632
633 if (st_text.slen == 2)
634 st_text = *pjsip_get_status_text(st_code);
635
636 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
637 st_code, &st_text, NULL, NULL, NULL);
638 PJSUA_UNLOCK();
639 return PJ_TRUE;
640 }
641 }
642
Benny Prijonod8179652008-01-23 20:39:07 +0000643 /*
644 * Get which account is most likely to be associated with this incoming
645 * call. We need the account to find which contact URI to put for
646 * the call.
647 */
648 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
649
650#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
651 /* Get signaling security level, only when required by SRTP */
652 if (pjsua_var.acc[acc_id].cfg.srtp_secure_signaling < 2) {
653 secure_level = PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport)!=0;
654 } else
655#endif
656
657 {
658 char *uri;
659 int uri_len;
660 pj_str_t dst;
661
662 uri = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
663 uri_len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
664 rdata->msg_info.msg->line.req.uri,
665 uri, PJSIP_MAX_URL_SIZE);
666 if (uri_len < 1) {
667 pjsua_perror(THIS_FILE, "Error analyzing dst URI",
668 PJSIP_EURITOOLONG);
669 uri_len = 0;
670 }
671
672 dst.ptr = uri;
673 dst.slen = uri_len;
674
675 secure_level = get_secure_level(&dst);
676 }
Benny Prijono053f5222006-11-11 16:16:04 +0000677
Benny Prijonoc97608e2007-03-23 16:34:20 +0000678 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000679 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000680 secure_level, &sip_err_code);
Benny Prijono26ff9062006-02-21 23:47:00 +0000681 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000682 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
683 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
684 sip_err_code, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000685 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000686 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000687 return PJ_TRUE;
688 }
689
Benny Prijonod8179652008-01-23 20:39:07 +0000690 /* Parse SDP from incoming request */
691 if (rdata->msg_info.msg->body) {
692 status = pjmedia_sdp_parse(rdata->tp_info.pool,
693 rdata->msg_info.msg->body->data,
694 rdata->msg_info.msg->body->len, &offer);
695 if (status != PJ_SUCCESS) {
696 pjsua_perror(THIS_FILE, "Error parsing SDP in incoming INVITE", status);
697 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
698 NULL, NULL);
699 pjsua_media_channel_deinit(call->index);
700 PJSUA_UNLOCK();
701 return PJ_TRUE;
702 }
703 } else {
704 offer = NULL;
705 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000706
Benny Prijonoc97608e2007-03-23 16:34:20 +0000707 /* Get media capability from media endpoint: */
Benny Prijonod8179652008-01-23 20:39:07 +0000708 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000709 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000710 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000711 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
712 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
713 sip_err_code, NULL,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000714 NULL, NULL);
715 pjsua_media_channel_deinit(call->index);
716 PJSUA_UNLOCK();
717 return PJ_TRUE;
718 }
719
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000720 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000721 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000722 if (pjsua_var.acc[acc_id].cfg.require_100rel)
723 options |= PJSIP_INV_REQUIRE_100REL;
724
Benny Prijonod8179652008-01-23 20:39:07 +0000725 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
726 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000727 if (status != PJ_SUCCESS) {
728
729 /*
730 * No we can't handle the incoming INVITE request.
731 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000732 if (response) {
733 pjsip_response_addr res_addr;
734
735 pjsip_get_response_addr(response->pool, rdata, &res_addr);
736 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
737 NULL, NULL);
738
739 } else {
740
741 /* Respond with 500 (Internal Server Error) */
742 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
743 NULL, NULL);
744 }
745
Benny Prijonoc97608e2007-03-23 16:34:20 +0000746 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000747 PJSUA_UNLOCK();
748 return PJ_TRUE;
749 }
750
751
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000752 /* Get suitable Contact header */
753 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
754 acc_id, rdata);
755 if (status != PJ_SUCCESS) {
756 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
757 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
758 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000759 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000760 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000761 return PJ_TRUE;
762 }
763
Benny Prijono26ff9062006-02-21 23:47:00 +0000764 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000765 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000766 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000767 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000768 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000769 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000770 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000771 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000772 return PJ_TRUE;
773 }
774
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000775 /* Set credentials */
776 if (pjsua_var.acc[acc_id].cred_cnt) {
777 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
778 pjsua_var.acc[acc_id].cred_cnt,
779 pjsua_var.acc[acc_id].cred);
780 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000781
Benny Prijono48ab2b72007-11-08 09:24:30 +0000782 /* Set preference */
783 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
784 &pjsua_var.acc[acc_id].cfg.auth_pref);
785
Benny Prijono26ff9062006-02-21 23:47:00 +0000786 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000787 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000788 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000789 pjsip_hdr hdr_list;
790 pjsip_warning_hdr *w;
791
792 w = pjsip_warning_hdr_create_from_status(dlg->pool,
793 pjsip_endpt_name(pjsua_var.endpt),
794 status);
795 pj_list_init(&hdr_list);
796 pj_list_push_back(&hdr_list, w);
797
798 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
799
800 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000801 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000802 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000803 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000804 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000805 return PJ_TRUE;
806 }
807
Benny Prijonoea9fd392007-11-06 03:41:40 +0000808 /* Update NAT type of remote endpoint, only when there is SDP in
809 * incoming INVITE!
810 */
811 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
812 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
813 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000814 const pjmedia_sdp_session *remote_sdp;
815
816 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
817 update_remote_nat_type(call, remote_sdp);
818 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000819
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000820 /* Create and attach pjsua_var data to the dialog: */
821 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000822
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000823 dlg->mod_data[pjsua_var.mod.id] = call;
824 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000825
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000826 /* If account is locked to specific transport, then lock dialog
827 * to this transport too.
828 */
829 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
830 pjsip_tpselector tp_sel;
831
832 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
833 pjsip_dlg_set_transport(dlg, &tp_sel);
834 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000835
Benny Prijono64f851e2006-02-23 13:49:28 +0000836 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000837 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000838 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000839 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000840 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000841 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
842 status);
843
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000844 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
845 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000846 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000847 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000848 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000849
850 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000851 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000852 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000853 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000854 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000855 }
856
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000857 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000858
Benny Prijono105217f2006-03-06 16:25:59 +0000859
Benny Prijono053f5222006-11-11 16:16:04 +0000860 /* Check if this request should replace existing call */
861 if (replaced_dlg) {
862 pjsip_inv_session *replaced_inv;
863 struct pjsua_call *replaced_call;
864 pjsip_tx_data *tdata;
865
866 /* Get the invite session in the dialog */
867 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
868
869 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000870 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000871
872 /* Notify application */
873 if (pjsua_var.ua_cfg.cb.on_call_replaced)
874 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
875 call_id);
876
877 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
878 call_id));
879
880 /* Answer the new call with 200 response */
881 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
882 if (status == PJ_SUCCESS)
883 status = pjsip_inv_send_msg(inv, tdata);
884
885 if (status != PJ_SUCCESS)
886 pjsua_perror(THIS_FILE, "Error answering session", status);
887
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000888 /* Note that inv may be invalid if 200/OK has caused error in
889 * starting the media.
890 */
Benny Prijono053f5222006-11-11 16:16:04 +0000891
892 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
893 replaced_call->index));
894
895 /* Disconnect replaced invite session */
896 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
897 &tdata);
898 if (status == PJ_SUCCESS && tdata)
899 status = pjsip_inv_send_msg(replaced_inv, tdata);
900
901 if (status != PJ_SUCCESS)
902 pjsua_perror(THIS_FILE, "Error terminating session", status);
903
904
905 } else {
906
Benny Prijonob5388cf2007-01-04 22:45:08 +0000907 /* Notify application if on_incoming_call() is overriden,
908 * otherwise hangup the call with 480
909 */
910 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000911 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000912 } else {
913 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
914 NULL, NULL);
915 }
Benny Prijono053f5222006-11-11 16:16:04 +0000916 }
917
Benny Prijono8b1889b2006-06-06 18:40:40 +0000918
Benny Prijono26ff9062006-02-21 23:47:00 +0000919 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000920 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000921 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000922}
923
924
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000925
926/*
927 * Check if the specified call has active INVITE session and the INVITE
928 * session has not been disconnected.
929 */
930PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
931{
932 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
933 PJ_EINVAL);
934 return pjsua_var.calls[call_id].inv != NULL &&
935 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
936}
937
938
939/*
940 * Check if call has an active media session.
941 */
942PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
943{
944 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
945 PJ_EINVAL);
946 return pjsua_var.calls[call_id].session != NULL;
947}
948
949
Benny Prijono148c9dd2006-09-19 13:37:53 +0000950/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000951pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000952 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000953 pjsua_call **p_call,
954 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000955{
956 enum { MAX_RETRY=50 };
957 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000958 pjsua_call *call = NULL;
959 pj_bool_t has_pjsua_lock = PJ_FALSE;
960 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000961
962 for (retry=0; retry<MAX_RETRY; ++retry) {
963
964 has_pjsua_lock = PJ_FALSE;
965
966 status = PJSUA_TRY_LOCK();
967 if (status != PJ_SUCCESS) {
968 pj_thread_sleep(retry/10);
969 continue;
970 }
971
972 has_pjsua_lock = PJ_TRUE;
973 call = &pjsua_var.calls[call_id];
974
975 if (call->inv == NULL) {
976 PJSUA_UNLOCK();
977 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
978 return PJSIP_ESESSIONTERMINATED;
979 }
980
981 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
982 if (status != PJ_SUCCESS) {
983 PJSUA_UNLOCK();
984 pj_thread_sleep(retry/10);
985 continue;
986 }
987
988 PJSUA_UNLOCK();
989
990 break;
991 }
992
993 if (status != PJ_SUCCESS) {
994 if (has_pjsua_lock == PJ_FALSE)
995 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
996 "(possibly system has deadlocked) in %s",
997 title));
998 else
999 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1000 "(possibly system has deadlocked) in %s",
1001 title));
1002 return PJ_ETIMEDOUT;
1003 }
1004
1005 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001006 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001007
1008 return PJ_SUCCESS;
1009}
1010
1011
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001012/*
1013 * Get the conference port identification associated with the call.
1014 */
1015PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1016{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001017 pjsua_call *call;
1018 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001019 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001020 pj_status_t status;
1021
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001022 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1023 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001024
Benny Prijonodc752ca2006-09-22 16:55:42 +00001025 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001026 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001027 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001028
1029 port_id = call->conf_slot;
1030
Benny Prijonodc752ca2006-09-22 16:55:42 +00001031 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001032
1033 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001034}
1035
1036
Benny Prijono148c9dd2006-09-19 13:37:53 +00001037
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001038/*
1039 * Obtain detail information about the specified call.
1040 */
1041PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1042 pjsua_call_info *info)
1043{
1044 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001045 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001046 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001047
1048 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1049 PJ_EINVAL);
1050
Benny Prijonoac623b32006-07-03 15:19:31 +00001051 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001052
Benny Prijonodc752ca2006-09-22 16:55:42 +00001053 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001054 if (status != PJ_SUCCESS) {
1055 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001056 }
1057
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001058 /* id and role */
1059 info->id = call_id;
1060 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001061 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001062
1063 /* local info */
1064 info->local_info.ptr = info->buf_.local_info;
1065 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1066 sizeof(info->buf_.local_info));
1067
1068 /* local contact */
1069 info->local_contact.ptr = info->buf_.local_contact;
1070 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1071 call->inv->dlg->local.contact->uri,
1072 info->local_contact.ptr,
1073 sizeof(info->buf_.local_contact));
1074
1075 /* remote info */
1076 info->remote_info.ptr = info->buf_.remote_info;
1077 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1078 sizeof(info->buf_.remote_info));
1079
1080 /* remote contact */
1081 if (call->inv->dlg->remote.contact) {
1082 int len;
1083 info->remote_contact.ptr = info->buf_.remote_contact;
1084 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1085 call->inv->dlg->remote.contact->uri,
1086 info->remote_contact.ptr,
1087 sizeof(info->buf_.remote_contact));
1088 if (len < 0) len = 0;
1089 info->remote_contact.slen = len;
1090 } else {
1091 info->remote_contact.slen = 0;
1092 }
1093
1094 /* call id */
1095 info->call_id.ptr = info->buf_.call_id;
1096 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1097 sizeof(info->buf_.call_id));
1098
1099 /* state, state_text */
1100 info->state = call->inv->state;
1101 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1102
1103 /* If call is disconnected, set the last_status from the cause code */
1104 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1105 /* last_status, last_status_text */
1106 info->last_status = call->inv->cause;
1107
1108 info->last_status_text.ptr = info->buf_.last_status_text;
1109 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1110 sizeof(info->buf_.last_status_text));
1111 } else {
1112 /* last_status, last_status_text */
1113 info->last_status = call->last_code;
1114
1115 info->last_status_text.ptr = info->buf_.last_status_text;
1116 pj_strncpy(&info->last_status_text, &call->last_text,
1117 sizeof(info->buf_.last_status_text));
1118 }
1119
1120 /* media status and dir */
1121 info->media_status = call->media_st;
1122 info->media_dir = call->media_dir;
1123
1124
1125 /* conference slot number */
1126 info->conf_slot = call->conf_slot;
1127
1128 /* calculate duration */
1129 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1130
1131 info->total_duration = call->dis_time;
1132 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1133
1134 if (call->conn_time.sec) {
1135 info->connect_duration = call->dis_time;
1136 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1137 }
1138
1139 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1140
1141 pj_gettimeofday(&info->total_duration);
1142 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1143
1144 pj_gettimeofday(&info->connect_duration);
1145 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1146
1147 } else {
1148 pj_gettimeofday(&info->total_duration);
1149 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1150 }
1151
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001153
1154 return PJ_SUCCESS;
1155}
1156
1157
1158/*
1159 * Attach application specific data to the call.
1160 */
1161PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1162 void *user_data)
1163{
1164 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1165 PJ_EINVAL);
1166 pjsua_var.calls[call_id].user_data = user_data;
1167
1168 return PJ_SUCCESS;
1169}
1170
1171
1172/*
1173 * Get user data attached to the call.
1174 */
1175PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1176{
1177 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1178 NULL);
1179 return pjsua_var.calls[call_id].user_data;
1180}
1181
1182
1183/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001184 * Get remote's NAT type.
1185 */
1186PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1187 pj_stun_nat_type *p_type)
1188{
1189 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1190 PJ_EINVAL);
1191 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1192
1193 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1194 return PJ_SUCCESS;
1195}
1196
1197
1198/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001199 * Send response to incoming INVITE request.
1200 */
1201PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1202 unsigned code,
1203 const pj_str_t *reason,
1204 const pjsua_msg_data *msg_data)
1205{
1206 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001207 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208 pjsip_tx_data *tdata;
1209 pj_status_t status;
1210
1211 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1212 PJ_EINVAL);
1213
Benny Prijonodc752ca2006-09-22 16:55:42 +00001214 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001215 if (status != PJ_SUCCESS)
1216 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001217
Benny Prijono2e507c22006-06-23 15:04:11 +00001218 if (call->res_time.sec == 0)
1219 pj_gettimeofday(&call->res_time);
1220
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001221 if (reason && reason->slen == 0)
1222 reason = NULL;
1223
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224 /* Create response message */
1225 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1226 if (status != PJ_SUCCESS) {
1227 pjsua_perror(THIS_FILE, "Error creating response",
1228 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001229 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230 return status;
1231 }
1232
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001233 /* Call might have been disconnected if application is answering with
1234 * 200/OK and the media failed to start.
1235 */
1236 if (call->inv == NULL) {
1237 pjsip_dlg_dec_lock(dlg);
1238 return PJSIP_ESESSIONTERMINATED;
1239 }
1240
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 /* Add additional headers etc */
1242 pjsua_process_msg_data( tdata, msg_data);
1243
1244 /* Send the message */
1245 status = pjsip_inv_send_msg(call->inv, tdata);
1246 if (status != PJ_SUCCESS)
1247 pjsua_perror(THIS_FILE, "Error sending response",
1248 status);
1249
Benny Prijonodc752ca2006-09-22 16:55:42 +00001250 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251
1252 return status;
1253}
1254
1255
1256/*
1257 * Hangup call by using method that is appropriate according to the
1258 * call state.
1259 */
1260PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1261 unsigned code,
1262 const pj_str_t *reason,
1263 const pjsua_msg_data *msg_data)
1264{
1265 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001266 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267 pj_status_t status;
1268 pjsip_tx_data *tdata;
1269
1270
Benny Prijono148c9dd2006-09-19 13:37:53 +00001271 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1272 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1273 call_id));
1274 }
1275
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001276 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1277 PJ_EINVAL);
1278
Benny Prijonodc752ca2006-09-22 16:55:42 +00001279 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001280 if (status != PJ_SUCCESS)
1281 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001282
1283 if (code==0) {
1284 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1285 code = PJSIP_SC_OK;
1286 else if (call->inv->role == PJSIP_ROLE_UAS)
1287 code = PJSIP_SC_DECLINE;
1288 else
1289 code = PJSIP_SC_REQUEST_TERMINATED;
1290 }
1291
1292 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1293 if (status != PJ_SUCCESS) {
1294 pjsua_perror(THIS_FILE,
1295 "Failed to create end session message",
1296 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001297 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 return status;
1299 }
1300
1301 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1302 * as p_tdata when INVITE transaction has not been answered
1303 * with any provisional responses.
1304 */
1305 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001306 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 return PJ_SUCCESS;
1308 }
1309
1310 /* Add additional headers etc */
1311 pjsua_process_msg_data( tdata, msg_data);
1312
1313 /* Send the message */
1314 status = pjsip_inv_send_msg(call->inv, tdata);
1315 if (status != PJ_SUCCESS) {
1316 pjsua_perror(THIS_FILE,
1317 "Failed to send end session message",
1318 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001319 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 return status;
1321 }
1322
Benny Prijonodc752ca2006-09-22 16:55:42 +00001323 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001324
1325 return PJ_SUCCESS;
1326}
1327
1328
1329/*
1330 * Put the specified call on hold.
1331 */
1332PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1333 const pjsua_msg_data *msg_data)
1334{
1335 pjmedia_sdp_session *sdp;
1336 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001337 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001338 pjsip_tx_data *tdata;
1339 pj_status_t status;
1340
1341 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1342 PJ_EINVAL);
1343
Benny Prijonodc752ca2006-09-22 16:55:42 +00001344 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001345 if (status != PJ_SUCCESS)
1346 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001347
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001348
1349 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1350 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001351 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001352 return PJSIP_ESESSIONSTATE;
1353 }
1354
1355 status = create_inactive_sdp(call, &sdp);
1356 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001357 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358 return status;
1359 }
1360
Benny Prijono7129cc72007-11-05 05:54:25 +00001361 update_sdp_version(call, sdp);
1362
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001363 /* Create re-INVITE with new offer */
1364 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1365 if (status != PJ_SUCCESS) {
1366 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001367 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001368 return status;
1369 }
1370
1371 /* Add additional headers etc */
1372 pjsua_process_msg_data( tdata, msg_data);
1373
1374 /* Send the request */
1375 status = pjsip_inv_send_msg( call->inv, tdata);
1376 if (status != PJ_SUCCESS) {
1377 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001378 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001379 return status;
1380 }
1381
Benny Prijonodc752ca2006-09-22 16:55:42 +00001382 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383
1384 return PJ_SUCCESS;
1385}
1386
1387
1388/*
1389 * Send re-INVITE (to release hold).
1390 */
1391PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1392 pj_bool_t unhold,
1393 const pjsua_msg_data *msg_data)
1394{
1395 pjmedia_sdp_session *sdp;
1396 pjsip_tx_data *tdata;
1397 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001398 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001399 pj_status_t status;
1400
1401
1402 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1403 PJ_EINVAL);
1404
Benny Prijonodc752ca2006-09-22 16:55:42 +00001405 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001406 if (status != PJ_SUCCESS)
1407 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001408
1409 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1410 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001411 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412 return PJSIP_ESESSIONSTATE;
1413 }
1414
Benny Prijono667952e2007-04-02 19:27:54 +00001415 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001416 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001417 get_secure_level(&dlg->remote.info_str),
1418 NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00001419 if (status != PJ_SUCCESS) {
1420 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1421 pjsip_dlg_dec_lock(dlg);
1422 return PJSIP_ESESSIONSTATE;
1423 }
1424
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001425 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001426 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001427 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonod8179652008-01-23 20:39:07 +00001428 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001429 NULL, &sdp, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001430 if (status != PJ_SUCCESS) {
1431 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1432 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001433 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001434 return status;
1435 }
1436
Benny Prijono7129cc72007-11-05 05:54:25 +00001437 update_sdp_version(call, sdp);
1438
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439 /* Create re-INVITE with new offer */
1440 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1441 if (status != PJ_SUCCESS) {
1442 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001443 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 return status;
1445 }
1446
1447 /* Add additional headers etc */
1448 pjsua_process_msg_data( tdata, msg_data);
1449
1450 /* Send the request */
1451 status = pjsip_inv_send_msg( call->inv, tdata);
1452 if (status != PJ_SUCCESS) {
1453 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001454 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001455 return status;
1456 }
1457
Benny Prijonodc752ca2006-09-22 16:55:42 +00001458 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459
1460 return PJ_SUCCESS;
1461}
1462
1463
1464/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001465 * Send UPDATE request.
1466 */
1467PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1468 unsigned options,
1469 const pjsua_msg_data *msg_data)
1470{
1471 pjmedia_sdp_session *sdp;
1472 pjsip_tx_data *tdata;
1473 pjsua_call *call;
1474 pjsip_dialog *dlg;
1475 pj_status_t status;
1476
1477 PJ_UNUSED_ARG(options);
1478
1479 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1480 PJ_EINVAL);
1481
1482 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1483 if (status != PJ_SUCCESS)
1484 return status;
1485
1486 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00001487 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001488 get_secure_level(&dlg->remote.info_str),
1489 NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001490 if (status != PJ_SUCCESS) {
1491 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1492 pjsip_dlg_dec_lock(dlg);
1493 return PJSIP_ESESSIONSTATE;
1494 }
1495
1496 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00001497 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001498 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001499 if (status != PJ_SUCCESS) {
1500 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1501 status);
1502 pjsip_dlg_dec_lock(dlg);
1503 return status;
1504 }
1505
1506 /* Create re-INVITE with new offer */
1507 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1508 if (status != PJ_SUCCESS) {
1509 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1510 pjsip_dlg_dec_lock(dlg);
1511 return status;
1512 }
1513
1514 /* Add additional headers etc */
1515 pjsua_process_msg_data( tdata, msg_data);
1516
1517 /* Send the request */
1518 status = pjsip_inv_send_msg( call->inv, tdata);
1519 if (status != PJ_SUCCESS) {
1520 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1521 pjsip_dlg_dec_lock(dlg);
1522 return status;
1523 }
1524
1525 pjsip_dlg_dec_lock(dlg);
1526
1527 return PJ_SUCCESS;
1528}
1529
1530
1531/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001532 * Initiate call transfer to the specified address.
1533 */
1534PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1535 const pj_str_t *dest,
1536 const pjsua_msg_data *msg_data)
1537{
1538 pjsip_evsub *sub;
1539 pjsip_tx_data *tdata;
1540 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001541 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001542 pjsip_generic_string_hdr *gs_hdr;
1543 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001544 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001545 pj_status_t status;
1546
1547
1548 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1549 PJ_EINVAL);
1550
Benny Prijonodc752ca2006-09-22 16:55:42 +00001551 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001552 if (status != PJ_SUCCESS)
1553 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555
Benny Prijonod524e822006-09-22 12:48:18 +00001556 /* Create xfer client subscription. */
1557 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001558 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001559
1560 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001561 if (status != PJ_SUCCESS) {
1562 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001563 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564 return status;
1565 }
1566
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001567 /* Associate this call with the client subscription */
1568 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1569
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 /*
1571 * Create REFER request.
1572 */
1573 status = pjsip_xfer_initiate(sub, dest, &tdata);
1574 if (status != PJ_SUCCESS) {
1575 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001576 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001577 return status;
1578 }
1579
Benny Prijono053f5222006-11-11 16:16:04 +00001580 /* Add Referred-By header */
1581 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1582 &dlg->local.info_str);
1583 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1584
1585
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586 /* Add additional headers etc */
1587 pjsua_process_msg_data( tdata, msg_data);
1588
1589 /* Send. */
1590 status = pjsip_xfer_send_request(sub, tdata);
1591 if (status != PJ_SUCCESS) {
1592 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001593 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001594 return status;
1595 }
1596
1597 /* For simplicity (that's what this program is intended to be!),
1598 * leave the original invite session as it is. More advanced application
1599 * may want to hold the INVITE, or terminate the invite, or whatever.
1600 */
1601
Benny Prijonodc752ca2006-09-22 16:55:42 +00001602 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603
1604 return PJ_SUCCESS;
1605
1606}
1607
1608
1609/*
Benny Prijono053f5222006-11-11 16:16:04 +00001610 * Initiate attended call transfer to the specified address.
1611 */
1612PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1613 pjsua_call_id dest_call_id,
1614 unsigned options,
1615 const pjsua_msg_data *msg_data)
1616{
1617 pjsua_call *dest_call;
1618 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001619 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001620 pj_str_t str_dest;
1621 int len;
1622 pjsip_uri *uri;
1623 pj_status_t status;
1624
1625
1626 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1627 PJ_EINVAL);
1628 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1629 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1630 PJ_EINVAL);
1631
1632 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1633 &dest_call, &dest_dlg);
1634 if (status != PJ_SUCCESS)
1635 return status;
1636
1637 /*
1638 * Create REFER destination URI with Replaces field.
1639 */
1640
1641 /* Make sure we have sufficient buffer's length */
1642 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1643 dest_dlg->call_id->id.slen +
1644 dest_dlg->remote.info->tag.slen +
1645 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001646 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001647
1648 /* Print URI */
1649 str_dest_buf[0] = '<';
1650 str_dest.slen = 1;
1651
Benny Prijonoa1e69682007-05-11 15:14:34 +00001652 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001653 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1654 str_dest_buf+1, sizeof(str_dest_buf)-1);
1655 if (len < 0)
1656 return PJSIP_EURITOOLONG;
1657
1658 str_dest.slen += len;
1659
1660
1661 /* Build the URI */
1662 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1663 sizeof(str_dest_buf) - str_dest.slen,
1664 "?%s"
1665 "Replaces=%.*s"
1666 "%%3Bto-tag%%3D%.*s"
1667 "%%3Bfrom-tag%%3D%.*s>",
1668 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1669 "" : "Require=replaces&"),
1670 (int)dest_dlg->call_id->id.slen,
1671 dest_dlg->call_id->id.ptr,
1672 (int)dest_dlg->remote.info->tag.slen,
1673 dest_dlg->remote.info->tag.ptr,
1674 (int)dest_dlg->local.info->tag.slen,
1675 dest_dlg->local.info->tag.ptr);
1676
1677 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1678 PJSIP_EURITOOLONG);
1679
1680 str_dest.ptr = str_dest_buf;
1681 str_dest.slen += len;
1682
1683 pjsip_dlg_dec_lock(dest_dlg);
1684
1685 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1686}
1687
1688
1689/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001690 * Send DTMF digits to remote using RFC 2833 payload formats.
1691 */
1692PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1693 const pj_str_t *digits)
1694{
1695 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001696 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001697 pj_status_t status;
1698
1699 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1700 PJ_EINVAL);
1701
Benny Prijonodc752ca2006-09-22 16:55:42 +00001702 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001703 if (status != PJ_SUCCESS)
1704 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001705
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001706 if (!call->session) {
1707 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001708 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001709 return PJ_EINVALIDOP;
1710 }
1711
1712 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1713
Benny Prijonodc752ca2006-09-22 16:55:42 +00001714 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001715
1716 return status;
1717}
1718
1719
1720/**
1721 * Send instant messaging inside INVITE session.
1722 */
1723PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1724 const pj_str_t *mime_type,
1725 const pj_str_t *content,
1726 const pjsua_msg_data *msg_data,
1727 void *user_data)
1728{
1729 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001730 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001731 const pj_str_t mime_text_plain = pj_str("text/plain");
1732 pjsip_media_type ctype;
1733 pjsua_im_data *im_data;
1734 pjsip_tx_data *tdata;
1735 pj_status_t status;
1736
1737
1738 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1739 PJ_EINVAL);
1740
Benny Prijonodc752ca2006-09-22 16:55:42 +00001741 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001742 if (status != PJ_SUCCESS)
1743 return status;
1744
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001745 /* Set default media type if none is specified */
1746 if (mime_type == NULL) {
1747 mime_type = &mime_text_plain;
1748 }
1749
1750 /* Create request message. */
1751 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1752 -1, &tdata);
1753 if (status != PJ_SUCCESS) {
1754 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1755 goto on_return;
1756 }
1757
1758 /* Add accept header. */
1759 pjsip_msg_add_hdr( tdata->msg,
1760 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1761
1762 /* Parse MIME type */
1763 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1764
1765 /* Create "text/plain" message body. */
1766 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1767 &ctype.subtype, content);
1768 if (tdata->msg->body == NULL) {
1769 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1770 pjsip_tx_data_dec_ref(tdata);
1771 goto on_return;
1772 }
1773
1774 /* Add additional headers etc */
1775 pjsua_process_msg_data( tdata, msg_data);
1776
1777 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001778 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779 im_data->acc_id = call->acc_id;
1780 im_data->call_id = call_id;
1781 im_data->to = call->inv->dlg->remote.info_str;
1782 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1783 im_data->user_data = user_data;
1784
1785
1786 /* Send the request. */
1787 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1788 pjsua_var.mod.id, im_data);
1789 if (status != PJ_SUCCESS) {
1790 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1791 goto on_return;
1792 }
1793
1794on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001795 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001796 return status;
1797}
1798
1799
1800/*
1801 * Send IM typing indication inside INVITE session.
1802 */
1803PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1804 pj_bool_t is_typing,
1805 const pjsua_msg_data*msg_data)
1806{
1807 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001808 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001809 pjsip_tx_data *tdata;
1810 pj_status_t status;
1811
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001812 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1813 PJ_EINVAL);
1814
Benny Prijonodc752ca2006-09-22 16:55:42 +00001815 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001816 if (status != PJ_SUCCESS)
1817 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001819 /* Create request message. */
1820 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1821 -1, &tdata);
1822 if (status != PJ_SUCCESS) {
1823 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1824 goto on_return;
1825 }
1826
1827 /* Create "application/im-iscomposing+xml" msg body. */
1828 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1829 NULL, NULL, -1);
1830
1831 /* Add additional headers etc */
1832 pjsua_process_msg_data( tdata, msg_data);
1833
1834 /* Send the request. */
1835 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1836 if (status != PJ_SUCCESS) {
1837 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1838 goto on_return;
1839 }
1840
1841on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001842 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001843 return status;
1844}
1845
1846
1847/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001848 * Send arbitrary request.
1849 */
1850PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1851 const pj_str_t *method_str,
1852 const pjsua_msg_data *msg_data)
1853{
1854 pjsua_call *call;
1855 pjsip_dialog *dlg;
1856 pjsip_method method;
1857 pjsip_tx_data *tdata;
1858 pj_status_t status;
1859
1860 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1861 PJ_EINVAL);
1862
1863 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1864 if (status != PJ_SUCCESS)
1865 return status;
1866
1867 /* Init method */
1868 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1869
1870 /* Create request message. */
1871 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1872 if (status != PJ_SUCCESS) {
1873 pjsua_perror(THIS_FILE, "Unable to create request", status);
1874 goto on_return;
1875 }
1876
1877 /* Add additional headers etc */
1878 pjsua_process_msg_data( tdata, msg_data);
1879
1880 /* Send the request. */
1881 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1882 if (status != PJ_SUCCESS) {
1883 pjsua_perror(THIS_FILE, "Unable to send request", status);
1884 goto on_return;
1885 }
1886
1887on_return:
1888 pjsip_dlg_dec_lock(dlg);
1889 return status;
1890}
1891
1892
1893/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001894 * Terminate all calls.
1895 */
1896PJ_DEF(void) pjsua_call_hangup_all(void)
1897{
1898 unsigned i;
1899
1900 PJSUA_LOCK();
1901
1902 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1903 if (pjsua_var.calls[i].inv)
1904 pjsua_call_hangup(i, 0, NULL, NULL);
1905 }
1906
1907 PJSUA_UNLOCK();
1908}
1909
1910
Benny Prijono627cbb42007-09-25 20:48:49 +00001911const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001912{
1913 if (val < 1000) {
1914 pj_ansi_sprintf(buf, "%d", val);
1915 } else if (val < 1000000) {
1916 pj_ansi_sprintf(buf, "%d.%dK",
1917 val / 1000,
1918 (val % 1000) / 100);
1919 } else {
1920 pj_ansi_sprintf(buf, "%d.%02dM",
1921 val / 1000000,
1922 (val % 1000000) / 10000);
1923 }
1924
1925 return buf;
1926}
1927
1928
1929/* Dump media session */
1930static void dump_media_session(const char *indent,
1931 char *buf, unsigned maxlen,
1932 pjmedia_session *session)
1933{
1934 unsigned i;
1935 char *p = buf, *end = buf+maxlen;
1936 int len;
1937 pjmedia_session_info info;
1938
1939 pjmedia_session_get_info(session, &info);
1940
1941 for (i=0; i<info.stream_cnt; ++i) {
1942 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00001943 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001944 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001945 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001946 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001947 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001948 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949
1950 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00001951 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
1952 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001953
1954 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1955 dir = "sendonly";
1956 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1957 dir = "recvonly";
1958 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1959 dir = "sendrecv";
1960 else
1961 dir = "inactive";
1962
1963
1964 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00001965 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001966 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001967 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001968 info.stream_info[i].fmt.encoding_name.ptr,
1969 info.stream_info[i].fmt.clock_rate / 1000,
1970 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00001971 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001972 if (len < 1 || len > end-p) {
1973 *p = '\0';
1974 return;
1975 }
1976
1977 p += len;
1978 *p++ = '\n';
1979 *p = '\0';
1980
1981 if (stat.rx.update_cnt == 0)
1982 strcpy(last_update, "never");
1983 else {
1984 pj_gettimeofday(&now);
1985 PJ_TIME_VAL_SUB(now, stat.rx.update);
1986 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1987 now.sec / 3600,
1988 (now.sec % 3600) / 60,
1989 now.sec % 60,
1990 now.msec);
1991 }
1992
Benny Prijono80019eb2006-08-07 13:22:23 +00001993 pj_gettimeofday(&media_duration);
1994 PJ_TIME_VAL_SUB(media_duration, stat.start);
1995 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1996 media_duration.msec = 1;
1997
Benny Prijono1402a4a2008-01-08 23:41:22 +00001998 /* protect against division by zero */
1999 if (stat.rx.pkt == 0)
2000 stat.rx.pkt = 1;
2001 if (stat.tx.pkt == 0)
2002 stat.tx.pkt = 1;
2003
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002004 len = pj_ansi_snprintf(p, end-p,
2005 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002006 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002007 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2008 "%s (msec) min avg max last\n"
2009 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2010 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2011 indent, info.stream_info[i].fmt.pt,
2012 last_update,
2013 indent,
2014 good_number(packets, stat.rx.pkt),
2015 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002016 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002017 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2018 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 +00002019 indent,
2020 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002021 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002022 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002023 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002025 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002026 indent, indent,
2027 stat.rx.loss_period.min / 1000.0,
2028 stat.rx.loss_period.avg / 1000.0,
2029 stat.rx.loss_period.max / 1000.0,
2030 stat.rx.loss_period.last / 1000.0,
2031 indent,
2032 stat.rx.jitter.min / 1000.0,
2033 stat.rx.jitter.avg / 1000.0,
2034 stat.rx.jitter.max / 1000.0,
2035 stat.rx.jitter.last / 1000.0,
2036 ""
2037 );
2038
2039 if (len < 1 || len > end-p) {
2040 *p = '\0';
2041 return;
2042 }
2043
2044 p += len;
2045 *p++ = '\n';
2046 *p = '\0';
2047
2048 if (stat.tx.update_cnt == 0)
2049 strcpy(last_update, "never");
2050 else {
2051 pj_gettimeofday(&now);
2052 PJ_TIME_VAL_SUB(now, stat.tx.update);
2053 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2054 now.sec / 3600,
2055 (now.sec % 3600) / 60,
2056 now.sec % 60,
2057 now.msec);
2058 }
2059
2060 len = pj_ansi_snprintf(p, end-p,
2061 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002062 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2064 "%s (msec) min avg max last\n"
2065 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
2066 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
2067 indent,
2068 info.stream_info[i].tx_pt,
2069 info.stream_info[i].param->info.frm_ptime *
2070 info.stream_info[i].param->setting.frm_per_pkt,
2071 last_update,
2072
2073 indent,
2074 good_number(packets, stat.tx.pkt),
2075 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002076 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002077 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2078 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 +00002079
2080 indent,
2081 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002082 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002083 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002084 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002085 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002086 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002087
2088 indent, indent,
2089 stat.tx.loss_period.min / 1000.0,
2090 stat.tx.loss_period.avg / 1000.0,
2091 stat.tx.loss_period.max / 1000.0,
2092 stat.tx.loss_period.last / 1000.0,
2093 indent,
2094 stat.tx.jitter.min / 1000.0,
2095 stat.tx.jitter.avg / 1000.0,
2096 stat.tx.jitter.max / 1000.0,
2097 stat.tx.jitter.last / 1000.0,
2098 ""
2099 );
2100
2101 if (len < 1 || len > end-p) {
2102 *p = '\0';
2103 return;
2104 }
2105
2106 p += len;
2107 *p++ = '\n';
2108 *p = '\0';
2109
2110 len = pj_ansi_snprintf(p, end-p,
2111 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
2112 indent,
2113 stat.rtt.min / 1000.0,
2114 stat.rtt.avg / 1000.0,
2115 stat.rtt.max / 1000.0,
2116 stat.rtt.last / 1000.0
2117 );
2118 if (len < 1 || len > end-p) {
2119 *p = '\0';
2120 return;
2121 }
2122
2123 p += len;
2124 *p++ = '\n';
2125 *p = '\0';
2126 }
2127}
2128
2129
2130/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002131void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002132 int call_id,
2133 char *buf, pj_size_t size)
2134{
2135 int len;
2136 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2137 pjsip_dialog *dlg = inv->dlg;
2138 char userinfo[128];
2139
2140 /* Dump invite sesion info. */
2141
2142 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2143 if (len < 1)
2144 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2145 else
2146 userinfo[len] = '\0';
2147
2148 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2149 title,
2150 pjsip_inv_state_name(inv->state),
2151 userinfo);
2152 if (len < 1 || len >= (int)size) {
2153 pj_ansi_strcpy(buf, "<--uri too long-->");
2154 len = 18;
2155 } else
2156 buf[len] = '\0';
2157}
2158
2159
2160/*
2161 * Dump call and media statistics to string.
2162 */
2163PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2164 pj_bool_t with_media,
2165 char *buffer,
2166 unsigned maxlen,
2167 const char *indent)
2168{
2169 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002170 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 pj_time_val duration, res_delay, con_delay;
2172 char tmp[128];
2173 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002174 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002175 int len;
2176
2177 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2178 PJ_EINVAL);
2179
Benny Prijonodc752ca2006-09-22 16:55:42 +00002180 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002181 if (status != PJ_SUCCESS)
2182 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183
2184 *buffer = '\0';
2185 p = buffer;
2186 end = buffer + maxlen;
2187 len = 0;
2188
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189 print_call(indent, call_id, tmp, sizeof(tmp));
2190
2191 len = pj_ansi_strlen(tmp);
2192 pj_ansi_strcpy(buffer, tmp);
2193
2194 p += len;
2195 *p++ = '\r';
2196 *p++ = '\n';
2197
2198 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002199 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002200 pj_gettimeofday(&duration);
2201 PJ_TIME_VAL_SUB(duration, call->conn_time);
2202 con_delay = call->conn_time;
2203 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2204 } else {
2205 duration.sec = duration.msec = 0;
2206 con_delay.sec = con_delay.msec = 0;
2207 }
2208
2209 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002210 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002211 res_delay = call->res_time;
2212 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2213 } else {
2214 res_delay.sec = res_delay.msec = 0;
2215 }
2216
2217 /* Print duration */
2218 len = pj_ansi_snprintf(p, end-p,
2219 "%s Call time: %02dh:%02dm:%02ds, "
2220 "1st res in %d ms, conn in %dms",
2221 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002222 (int)(duration.sec / 3600),
2223 (int)((duration.sec % 3600)/60),
2224 (int)(duration.sec % 60),
2225 (int)PJ_TIME_VAL_MSEC(res_delay),
2226 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002227
2228 if (len > 0 && len < end-p) {
2229 p += len;
2230 *p++ = '\n';
2231 *p = '\0';
2232 }
2233
2234 /* Dump session statistics */
2235 if (with_media && call->session)
2236 dump_media_session(indent, p, end-p, call->session);
2237
Benny Prijonodc752ca2006-09-22 16:55:42 +00002238 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002239
2240 return PJ_SUCCESS;
2241}
2242
2243
2244/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002245 * This callback receives notification from invite session when the
2246 * session state has changed.
2247 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002248static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2249 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002250{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002251 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002252
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002253 PJSUA_LOCK();
2254
Benny Prijonoa1e69682007-05-11 15:14:34 +00002255 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002256
2257 if (!call) {
2258 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002259 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002260 }
2261
Benny Prijonoe21e7842006-04-09 16:46:05 +00002262
2263 /* Get call times */
2264 switch (inv->state) {
2265 case PJSIP_INV_STATE_EARLY:
2266 case PJSIP_INV_STATE_CONNECTING:
2267 if (call->res_time.sec == 0)
2268 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002269 call->last_code = (pjsip_status_code)
2270 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002271 pj_strncpy(&call->last_text,
2272 &e->body.tsx_state.tsx->status_text,
2273 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002274 break;
2275 case PJSIP_INV_STATE_CONFIRMED:
2276 pj_gettimeofday(&call->conn_time);
2277 break;
2278 case PJSIP_INV_STATE_DISCONNECTED:
2279 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002280 if (call->res_time.sec == 0)
2281 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002282 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002283 call->last_code = (pjsip_status_code)
2284 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002285 pj_strncpy(&call->last_text,
2286 &e->body.tsx_state.tsx->status_text,
2287 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002288 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002289 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002290 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002291 call->last_code = (pjsip_status_code)
2292 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002293 pj_strncpy(&call->last_text,
2294 &e->body.tsx_state.tsx->status_text,
2295 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002296 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002297 }
2298
Benny Prijono26ff9062006-02-21 23:47:00 +00002299 /* If this is an outgoing INVITE that was created because of
2300 * REFER/transfer, send NOTIFY to transferer.
2301 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002302 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002303 int st_code = -1;
2304 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2305
2306
Benny Prijonoa91a0032006-02-26 21:23:45 +00002307 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002308 case PJSIP_INV_STATE_NULL:
2309 case PJSIP_INV_STATE_CALLING:
2310 /* Do nothing */
2311 break;
2312
2313 case PJSIP_INV_STATE_EARLY:
2314 case PJSIP_INV_STATE_CONNECTING:
2315 st_code = e->body.tsx_state.tsx->status_code;
2316 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2317 break;
2318
2319 case PJSIP_INV_STATE_CONFIRMED:
2320 /* When state is confirmed, send the final 200/OK and terminate
2321 * subscription.
2322 */
2323 st_code = e->body.tsx_state.tsx->status_code;
2324 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2325 break;
2326
2327 case PJSIP_INV_STATE_DISCONNECTED:
2328 st_code = e->body.tsx_state.tsx->status_code;
2329 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2330 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002331
Benny Prijono8b1889b2006-06-06 18:40:40 +00002332 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002333 /* Nothing to do. Just to keep gcc from complaining about
2334 * unused enums.
2335 */
2336 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002337 }
2338
2339 if (st_code != -1) {
2340 pjsip_tx_data *tdata;
2341 pj_status_t status;
2342
Benny Prijonoa91a0032006-02-26 21:23:45 +00002343 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002344 ev_state, st_code,
2345 NULL, &tdata);
2346 if (status != PJ_SUCCESS) {
2347 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2348 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002349 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002350 if (status != PJ_SUCCESS) {
2351 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2352 }
2353 }
2354 }
2355 }
2356
Benny Prijono84126ab2006-02-09 09:30:09 +00002357
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002358 if (pjsua_var.ua_cfg.cb.on_call_state)
2359 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002360
2361 /* call->inv may be NULL now */
2362
Benny Prijono84126ab2006-02-09 09:30:09 +00002363 /* Destroy media session when invite session is disconnected. */
2364 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002365
Benny Prijonoa91a0032006-02-26 21:23:45 +00002366 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002367
Benny Prijono275fd682006-03-22 11:59:11 +00002368 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002369 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002370
Benny Prijono105217f2006-03-06 16:25:59 +00002371 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002372 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002373 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002374
2375 /* Reset call */
2376 reset_call(call->index);
2377
Benny Prijono84126ab2006-02-09 09:30:09 +00002378 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002379
2380 PJSUA_UNLOCK();
2381}
2382
2383/*
2384 * This callback is called by invite session framework when UAC session
2385 * has forked.
2386 */
2387static void pjsua_call_on_forked( pjsip_inv_session *inv,
2388 pjsip_event *e)
2389{
2390 PJ_UNUSED_ARG(inv);
2391 PJ_UNUSED_ARG(e);
2392
2393 PJ_TODO(HANDLE_FORKED_DIALOG);
2394}
2395
2396
2397/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002398 * Disconnect call upon error.
2399 */
2400static void call_disconnect( pjsip_inv_session *inv,
2401 int code )
2402{
Benny Prijono59b3aed2008-01-15 16:54:54 +00002403 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00002404 pjsip_tx_data *tdata;
2405 pj_status_t status;
2406
Benny Prijono59b3aed2008-01-15 16:54:54 +00002407 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2408
Benny Prijonoa38ada02006-07-02 14:22:35 +00002409 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002410 if (status != PJ_SUCCESS)
2411 return;
2412
2413 /* Add SDP in 488 status */
2414 if (call && call->med_tp && code==PJSIP_SC_NOT_ACCEPTABLE_HERE) {
2415 pjmedia_sdp_session *local_sdp;
2416 pjmedia_sock_info si;
2417
2418 call->med_tp->op->get_info(call->med_tp, &si);
2419 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
2420 1, &si, &local_sdp);
2421 if (status == PJ_SUCCESS) {
2422 pjsip_create_sdp_body(tdata->pool, local_sdp,
2423 &tdata->msg->body);
2424 }
2425 }
2426
2427 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002428}
2429
2430/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002431 * Callback to be called when SDP offer/answer negotiation has just completed
2432 * in the session. This function will start/update media if negotiation
2433 * has succeeded.
2434 */
2435static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2436 pj_status_t status)
2437{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002438 pjsua_call *call;
Benny Prijonod8179652008-01-23 20:39:07 +00002439 const pjmedia_sdp_session *c_local;
2440 pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002441 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002442
2443 PJSUA_LOCK();
2444
Benny Prijonoa1e69682007-05-11 15:14:34 +00002445 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002446
2447 if (status != PJ_SUCCESS) {
2448
2449 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2450
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002451 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002452 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002453
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002454 /* Disconnect call if we're not in the middle of initializing an
2455 * UAS dialog and if this is not a re-INVITE
2456 */
2457 if (inv->state != PJSIP_INV_STATE_NULL &&
2458 inv->state != PJSIP_INV_STATE_CONFIRMED)
2459 {
2460 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2461 }
2462
2463 PJSUA_UNLOCK();
2464 return;
2465 }
2466
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002467
2468 /* Get local and remote SDP */
Benny Prijonod8179652008-01-23 20:39:07 +00002469 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &c_local);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002470 if (status != PJ_SUCCESS) {
2471 pjsua_perror(THIS_FILE,
2472 "Unable to retrieve currently active local SDP",
2473 status);
2474 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2475 PJSUA_UNLOCK();
2476 return;
2477 }
Benny Prijonod8179652008-01-23 20:39:07 +00002478 local_sdp = (pjmedia_sdp_session*) c_local;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002479
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002480 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2481 if (status != PJ_SUCCESS) {
2482 pjsua_perror(THIS_FILE,
2483 "Unable to retrieve currently active remote SDP",
2484 status);
2485 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2486 PJSUA_UNLOCK();
2487 return;
2488 }
2489
Benny Prijono91a6a172007-10-31 08:59:29 +00002490 /* Update remote's NAT type */
2491 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2492 update_remote_nat_type(call, remote_sdp);
2493 }
2494
2495 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002496 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002497 if (status != PJ_SUCCESS) {
2498 pjsua_perror(THIS_FILE, "Unable to create media session",
2499 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00002500 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002501 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002502 PJSUA_UNLOCK();
2503 return;
2504 }
2505
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002506
2507 /* Call application callback, if any */
2508 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2509 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2510
2511
2512 PJSUA_UNLOCK();
2513}
2514
2515
2516/*
2517 * Create inactive SDP for call hold.
2518 */
2519static pj_status_t create_inactive_sdp(pjsua_call *call,
2520 pjmedia_sdp_session **p_answer)
2521{
2522 pj_status_t status;
2523 pjmedia_sdp_conn *conn;
2524 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002525 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002526 pjmedia_sdp_session *sdp;
2527
Benny Prijono617c5bc2007-04-02 19:51:21 +00002528 /* Get media socket info */
2529 pjmedia_transport_get_info(call->med_tp, &skinfo);
2530
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002531 /* Create new offer */
2532 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002533 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002534 if (status != PJ_SUCCESS) {
2535 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2536 return status;
2537 }
2538
2539 /* Get SDP media connection line */
2540 conn = sdp->media[0]->conn;
2541 if (!conn)
2542 conn = sdp->conn;
2543
2544 /* Modify address */
2545 conn->addr = pj_str("0.0.0.0");
2546
2547 /* Remove existing directions attributes */
2548 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2549 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2550 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2551 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2552
2553 /* Add inactive attribute */
2554 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2555 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2556
2557 *p_answer = sdp;
2558
2559 return status;
2560}
2561
2562
2563/*
2564 * Called when session received new offer.
2565 */
2566static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2567 const pjmedia_sdp_session *offer)
2568{
2569 const char *remote_state;
2570 pjsua_call *call;
2571 pjmedia_sdp_conn *conn;
2572 pjmedia_sdp_session *answer;
2573 pj_bool_t is_remote_active;
2574 pj_status_t status;
2575
2576 PJSUA_LOCK();
2577
Benny Prijonoa1e69682007-05-11 15:14:34 +00002578 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002579
2580 /*
2581 * See if remote is offering active media (i.e. not on-hold)
2582 */
2583 is_remote_active = PJ_TRUE;
2584
2585 conn = offer->media[0]->conn;
2586 if (!conn)
2587 conn = offer->conn;
2588
2589 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2590 pj_strcmp2(&conn->addr, "0")==0)
2591 {
2592 is_remote_active = PJ_FALSE;
2593
2594 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002595 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2596 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002597 {
2598 is_remote_active = PJ_FALSE;
2599 }
2600
2601 remote_state = (is_remote_active ? "active" : "inactive");
2602
2603 /* Supply candidate answer */
2604 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2605 PJ_LOG(4,(THIS_FILE,
2606 "Call %d: RX new media offer, creating inactive SDP "
2607 "(media in offer is %s)", call->index, remote_state));
2608 status = create_inactive_sdp( call, &answer );
2609 } else {
Benny Prijonod8179652008-01-23 20:39:07 +00002610 int secure_level;
Benny Prijono667952e2007-04-02 19:27:54 +00002611
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002612 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2613 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002614
2615 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002616 secure_level = get_secure_level(&call->inv->dlg->remote.info_str);
2617 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002618 secure_level, NULL);
Benny Prijono667952e2007-04-02 19:27:54 +00002619 if (status != PJ_SUCCESS) {
2620 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2621 PJSUA_UNLOCK();
2622 return;
2623 }
2624
Benny Prijonod8179652008-01-23 20:39:07 +00002625 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002626 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002627 }
2628
2629 if (status != PJ_SUCCESS) {
2630 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2631 PJSUA_UNLOCK();
2632 return;
2633 }
2634
2635 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2636 if (status != PJ_SUCCESS) {
2637 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2638 PJSUA_UNLOCK();
2639 return;
2640 }
2641
2642 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002643}
2644
2645
2646/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002647 * Called to generate new offer.
2648 */
2649static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2650 pjmedia_sdp_session **offer)
2651{
2652 pjsua_call *call;
2653 pj_status_t status;
2654
2655 PJSUA_LOCK();
2656
2657 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2658
2659 /* See if we've put call on hold. */
2660 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2661 PJ_LOG(4,(THIS_FILE,
2662 "Call %d: call is on-hold locally, creating inactive SDP ",
2663 call->index));
2664 status = create_inactive_sdp( call, offer );
2665 } else {
Benny Prijonod8179652008-01-23 20:39:07 +00002666 int secure_level;
Benny Prijono77998ce2007-06-20 10:03:46 +00002667
2668 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2669 call->index));
2670
2671 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +00002672 secure_level = get_secure_level(&call->inv->dlg->remote.info_str);
2673 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002674 secure_level, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002675 if (status != PJ_SUCCESS) {
2676 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2677 PJSUA_UNLOCK();
2678 return;
2679 }
2680
Benny Prijonod8179652008-01-23 20:39:07 +00002681 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +00002682 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00002683 }
2684
2685 if (status != PJ_SUCCESS) {
2686 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2687 PJSUA_UNLOCK();
2688 return;
2689 }
2690
Benny Prijono7129cc72007-11-05 05:54:25 +00002691 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002692
2693 PJSUA_UNLOCK();
2694}
2695
2696
2697/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002698 * Callback called by event framework when the xfer subscription state
2699 * has changed.
2700 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002701static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2702{
2703
2704 PJ_UNUSED_ARG(event);
2705
2706 /*
2707 * When subscription is accepted (got 200/OK to REFER), check if
2708 * subscription suppressed.
2709 */
2710 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2711
2712 pjsip_rx_data *rdata;
2713 pjsip_generic_string_hdr *refer_sub;
2714 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2715 pjsua_call *call;
2716
Benny Prijonoa1e69682007-05-11 15:14:34 +00002717 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002718
2719 /* Must be receipt of response message */
2720 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2721 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2722 rdata = event->body.tsx_state.src.rdata;
2723
2724 /* Find Refer-Sub header */
2725 refer_sub = (pjsip_generic_string_hdr*)
2726 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2727 &REFER_SUB, NULL);
2728
2729 /* Check if subscription is suppressed */
2730 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2731 /* Since no subscription is desired, assume that call has been
2732 * transfered successfully.
2733 */
2734 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2735 const pj_str_t ACCEPTED = { "Accepted", 8 };
2736 pj_bool_t cont = PJ_FALSE;
2737 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2738 200,
2739 &ACCEPTED,
2740 PJ_TRUE,
2741 &cont);
2742 }
2743
2744 /* Yes, subscription is suppressed.
2745 * Terminate our subscription now.
2746 */
2747 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2748 "event subcription..."));
2749 pjsip_evsub_terminate(sub, PJ_TRUE);
2750
2751 } else {
2752 /* Notify application about call transfer progress.
2753 * Initially notify with 100/Accepted status.
2754 */
2755 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2756 const pj_str_t ACCEPTED = { "Accepted", 8 };
2757 pj_bool_t cont = PJ_FALSE;
2758 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2759 100,
2760 &ACCEPTED,
2761 PJ_FALSE,
2762 &cont);
2763 }
2764 }
2765 }
2766 /*
2767 * On incoming NOTIFY, notify application about call transfer progress.
2768 */
2769 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2770 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2771 {
2772 pjsua_call *call;
2773 pjsip_msg *msg;
2774 pjsip_msg_body *body;
2775 pjsip_status_line status_line;
2776 pj_bool_t is_last;
2777 pj_bool_t cont;
2778 pj_status_t status;
2779
Benny Prijonoa1e69682007-05-11 15:14:34 +00002780 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002781
2782 /* When subscription is terminated, clear the xfer_sub member of
2783 * the inv_data.
2784 */
2785 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2786 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2787 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2788
2789 }
2790
2791 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2792 /* Application is not interested with call progress status */
2793 return;
2794 }
2795
2796 /* This better be a NOTIFY request */
2797 if (event->type == PJSIP_EVENT_TSX_STATE &&
2798 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2799 {
2800 pjsip_rx_data *rdata;
2801
2802 rdata = event->body.tsx_state.src.rdata;
2803
2804 /* Check if there's body */
2805 msg = rdata->msg_info.msg;
2806 body = msg->body;
2807 if (!body) {
2808 PJ_LOG(4,(THIS_FILE,
2809 "Warning: received NOTIFY without message body"));
2810 return;
2811 }
2812
2813 /* Check for appropriate content */
2814 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2815 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2816 {
2817 PJ_LOG(4,(THIS_FILE,
2818 "Warning: received NOTIFY with non message/sipfrag "
2819 "content"));
2820 return;
2821 }
2822
2823 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002824 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002825 &status_line);
2826 if (status != PJ_SUCCESS) {
2827 PJ_LOG(4,(THIS_FILE,
2828 "Warning: received NOTIFY with invalid "
2829 "message/sipfrag content"));
2830 return;
2831 }
2832
2833 } else {
2834 status_line.code = 500;
2835 status_line.reason = *pjsip_get_status_text(500);
2836 }
2837
2838 /* Notify application */
2839 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2840 cont = !is_last;
2841 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2842 status_line.code,
2843 &status_line.reason,
2844 is_last, &cont);
2845
2846 if (!cont) {
2847 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2848 }
2849 }
2850}
2851
2852
2853/*
2854 * Callback called by event framework when the xfer subscription state
2855 * has changed.
2856 */
2857static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002858{
2859
2860 PJ_UNUSED_ARG(event);
2861
2862 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002863 * When subscription is terminated, clear the xfer_sub member of
2864 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002865 */
2866 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002867 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002868
Benny Prijonoa1e69682007-05-11 15:14:34 +00002869 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002870 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002871 return;
2872
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002873 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002874 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002875
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002876 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002877 }
2878}
2879
2880
2881/*
2882 * Follow transfer (REFER) request.
2883 */
2884static void on_call_transfered( pjsip_inv_session *inv,
2885 pjsip_rx_data *rdata )
2886{
2887 pj_status_t status;
2888 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002889 pjsua_call *existing_call;
2890 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002891 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002892 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002893 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002894 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002895 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002896 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002897 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002898 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002899 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002900 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002901 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002902 pjsip_evsub *sub;
2903
Benny Prijonoa1e69682007-05-11 15:14:34 +00002904 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002905
Benny Prijono26ff9062006-02-21 23:47:00 +00002906 /* Find the Refer-To header */
2907 refer_to = (pjsip_generic_string_hdr*)
2908 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2909
2910 if (refer_to == NULL) {
2911 /* Invalid Request.
2912 * No Refer-To header!
2913 */
2914 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002915 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002916 return;
2917 }
2918
Benny Prijonoc8141a82006-08-20 09:12:19 +00002919 /* Find optional Refer-Sub header */
2920 refer_sub = (pjsip_generic_string_hdr*)
2921 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2922
2923 if (refer_sub) {
2924 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2925 no_refer_sub = PJ_TRUE;
2926 }
2927
Benny Prijono053f5222006-11-11 16:16:04 +00002928 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2929 * request.
2930 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002931 ref_by_hdr = (pjsip_hdr*)
2932 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002933 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002934
Benny Prijono9fc735d2006-05-28 14:58:12 +00002935 /* Notify callback */
2936 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002937 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2938 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2939 &refer_to->hvalue,
2940 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002941
2942 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002943 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002944 if (code >= 300) {
2945 /* Application rejects call transfer request */
2946 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2947 return;
2948 }
2949
Benny Prijono26ff9062006-02-21 23:47:00 +00002950 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2951 (int)inv->dlg->remote.info_str.slen,
2952 inv->dlg->remote.info_str.ptr,
2953 (int)refer_to->hvalue.slen,
2954 refer_to->hvalue.ptr));
2955
Benny Prijonoc8141a82006-08-20 09:12:19 +00002956 if (no_refer_sub) {
2957 /*
2958 * Always answer with 200.
2959 */
2960 pjsip_tx_data *tdata;
2961 const pj_str_t str_false = { "false", 5};
2962 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002963
Benny Prijonoc8141a82006-08-20 09:12:19 +00002964 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2965 if (status != PJ_SUCCESS) {
2966 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2967 status);
2968 return;
2969 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002970
Benny Prijonoc8141a82006-08-20 09:12:19 +00002971 /* Add Refer-Sub header */
2972 hdr = (pjsip_hdr*)
2973 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2974 &str_false);
2975 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002976
Benny Prijono26ff9062006-02-21 23:47:00 +00002977
Benny Prijonoc8141a82006-08-20 09:12:19 +00002978 /* Send answer */
2979 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2980 tdata);
2981 if (status != PJ_SUCCESS) {
2982 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2983 status);
2984 return;
2985 }
2986
2987 /* Don't have subscription */
2988 sub = NULL;
2989
2990 } else {
2991 struct pjsip_evsub_user xfer_cb;
2992 pjsip_hdr hdr_list;
2993
2994 /* Init callback */
2995 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002996 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002997
2998 /* Init additional header list to be sent with REFER response */
2999 pj_list_init(&hdr_list);
3000
3001 /* Create transferee event subscription */
3002 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3003 if (status != PJ_SUCCESS) {
3004 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3005 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3006 return;
3007 }
3008
3009 /* If there's Refer-Sub header and the value is "true", send back
3010 * Refer-Sub in the response with value "true" too.
3011 */
3012 if (refer_sub) {
3013 const pj_str_t str_true = { "true", 4 };
3014 pjsip_hdr *hdr;
3015
3016 hdr = (pjsip_hdr*)
3017 pjsip_generic_string_hdr_create(inv->dlg->pool,
3018 &str_refer_sub,
3019 &str_true);
3020 pj_list_push_back(&hdr_list, hdr);
3021
3022 }
3023
3024 /* Accept the REFER request, send 200 (OK). */
3025 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3026
3027 /* Create initial NOTIFY request */
3028 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3029 100, NULL, &tdata);
3030 if (status != PJ_SUCCESS) {
3031 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3032 status);
3033 return;
3034 }
3035
3036 /* Send initial NOTIFY request */
3037 status = pjsip_xfer_send_request( sub, tdata);
3038 if (status != PJ_SUCCESS) {
3039 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3040 return;
3041 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003042 }
3043
3044 /* We're cheating here.
3045 * We need to get a null terminated string from a pj_str_t.
3046 * So grab the pointer from the hvalue and NULL terminate it, knowing
3047 * that the NULL position will be occupied by a newline.
3048 */
3049 uri = refer_to->hvalue.ptr;
3050 uri[refer_to->hvalue.slen] = '\0';
3051
Benny Prijono053f5222006-11-11 16:16:04 +00003052 /* Init msg_data */
3053 pjsua_msg_data_init(&msg_data);
3054
3055 /* If Referred-By header is present in the REFER request, copy this
3056 * to the outgoing INVITE request.
3057 */
3058 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003059 pjsip_hdr *dup = (pjsip_hdr*)
3060 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003061 pj_list_push_back(&msg_data.hdr_list, dup);
3062 }
3063
Benny Prijono26ff9062006-02-21 23:47:00 +00003064 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003065 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003066 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003067 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003068 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003069 if (status != PJ_SUCCESS) {
3070
Benny Prijonoc8141a82006-08-20 09:12:19 +00003071 /* Notify xferer about the error (if we have subscription) */
3072 if (sub) {
3073 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3074 500, NULL, &tdata);
3075 if (status != PJ_SUCCESS) {
3076 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3077 status);
3078 return;
3079 }
3080 status = pjsip_xfer_send_request(sub, tdata);
3081 if (status != PJ_SUCCESS) {
3082 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3083 status);
3084 return;
3085 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003086 }
3087 return;
3088 }
3089
Benny Prijonoc8141a82006-08-20 09:12:19 +00003090 if (sub) {
3091 /* Put the server subscription in inv_data.
3092 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3093 * reported back to the server subscription.
3094 */
3095 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003096
Benny Prijonoc8141a82006-08-20 09:12:19 +00003097 /* Put the invite_data in the subscription. */
3098 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3099 &pjsua_var.calls[new_call]);
3100 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003101}
3102
3103
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003104
Benny Prijono26ff9062006-02-21 23:47:00 +00003105/*
3106 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003107 * session. We use this to trap:
3108 * - incoming REFER request.
3109 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003110 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003111static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3112 pjsip_transaction *tsx,
3113 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003114{
Benny Prijonoa1e69682007-05-11 15:14:34 +00003115 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003116
3117 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003118
Benny Prijonofeb69f42007-10-05 09:12:26 +00003119 /* Notify application callback first */
3120 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3121 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3122 }
3123
Benny Prijono26ff9062006-02-21 23:47:00 +00003124 if (tsx->role==PJSIP_ROLE_UAS &&
3125 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003126 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003127 {
3128 /*
3129 * Incoming REFER request.
3130 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003131 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003132
Benny Prijono26ff9062006-02-21 23:47:00 +00003133 }
Benny Prijonob0808372006-03-02 21:18:58 +00003134 else if (tsx->role==PJSIP_ROLE_UAS &&
3135 tsx->state==PJSIP_TSX_STATE_TRYING &&
3136 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3137 {
3138 /*
3139 * Incoming MESSAGE request!
3140 */
3141 pjsip_rx_data *rdata;
3142 pjsip_msg *msg;
3143 pjsip_accept_hdr *accept_hdr;
3144 pj_status_t status;
3145
3146 rdata = e->body.tsx_state.src.rdata;
3147 msg = rdata->msg_info.msg;
3148
3149 /* Request MUST have message body, with Content-Type equal to
3150 * "text/plain".
3151 */
3152 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3153
3154 pjsip_hdr hdr_list;
3155
3156 pj_list_init(&hdr_list);
3157 pj_list_push_back(&hdr_list, accept_hdr);
3158
3159 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3160 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003161 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003162 return;
3163 }
3164
3165 /* Respond with 200 first, so that remote doesn't retransmit in case
3166 * the UI takes too long to process the message.
3167 */
3168 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3169
3170 /* Process MESSAGE request */
3171 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3172 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003173
Benny Prijonob0808372006-03-02 21:18:58 +00003174 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003175 else if (tsx->role == PJSIP_ROLE_UAC &&
3176 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003177 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003178 /* Handle outgoing pager status */
3179 if (tsx->status_code >= 200) {
3180 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003181
Benny Prijonoa1e69682007-05-11 15:14:34 +00003182 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003183 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003185 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3186 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3187 &im_data->to,
3188 &im_data->body,
3189 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003190 (pjsip_status_code)
3191 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003192 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003193 }
Benny Prijonofccab712006-02-22 22:23:22 +00003194 }
Benny Prijono834aee32006-02-19 01:38:06 +00003195 }
Benny Prijono834aee32006-02-19 01:38:06 +00003196
Benny Prijono26ff9062006-02-21 23:47:00 +00003197
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003198 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003199}