blob: ac5db1dd347fda5a8440d44e8ea008f13804a848 [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 Prijonod524e822006-09-22 12:48:18 +000074/*
75 * Callback called by event framework when the xfer subscription state
76 * has changed.
77 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000078static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
79static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
80
81/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082 * Reset call descriptor.
83 */
84static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000085{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000087
Benny Prijonoeebe9af2006-06-13 22:57:13 +000088 call->index = id;
89 call->inv = NULL;
90 call->user_data = NULL;
91 call->session = NULL;
92 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +000093 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000094 call->conf_slot = PJSUA_INVALID_ID;
95 call->last_text.ptr = call->last_text_buf_;
96 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +000097 call->conn_time.sec = 0;
98 call->conn_time.msec = 0;
99 call->res_time.sec = 0;
100 call->res_time.msec = 0;
Benny Prijono105217f2006-03-06 16:25:59 +0000101}
102
103
Benny Prijono275fd682006-03-22 11:59:11 +0000104/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000105 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000106 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000108{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109 pjsip_inv_callback inv_cb;
110 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000111 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000112 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000113
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000114 /* Init calls array. */
115 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
116 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000117
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000118 /* Copy config */
119 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000120
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000121 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000122 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
124 inv_cb.on_new_session = &pjsua_call_on_forked;
125 inv_cb.on_media_update = &pjsua_call_on_media_update;
126 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000127 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000129
Benny Prijono275fd682006-03-22 11:59:11 +0000130
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 /* Initialize invite session module: */
132 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
133 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
134
Benny Prijonoc8141a82006-08-20 09:12:19 +0000135 /* Add "norefersub" in Supported header */
136 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
137 NULL, 1, &str_norefersub);
138
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000139 return status;
140}
141
142
143/*
144 * Start call subsystem.
145 */
146pj_status_t pjsua_call_subsys_start(void)
147{
148 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000149 return PJ_SUCCESS;
150}
151
152
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000154 * Get maximum number of calls configured in pjsua.
155 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000156PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000157{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000158 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000159}
160
161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162/*
163 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000164 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000165PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000166{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000167 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168}
169
170
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000171/*
172 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
175 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000178
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000179 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
184 if (!pjsua_var.calls[i].inv)
185 continue;
186 ids[c] = i;
187 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188 }
189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000192 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193
194 return PJ_SUCCESS;
195}
196
197
Benny Prijono1f7767b2007-10-03 18:28:49 +0000198#define LATE_SDP 0
199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200/*
201 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000202 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
204 const pj_str_t *dest_uri,
205 unsigned options,
206 void *user_data,
207 const pjsua_msg_data *msg_data,
208 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000209{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000210 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000211 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000212 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 pjsua_acc *acc;
214 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000215 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000216 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000217 pjsip_tx_data *tdata;
218 pj_status_t status;
219
Benny Prijono9fc735d2006-05-28 14:58:12 +0000220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000222 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000223 PJ_EINVAL);
224
Benny Prijono320fa4d2006-12-07 10:09:16 +0000225 /* Check arguments */
226 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
227
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 PJSUA_LOCK();
229
230 acc = &pjsua_var.acc[acc_id];
231 if (!acc->valid) {
232 pjsua_perror(THIS_FILE, "Unable to make call because account "
233 "is not valid", PJ_EINVALIDOP);
234 PJSUA_UNLOCK();
235 return PJ_EINVALIDOP;
236 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000237
Benny Prijonoa91a0032006-02-26 21:23:45 +0000238 /* Find free call slot. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000239 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000241 break;
242 }
243
Benny Prijonoa1e69682007-05-11 15:14:34 +0000244 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000245 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
246 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000247 return PJ_ETOOMANY;
248 }
249
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000250 call = &pjsua_var.calls[call_id];
251
Benny Prijono320fa4d2006-12-07 10:09:16 +0000252 /* Verify that destination URI is valid before calling
253 * pjsua_acc_create_uac_contact, or otherwise there
254 * a misleading "Invalid Contact URI" error will be printed
255 * when pjsua_acc_create_uac_contact() fails.
256 */
257 if (1) {
258 pj_pool_t *pool;
259 pjsip_uri *uri;
260 pj_str_t dup;
261
262 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
263 if (!pool) {
264 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
265 PJSUA_UNLOCK();
266 return PJ_ENOMEM;
267 }
268
269 pj_strdup_with_null(pool, &dup, dest_uri);
270 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
271 pj_pool_release(pool);
272
273 if (uri == NULL) {
274 pjsua_perror(THIS_FILE, "Unable to make call",
275 PJSIP_EINVALIDREQURI);
276 PJSUA_UNLOCK();
277 return PJSIP_EINVALIDREQURI;
278 }
279 }
280
Benny Prijono093d3022006-09-24 00:07:11 +0000281 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
282 (int)dest_uri->slen, dest_uri->ptr));
283
Benny Prijonoe21e7842006-04-09 16:46:05 +0000284 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000285 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000286
Benny Prijonoe21e7842006-04-09 16:46:05 +0000287 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000288 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000289
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000290 /* Create suitable Contact header */
291 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
292 acc_id, dest_uri);
293 if (status != PJ_SUCCESS) {
294 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
295 PJSUA_UNLOCK();
296 return status;
297 }
298
Benny Prijonoe21e7842006-04-09 16:46:05 +0000299 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000300 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000301 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000303 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000304 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000305 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000306 return status;
307 }
308
Benny Prijonoc97608e2007-03-23 16:34:20 +0000309 /* Init media channel */
310 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
311 if (status != PJ_SUCCESS) {
312 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
313 goto on_error;
314 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000315
Benny Prijonoc97608e2007-03-23 16:34:20 +0000316 /* Create SDP offer */
Benny Prijono1f7767b2007-10-03 18:28:49 +0000317#if LATE_SDP
318 offer = NULL;
319#else
Benny Prijonoc97608e2007-03-23 16:34:20 +0000320 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000321 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000322 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000323 goto on_error;
324 }
Benny Prijono1f7767b2007-10-03 18:28:49 +0000325#endif
Benny Prijono84126ab2006-02-09 09:30:09 +0000326
327 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000328#if PJSIP_HAS_100REL
329 options |= PJSIP_INV_SUPPORT_100REL;
330#endif
331 if (acc->cfg.require_100rel)
332 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000333
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000334 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000335 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000336 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000337 goto on_error;
338 }
339
340
341 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000342 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000343 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000344
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000345 dlg->mod_data[pjsua_var.mod.id] = call;
346 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000347
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000348 /* Attach user data */
349 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000350
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000351 /* If account is locked to specific transport, then lock dialog
352 * to this transport too.
353 */
354 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
355 pjsip_tpselector tp_sel;
356
357 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
358 pjsip_dlg_set_transport(dlg, &tp_sel);
359 }
360
Benny Prijono84126ab2006-02-09 09:30:09 +0000361 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362 if (!pj_list_empty(&acc->route_set))
363 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000364
365
366 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000368 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000369 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000370 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000371
372
373 /* Create initial INVITE: */
374
375 status = pjsip_inv_invite(inv, &tdata);
376 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000377 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
378 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000379 goto on_error;
380 }
381
382
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 /* Add additional headers etc */
384
385 pjsua_process_msg_data( tdata, msg_data);
386
Benny Prijono093d3022006-09-24 00:07:11 +0000387 /* Must increment call counter now */
388 ++pjsua_var.call_cnt;
389
Benny Prijono84126ab2006-02-09 09:30:09 +0000390 /* Send initial INVITE: */
391
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000392 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000393 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000394 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
395 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000396
397 /* Upon failure to send first request, both dialog and invite
398 * session would have been cleared.
399 */
400 inv = NULL;
401 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000402 goto on_error;
403 }
404
Benny Prijono84126ab2006-02-09 09:30:09 +0000405 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000406
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000407 if (p_call_id)
408 *p_call_id = call_id;
409
410 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000411
412 return PJ_SUCCESS;
413
414
415on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000416 if (inv != NULL) {
417 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000418 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000419 pjsip_dlg_terminate(dlg);
420 }
421
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000422 if (call_id != -1) {
423 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000424 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000425 }
426
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000428 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000429}
430
431
432/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000433 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000435 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000436pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000437{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000438 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000439 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000440 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000441 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
442 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000443 pjsip_tx_data *response = NULL;
444 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000445 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 int acc_id;
447 pjsua_call *call;
448 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000449 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000450 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000451
Benny Prijono26ff9062006-02-21 23:47:00 +0000452 /* Don't want to handle anything but INVITE */
453 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
454 return PJ_FALSE;
455
456 /* Don't want to handle anything that's already associated with
457 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000459 if (dlg || tsx)
460 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000461
Benny Prijono148c9dd2006-09-19 13:37:53 +0000462 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000463
Benny Prijono26ff9062006-02-21 23:47:00 +0000464 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
466 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000467 break;
468 }
469
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
471 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000472 PJSIP_SC_BUSY_HERE, NULL,
473 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000474 PJ_LOG(2,(THIS_FILE,
475 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000476 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000477 return PJ_TRUE;
478 }
479
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000480 /* Clear call descriptor */
481 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000482
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483 call = &pjsua_var.calls[call_id];
484
485 /* Mark call start time. */
486 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000487
Benny Prijono053f5222006-11-11 16:16:04 +0000488 /* Check INVITE request for Replaces header. If Replaces header is
489 * present, the function will make sure that we can handle the request.
490 */
491 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
492 &response);
493 if (status != PJ_SUCCESS) {
494 /*
495 * Something wrong with the Replaces header.
496 */
497 if (response) {
498 pjsip_response_addr res_addr;
499
500 pjsip_get_response_addr(response->pool, rdata, &res_addr);
501 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
502 NULL, NULL);
503
504 } else {
505
506 /* Respond with 500 (Internal Server Error) */
507 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
508 NULL, NULL);
509 }
510
511 PJSUA_UNLOCK();
512 return PJ_TRUE;
513 }
514
515 /* If this INVITE request contains Replaces header, notify application
516 * about the request so that application can do subsequent checking
517 * if it wants to.
518 */
519 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
520 pjsua_call *replaced_call;
521 int st_code = 200;
522 pj_str_t st_text = { "OK", 2 };
523
524 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000525 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000526
527 /* Notify application */
528 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
529 rdata, &st_code, &st_text);
530
531 /* Must specify final response */
532 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
533
534 /* Check if application rejects this request. */
535 if (st_code >= 300) {
536
537 if (st_text.slen == 2)
538 st_text = *pjsip_get_status_text(st_code);
539
540 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
541 st_code, &st_text, NULL, NULL, NULL);
542 PJSUA_UNLOCK();
543 return PJ_TRUE;
544 }
545 }
546
547
Benny Prijonoc97608e2007-03-23 16:34:20 +0000548 /* Init media channel */
549 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000550 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000552 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000553 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000554 return PJ_TRUE;
555 }
556
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000557
Benny Prijonoc97608e2007-03-23 16:34:20 +0000558 /* Get media capability from media endpoint: */
559 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
560 if (status != PJ_SUCCESS) {
561 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
562 NULL, NULL);
563 pjsua_media_channel_deinit(call->index);
564 PJSUA_UNLOCK();
565 return PJ_TRUE;
566 }
567
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000568 /*
569 * Get which account is most likely to be associated with this incoming
570 * call. We need the account to find which contact URI to put for
571 * the call.
572 */
573 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000574
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000575 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000576#if PJSIP_HAS_100REL
577 options |= PJSIP_INV_SUPPORT_100REL;
578#endif
579 if (pjsua_var.acc[acc_id].cfg.require_100rel)
580 options |= PJSIP_INV_REQUIRE_100REL;
581
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000582 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
583 pjsua_var.endpt, &response);
584 if (status != PJ_SUCCESS) {
585
586 /*
587 * No we can't handle the incoming INVITE request.
588 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000589 if (response) {
590 pjsip_response_addr res_addr;
591
592 pjsip_get_response_addr(response->pool, rdata, &res_addr);
593 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
594 NULL, NULL);
595
596 } else {
597
598 /* Respond with 500 (Internal Server Error) */
599 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
600 NULL, NULL);
601 }
602
Benny Prijonoc97608e2007-03-23 16:34:20 +0000603 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000604 PJSUA_UNLOCK();
605 return PJ_TRUE;
606 }
607
608
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000609 /* Get suitable Contact header */
610 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
611 acc_id, rdata);
612 if (status != PJ_SUCCESS) {
613 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
614 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
615 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000616 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000617 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000618 return PJ_TRUE;
619 }
620
Benny Prijono26ff9062006-02-21 23:47:00 +0000621 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000622 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000623 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000624 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000625 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000626 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000627 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000628 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000629 return PJ_TRUE;
630 }
631
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 /* Set credentials */
633 if (pjsua_var.acc[acc_id].cred_cnt) {
634 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
635 pjsua_var.acc[acc_id].cred_cnt,
636 pjsua_var.acc[acc_id].cred);
637 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000638
639 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000640 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000641 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000642 pjsip_hdr hdr_list;
643 pjsip_warning_hdr *w;
644
645 w = pjsip_warning_hdr_create_from_status(dlg->pool,
646 pjsip_endpt_name(pjsua_var.endpt),
647 status);
648 pj_list_init(&hdr_list);
649 pj_list_push_back(&hdr_list, w);
650
651 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
652
653 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000654 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000655 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000656 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000657 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 return PJ_TRUE;
659 }
660
661
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000662 /* Create and attach pjsua_var data to the dialog: */
663 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000664
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000665 dlg->mod_data[pjsua_var.mod.id] = call;
666 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000667
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000668 /* If account is locked to specific transport, then lock dialog
669 * to this transport too.
670 */
671 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
672 pjsip_tpselector tp_sel;
673
674 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
675 pjsip_dlg_set_transport(dlg, &tp_sel);
676 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000677
Benny Prijono64f851e2006-02-23 13:49:28 +0000678 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000679 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000680 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000681 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000682 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000683 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
684 status);
685
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000686 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
687 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000688 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000689 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000690 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000691
692 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000693 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000694 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000695 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000696 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000697 }
698
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000699 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000700
Benny Prijono105217f2006-03-06 16:25:59 +0000701
Benny Prijono053f5222006-11-11 16:16:04 +0000702 /* Check if this request should replace existing call */
703 if (replaced_dlg) {
704 pjsip_inv_session *replaced_inv;
705 struct pjsua_call *replaced_call;
706 pjsip_tx_data *tdata;
707
708 /* Get the invite session in the dialog */
709 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
710
711 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000712 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000713
714 /* Notify application */
715 if (pjsua_var.ua_cfg.cb.on_call_replaced)
716 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
717 call_id);
718
719 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
720 call_id));
721
722 /* Answer the new call with 200 response */
723 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
724 if (status == PJ_SUCCESS)
725 status = pjsip_inv_send_msg(inv, tdata);
726
727 if (status != PJ_SUCCESS)
728 pjsua_perror(THIS_FILE, "Error answering session", status);
729
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000730 /* Note that inv may be invalid if 200/OK has caused error in
731 * starting the media.
732 */
Benny Prijono053f5222006-11-11 16:16:04 +0000733
734 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
735 replaced_call->index));
736
737 /* Disconnect replaced invite session */
738 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
739 &tdata);
740 if (status == PJ_SUCCESS && tdata)
741 status = pjsip_inv_send_msg(replaced_inv, tdata);
742
743 if (status != PJ_SUCCESS)
744 pjsua_perror(THIS_FILE, "Error terminating session", status);
745
746
747 } else {
748
Benny Prijonob5388cf2007-01-04 22:45:08 +0000749 /* Notify application if on_incoming_call() is overriden,
750 * otherwise hangup the call with 480
751 */
752 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000753 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000754 } else {
755 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
756 NULL, NULL);
757 }
Benny Prijono053f5222006-11-11 16:16:04 +0000758 }
759
Benny Prijono8b1889b2006-06-06 18:40:40 +0000760
Benny Prijono26ff9062006-02-21 23:47:00 +0000761 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000762 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000763 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000764}
765
766
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000767
768/*
769 * Check if the specified call has active INVITE session and the INVITE
770 * session has not been disconnected.
771 */
772PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
773{
774 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
775 PJ_EINVAL);
776 return pjsua_var.calls[call_id].inv != NULL &&
777 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
778}
779
780
781/*
782 * Check if call has an active media session.
783 */
784PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
785{
786 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
787 PJ_EINVAL);
788 return pjsua_var.calls[call_id].session != NULL;
789}
790
791
Benny Prijono148c9dd2006-09-19 13:37:53 +0000792/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000793pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000794 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000795 pjsua_call **p_call,
796 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000797{
798 enum { MAX_RETRY=50 };
799 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000800 pjsua_call *call = NULL;
801 pj_bool_t has_pjsua_lock = PJ_FALSE;
802 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000803
804 for (retry=0; retry<MAX_RETRY; ++retry) {
805
806 has_pjsua_lock = PJ_FALSE;
807
808 status = PJSUA_TRY_LOCK();
809 if (status != PJ_SUCCESS) {
810 pj_thread_sleep(retry/10);
811 continue;
812 }
813
814 has_pjsua_lock = PJ_TRUE;
815 call = &pjsua_var.calls[call_id];
816
817 if (call->inv == NULL) {
818 PJSUA_UNLOCK();
819 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
820 return PJSIP_ESESSIONTERMINATED;
821 }
822
823 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
824 if (status != PJ_SUCCESS) {
825 PJSUA_UNLOCK();
826 pj_thread_sleep(retry/10);
827 continue;
828 }
829
830 PJSUA_UNLOCK();
831
832 break;
833 }
834
835 if (status != PJ_SUCCESS) {
836 if (has_pjsua_lock == PJ_FALSE)
837 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
838 "(possibly system has deadlocked) in %s",
839 title));
840 else
841 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
842 "(possibly system has deadlocked) in %s",
843 title));
844 return PJ_ETIMEDOUT;
845 }
846
847 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000848 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000849
850 return PJ_SUCCESS;
851}
852
853
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000854/*
855 * Get the conference port identification associated with the call.
856 */
857PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
858{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000859 pjsua_call *call;
860 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000861 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000862 pj_status_t status;
863
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000864 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
865 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000866
Benny Prijonodc752ca2006-09-22 16:55:42 +0000867 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000868 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000869 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000870
871 port_id = call->conf_slot;
872
Benny Prijonodc752ca2006-09-22 16:55:42 +0000873 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000874
875 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000876}
877
878
Benny Prijono148c9dd2006-09-19 13:37:53 +0000879
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880/*
881 * Obtain detail information about the specified call.
882 */
883PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
884 pjsua_call_info *info)
885{
886 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000887 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000888 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000889
890 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
891 PJ_EINVAL);
892
Benny Prijonoac623b32006-07-03 15:19:31 +0000893 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894
Benny Prijonodc752ca2006-09-22 16:55:42 +0000895 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000896 if (status != PJ_SUCCESS) {
897 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000898 }
899
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000900 /* id and role */
901 info->id = call_id;
902 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000903 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000904
905 /* local info */
906 info->local_info.ptr = info->buf_.local_info;
907 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
908 sizeof(info->buf_.local_info));
909
910 /* local contact */
911 info->local_contact.ptr = info->buf_.local_contact;
912 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
913 call->inv->dlg->local.contact->uri,
914 info->local_contact.ptr,
915 sizeof(info->buf_.local_contact));
916
917 /* remote info */
918 info->remote_info.ptr = info->buf_.remote_info;
919 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
920 sizeof(info->buf_.remote_info));
921
922 /* remote contact */
923 if (call->inv->dlg->remote.contact) {
924 int len;
925 info->remote_contact.ptr = info->buf_.remote_contact;
926 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
927 call->inv->dlg->remote.contact->uri,
928 info->remote_contact.ptr,
929 sizeof(info->buf_.remote_contact));
930 if (len < 0) len = 0;
931 info->remote_contact.slen = len;
932 } else {
933 info->remote_contact.slen = 0;
934 }
935
936 /* call id */
937 info->call_id.ptr = info->buf_.call_id;
938 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
939 sizeof(info->buf_.call_id));
940
941 /* state, state_text */
942 info->state = call->inv->state;
943 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
944
945 /* If call is disconnected, set the last_status from the cause code */
946 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
947 /* last_status, last_status_text */
948 info->last_status = call->inv->cause;
949
950 info->last_status_text.ptr = info->buf_.last_status_text;
951 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
952 sizeof(info->buf_.last_status_text));
953 } else {
954 /* last_status, last_status_text */
955 info->last_status = call->last_code;
956
957 info->last_status_text.ptr = info->buf_.last_status_text;
958 pj_strncpy(&info->last_status_text, &call->last_text,
959 sizeof(info->buf_.last_status_text));
960 }
961
962 /* media status and dir */
963 info->media_status = call->media_st;
964 info->media_dir = call->media_dir;
965
966
967 /* conference slot number */
968 info->conf_slot = call->conf_slot;
969
970 /* calculate duration */
971 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
972
973 info->total_duration = call->dis_time;
974 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
975
976 if (call->conn_time.sec) {
977 info->connect_duration = call->dis_time;
978 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
979 }
980
981 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
982
983 pj_gettimeofday(&info->total_duration);
984 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
985
986 pj_gettimeofday(&info->connect_duration);
987 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
988
989 } else {
990 pj_gettimeofday(&info->total_duration);
991 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
992 }
993
Benny Prijonodc752ca2006-09-22 16:55:42 +0000994 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000995
996 return PJ_SUCCESS;
997}
998
999
1000/*
1001 * Attach application specific data to the call.
1002 */
1003PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1004 void *user_data)
1005{
1006 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1007 PJ_EINVAL);
1008 pjsua_var.calls[call_id].user_data = user_data;
1009
1010 return PJ_SUCCESS;
1011}
1012
1013
1014/*
1015 * Get user data attached to the call.
1016 */
1017PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1018{
1019 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1020 NULL);
1021 return pjsua_var.calls[call_id].user_data;
1022}
1023
1024
1025/*
1026 * Send response to incoming INVITE request.
1027 */
1028PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1029 unsigned code,
1030 const pj_str_t *reason,
1031 const pjsua_msg_data *msg_data)
1032{
1033 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001034 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001035 pjsip_tx_data *tdata;
1036 pj_status_t status;
1037
1038 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1039 PJ_EINVAL);
1040
Benny Prijonodc752ca2006-09-22 16:55:42 +00001041 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001042 if (status != PJ_SUCCESS)
1043 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001044
Benny Prijono2e507c22006-06-23 15:04:11 +00001045 if (call->res_time.sec == 0)
1046 pj_gettimeofday(&call->res_time);
1047
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001048 if (reason && reason->slen == 0)
1049 reason = NULL;
1050
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001051 /* Create response message */
1052 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1053 if (status != PJ_SUCCESS) {
1054 pjsua_perror(THIS_FILE, "Error creating response",
1055 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001056 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001057 return status;
1058 }
1059
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001060 /* Call might have been disconnected if application is answering with
1061 * 200/OK and the media failed to start.
1062 */
1063 if (call->inv == NULL) {
1064 pjsip_dlg_dec_lock(dlg);
1065 return PJSIP_ESESSIONTERMINATED;
1066 }
1067
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001068 /* Add additional headers etc */
1069 pjsua_process_msg_data( tdata, msg_data);
1070
1071 /* Send the message */
1072 status = pjsip_inv_send_msg(call->inv, tdata);
1073 if (status != PJ_SUCCESS)
1074 pjsua_perror(THIS_FILE, "Error sending response",
1075 status);
1076
Benny Prijonodc752ca2006-09-22 16:55:42 +00001077 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001078
1079 return status;
1080}
1081
1082
1083/*
1084 * Hangup call by using method that is appropriate according to the
1085 * call state.
1086 */
1087PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1088 unsigned code,
1089 const pj_str_t *reason,
1090 const pjsua_msg_data *msg_data)
1091{
1092 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001093 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001094 pj_status_t status;
1095 pjsip_tx_data *tdata;
1096
1097
Benny Prijono148c9dd2006-09-19 13:37:53 +00001098 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1099 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1100 call_id));
1101 }
1102
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001103 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1104 PJ_EINVAL);
1105
Benny Prijonodc752ca2006-09-22 16:55:42 +00001106 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001107 if (status != PJ_SUCCESS)
1108 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001109
1110 if (code==0) {
1111 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1112 code = PJSIP_SC_OK;
1113 else if (call->inv->role == PJSIP_ROLE_UAS)
1114 code = PJSIP_SC_DECLINE;
1115 else
1116 code = PJSIP_SC_REQUEST_TERMINATED;
1117 }
1118
1119 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1120 if (status != PJ_SUCCESS) {
1121 pjsua_perror(THIS_FILE,
1122 "Failed to create end session message",
1123 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001124 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001125 return status;
1126 }
1127
1128 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1129 * as p_tdata when INVITE transaction has not been answered
1130 * with any provisional responses.
1131 */
1132 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001133 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001134 return PJ_SUCCESS;
1135 }
1136
1137 /* Add additional headers etc */
1138 pjsua_process_msg_data( tdata, msg_data);
1139
1140 /* Send the message */
1141 status = pjsip_inv_send_msg(call->inv, tdata);
1142 if (status != PJ_SUCCESS) {
1143 pjsua_perror(THIS_FILE,
1144 "Failed to send end session message",
1145 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001146 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001147 return status;
1148 }
1149
Benny Prijonodc752ca2006-09-22 16:55:42 +00001150 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001151
1152 return PJ_SUCCESS;
1153}
1154
1155
1156/*
1157 * Put the specified call on hold.
1158 */
1159PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1160 const pjsua_msg_data *msg_data)
1161{
1162 pjmedia_sdp_session *sdp;
1163 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001164 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001165 pjsip_tx_data *tdata;
1166 pj_status_t status;
1167
1168 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1169 PJ_EINVAL);
1170
Benny Prijonodc752ca2006-09-22 16:55:42 +00001171 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001172 if (status != PJ_SUCCESS)
1173 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001174
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001175
1176 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1177 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001178 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001179 return PJSIP_ESESSIONSTATE;
1180 }
1181
1182 status = create_inactive_sdp(call, &sdp);
1183 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001184 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 return status;
1186 }
1187
1188 /* Create re-INVITE with new offer */
1189 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1190 if (status != PJ_SUCCESS) {
1191 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001192 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001193 return status;
1194 }
1195
1196 /* Add additional headers etc */
1197 pjsua_process_msg_data( tdata, msg_data);
1198
1199 /* Send the request */
1200 status = pjsip_inv_send_msg( call->inv, tdata);
1201 if (status != PJ_SUCCESS) {
1202 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001203 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001204 return status;
1205 }
1206
Benny Prijonodc752ca2006-09-22 16:55:42 +00001207 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208
1209 return PJ_SUCCESS;
1210}
1211
1212
1213/*
1214 * Send re-INVITE (to release hold).
1215 */
1216PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1217 pj_bool_t unhold,
1218 const pjsua_msg_data *msg_data)
1219{
1220 pjmedia_sdp_session *sdp;
1221 pjsip_tx_data *tdata;
1222 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001223 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224 pj_status_t status;
1225
1226
1227 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1228 PJ_EINVAL);
1229
Benny Prijonodc752ca2006-09-22 16:55:42 +00001230 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231 if (status != PJ_SUCCESS)
1232 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233
1234 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1235 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001236 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237 return PJSIP_ESESSIONSTATE;
1238 }
1239
Benny Prijono667952e2007-04-02 19:27:54 +00001240 /* Init media channel */
1241 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1242 if (status != PJ_SUCCESS) {
1243 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1244 pjsip_dlg_dec_lock(dlg);
1245 return PJSIP_ESESSIONSTATE;
1246 }
1247
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001249 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001250 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001251 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 if (status != PJ_SUCCESS) {
1253 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1254 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001255 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001256 return status;
1257 }
1258
1259 /* Create re-INVITE with new offer */
1260 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1261 if (status != PJ_SUCCESS) {
1262 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001263 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 return status;
1265 }
1266
1267 /* Add additional headers etc */
1268 pjsua_process_msg_data( tdata, msg_data);
1269
1270 /* Send the request */
1271 status = pjsip_inv_send_msg( call->inv, tdata);
1272 if (status != PJ_SUCCESS) {
1273 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001274 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001275 return status;
1276 }
1277
Benny Prijonodc752ca2006-09-22 16:55:42 +00001278 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279
1280 return PJ_SUCCESS;
1281}
1282
1283
1284/*
1285 * Initiate call transfer to the specified address.
1286 */
1287PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1288 const pj_str_t *dest,
1289 const pjsua_msg_data *msg_data)
1290{
1291 pjsip_evsub *sub;
1292 pjsip_tx_data *tdata;
1293 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001294 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001295 pjsip_generic_string_hdr *gs_hdr;
1296 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001297 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 pj_status_t status;
1299
1300
1301 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1302 PJ_EINVAL);
1303
Benny Prijonodc752ca2006-09-22 16:55:42 +00001304 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001305 if (status != PJ_SUCCESS)
1306 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001308
Benny Prijonod524e822006-09-22 12:48:18 +00001309 /* Create xfer client subscription. */
1310 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001311 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001312
1313 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001314 if (status != PJ_SUCCESS) {
1315 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001316 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001317 return status;
1318 }
1319
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001320 /* Associate this call with the client subscription */
1321 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1322
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323 /*
1324 * Create REFER request.
1325 */
1326 status = pjsip_xfer_initiate(sub, dest, &tdata);
1327 if (status != PJ_SUCCESS) {
1328 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001329 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330 return status;
1331 }
1332
Benny Prijono053f5222006-11-11 16:16:04 +00001333 /* Add Referred-By header */
1334 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1335 &dlg->local.info_str);
1336 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1337
1338
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001339 /* Add additional headers etc */
1340 pjsua_process_msg_data( tdata, msg_data);
1341
1342 /* Send. */
1343 status = pjsip_xfer_send_request(sub, tdata);
1344 if (status != PJ_SUCCESS) {
1345 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001346 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001347 return status;
1348 }
1349
1350 /* For simplicity (that's what this program is intended to be!),
1351 * leave the original invite session as it is. More advanced application
1352 * may want to hold the INVITE, or terminate the invite, or whatever.
1353 */
1354
Benny Prijonodc752ca2006-09-22 16:55:42 +00001355 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356
1357 return PJ_SUCCESS;
1358
1359}
1360
1361
1362/*
Benny Prijono053f5222006-11-11 16:16:04 +00001363 * Initiate attended call transfer to the specified address.
1364 */
1365PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1366 pjsua_call_id dest_call_id,
1367 unsigned options,
1368 const pjsua_msg_data *msg_data)
1369{
1370 pjsua_call *dest_call;
1371 pjsip_dialog *dest_dlg;
1372 char str_dest_buf[512];
1373 pj_str_t str_dest;
1374 int len;
1375 pjsip_uri *uri;
1376 pj_status_t status;
1377
1378
1379 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1380 PJ_EINVAL);
1381 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1382 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1383 PJ_EINVAL);
1384
1385 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1386 &dest_call, &dest_dlg);
1387 if (status != PJ_SUCCESS)
1388 return status;
1389
1390 /*
1391 * Create REFER destination URI with Replaces field.
1392 */
1393
1394 /* Make sure we have sufficient buffer's length */
1395 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1396 dest_dlg->call_id->id.slen +
1397 dest_dlg->remote.info->tag.slen +
1398 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001399 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001400
1401 /* Print URI */
1402 str_dest_buf[0] = '<';
1403 str_dest.slen = 1;
1404
Benny Prijonoa1e69682007-05-11 15:14:34 +00001405 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001406 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1407 str_dest_buf+1, sizeof(str_dest_buf)-1);
1408 if (len < 0)
1409 return PJSIP_EURITOOLONG;
1410
1411 str_dest.slen += len;
1412
1413
1414 /* Build the URI */
1415 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1416 sizeof(str_dest_buf) - str_dest.slen,
1417 "?%s"
1418 "Replaces=%.*s"
1419 "%%3Bto-tag%%3D%.*s"
1420 "%%3Bfrom-tag%%3D%.*s>",
1421 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1422 "" : "Require=replaces&"),
1423 (int)dest_dlg->call_id->id.slen,
1424 dest_dlg->call_id->id.ptr,
1425 (int)dest_dlg->remote.info->tag.slen,
1426 dest_dlg->remote.info->tag.ptr,
1427 (int)dest_dlg->local.info->tag.slen,
1428 dest_dlg->local.info->tag.ptr);
1429
1430 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1431 PJSIP_EURITOOLONG);
1432
1433 str_dest.ptr = str_dest_buf;
1434 str_dest.slen += len;
1435
1436 pjsip_dlg_dec_lock(dest_dlg);
1437
1438 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1439}
1440
1441
1442/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 * Send DTMF digits to remote using RFC 2833 payload formats.
1444 */
1445PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1446 const pj_str_t *digits)
1447{
1448 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001449 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001450 pj_status_t status;
1451
1452 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1453 PJ_EINVAL);
1454
Benny Prijonodc752ca2006-09-22 16:55:42 +00001455 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001456 if (status != PJ_SUCCESS)
1457 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001458
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459 if (!call->session) {
1460 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001461 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001462 return PJ_EINVALIDOP;
1463 }
1464
1465 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1466
Benny Prijonodc752ca2006-09-22 16:55:42 +00001467 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468
1469 return status;
1470}
1471
1472
1473/**
1474 * Send instant messaging inside INVITE session.
1475 */
1476PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1477 const pj_str_t *mime_type,
1478 const pj_str_t *content,
1479 const pjsua_msg_data *msg_data,
1480 void *user_data)
1481{
1482 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001483 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001484 const pj_str_t mime_text_plain = pj_str("text/plain");
1485 pjsip_media_type ctype;
1486 pjsua_im_data *im_data;
1487 pjsip_tx_data *tdata;
1488 pj_status_t status;
1489
1490
1491 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1492 PJ_EINVAL);
1493
Benny Prijonodc752ca2006-09-22 16:55:42 +00001494 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001495 if (status != PJ_SUCCESS)
1496 return status;
1497
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001498 /* Set default media type if none is specified */
1499 if (mime_type == NULL) {
1500 mime_type = &mime_text_plain;
1501 }
1502
1503 /* Create request message. */
1504 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1505 -1, &tdata);
1506 if (status != PJ_SUCCESS) {
1507 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1508 goto on_return;
1509 }
1510
1511 /* Add accept header. */
1512 pjsip_msg_add_hdr( tdata->msg,
1513 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1514
1515 /* Parse MIME type */
1516 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1517
1518 /* Create "text/plain" message body. */
1519 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1520 &ctype.subtype, content);
1521 if (tdata->msg->body == NULL) {
1522 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1523 pjsip_tx_data_dec_ref(tdata);
1524 goto on_return;
1525 }
1526
1527 /* Add additional headers etc */
1528 pjsua_process_msg_data( tdata, msg_data);
1529
1530 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001531 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001532 im_data->acc_id = call->acc_id;
1533 im_data->call_id = call_id;
1534 im_data->to = call->inv->dlg->remote.info_str;
1535 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1536 im_data->user_data = user_data;
1537
1538
1539 /* Send the request. */
1540 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1541 pjsua_var.mod.id, im_data);
1542 if (status != PJ_SUCCESS) {
1543 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1544 goto on_return;
1545 }
1546
1547on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001548 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001549 return status;
1550}
1551
1552
1553/*
1554 * Send IM typing indication inside INVITE session.
1555 */
1556PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1557 pj_bool_t is_typing,
1558 const pjsua_msg_data*msg_data)
1559{
1560 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001561 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001562 pjsip_tx_data *tdata;
1563 pj_status_t status;
1564
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1566 PJ_EINVAL);
1567
Benny Prijonodc752ca2006-09-22 16:55:42 +00001568 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001569 if (status != PJ_SUCCESS)
1570 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 /* Create request message. */
1573 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1574 -1, &tdata);
1575 if (status != PJ_SUCCESS) {
1576 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1577 goto on_return;
1578 }
1579
1580 /* Create "application/im-iscomposing+xml" msg body. */
1581 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1582 NULL, NULL, -1);
1583
1584 /* Add additional headers etc */
1585 pjsua_process_msg_data( tdata, msg_data);
1586
1587 /* Send the request. */
1588 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1589 if (status != PJ_SUCCESS) {
1590 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1591 goto on_return;
1592 }
1593
1594on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001595 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596 return status;
1597}
1598
1599
1600/*
1601 * Terminate all calls.
1602 */
1603PJ_DEF(void) pjsua_call_hangup_all(void)
1604{
1605 unsigned i;
1606
1607 PJSUA_LOCK();
1608
1609 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1610 if (pjsua_var.calls[i].inv)
1611 pjsua_call_hangup(i, 0, NULL, NULL);
1612 }
1613
1614 PJSUA_UNLOCK();
1615}
1616
1617
Benny Prijono627cbb42007-09-25 20:48:49 +00001618const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619{
1620 if (val < 1000) {
1621 pj_ansi_sprintf(buf, "%d", val);
1622 } else if (val < 1000000) {
1623 pj_ansi_sprintf(buf, "%d.%dK",
1624 val / 1000,
1625 (val % 1000) / 100);
1626 } else {
1627 pj_ansi_sprintf(buf, "%d.%02dM",
1628 val / 1000000,
1629 (val % 1000000) / 10000);
1630 }
1631
1632 return buf;
1633}
1634
1635
1636/* Dump media session */
1637static void dump_media_session(const char *indent,
1638 char *buf, unsigned maxlen,
1639 pjmedia_session *session)
1640{
1641 unsigned i;
1642 char *p = buf, *end = buf+maxlen;
1643 int len;
1644 pjmedia_session_info info;
1645
1646 pjmedia_session_get_info(session, &info);
1647
1648 for (i=0; i<info.stream_cnt; ++i) {
1649 pjmedia_rtcp_stat stat;
1650 const char *rem_addr;
1651 int rem_port;
1652 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001653 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001654 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001655 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656
1657 pjmedia_session_get_stream_stat(session, i, &stat);
1658 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1659 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1660
1661 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1662 dir = "sendonly";
1663 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1664 dir = "recvonly";
1665 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1666 dir = "sendrecv";
1667 else
1668 dir = "inactive";
1669
1670
1671 len = pj_ansi_snprintf(buf, end-p,
1672 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1673 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001674 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675 info.stream_info[i].fmt.encoding_name.ptr,
1676 info.stream_info[i].fmt.clock_rate / 1000,
1677 dir,
1678 rem_addr, rem_port);
1679 if (len < 1 || len > end-p) {
1680 *p = '\0';
1681 return;
1682 }
1683
1684 p += len;
1685 *p++ = '\n';
1686 *p = '\0';
1687
1688 if (stat.rx.update_cnt == 0)
1689 strcpy(last_update, "never");
1690 else {
1691 pj_gettimeofday(&now);
1692 PJ_TIME_VAL_SUB(now, stat.rx.update);
1693 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1694 now.sec / 3600,
1695 (now.sec % 3600) / 60,
1696 now.sec % 60,
1697 now.msec);
1698 }
1699
Benny Prijono80019eb2006-08-07 13:22:23 +00001700 pj_gettimeofday(&media_duration);
1701 PJ_TIME_VAL_SUB(media_duration, stat.start);
1702 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1703 media_duration.msec = 1;
1704
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001705 len = pj_ansi_snprintf(p, end-p,
1706 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001707 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001708 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1709 "%s (msec) min avg max last\n"
1710 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1711 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1712 indent, info.stream_info[i].fmt.pt,
1713 last_update,
1714 indent,
1715 good_number(packets, stat.rx.pkt),
1716 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001717 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001718 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001719 good_number(avg_ipbps, (stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001720 indent,
1721 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001722 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001723 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001724 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001726 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727 indent, indent,
1728 stat.rx.loss_period.min / 1000.0,
1729 stat.rx.loss_period.avg / 1000.0,
1730 stat.rx.loss_period.max / 1000.0,
1731 stat.rx.loss_period.last / 1000.0,
1732 indent,
1733 stat.rx.jitter.min / 1000.0,
1734 stat.rx.jitter.avg / 1000.0,
1735 stat.rx.jitter.max / 1000.0,
1736 stat.rx.jitter.last / 1000.0,
1737 ""
1738 );
1739
1740 if (len < 1 || len > end-p) {
1741 *p = '\0';
1742 return;
1743 }
1744
1745 p += len;
1746 *p++ = '\n';
1747 *p = '\0';
1748
1749 if (stat.tx.update_cnt == 0)
1750 strcpy(last_update, "never");
1751 else {
1752 pj_gettimeofday(&now);
1753 PJ_TIME_VAL_SUB(now, stat.tx.update);
1754 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1755 now.sec / 3600,
1756 (now.sec % 3600) / 60,
1757 now.sec % 60,
1758 now.msec);
1759 }
1760
1761 len = pj_ansi_snprintf(p, end-p,
1762 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001763 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001764 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1765 "%s (msec) min avg max last\n"
1766 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1767 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1768 indent,
1769 info.stream_info[i].tx_pt,
1770 info.stream_info[i].param->info.frm_ptime *
1771 info.stream_info[i].param->setting.frm_per_pkt,
1772 last_update,
1773
1774 indent,
1775 good_number(packets, stat.tx.pkt),
1776 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001777 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001778 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001779 good_number(avg_ipbps, (stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001780
1781 indent,
1782 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001783 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001784 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001785 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001786 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001787 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001788
1789 indent, indent,
1790 stat.tx.loss_period.min / 1000.0,
1791 stat.tx.loss_period.avg / 1000.0,
1792 stat.tx.loss_period.max / 1000.0,
1793 stat.tx.loss_period.last / 1000.0,
1794 indent,
1795 stat.tx.jitter.min / 1000.0,
1796 stat.tx.jitter.avg / 1000.0,
1797 stat.tx.jitter.max / 1000.0,
1798 stat.tx.jitter.last / 1000.0,
1799 ""
1800 );
1801
1802 if (len < 1 || len > end-p) {
1803 *p = '\0';
1804 return;
1805 }
1806
1807 p += len;
1808 *p++ = '\n';
1809 *p = '\0';
1810
1811 len = pj_ansi_snprintf(p, end-p,
1812 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1813 indent,
1814 stat.rtt.min / 1000.0,
1815 stat.rtt.avg / 1000.0,
1816 stat.rtt.max / 1000.0,
1817 stat.rtt.last / 1000.0
1818 );
1819 if (len < 1 || len > end-p) {
1820 *p = '\0';
1821 return;
1822 }
1823
1824 p += len;
1825 *p++ = '\n';
1826 *p = '\0';
1827 }
1828}
1829
1830
1831/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00001832void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001833 int call_id,
1834 char *buf, pj_size_t size)
1835{
1836 int len;
1837 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1838 pjsip_dialog *dlg = inv->dlg;
1839 char userinfo[128];
1840
1841 /* Dump invite sesion info. */
1842
1843 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1844 if (len < 1)
1845 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1846 else
1847 userinfo[len] = '\0';
1848
1849 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1850 title,
1851 pjsip_inv_state_name(inv->state),
1852 userinfo);
1853 if (len < 1 || len >= (int)size) {
1854 pj_ansi_strcpy(buf, "<--uri too long-->");
1855 len = 18;
1856 } else
1857 buf[len] = '\0';
1858}
1859
1860
1861/*
1862 * Dump call and media statistics to string.
1863 */
1864PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1865 pj_bool_t with_media,
1866 char *buffer,
1867 unsigned maxlen,
1868 const char *indent)
1869{
1870 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001871 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872 pj_time_val duration, res_delay, con_delay;
1873 char tmp[128];
1874 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001875 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001876 int len;
1877
1878 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1879 PJ_EINVAL);
1880
Benny Prijonodc752ca2006-09-22 16:55:42 +00001881 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001882 if (status != PJ_SUCCESS)
1883 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001884
1885 *buffer = '\0';
1886 p = buffer;
1887 end = buffer + maxlen;
1888 len = 0;
1889
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001890 print_call(indent, call_id, tmp, sizeof(tmp));
1891
1892 len = pj_ansi_strlen(tmp);
1893 pj_ansi_strcpy(buffer, tmp);
1894
1895 p += len;
1896 *p++ = '\r';
1897 *p++ = '\n';
1898
1899 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001900 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001901 pj_gettimeofday(&duration);
1902 PJ_TIME_VAL_SUB(duration, call->conn_time);
1903 con_delay = call->conn_time;
1904 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1905 } else {
1906 duration.sec = duration.msec = 0;
1907 con_delay.sec = con_delay.msec = 0;
1908 }
1909
1910 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001911 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001912 res_delay = call->res_time;
1913 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1914 } else {
1915 res_delay.sec = res_delay.msec = 0;
1916 }
1917
1918 /* Print duration */
1919 len = pj_ansi_snprintf(p, end-p,
1920 "%s Call time: %02dh:%02dm:%02ds, "
1921 "1st res in %d ms, conn in %dms",
1922 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001923 (int)(duration.sec / 3600),
1924 (int)((duration.sec % 3600)/60),
1925 (int)(duration.sec % 60),
1926 (int)PJ_TIME_VAL_MSEC(res_delay),
1927 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001928
1929 if (len > 0 && len < end-p) {
1930 p += len;
1931 *p++ = '\n';
1932 *p = '\0';
1933 }
1934
1935 /* Dump session statistics */
1936 if (with_media && call->session)
1937 dump_media_session(indent, p, end-p, call->session);
1938
Benny Prijonodc752ca2006-09-22 16:55:42 +00001939 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940
1941 return PJ_SUCCESS;
1942}
1943
1944
1945/*
Benny Prijono84126ab2006-02-09 09:30:09 +00001946 * This callback receives notification from invite session when the
1947 * session state has changed.
1948 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001949static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1950 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001951{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001952 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001953
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001954 PJSUA_LOCK();
1955
Benny Prijonoa1e69682007-05-11 15:14:34 +00001956 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001957
1958 if (!call) {
1959 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001960 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961 }
1962
Benny Prijonoe21e7842006-04-09 16:46:05 +00001963
1964 /* Get call times */
1965 switch (inv->state) {
1966 case PJSIP_INV_STATE_EARLY:
1967 case PJSIP_INV_STATE_CONNECTING:
1968 if (call->res_time.sec == 0)
1969 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00001970 call->last_code = (pjsip_status_code)
1971 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001972 pj_strncpy(&call->last_text,
1973 &e->body.tsx_state.tsx->status_text,
1974 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001975 break;
1976 case PJSIP_INV_STATE_CONFIRMED:
1977 pj_gettimeofday(&call->conn_time);
1978 break;
1979 case PJSIP_INV_STATE_DISCONNECTED:
1980 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001981 if (call->res_time.sec == 0)
1982 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001983 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00001984 call->last_code = (pjsip_status_code)
1985 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001986 pj_strncpy(&call->last_text,
1987 &e->body.tsx_state.tsx->status_text,
1988 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001989 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001990 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001991 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00001992 call->last_code = (pjsip_status_code)
1993 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001994 pj_strncpy(&call->last_text,
1995 &e->body.tsx_state.tsx->status_text,
1996 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001997 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001998 }
1999
Benny Prijono26ff9062006-02-21 23:47:00 +00002000 /* If this is an outgoing INVITE that was created because of
2001 * REFER/transfer, send NOTIFY to transferer.
2002 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002003 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002004 int st_code = -1;
2005 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2006
2007
Benny Prijonoa91a0032006-02-26 21:23:45 +00002008 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002009 case PJSIP_INV_STATE_NULL:
2010 case PJSIP_INV_STATE_CALLING:
2011 /* Do nothing */
2012 break;
2013
2014 case PJSIP_INV_STATE_EARLY:
2015 case PJSIP_INV_STATE_CONNECTING:
2016 st_code = e->body.tsx_state.tsx->status_code;
2017 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2018 break;
2019
2020 case PJSIP_INV_STATE_CONFIRMED:
2021 /* When state is confirmed, send the final 200/OK and terminate
2022 * subscription.
2023 */
2024 st_code = e->body.tsx_state.tsx->status_code;
2025 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2026 break;
2027
2028 case PJSIP_INV_STATE_DISCONNECTED:
2029 st_code = e->body.tsx_state.tsx->status_code;
2030 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2031 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002032
Benny Prijono8b1889b2006-06-06 18:40:40 +00002033 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002034 /* Nothing to do. Just to keep gcc from complaining about
2035 * unused enums.
2036 */
2037 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002038 }
2039
2040 if (st_code != -1) {
2041 pjsip_tx_data *tdata;
2042 pj_status_t status;
2043
Benny Prijonoa91a0032006-02-26 21:23:45 +00002044 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002045 ev_state, st_code,
2046 NULL, &tdata);
2047 if (status != PJ_SUCCESS) {
2048 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2049 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002050 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002051 if (status != PJ_SUCCESS) {
2052 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2053 }
2054 }
2055 }
2056 }
2057
Benny Prijono84126ab2006-02-09 09:30:09 +00002058
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059 if (pjsua_var.ua_cfg.cb.on_call_state)
2060 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002061
2062 /* call->inv may be NULL now */
2063
Benny Prijono84126ab2006-02-09 09:30:09 +00002064 /* Destroy media session when invite session is disconnected. */
2065 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002066
Benny Prijonoa91a0032006-02-26 21:23:45 +00002067 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002068
Benny Prijono275fd682006-03-22 11:59:11 +00002069 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002070 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002071
Benny Prijono105217f2006-03-06 16:25:59 +00002072 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002073 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002075
2076 /* Reset call */
2077 reset_call(call->index);
2078
Benny Prijono84126ab2006-02-09 09:30:09 +00002079 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002080
2081 PJSUA_UNLOCK();
2082}
2083
2084/*
2085 * This callback is called by invite session framework when UAC session
2086 * has forked.
2087 */
2088static void pjsua_call_on_forked( pjsip_inv_session *inv,
2089 pjsip_event *e)
2090{
2091 PJ_UNUSED_ARG(inv);
2092 PJ_UNUSED_ARG(e);
2093
2094 PJ_TODO(HANDLE_FORKED_DIALOG);
2095}
2096
2097
2098/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002099 * Disconnect call upon error.
2100 */
2101static void call_disconnect( pjsip_inv_session *inv,
2102 int code )
2103{
2104 pjsip_tx_data *tdata;
2105 pj_status_t status;
2106
2107 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2108 if (status == PJ_SUCCESS)
2109 pjsip_inv_send_msg(inv, tdata);
2110}
2111
2112/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113 * Callback to be called when SDP offer/answer negotiation has just completed
2114 * in the session. This function will start/update media if negotiation
2115 * has succeeded.
2116 */
2117static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2118 pj_status_t status)
2119{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002120 pjsua_call *call;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002121 const pjmedia_sdp_session *local_sdp;
2122 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002123
2124 PJSUA_LOCK();
2125
Benny Prijonoa1e69682007-05-11 15:14:34 +00002126 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002127
2128 if (status != PJ_SUCCESS) {
2129
2130 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2131
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002132 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002133 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002134
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002135 /* Disconnect call if we're not in the middle of initializing an
2136 * UAS dialog and if this is not a re-INVITE
2137 */
2138 if (inv->state != PJSIP_INV_STATE_NULL &&
2139 inv->state != PJSIP_INV_STATE_CONFIRMED)
2140 {
2141 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2142 }
2143
2144 PJSUA_UNLOCK();
2145 return;
2146 }
2147
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002148
2149 /* Get local and remote SDP */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002150 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2151 if (status != PJ_SUCCESS) {
2152 pjsua_perror(THIS_FILE,
2153 "Unable to retrieve currently active local SDP",
2154 status);
2155 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2156 PJSUA_UNLOCK();
2157 return;
2158 }
2159
2160
2161 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2162 if (status != PJ_SUCCESS) {
2163 pjsua_perror(THIS_FILE,
2164 "Unable to retrieve currently active remote SDP",
2165 status);
2166 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2167 PJSUA_UNLOCK();
2168 return;
2169 }
2170
Benny Prijonoc97608e2007-03-23 16:34:20 +00002171 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002172 if (status != PJ_SUCCESS) {
2173 pjsua_perror(THIS_FILE, "Unable to create media session",
2174 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002175 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002176 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177 PJSUA_UNLOCK();
2178 return;
2179 }
2180
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002181
2182 /* Call application callback, if any */
2183 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2184 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2185
2186
2187 PJSUA_UNLOCK();
2188}
2189
2190
2191/*
2192 * Create inactive SDP for call hold.
2193 */
2194static pj_status_t create_inactive_sdp(pjsua_call *call,
2195 pjmedia_sdp_session **p_answer)
2196{
2197 pj_status_t status;
2198 pjmedia_sdp_conn *conn;
2199 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002200 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002201 pjmedia_sdp_session *sdp;
2202
Benny Prijono617c5bc2007-04-02 19:51:21 +00002203 /* Get media socket info */
2204 pjmedia_transport_get_info(call->med_tp, &skinfo);
2205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002206 /* Create new offer */
2207 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002208 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209 if (status != PJ_SUCCESS) {
2210 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2211 return status;
2212 }
2213
2214 /* Get SDP media connection line */
2215 conn = sdp->media[0]->conn;
2216 if (!conn)
2217 conn = sdp->conn;
2218
2219 /* Modify address */
2220 conn->addr = pj_str("0.0.0.0");
2221
2222 /* Remove existing directions attributes */
2223 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2224 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2225 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2226 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2227
2228 /* Add inactive attribute */
2229 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2230 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2231
2232 *p_answer = sdp;
2233
2234 return status;
2235}
2236
2237
2238/*
2239 * Called when session received new offer.
2240 */
2241static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2242 const pjmedia_sdp_session *offer)
2243{
2244 const char *remote_state;
2245 pjsua_call *call;
2246 pjmedia_sdp_conn *conn;
2247 pjmedia_sdp_session *answer;
2248 pj_bool_t is_remote_active;
2249 pj_status_t status;
2250
2251 PJSUA_LOCK();
2252
Benny Prijonoa1e69682007-05-11 15:14:34 +00002253 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002254
2255 /*
2256 * See if remote is offering active media (i.e. not on-hold)
2257 */
2258 is_remote_active = PJ_TRUE;
2259
2260 conn = offer->media[0]->conn;
2261 if (!conn)
2262 conn = offer->conn;
2263
2264 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2265 pj_strcmp2(&conn->addr, "0")==0)
2266 {
2267 is_remote_active = PJ_FALSE;
2268
2269 }
2270 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2271 {
2272 is_remote_active = PJ_FALSE;
2273 }
2274
2275 remote_state = (is_remote_active ? "active" : "inactive");
2276
2277 /* Supply candidate answer */
2278 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2279 PJ_LOG(4,(THIS_FILE,
2280 "Call %d: RX new media offer, creating inactive SDP "
2281 "(media in offer is %s)", call->index, remote_state));
2282 status = create_inactive_sdp( call, &answer );
2283 } else {
Benny Prijono667952e2007-04-02 19:27:54 +00002284
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002285 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2286 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002287
2288 /* Init media channel */
Benny Prijonoc9f6ea72007-05-23 07:12:23 +00002289 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono667952e2007-04-02 19:27:54 +00002290 if (status != PJ_SUCCESS) {
2291 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2292 PJSUA_UNLOCK();
2293 return;
2294 }
2295
Benny Prijonoc97608e2007-03-23 16:34:20 +00002296 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002297 }
2298
2299 if (status != PJ_SUCCESS) {
2300 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2301 PJSUA_UNLOCK();
2302 return;
2303 }
2304
2305 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2306 if (status != PJ_SUCCESS) {
2307 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2308 PJSUA_UNLOCK();
2309 return;
2310 }
2311
2312 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002313}
2314
2315
2316/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002317 * Called to generate new offer.
2318 */
2319static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2320 pjmedia_sdp_session **offer)
2321{
2322 pjsua_call *call;
2323 pj_status_t status;
2324
2325 PJSUA_LOCK();
2326
2327 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2328
2329 /* See if we've put call on hold. */
2330 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2331 PJ_LOG(4,(THIS_FILE,
2332 "Call %d: call is on-hold locally, creating inactive SDP ",
2333 call->index));
2334 status = create_inactive_sdp( call, offer );
2335 } else {
2336
2337 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2338 call->index));
2339
2340 /* Init media channel */
2341 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
2342 if (status != PJ_SUCCESS) {
2343 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2344 PJSUA_UNLOCK();
2345 return;
2346 }
2347
2348 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, offer);
2349 }
2350
2351 if (status != PJ_SUCCESS) {
2352 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2353 PJSUA_UNLOCK();
2354 return;
2355 }
2356
2357
2358 PJSUA_UNLOCK();
2359}
2360
2361
2362/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002363 * Callback called by event framework when the xfer subscription state
2364 * has changed.
2365 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002366static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2367{
2368
2369 PJ_UNUSED_ARG(event);
2370
2371 /*
2372 * When subscription is accepted (got 200/OK to REFER), check if
2373 * subscription suppressed.
2374 */
2375 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2376
2377 pjsip_rx_data *rdata;
2378 pjsip_generic_string_hdr *refer_sub;
2379 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2380 pjsua_call *call;
2381
Benny Prijonoa1e69682007-05-11 15:14:34 +00002382 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002383
2384 /* Must be receipt of response message */
2385 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2386 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2387 rdata = event->body.tsx_state.src.rdata;
2388
2389 /* Find Refer-Sub header */
2390 refer_sub = (pjsip_generic_string_hdr*)
2391 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2392 &REFER_SUB, NULL);
2393
2394 /* Check if subscription is suppressed */
2395 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2396 /* Since no subscription is desired, assume that call has been
2397 * transfered successfully.
2398 */
2399 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2400 const pj_str_t ACCEPTED = { "Accepted", 8 };
2401 pj_bool_t cont = PJ_FALSE;
2402 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2403 200,
2404 &ACCEPTED,
2405 PJ_TRUE,
2406 &cont);
2407 }
2408
2409 /* Yes, subscription is suppressed.
2410 * Terminate our subscription now.
2411 */
2412 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2413 "event subcription..."));
2414 pjsip_evsub_terminate(sub, PJ_TRUE);
2415
2416 } else {
2417 /* Notify application about call transfer progress.
2418 * Initially notify with 100/Accepted status.
2419 */
2420 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2421 const pj_str_t ACCEPTED = { "Accepted", 8 };
2422 pj_bool_t cont = PJ_FALSE;
2423 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2424 100,
2425 &ACCEPTED,
2426 PJ_FALSE,
2427 &cont);
2428 }
2429 }
2430 }
2431 /*
2432 * On incoming NOTIFY, notify application about call transfer progress.
2433 */
2434 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2435 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2436 {
2437 pjsua_call *call;
2438 pjsip_msg *msg;
2439 pjsip_msg_body *body;
2440 pjsip_status_line status_line;
2441 pj_bool_t is_last;
2442 pj_bool_t cont;
2443 pj_status_t status;
2444
Benny Prijonoa1e69682007-05-11 15:14:34 +00002445 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002446
2447 /* When subscription is terminated, clear the xfer_sub member of
2448 * the inv_data.
2449 */
2450 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2451 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2452 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2453
2454 }
2455
2456 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2457 /* Application is not interested with call progress status */
2458 return;
2459 }
2460
2461 /* This better be a NOTIFY request */
2462 if (event->type == PJSIP_EVENT_TSX_STATE &&
2463 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2464 {
2465 pjsip_rx_data *rdata;
2466
2467 rdata = event->body.tsx_state.src.rdata;
2468
2469 /* Check if there's body */
2470 msg = rdata->msg_info.msg;
2471 body = msg->body;
2472 if (!body) {
2473 PJ_LOG(4,(THIS_FILE,
2474 "Warning: received NOTIFY without message body"));
2475 return;
2476 }
2477
2478 /* Check for appropriate content */
2479 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2480 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2481 {
2482 PJ_LOG(4,(THIS_FILE,
2483 "Warning: received NOTIFY with non message/sipfrag "
2484 "content"));
2485 return;
2486 }
2487
2488 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002489 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002490 &status_line);
2491 if (status != PJ_SUCCESS) {
2492 PJ_LOG(4,(THIS_FILE,
2493 "Warning: received NOTIFY with invalid "
2494 "message/sipfrag content"));
2495 return;
2496 }
2497
2498 } else {
2499 status_line.code = 500;
2500 status_line.reason = *pjsip_get_status_text(500);
2501 }
2502
2503 /* Notify application */
2504 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2505 cont = !is_last;
2506 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2507 status_line.code,
2508 &status_line.reason,
2509 is_last, &cont);
2510
2511 if (!cont) {
2512 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2513 }
2514 }
2515}
2516
2517
2518/*
2519 * Callback called by event framework when the xfer subscription state
2520 * has changed.
2521 */
2522static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002523{
2524
2525 PJ_UNUSED_ARG(event);
2526
2527 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002528 * When subscription is terminated, clear the xfer_sub member of
2529 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002530 */
2531 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002532 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002533
Benny Prijonoa1e69682007-05-11 15:14:34 +00002534 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002535 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002536 return;
2537
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002538 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002539 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002540
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002541 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002542 }
2543}
2544
2545
2546/*
2547 * Follow transfer (REFER) request.
2548 */
2549static void on_call_transfered( pjsip_inv_session *inv,
2550 pjsip_rx_data *rdata )
2551{
2552 pj_status_t status;
2553 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002554 pjsua_call *existing_call;
2555 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002556 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002557 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002558 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002559 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002560 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002561 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002562 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002563 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002564 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002565 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002566 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002567 pjsip_evsub *sub;
2568
Benny Prijonoa1e69682007-05-11 15:14:34 +00002569 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002570
Benny Prijono26ff9062006-02-21 23:47:00 +00002571 /* Find the Refer-To header */
2572 refer_to = (pjsip_generic_string_hdr*)
2573 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2574
2575 if (refer_to == NULL) {
2576 /* Invalid Request.
2577 * No Refer-To header!
2578 */
2579 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002580 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002581 return;
2582 }
2583
Benny Prijonoc8141a82006-08-20 09:12:19 +00002584 /* Find optional Refer-Sub header */
2585 refer_sub = (pjsip_generic_string_hdr*)
2586 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2587
2588 if (refer_sub) {
2589 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2590 no_refer_sub = PJ_TRUE;
2591 }
2592
Benny Prijono053f5222006-11-11 16:16:04 +00002593 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2594 * request.
2595 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002596 ref_by_hdr = (pjsip_hdr*)
2597 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002598 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002599
Benny Prijono9fc735d2006-05-28 14:58:12 +00002600 /* Notify callback */
2601 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002602 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2603 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2604 &refer_to->hvalue,
2605 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002606
2607 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002608 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002609 if (code >= 300) {
2610 /* Application rejects call transfer request */
2611 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2612 return;
2613 }
2614
Benny Prijono26ff9062006-02-21 23:47:00 +00002615 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2616 (int)inv->dlg->remote.info_str.slen,
2617 inv->dlg->remote.info_str.ptr,
2618 (int)refer_to->hvalue.slen,
2619 refer_to->hvalue.ptr));
2620
Benny Prijonoc8141a82006-08-20 09:12:19 +00002621 if (no_refer_sub) {
2622 /*
2623 * Always answer with 200.
2624 */
2625 pjsip_tx_data *tdata;
2626 const pj_str_t str_false = { "false", 5};
2627 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002628
Benny Prijonoc8141a82006-08-20 09:12:19 +00002629 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2630 if (status != PJ_SUCCESS) {
2631 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2632 status);
2633 return;
2634 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002635
Benny Prijonoc8141a82006-08-20 09:12:19 +00002636 /* Add Refer-Sub header */
2637 hdr = (pjsip_hdr*)
2638 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2639 &str_false);
2640 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002641
Benny Prijono26ff9062006-02-21 23:47:00 +00002642
Benny Prijonoc8141a82006-08-20 09:12:19 +00002643 /* Send answer */
2644 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2645 tdata);
2646 if (status != PJ_SUCCESS) {
2647 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2648 status);
2649 return;
2650 }
2651
2652 /* Don't have subscription */
2653 sub = NULL;
2654
2655 } else {
2656 struct pjsip_evsub_user xfer_cb;
2657 pjsip_hdr hdr_list;
2658
2659 /* Init callback */
2660 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002661 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002662
2663 /* Init additional header list to be sent with REFER response */
2664 pj_list_init(&hdr_list);
2665
2666 /* Create transferee event subscription */
2667 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2668 if (status != PJ_SUCCESS) {
2669 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2670 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2671 return;
2672 }
2673
2674 /* If there's Refer-Sub header and the value is "true", send back
2675 * Refer-Sub in the response with value "true" too.
2676 */
2677 if (refer_sub) {
2678 const pj_str_t str_true = { "true", 4 };
2679 pjsip_hdr *hdr;
2680
2681 hdr = (pjsip_hdr*)
2682 pjsip_generic_string_hdr_create(inv->dlg->pool,
2683 &str_refer_sub,
2684 &str_true);
2685 pj_list_push_back(&hdr_list, hdr);
2686
2687 }
2688
2689 /* Accept the REFER request, send 200 (OK). */
2690 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2691
2692 /* Create initial NOTIFY request */
2693 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2694 100, NULL, &tdata);
2695 if (status != PJ_SUCCESS) {
2696 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2697 status);
2698 return;
2699 }
2700
2701 /* Send initial NOTIFY request */
2702 status = pjsip_xfer_send_request( sub, tdata);
2703 if (status != PJ_SUCCESS) {
2704 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2705 return;
2706 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002707 }
2708
2709 /* We're cheating here.
2710 * We need to get a null terminated string from a pj_str_t.
2711 * So grab the pointer from the hvalue and NULL terminate it, knowing
2712 * that the NULL position will be occupied by a newline.
2713 */
2714 uri = refer_to->hvalue.ptr;
2715 uri[refer_to->hvalue.slen] = '\0';
2716
Benny Prijono053f5222006-11-11 16:16:04 +00002717 /* Init msg_data */
2718 pjsua_msg_data_init(&msg_data);
2719
2720 /* If Referred-By header is present in the REFER request, copy this
2721 * to the outgoing INVITE request.
2722 */
2723 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002724 pjsip_hdr *dup = (pjsip_hdr*)
2725 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00002726 pj_list_push_back(&msg_data.hdr_list, dup);
2727 }
2728
Benny Prijono26ff9062006-02-21 23:47:00 +00002729 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002730 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002731 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002732 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002733 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002734 if (status != PJ_SUCCESS) {
2735
Benny Prijonoc8141a82006-08-20 09:12:19 +00002736 /* Notify xferer about the error (if we have subscription) */
2737 if (sub) {
2738 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2739 500, NULL, &tdata);
2740 if (status != PJ_SUCCESS) {
2741 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2742 status);
2743 return;
2744 }
2745 status = pjsip_xfer_send_request(sub, tdata);
2746 if (status != PJ_SUCCESS) {
2747 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2748 status);
2749 return;
2750 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002751 }
2752 return;
2753 }
2754
Benny Prijonoc8141a82006-08-20 09:12:19 +00002755 if (sub) {
2756 /* Put the server subscription in inv_data.
2757 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2758 * reported back to the server subscription.
2759 */
2760 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002761
Benny Prijonoc8141a82006-08-20 09:12:19 +00002762 /* Put the invite_data in the subscription. */
2763 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2764 &pjsua_var.calls[new_call]);
2765 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002766}
2767
2768
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002769
Benny Prijono26ff9062006-02-21 23:47:00 +00002770/*
2771 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002772 * session. We use this to trap:
2773 * - incoming REFER request.
2774 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002775 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002776static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2777 pjsip_transaction *tsx,
2778 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002779{
Benny Prijonoa1e69682007-05-11 15:14:34 +00002780 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002781
2782 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002783
Benny Prijono26ff9062006-02-21 23:47:00 +00002784 if (tsx->role==PJSIP_ROLE_UAS &&
2785 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00002786 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002787 {
2788 /*
2789 * Incoming REFER request.
2790 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002791 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002792
Benny Prijono26ff9062006-02-21 23:47:00 +00002793 }
Benny Prijonob0808372006-03-02 21:18:58 +00002794 else if (tsx->role==PJSIP_ROLE_UAS &&
2795 tsx->state==PJSIP_TSX_STATE_TRYING &&
2796 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2797 {
2798 /*
2799 * Incoming MESSAGE request!
2800 */
2801 pjsip_rx_data *rdata;
2802 pjsip_msg *msg;
2803 pjsip_accept_hdr *accept_hdr;
2804 pj_status_t status;
2805
2806 rdata = e->body.tsx_state.src.rdata;
2807 msg = rdata->msg_info.msg;
2808
2809 /* Request MUST have message body, with Content-Type equal to
2810 * "text/plain".
2811 */
2812 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2813
2814 pjsip_hdr hdr_list;
2815
2816 pj_list_init(&hdr_list);
2817 pj_list_push_back(&hdr_list, accept_hdr);
2818
2819 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2820 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002821 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002822 return;
2823 }
2824
2825 /* Respond with 200 first, so that remote doesn't retransmit in case
2826 * the UI takes too long to process the message.
2827 */
2828 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2829
2830 /* Process MESSAGE request */
2831 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2832 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002833
Benny Prijonob0808372006-03-02 21:18:58 +00002834 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002835 else if (tsx->role == PJSIP_ROLE_UAC &&
2836 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002837 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002838 /* Handle outgoing pager status */
2839 if (tsx->status_code >= 200) {
2840 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002841
Benny Prijonoa1e69682007-05-11 15:14:34 +00002842 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002843 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002844
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002845 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2846 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2847 &im_data->to,
2848 &im_data->body,
2849 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00002850 (pjsip_status_code)
2851 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002852 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002853 }
Benny Prijonofccab712006-02-22 22:23:22 +00002854 }
Benny Prijono834aee32006-02-19 01:38:06 +00002855 }
Benny Prijono834aee32006-02-19 01:38:06 +00002856
Benny Prijono26ff9062006-02-21 23:47:00 +00002857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002858 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002859}