blob: d1042da9a749afe55e10e175aa838dc3d92ad91f [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 Prijonoeebe9af2006-06-13 22:57:13 +0000198/*
199 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
202 const pj_str_t *dest_uri,
203 unsigned options,
204 void *user_data,
205 const pjsua_msg_data *msg_data,
206 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000207{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000208 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000209 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000210 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 pjsua_acc *acc;
212 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000213 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000214 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000215 pjsip_tx_data *tdata;
216 pj_status_t status;
217
Benny Prijono9fc735d2006-05-28 14:58:12 +0000218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000220 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000221 PJ_EINVAL);
222
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000223 /* Options must be zero for now */
224 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
225
Benny Prijono320fa4d2006-12-07 10:09:16 +0000226 /* Check arguments */
227 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
228
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000229 PJSUA_LOCK();
230
231 acc = &pjsua_var.acc[acc_id];
232 if (!acc->valid) {
233 pjsua_perror(THIS_FILE, "Unable to make call because account "
234 "is not valid", PJ_EINVALIDOP);
235 PJSUA_UNLOCK();
236 return PJ_EINVALIDOP;
237 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000238
Benny Prijonoa91a0032006-02-26 21:23:45 +0000239 /* Find free call slot. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000240 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000241 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000242 break;
243 }
244
Benny Prijonoa1e69682007-05-11 15:14:34 +0000245 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000246 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
247 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000248 return PJ_ETOOMANY;
249 }
250
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000251 call = &pjsua_var.calls[call_id];
252
Benny Prijono320fa4d2006-12-07 10:09:16 +0000253 /* Verify that destination URI is valid before calling
254 * pjsua_acc_create_uac_contact, or otherwise there
255 * a misleading "Invalid Contact URI" error will be printed
256 * when pjsua_acc_create_uac_contact() fails.
257 */
258 if (1) {
259 pj_pool_t *pool;
260 pjsip_uri *uri;
261 pj_str_t dup;
262
263 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
264 if (!pool) {
265 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
266 PJSUA_UNLOCK();
267 return PJ_ENOMEM;
268 }
269
270 pj_strdup_with_null(pool, &dup, dest_uri);
271 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
272 pj_pool_release(pool);
273
274 if (uri == NULL) {
275 pjsua_perror(THIS_FILE, "Unable to make call",
276 PJSIP_EINVALIDREQURI);
277 PJSUA_UNLOCK();
278 return PJSIP_EINVALIDREQURI;
279 }
280 }
281
Benny Prijono093d3022006-09-24 00:07:11 +0000282 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
283 (int)dest_uri->slen, dest_uri->ptr));
284
Benny Prijonoe21e7842006-04-09 16:46:05 +0000285 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000286 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000287
Benny Prijonoe21e7842006-04-09 16:46:05 +0000288 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000289 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000290
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000291 /* Create suitable Contact header */
292 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
293 acc_id, dest_uri);
294 if (status != PJ_SUCCESS) {
295 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
296 PJSUA_UNLOCK();
297 return status;
298 }
299
Benny Prijonoe21e7842006-04-09 16:46:05 +0000300 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000301 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000302 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000303 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000304 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000305 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000306 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000307 return status;
308 }
309
Benny Prijonoc97608e2007-03-23 16:34:20 +0000310 /* Init media channel */
311 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
312 if (status != PJ_SUCCESS) {
313 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
314 goto on_error;
315 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000316
Benny Prijonoc97608e2007-03-23 16:34:20 +0000317 /* Create SDP offer */
318 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000319 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000320 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000321 goto on_error;
322 }
323
324 /* Create the INVITE session: */
325
326 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
327 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000328 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000329 goto on_error;
330 }
331
332
333 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000334 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000335 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000336
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 dlg->mod_data[pjsua_var.mod.id] = call;
338 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000339
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000340 /* Attach user data */
341 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000342
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000343 /* If account is locked to specific transport, then lock dialog
344 * to this transport too.
345 */
346 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
347 pjsip_tpselector tp_sel;
348
349 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
350 pjsip_dlg_set_transport(dlg, &tp_sel);
351 }
352
Benny Prijono84126ab2006-02-09 09:30:09 +0000353 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 if (!pj_list_empty(&acc->route_set))
355 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000356
357
358 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000359 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000360 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000362 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000363
364
365 /* Create initial INVITE: */
366
367 status = pjsip_inv_invite(inv, &tdata);
368 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000369 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
370 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000371 goto on_error;
372 }
373
374
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000375 /* Add additional headers etc */
376
377 pjsua_process_msg_data( tdata, msg_data);
378
Benny Prijono093d3022006-09-24 00:07:11 +0000379 /* Must increment call counter now */
380 ++pjsua_var.call_cnt;
381
Benny Prijono84126ab2006-02-09 09:30:09 +0000382 /* Send initial INVITE: */
383
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000384 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000385 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000386 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
387 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000388
389 /* Upon failure to send first request, both dialog and invite
390 * session would have been cleared.
391 */
392 inv = NULL;
393 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000394 goto on_error;
395 }
396
Benny Prijono84126ab2006-02-09 09:30:09 +0000397 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000398
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 if (p_call_id)
400 *p_call_id = call_id;
401
402 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000403
404 return PJ_SUCCESS;
405
406
407on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000408 if (inv != NULL) {
409 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000410 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000411 pjsip_dlg_terminate(dlg);
412 }
413
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000414 if (call_id != -1) {
415 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000416 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000417 }
418
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000420 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000421}
422
423
424/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000425 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000427 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000428pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000429{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000430 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000431 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000432 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000433 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
434 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000435 pjsip_tx_data *response = NULL;
436 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000437 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 int acc_id;
439 pjsua_call *call;
440 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000441 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000442 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000443
Benny Prijono26ff9062006-02-21 23:47:00 +0000444 /* Don't want to handle anything but INVITE */
445 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
446 return PJ_FALSE;
447
448 /* Don't want to handle anything that's already associated with
449 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000450 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000451 if (dlg || tsx)
452 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000453
Benny Prijono148c9dd2006-09-19 13:37:53 +0000454 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000455
Benny Prijono26ff9062006-02-21 23:47:00 +0000456 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
458 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000459 break;
460 }
461
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
463 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000464 PJSIP_SC_BUSY_HERE, NULL,
465 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000466 PJ_LOG(2,(THIS_FILE,
467 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000468 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000469 return PJ_TRUE;
470 }
471
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000472 /* Clear call descriptor */
473 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000474
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000475 call = &pjsua_var.calls[call_id];
476
477 /* Mark call start time. */
478 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000479
Benny Prijono053f5222006-11-11 16:16:04 +0000480 /* Check INVITE request for Replaces header. If Replaces header is
481 * present, the function will make sure that we can handle the request.
482 */
483 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
484 &response);
485 if (status != PJ_SUCCESS) {
486 /*
487 * Something wrong with the Replaces header.
488 */
489 if (response) {
490 pjsip_response_addr res_addr;
491
492 pjsip_get_response_addr(response->pool, rdata, &res_addr);
493 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
494 NULL, NULL);
495
496 } else {
497
498 /* Respond with 500 (Internal Server Error) */
499 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
500 NULL, NULL);
501 }
502
503 PJSUA_UNLOCK();
504 return PJ_TRUE;
505 }
506
507 /* If this INVITE request contains Replaces header, notify application
508 * about the request so that application can do subsequent checking
509 * if it wants to.
510 */
511 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
512 pjsua_call *replaced_call;
513 int st_code = 200;
514 pj_str_t st_text = { "OK", 2 };
515
516 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000517 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000518
519 /* Notify application */
520 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
521 rdata, &st_code, &st_text);
522
523 /* Must specify final response */
524 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
525
526 /* Check if application rejects this request. */
527 if (st_code >= 300) {
528
529 if (st_text.slen == 2)
530 st_text = *pjsip_get_status_text(st_code);
531
532 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
533 st_code, &st_text, NULL, NULL, NULL);
534 PJSUA_UNLOCK();
535 return PJ_TRUE;
536 }
537 }
538
539
Benny Prijonoc97608e2007-03-23 16:34:20 +0000540 /* Init media channel */
541 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000542 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000543 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000544 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000545 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000546 return PJ_TRUE;
547 }
548
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000549
Benny Prijonoc97608e2007-03-23 16:34:20 +0000550 /* Get media capability from media endpoint: */
551 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
552 if (status != PJ_SUCCESS) {
553 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
554 NULL, NULL);
555 pjsua_media_channel_deinit(call->index);
556 PJSUA_UNLOCK();
557 return PJ_TRUE;
558 }
559
560
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000561 /* Verify that we can handle the request. */
562 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
563 pjsua_var.endpt, &response);
564 if (status != PJ_SUCCESS) {
565
566 /*
567 * No we can't handle the incoming INVITE request.
568 */
569
570 if (response) {
571 pjsip_response_addr res_addr;
572
573 pjsip_get_response_addr(response->pool, rdata, &res_addr);
574 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
575 NULL, NULL);
576
577 } else {
578
579 /* Respond with 500 (Internal Server Error) */
580 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
581 NULL, NULL);
582 }
583
Benny Prijonoc97608e2007-03-23 16:34:20 +0000584 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000585 PJSUA_UNLOCK();
586 return PJ_TRUE;
587 }
588
589
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000590 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000591 * Get which account is most likely to be associated with this incoming
592 * call. We need the account to find which contact URI to put for
593 * the call.
594 */
Benny Prijono093d3022006-09-24 00:07:11 +0000595 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000596
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000597 /* Get suitable Contact header */
598 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
599 acc_id, rdata);
600 if (status != PJ_SUCCESS) {
601 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
602 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
603 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000604 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000605 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000606 return PJ_TRUE;
607 }
608
Benny Prijono26ff9062006-02-21 23:47:00 +0000609 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000610 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000611 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000612 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000614 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000615 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000616 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000617 return PJ_TRUE;
618 }
619
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000620 /* Set credentials */
621 if (pjsua_var.acc[acc_id].cred_cnt) {
622 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
623 pjsua_var.acc[acc_id].cred_cnt,
624 pjsua_var.acc[acc_id].cred);
625 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000626
627 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000628 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
629 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000630 pjsip_hdr hdr_list;
631 pjsip_warning_hdr *w;
632
633 w = pjsip_warning_hdr_create_from_status(dlg->pool,
634 pjsip_endpt_name(pjsua_var.endpt),
635 status);
636 pj_list_init(&hdr_list);
637 pj_list_push_back(&hdr_list, w);
638
639 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
640
641 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000642 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000643 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000644 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000645 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000646 return PJ_TRUE;
647 }
648
649
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000650 /* Create and attach pjsua_var data to the dialog: */
651 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000652
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000653 dlg->mod_data[pjsua_var.mod.id] = call;
654 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000655
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000656 /* If account is locked to specific transport, then lock dialog
657 * to this transport too.
658 */
659 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
660 pjsip_tpselector tp_sel;
661
662 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
663 pjsip_dlg_set_transport(dlg, &tp_sel);
664 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000665
Benny Prijono64f851e2006-02-23 13:49:28 +0000666 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000667 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000668 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000669 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000670 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000671 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
672 status);
673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
675 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000676 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000677 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000678 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000679
680 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000681 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000682 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000683 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000684 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000685 }
686
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000688
Benny Prijono105217f2006-03-06 16:25:59 +0000689
Benny Prijono053f5222006-11-11 16:16:04 +0000690 /* Check if this request should replace existing call */
691 if (replaced_dlg) {
692 pjsip_inv_session *replaced_inv;
693 struct pjsua_call *replaced_call;
694 pjsip_tx_data *tdata;
695
696 /* Get the invite session in the dialog */
697 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
698
699 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000700 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000701
702 /* Notify application */
703 if (pjsua_var.ua_cfg.cb.on_call_replaced)
704 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
705 call_id);
706
707 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
708 call_id));
709
710 /* Answer the new call with 200 response */
711 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
712 if (status == PJ_SUCCESS)
713 status = pjsip_inv_send_msg(inv, tdata);
714
715 if (status != PJ_SUCCESS)
716 pjsua_perror(THIS_FILE, "Error answering session", status);
717
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000718 /* Note that inv may be invalid if 200/OK has caused error in
719 * starting the media.
720 */
Benny Prijono053f5222006-11-11 16:16:04 +0000721
722 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
723 replaced_call->index));
724
725 /* Disconnect replaced invite session */
726 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
727 &tdata);
728 if (status == PJ_SUCCESS && tdata)
729 status = pjsip_inv_send_msg(replaced_inv, tdata);
730
731 if (status != PJ_SUCCESS)
732 pjsua_perror(THIS_FILE, "Error terminating session", status);
733
734
735 } else {
736
Benny Prijonob5388cf2007-01-04 22:45:08 +0000737 /* Notify application if on_incoming_call() is overriden,
738 * otherwise hangup the call with 480
739 */
740 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000741 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000742 } else {
743 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
744 NULL, NULL);
745 }
Benny Prijono053f5222006-11-11 16:16:04 +0000746 }
747
Benny Prijono8b1889b2006-06-06 18:40:40 +0000748
Benny Prijono26ff9062006-02-21 23:47:00 +0000749 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000750 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000751 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000752}
753
754
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000755
756/*
757 * Check if the specified call has active INVITE session and the INVITE
758 * session has not been disconnected.
759 */
760PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
761{
762 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
763 PJ_EINVAL);
764 return pjsua_var.calls[call_id].inv != NULL &&
765 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
766}
767
768
769/*
770 * Check if call has an active media session.
771 */
772PJ_DEF(pj_bool_t) pjsua_call_has_media(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].session != NULL;
777}
778
779
Benny Prijono148c9dd2006-09-19 13:37:53 +0000780/* Acquire lock to the specified call_id */
781static pj_status_t acquire_call(const char *title,
782 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000783 pjsua_call **p_call,
784 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000785{
786 enum { MAX_RETRY=50 };
787 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000788 pjsua_call *call = NULL;
789 pj_bool_t has_pjsua_lock = PJ_FALSE;
790 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000791
792 for (retry=0; retry<MAX_RETRY; ++retry) {
793
794 has_pjsua_lock = PJ_FALSE;
795
796 status = PJSUA_TRY_LOCK();
797 if (status != PJ_SUCCESS) {
798 pj_thread_sleep(retry/10);
799 continue;
800 }
801
802 has_pjsua_lock = PJ_TRUE;
803 call = &pjsua_var.calls[call_id];
804
805 if (call->inv == NULL) {
806 PJSUA_UNLOCK();
807 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
808 return PJSIP_ESESSIONTERMINATED;
809 }
810
811 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
812 if (status != PJ_SUCCESS) {
813 PJSUA_UNLOCK();
814 pj_thread_sleep(retry/10);
815 continue;
816 }
817
818 PJSUA_UNLOCK();
819
820 break;
821 }
822
823 if (status != PJ_SUCCESS) {
824 if (has_pjsua_lock == PJ_FALSE)
825 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
826 "(possibly system has deadlocked) in %s",
827 title));
828 else
829 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
830 "(possibly system has deadlocked) in %s",
831 title));
832 return PJ_ETIMEDOUT;
833 }
834
835 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000836 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000837
838 return PJ_SUCCESS;
839}
840
841
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000842/*
843 * Get the conference port identification associated with the call.
844 */
845PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
846{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000847 pjsua_call *call;
848 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000849 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000850 pj_status_t status;
851
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000852 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
853 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000854
Benny Prijonodc752ca2006-09-22 16:55:42 +0000855 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000856 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000857 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000858
859 port_id = call->conf_slot;
860
Benny Prijonodc752ca2006-09-22 16:55:42 +0000861 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000862
863 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000864}
865
866
Benny Prijono148c9dd2006-09-19 13:37:53 +0000867
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000868/*
869 * Obtain detail information about the specified call.
870 */
871PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
872 pjsua_call_info *info)
873{
874 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000875 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000876 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000877
878 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
879 PJ_EINVAL);
880
Benny Prijonoac623b32006-07-03 15:19:31 +0000881 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000882
Benny Prijonodc752ca2006-09-22 16:55:42 +0000883 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000884 if (status != PJ_SUCCESS) {
885 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000886 }
887
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000888 /* id and role */
889 info->id = call_id;
890 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000891 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000892
893 /* local info */
894 info->local_info.ptr = info->buf_.local_info;
895 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
896 sizeof(info->buf_.local_info));
897
898 /* local contact */
899 info->local_contact.ptr = info->buf_.local_contact;
900 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
901 call->inv->dlg->local.contact->uri,
902 info->local_contact.ptr,
903 sizeof(info->buf_.local_contact));
904
905 /* remote info */
906 info->remote_info.ptr = info->buf_.remote_info;
907 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
908 sizeof(info->buf_.remote_info));
909
910 /* remote contact */
911 if (call->inv->dlg->remote.contact) {
912 int len;
913 info->remote_contact.ptr = info->buf_.remote_contact;
914 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
915 call->inv->dlg->remote.contact->uri,
916 info->remote_contact.ptr,
917 sizeof(info->buf_.remote_contact));
918 if (len < 0) len = 0;
919 info->remote_contact.slen = len;
920 } else {
921 info->remote_contact.slen = 0;
922 }
923
924 /* call id */
925 info->call_id.ptr = info->buf_.call_id;
926 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
927 sizeof(info->buf_.call_id));
928
929 /* state, state_text */
930 info->state = call->inv->state;
931 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
932
933 /* If call is disconnected, set the last_status from the cause code */
934 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
935 /* last_status, last_status_text */
936 info->last_status = call->inv->cause;
937
938 info->last_status_text.ptr = info->buf_.last_status_text;
939 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
940 sizeof(info->buf_.last_status_text));
941 } else {
942 /* last_status, last_status_text */
943 info->last_status = call->last_code;
944
945 info->last_status_text.ptr = info->buf_.last_status_text;
946 pj_strncpy(&info->last_status_text, &call->last_text,
947 sizeof(info->buf_.last_status_text));
948 }
949
950 /* media status and dir */
951 info->media_status = call->media_st;
952 info->media_dir = call->media_dir;
953
954
955 /* conference slot number */
956 info->conf_slot = call->conf_slot;
957
958 /* calculate duration */
959 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
960
961 info->total_duration = call->dis_time;
962 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
963
964 if (call->conn_time.sec) {
965 info->connect_duration = call->dis_time;
966 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
967 }
968
969 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
970
971 pj_gettimeofday(&info->total_duration);
972 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
973
974 pj_gettimeofday(&info->connect_duration);
975 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
976
977 } else {
978 pj_gettimeofday(&info->total_duration);
979 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
980 }
981
Benny Prijonodc752ca2006-09-22 16:55:42 +0000982 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000983
984 return PJ_SUCCESS;
985}
986
987
988/*
989 * Attach application specific data to the call.
990 */
991PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
992 void *user_data)
993{
994 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
995 PJ_EINVAL);
996 pjsua_var.calls[call_id].user_data = user_data;
997
998 return PJ_SUCCESS;
999}
1000
1001
1002/*
1003 * Get user data attached to the call.
1004 */
1005PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1006{
1007 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1008 NULL);
1009 return pjsua_var.calls[call_id].user_data;
1010}
1011
1012
1013/*
1014 * Send response to incoming INVITE request.
1015 */
1016PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1017 unsigned code,
1018 const pj_str_t *reason,
1019 const pjsua_msg_data *msg_data)
1020{
1021 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001022 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001023 pjsip_tx_data *tdata;
1024 pj_status_t status;
1025
1026 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1027 PJ_EINVAL);
1028
Benny Prijonodc752ca2006-09-22 16:55:42 +00001029 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001030 if (status != PJ_SUCCESS)
1031 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032
Benny Prijono2e507c22006-06-23 15:04:11 +00001033 if (call->res_time.sec == 0)
1034 pj_gettimeofday(&call->res_time);
1035
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001036 if (reason && reason->slen == 0)
1037 reason = NULL;
1038
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001039 /* Create response message */
1040 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1041 if (status != PJ_SUCCESS) {
1042 pjsua_perror(THIS_FILE, "Error creating response",
1043 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001044 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001045 return status;
1046 }
1047
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001048 /* Call might have been disconnected if application is answering with
1049 * 200/OK and the media failed to start.
1050 */
1051 if (call->inv == NULL) {
1052 pjsip_dlg_dec_lock(dlg);
1053 return PJSIP_ESESSIONTERMINATED;
1054 }
1055
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001056 /* Add additional headers etc */
1057 pjsua_process_msg_data( tdata, msg_data);
1058
1059 /* Send the message */
1060 status = pjsip_inv_send_msg(call->inv, tdata);
1061 if (status != PJ_SUCCESS)
1062 pjsua_perror(THIS_FILE, "Error sending response",
1063 status);
1064
Benny Prijonodc752ca2006-09-22 16:55:42 +00001065 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001066
1067 return status;
1068}
1069
1070
1071/*
1072 * Hangup call by using method that is appropriate according to the
1073 * call state.
1074 */
1075PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1076 unsigned code,
1077 const pj_str_t *reason,
1078 const pjsua_msg_data *msg_data)
1079{
1080 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001081 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001082 pj_status_t status;
1083 pjsip_tx_data *tdata;
1084
1085
Benny Prijono148c9dd2006-09-19 13:37:53 +00001086 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1087 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1088 call_id));
1089 }
1090
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001091 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1092 PJ_EINVAL);
1093
Benny Prijonodc752ca2006-09-22 16:55:42 +00001094 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001095 if (status != PJ_SUCCESS)
1096 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001097
1098 if (code==0) {
1099 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1100 code = PJSIP_SC_OK;
1101 else if (call->inv->role == PJSIP_ROLE_UAS)
1102 code = PJSIP_SC_DECLINE;
1103 else
1104 code = PJSIP_SC_REQUEST_TERMINATED;
1105 }
1106
1107 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1108 if (status != PJ_SUCCESS) {
1109 pjsua_perror(THIS_FILE,
1110 "Failed to create end session message",
1111 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001112 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 return status;
1114 }
1115
1116 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1117 * as p_tdata when INVITE transaction has not been answered
1118 * with any provisional responses.
1119 */
1120 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001121 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001122 return PJ_SUCCESS;
1123 }
1124
1125 /* Add additional headers etc */
1126 pjsua_process_msg_data( tdata, msg_data);
1127
1128 /* Send the message */
1129 status = pjsip_inv_send_msg(call->inv, tdata);
1130 if (status != PJ_SUCCESS) {
1131 pjsua_perror(THIS_FILE,
1132 "Failed to send end session message",
1133 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001134 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001135 return status;
1136 }
1137
Benny Prijonodc752ca2006-09-22 16:55:42 +00001138 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001139
1140 return PJ_SUCCESS;
1141}
1142
1143
1144/*
1145 * Put the specified call on hold.
1146 */
1147PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1148 const pjsua_msg_data *msg_data)
1149{
1150 pjmedia_sdp_session *sdp;
1151 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001153 pjsip_tx_data *tdata;
1154 pj_status_t status;
1155
1156 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1157 PJ_EINVAL);
1158
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160 if (status != PJ_SUCCESS)
1161 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001163
1164 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1165 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001166 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167 return PJSIP_ESESSIONSTATE;
1168 }
1169
1170 status = create_inactive_sdp(call, &sdp);
1171 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001172 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001173 return status;
1174 }
1175
1176 /* Create re-INVITE with new offer */
1177 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1178 if (status != PJ_SUCCESS) {
1179 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001180 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181 return status;
1182 }
1183
1184 /* Add additional headers etc */
1185 pjsua_process_msg_data( tdata, msg_data);
1186
1187 /* Send the request */
1188 status = pjsip_inv_send_msg( call->inv, tdata);
1189 if (status != PJ_SUCCESS) {
1190 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001191 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001192 return status;
1193 }
1194
Benny Prijonodc752ca2006-09-22 16:55:42 +00001195 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001196
1197 return PJ_SUCCESS;
1198}
1199
1200
1201/*
1202 * Send re-INVITE (to release hold).
1203 */
1204PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1205 pj_bool_t unhold,
1206 const pjsua_msg_data *msg_data)
1207{
1208 pjmedia_sdp_session *sdp;
1209 pjsip_tx_data *tdata;
1210 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001211 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001212 pj_status_t status;
1213
1214
1215 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1216 PJ_EINVAL);
1217
Benny Prijonodc752ca2006-09-22 16:55:42 +00001218 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001219 if (status != PJ_SUCCESS)
1220 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221
1222 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1223 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001224 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001225 return PJSIP_ESESSIONSTATE;
1226 }
1227
Benny Prijono667952e2007-04-02 19:27:54 +00001228 /* Init media channel */
1229 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1230 if (status != PJ_SUCCESS) {
1231 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1232 pjsip_dlg_dec_lock(dlg);
1233 return PJSIP_ESESSIONSTATE;
1234 }
1235
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001237 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001238 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001239 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001240 if (status != PJ_SUCCESS) {
1241 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1242 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001243 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 return status;
1245 }
1246
1247 /* Create re-INVITE with new offer */
1248 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1249 if (status != PJ_SUCCESS) {
1250 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001251 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 return status;
1253 }
1254
1255 /* Add additional headers etc */
1256 pjsua_process_msg_data( tdata, msg_data);
1257
1258 /* Send the request */
1259 status = pjsip_inv_send_msg( call->inv, tdata);
1260 if (status != PJ_SUCCESS) {
1261 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001262 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001263 return status;
1264 }
1265
Benny Prijonodc752ca2006-09-22 16:55:42 +00001266 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267
1268 return PJ_SUCCESS;
1269}
1270
1271
1272/*
1273 * Initiate call transfer to the specified address.
1274 */
1275PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1276 const pj_str_t *dest,
1277 const pjsua_msg_data *msg_data)
1278{
1279 pjsip_evsub *sub;
1280 pjsip_tx_data *tdata;
1281 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001282 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001283 pjsip_generic_string_hdr *gs_hdr;
1284 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001285 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001286 pj_status_t status;
1287
1288
1289 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1290 PJ_EINVAL);
1291
Benny Prijonodc752ca2006-09-22 16:55:42 +00001292 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001293 if (status != PJ_SUCCESS)
1294 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001295
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296
Benny Prijonod524e822006-09-22 12:48:18 +00001297 /* Create xfer client subscription. */
1298 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001299 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001300
1301 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001302 if (status != PJ_SUCCESS) {
1303 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001304 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001305 return status;
1306 }
1307
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001308 /* Associate this call with the client subscription */
1309 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1310
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001311 /*
1312 * Create REFER request.
1313 */
1314 status = pjsip_xfer_initiate(sub, dest, &tdata);
1315 if (status != PJ_SUCCESS) {
1316 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001317 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001318 return status;
1319 }
1320
Benny Prijono053f5222006-11-11 16:16:04 +00001321 /* Add Referred-By header */
1322 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1323 &dlg->local.info_str);
1324 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1325
1326
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001327 /* Add additional headers etc */
1328 pjsua_process_msg_data( tdata, msg_data);
1329
1330 /* Send. */
1331 status = pjsip_xfer_send_request(sub, tdata);
1332 if (status != PJ_SUCCESS) {
1333 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001334 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335 return status;
1336 }
1337
1338 /* For simplicity (that's what this program is intended to be!),
1339 * leave the original invite session as it is. More advanced application
1340 * may want to hold the INVITE, or terminate the invite, or whatever.
1341 */
1342
Benny Prijonodc752ca2006-09-22 16:55:42 +00001343 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001344
1345 return PJ_SUCCESS;
1346
1347}
1348
1349
1350/*
Benny Prijono053f5222006-11-11 16:16:04 +00001351 * Initiate attended call transfer to the specified address.
1352 */
1353PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1354 pjsua_call_id dest_call_id,
1355 unsigned options,
1356 const pjsua_msg_data *msg_data)
1357{
1358 pjsua_call *dest_call;
1359 pjsip_dialog *dest_dlg;
1360 char str_dest_buf[512];
1361 pj_str_t str_dest;
1362 int len;
1363 pjsip_uri *uri;
1364 pj_status_t status;
1365
1366
1367 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1368 PJ_EINVAL);
1369 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1370 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1371 PJ_EINVAL);
1372
1373 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1374 &dest_call, &dest_dlg);
1375 if (status != PJ_SUCCESS)
1376 return status;
1377
1378 /*
1379 * Create REFER destination URI with Replaces field.
1380 */
1381
1382 /* Make sure we have sufficient buffer's length */
1383 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1384 dest_dlg->call_id->id.slen +
1385 dest_dlg->remote.info->tag.slen +
1386 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001387 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001388
1389 /* Print URI */
1390 str_dest_buf[0] = '<';
1391 str_dest.slen = 1;
1392
Benny Prijonoa1e69682007-05-11 15:14:34 +00001393 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001394 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1395 str_dest_buf+1, sizeof(str_dest_buf)-1);
1396 if (len < 0)
1397 return PJSIP_EURITOOLONG;
1398
1399 str_dest.slen += len;
1400
1401
1402 /* Build the URI */
1403 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1404 sizeof(str_dest_buf) - str_dest.slen,
1405 "?%s"
1406 "Replaces=%.*s"
1407 "%%3Bto-tag%%3D%.*s"
1408 "%%3Bfrom-tag%%3D%.*s>",
1409 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1410 "" : "Require=replaces&"),
1411 (int)dest_dlg->call_id->id.slen,
1412 dest_dlg->call_id->id.ptr,
1413 (int)dest_dlg->remote.info->tag.slen,
1414 dest_dlg->remote.info->tag.ptr,
1415 (int)dest_dlg->local.info->tag.slen,
1416 dest_dlg->local.info->tag.ptr);
1417
1418 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1419 PJSIP_EURITOOLONG);
1420
1421 str_dest.ptr = str_dest_buf;
1422 str_dest.slen += len;
1423
1424 pjsip_dlg_dec_lock(dest_dlg);
1425
1426 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1427}
1428
1429
1430/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 * Send DTMF digits to remote using RFC 2833 payload formats.
1432 */
1433PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1434 const pj_str_t *digits)
1435{
1436 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001437 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438 pj_status_t status;
1439
1440 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1441 PJ_EINVAL);
1442
Benny Prijonodc752ca2006-09-22 16:55:42 +00001443 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001444 if (status != PJ_SUCCESS)
1445 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001446
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001447 if (!call->session) {
1448 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001449 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001450 return PJ_EINVALIDOP;
1451 }
1452
1453 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1454
Benny Prijonodc752ca2006-09-22 16:55:42 +00001455 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456
1457 return status;
1458}
1459
1460
1461/**
1462 * Send instant messaging inside INVITE session.
1463 */
1464PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1465 const pj_str_t *mime_type,
1466 const pj_str_t *content,
1467 const pjsua_msg_data *msg_data,
1468 void *user_data)
1469{
1470 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001471 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001472 const pj_str_t mime_text_plain = pj_str("text/plain");
1473 pjsip_media_type ctype;
1474 pjsua_im_data *im_data;
1475 pjsip_tx_data *tdata;
1476 pj_status_t status;
1477
1478
1479 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1480 PJ_EINVAL);
1481
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001483 if (status != PJ_SUCCESS)
1484 return status;
1485
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 /* Set default media type if none is specified */
1487 if (mime_type == NULL) {
1488 mime_type = &mime_text_plain;
1489 }
1490
1491 /* Create request message. */
1492 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1493 -1, &tdata);
1494 if (status != PJ_SUCCESS) {
1495 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1496 goto on_return;
1497 }
1498
1499 /* Add accept header. */
1500 pjsip_msg_add_hdr( tdata->msg,
1501 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1502
1503 /* Parse MIME type */
1504 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1505
1506 /* Create "text/plain" message body. */
1507 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1508 &ctype.subtype, content);
1509 if (tdata->msg->body == NULL) {
1510 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1511 pjsip_tx_data_dec_ref(tdata);
1512 goto on_return;
1513 }
1514
1515 /* Add additional headers etc */
1516 pjsua_process_msg_data( tdata, msg_data);
1517
1518 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001519 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001520 im_data->acc_id = call->acc_id;
1521 im_data->call_id = call_id;
1522 im_data->to = call->inv->dlg->remote.info_str;
1523 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1524 im_data->user_data = user_data;
1525
1526
1527 /* Send the request. */
1528 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1529 pjsua_var.mod.id, im_data);
1530 if (status != PJ_SUCCESS) {
1531 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1532 goto on_return;
1533 }
1534
1535on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001536 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001537 return status;
1538}
1539
1540
1541/*
1542 * Send IM typing indication inside INVITE session.
1543 */
1544PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1545 pj_bool_t is_typing,
1546 const pjsua_msg_data*msg_data)
1547{
1548 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001549 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001550 pjsip_tx_data *tdata;
1551 pj_status_t status;
1552
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1554 PJ_EINVAL);
1555
Benny Prijonodc752ca2006-09-22 16:55:42 +00001556 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001557 if (status != PJ_SUCCESS)
1558 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001559
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001560 /* Create request message. */
1561 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1562 -1, &tdata);
1563 if (status != PJ_SUCCESS) {
1564 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1565 goto on_return;
1566 }
1567
1568 /* Create "application/im-iscomposing+xml" msg body. */
1569 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1570 NULL, NULL, -1);
1571
1572 /* Add additional headers etc */
1573 pjsua_process_msg_data( tdata, msg_data);
1574
1575 /* Send the request. */
1576 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1577 if (status != PJ_SUCCESS) {
1578 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1579 goto on_return;
1580 }
1581
1582on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001583 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001584 return status;
1585}
1586
1587
1588/*
1589 * Terminate all calls.
1590 */
1591PJ_DEF(void) pjsua_call_hangup_all(void)
1592{
1593 unsigned i;
1594
1595 PJSUA_LOCK();
1596
1597 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1598 if (pjsua_var.calls[i].inv)
1599 pjsua_call_hangup(i, 0, NULL, NULL);
1600 }
1601
1602 PJSUA_UNLOCK();
1603}
1604
1605
1606static const char *good_number(char *buf, pj_int32_t val)
1607{
1608 if (val < 1000) {
1609 pj_ansi_sprintf(buf, "%d", val);
1610 } else if (val < 1000000) {
1611 pj_ansi_sprintf(buf, "%d.%dK",
1612 val / 1000,
1613 (val % 1000) / 100);
1614 } else {
1615 pj_ansi_sprintf(buf, "%d.%02dM",
1616 val / 1000000,
1617 (val % 1000000) / 10000);
1618 }
1619
1620 return buf;
1621}
1622
1623
1624/* Dump media session */
1625static void dump_media_session(const char *indent,
1626 char *buf, unsigned maxlen,
1627 pjmedia_session *session)
1628{
1629 unsigned i;
1630 char *p = buf, *end = buf+maxlen;
1631 int len;
1632 pjmedia_session_info info;
1633
1634 pjmedia_session_get_info(session, &info);
1635
1636 for (i=0; i<info.stream_cnt; ++i) {
1637 pjmedia_rtcp_stat stat;
1638 const char *rem_addr;
1639 int rem_port;
1640 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001641 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001642 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001643 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644
1645 pjmedia_session_get_stream_stat(session, i, &stat);
1646 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1647 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1648
1649 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1650 dir = "sendonly";
1651 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1652 dir = "recvonly";
1653 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1654 dir = "sendrecv";
1655 else
1656 dir = "inactive";
1657
1658
1659 len = pj_ansi_snprintf(buf, end-p,
1660 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1661 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001662 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 info.stream_info[i].fmt.encoding_name.ptr,
1664 info.stream_info[i].fmt.clock_rate / 1000,
1665 dir,
1666 rem_addr, rem_port);
1667 if (len < 1 || len > end-p) {
1668 *p = '\0';
1669 return;
1670 }
1671
1672 p += len;
1673 *p++ = '\n';
1674 *p = '\0';
1675
1676 if (stat.rx.update_cnt == 0)
1677 strcpy(last_update, "never");
1678 else {
1679 pj_gettimeofday(&now);
1680 PJ_TIME_VAL_SUB(now, stat.rx.update);
1681 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1682 now.sec / 3600,
1683 (now.sec % 3600) / 60,
1684 now.sec % 60,
1685 now.msec);
1686 }
1687
Benny Prijono80019eb2006-08-07 13:22:23 +00001688 pj_gettimeofday(&media_duration);
1689 PJ_TIME_VAL_SUB(media_duration, stat.start);
1690 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1691 media_duration.msec = 1;
1692
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001693 len = pj_ansi_snprintf(p, end-p,
1694 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001695 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001696 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1697 "%s (msec) min avg max last\n"
1698 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1699 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1700 indent, info.stream_info[i].fmt.pt,
1701 last_update,
1702 indent,
1703 good_number(packets, stat.rx.pkt),
1704 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001705 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001706 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001707 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 +00001708 indent,
1709 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001710 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001711 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001712 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001713 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001714 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001715 indent, indent,
1716 stat.rx.loss_period.min / 1000.0,
1717 stat.rx.loss_period.avg / 1000.0,
1718 stat.rx.loss_period.max / 1000.0,
1719 stat.rx.loss_period.last / 1000.0,
1720 indent,
1721 stat.rx.jitter.min / 1000.0,
1722 stat.rx.jitter.avg / 1000.0,
1723 stat.rx.jitter.max / 1000.0,
1724 stat.rx.jitter.last / 1000.0,
1725 ""
1726 );
1727
1728 if (len < 1 || len > end-p) {
1729 *p = '\0';
1730 return;
1731 }
1732
1733 p += len;
1734 *p++ = '\n';
1735 *p = '\0';
1736
1737 if (stat.tx.update_cnt == 0)
1738 strcpy(last_update, "never");
1739 else {
1740 pj_gettimeofday(&now);
1741 PJ_TIME_VAL_SUB(now, stat.tx.update);
1742 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1743 now.sec / 3600,
1744 (now.sec % 3600) / 60,
1745 now.sec % 60,
1746 now.msec);
1747 }
1748
1749 len = pj_ansi_snprintf(p, end-p,
1750 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001751 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1753 "%s (msec) min avg max last\n"
1754 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1755 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1756 indent,
1757 info.stream_info[i].tx_pt,
1758 info.stream_info[i].param->info.frm_ptime *
1759 info.stream_info[i].param->setting.frm_per_pkt,
1760 last_update,
1761
1762 indent,
1763 good_number(packets, stat.tx.pkt),
1764 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001765 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijono80019eb2006-08-07 13:22:23 +00001766 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijono427af7d2007-05-11 10:36:40 +00001767 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 +00001768
1769 indent,
1770 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001771 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001772 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001773 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001774 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001775 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776
1777 indent, indent,
1778 stat.tx.loss_period.min / 1000.0,
1779 stat.tx.loss_period.avg / 1000.0,
1780 stat.tx.loss_period.max / 1000.0,
1781 stat.tx.loss_period.last / 1000.0,
1782 indent,
1783 stat.tx.jitter.min / 1000.0,
1784 stat.tx.jitter.avg / 1000.0,
1785 stat.tx.jitter.max / 1000.0,
1786 stat.tx.jitter.last / 1000.0,
1787 ""
1788 );
1789
1790 if (len < 1 || len > end-p) {
1791 *p = '\0';
1792 return;
1793 }
1794
1795 p += len;
1796 *p++ = '\n';
1797 *p = '\0';
1798
1799 len = pj_ansi_snprintf(p, end-p,
1800 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1801 indent,
1802 stat.rtt.min / 1000.0,
1803 stat.rtt.avg / 1000.0,
1804 stat.rtt.max / 1000.0,
1805 stat.rtt.last / 1000.0
1806 );
1807 if (len < 1 || len > end-p) {
1808 *p = '\0';
1809 return;
1810 }
1811
1812 p += len;
1813 *p++ = '\n';
1814 *p = '\0';
1815 }
1816}
1817
1818
1819/* Print call info */
1820static void print_call(const char *title,
1821 int call_id,
1822 char *buf, pj_size_t size)
1823{
1824 int len;
1825 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1826 pjsip_dialog *dlg = inv->dlg;
1827 char userinfo[128];
1828
1829 /* Dump invite sesion info. */
1830
1831 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1832 if (len < 1)
1833 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1834 else
1835 userinfo[len] = '\0';
1836
1837 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1838 title,
1839 pjsip_inv_state_name(inv->state),
1840 userinfo);
1841 if (len < 1 || len >= (int)size) {
1842 pj_ansi_strcpy(buf, "<--uri too long-->");
1843 len = 18;
1844 } else
1845 buf[len] = '\0';
1846}
1847
1848
1849/*
1850 * Dump call and media statistics to string.
1851 */
1852PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1853 pj_bool_t with_media,
1854 char *buffer,
1855 unsigned maxlen,
1856 const char *indent)
1857{
1858 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001859 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 pj_time_val duration, res_delay, con_delay;
1861 char tmp[128];
1862 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001863 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001864 int len;
1865
1866 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1867 PJ_EINVAL);
1868
Benny Prijonodc752ca2006-09-22 16:55:42 +00001869 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001870 if (status != PJ_SUCCESS)
1871 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872
1873 *buffer = '\0';
1874 p = buffer;
1875 end = buffer + maxlen;
1876 len = 0;
1877
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001878 print_call(indent, call_id, tmp, sizeof(tmp));
1879
1880 len = pj_ansi_strlen(tmp);
1881 pj_ansi_strcpy(buffer, tmp);
1882
1883 p += len;
1884 *p++ = '\r';
1885 *p++ = '\n';
1886
1887 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001888 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001889 pj_gettimeofday(&duration);
1890 PJ_TIME_VAL_SUB(duration, call->conn_time);
1891 con_delay = call->conn_time;
1892 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1893 } else {
1894 duration.sec = duration.msec = 0;
1895 con_delay.sec = con_delay.msec = 0;
1896 }
1897
1898 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001899 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001900 res_delay = call->res_time;
1901 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1902 } else {
1903 res_delay.sec = res_delay.msec = 0;
1904 }
1905
1906 /* Print duration */
1907 len = pj_ansi_snprintf(p, end-p,
1908 "%s Call time: %02dh:%02dm:%02ds, "
1909 "1st res in %d ms, conn in %dms",
1910 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001911 (int)(duration.sec / 3600),
1912 (int)((duration.sec % 3600)/60),
1913 (int)(duration.sec % 60),
1914 (int)PJ_TIME_VAL_MSEC(res_delay),
1915 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001916
1917 if (len > 0 && len < end-p) {
1918 p += len;
1919 *p++ = '\n';
1920 *p = '\0';
1921 }
1922
1923 /* Dump session statistics */
1924 if (with_media && call->session)
1925 dump_media_session(indent, p, end-p, call->session);
1926
Benny Prijonodc752ca2006-09-22 16:55:42 +00001927 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001928
1929 return PJ_SUCCESS;
1930}
1931
1932
1933/*
Benny Prijono84126ab2006-02-09 09:30:09 +00001934 * This callback receives notification from invite session when the
1935 * session state has changed.
1936 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001937static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1938 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001939{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001941
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001942 PJSUA_LOCK();
1943
Benny Prijonoa1e69682007-05-11 15:14:34 +00001944 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001945
1946 if (!call) {
1947 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001948 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949 }
1950
Benny Prijonoe21e7842006-04-09 16:46:05 +00001951
1952 /* Get call times */
1953 switch (inv->state) {
1954 case PJSIP_INV_STATE_EARLY:
1955 case PJSIP_INV_STATE_CONNECTING:
1956 if (call->res_time.sec == 0)
1957 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00001958 call->last_code = (pjsip_status_code)
1959 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001960 pj_strncpy(&call->last_text,
1961 &e->body.tsx_state.tsx->status_text,
1962 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001963 break;
1964 case PJSIP_INV_STATE_CONFIRMED:
1965 pj_gettimeofday(&call->conn_time);
1966 break;
1967 case PJSIP_INV_STATE_DISCONNECTED:
1968 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001969 if (call->res_time.sec == 0)
1970 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001971 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00001972 call->last_code = (pjsip_status_code)
1973 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001974 pj_strncpy(&call->last_text,
1975 &e->body.tsx_state.tsx->status_text,
1976 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001977 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001978 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001979 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00001980 call->last_code = (pjsip_status_code)
1981 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001982 pj_strncpy(&call->last_text,
1983 &e->body.tsx_state.tsx->status_text,
1984 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001985 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001986 }
1987
Benny Prijono26ff9062006-02-21 23:47:00 +00001988 /* If this is an outgoing INVITE that was created because of
1989 * REFER/transfer, send NOTIFY to transferer.
1990 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001991 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001992 int st_code = -1;
1993 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1994
1995
Benny Prijonoa91a0032006-02-26 21:23:45 +00001996 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001997 case PJSIP_INV_STATE_NULL:
1998 case PJSIP_INV_STATE_CALLING:
1999 /* Do nothing */
2000 break;
2001
2002 case PJSIP_INV_STATE_EARLY:
2003 case PJSIP_INV_STATE_CONNECTING:
2004 st_code = e->body.tsx_state.tsx->status_code;
2005 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2006 break;
2007
2008 case PJSIP_INV_STATE_CONFIRMED:
2009 /* When state is confirmed, send the final 200/OK and terminate
2010 * subscription.
2011 */
2012 st_code = e->body.tsx_state.tsx->status_code;
2013 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2014 break;
2015
2016 case PJSIP_INV_STATE_DISCONNECTED:
2017 st_code = e->body.tsx_state.tsx->status_code;
2018 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2019 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002020
Benny Prijono8b1889b2006-06-06 18:40:40 +00002021 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002022 /* Nothing to do. Just to keep gcc from complaining about
2023 * unused enums.
2024 */
2025 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002026 }
2027
2028 if (st_code != -1) {
2029 pjsip_tx_data *tdata;
2030 pj_status_t status;
2031
Benny Prijonoa91a0032006-02-26 21:23:45 +00002032 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002033 ev_state, st_code,
2034 NULL, &tdata);
2035 if (status != PJ_SUCCESS) {
2036 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2037 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002038 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002039 if (status != PJ_SUCCESS) {
2040 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2041 }
2042 }
2043 }
2044 }
2045
Benny Prijono84126ab2006-02-09 09:30:09 +00002046
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002047 if (pjsua_var.ua_cfg.cb.on_call_state)
2048 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002049
2050 /* call->inv may be NULL now */
2051
Benny Prijono84126ab2006-02-09 09:30:09 +00002052 /* Destroy media session when invite session is disconnected. */
2053 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002054
Benny Prijonoa91a0032006-02-26 21:23:45 +00002055 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002056
Benny Prijono275fd682006-03-22 11:59:11 +00002057 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002058 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002059
Benny Prijono105217f2006-03-06 16:25:59 +00002060 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002061 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002062 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002063
2064 /* Reset call */
2065 reset_call(call->index);
2066
Benny Prijono84126ab2006-02-09 09:30:09 +00002067 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068
2069 PJSUA_UNLOCK();
2070}
2071
2072/*
2073 * This callback is called by invite session framework when UAC session
2074 * has forked.
2075 */
2076static void pjsua_call_on_forked( pjsip_inv_session *inv,
2077 pjsip_event *e)
2078{
2079 PJ_UNUSED_ARG(inv);
2080 PJ_UNUSED_ARG(e);
2081
2082 PJ_TODO(HANDLE_FORKED_DIALOG);
2083}
2084
2085
2086/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002087 * Disconnect call upon error.
2088 */
2089static void call_disconnect( pjsip_inv_session *inv,
2090 int code )
2091{
2092 pjsip_tx_data *tdata;
2093 pj_status_t status;
2094
2095 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2096 if (status == PJ_SUCCESS)
2097 pjsip_inv_send_msg(inv, tdata);
2098}
2099
2100/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002101 * Callback to be called when SDP offer/answer negotiation has just completed
2102 * in the session. This function will start/update media if negotiation
2103 * has succeeded.
2104 */
2105static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2106 pj_status_t status)
2107{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108 pjsua_call *call;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002109 const pjmedia_sdp_session *local_sdp;
2110 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111
2112 PJSUA_LOCK();
2113
Benny Prijonoa1e69682007-05-11 15:14:34 +00002114 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115
2116 if (status != PJ_SUCCESS) {
2117
2118 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2119
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002120 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002121 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002123 /* Disconnect call if we're not in the middle of initializing an
2124 * UAS dialog and if this is not a re-INVITE
2125 */
2126 if (inv->state != PJSIP_INV_STATE_NULL &&
2127 inv->state != PJSIP_INV_STATE_CONFIRMED)
2128 {
2129 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2130 }
2131
2132 PJSUA_UNLOCK();
2133 return;
2134 }
2135
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002136
2137 /* Get local and remote SDP */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002138 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2139 if (status != PJ_SUCCESS) {
2140 pjsua_perror(THIS_FILE,
2141 "Unable to retrieve currently active local SDP",
2142 status);
2143 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2144 PJSUA_UNLOCK();
2145 return;
2146 }
2147
2148
2149 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2150 if (status != PJ_SUCCESS) {
2151 pjsua_perror(THIS_FILE,
2152 "Unable to retrieve currently active remote SDP",
2153 status);
2154 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2155 PJSUA_UNLOCK();
2156 return;
2157 }
2158
Benny Prijonoc97608e2007-03-23 16:34:20 +00002159 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002160 if (status != PJ_SUCCESS) {
2161 pjsua_perror(THIS_FILE, "Unable to create media session",
2162 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002163 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002164 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002165 PJSUA_UNLOCK();
2166 return;
2167 }
2168
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169
2170 /* Call application callback, if any */
2171 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2172 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2173
2174
2175 PJSUA_UNLOCK();
2176}
2177
2178
2179/*
2180 * Create inactive SDP for call hold.
2181 */
2182static pj_status_t create_inactive_sdp(pjsua_call *call,
2183 pjmedia_sdp_session **p_answer)
2184{
2185 pj_status_t status;
2186 pjmedia_sdp_conn *conn;
2187 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002188 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189 pjmedia_sdp_session *sdp;
2190
Benny Prijono617c5bc2007-04-02 19:51:21 +00002191 /* Get media socket info */
2192 pjmedia_transport_get_info(call->med_tp, &skinfo);
2193
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002194 /* Create new offer */
2195 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002196 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002197 if (status != PJ_SUCCESS) {
2198 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2199 return status;
2200 }
2201
2202 /* Get SDP media connection line */
2203 conn = sdp->media[0]->conn;
2204 if (!conn)
2205 conn = sdp->conn;
2206
2207 /* Modify address */
2208 conn->addr = pj_str("0.0.0.0");
2209
2210 /* Remove existing directions attributes */
2211 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2212 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2213 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2214 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2215
2216 /* Add inactive attribute */
2217 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2218 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2219
2220 *p_answer = sdp;
2221
2222 return status;
2223}
2224
2225
2226/*
2227 * Called when session received new offer.
2228 */
2229static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2230 const pjmedia_sdp_session *offer)
2231{
2232 const char *remote_state;
2233 pjsua_call *call;
2234 pjmedia_sdp_conn *conn;
2235 pjmedia_sdp_session *answer;
2236 pj_bool_t is_remote_active;
2237 pj_status_t status;
2238
2239 PJSUA_LOCK();
2240
Benny Prijonoa1e69682007-05-11 15:14:34 +00002241 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242
2243 /*
2244 * See if remote is offering active media (i.e. not on-hold)
2245 */
2246 is_remote_active = PJ_TRUE;
2247
2248 conn = offer->media[0]->conn;
2249 if (!conn)
2250 conn = offer->conn;
2251
2252 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2253 pj_strcmp2(&conn->addr, "0")==0)
2254 {
2255 is_remote_active = PJ_FALSE;
2256
2257 }
2258 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2259 {
2260 is_remote_active = PJ_FALSE;
2261 }
2262
2263 remote_state = (is_remote_active ? "active" : "inactive");
2264
2265 /* Supply candidate answer */
2266 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2267 PJ_LOG(4,(THIS_FILE,
2268 "Call %d: RX new media offer, creating inactive SDP "
2269 "(media in offer is %s)", call->index, remote_state));
2270 status = create_inactive_sdp( call, &answer );
2271 } else {
Benny Prijono667952e2007-04-02 19:27:54 +00002272
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002273 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2274 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002275
2276 /* Init media channel */
Benny Prijonoc9f6ea72007-05-23 07:12:23 +00002277 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono667952e2007-04-02 19:27:54 +00002278 if (status != PJ_SUCCESS) {
2279 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2280 PJSUA_UNLOCK();
2281 return;
2282 }
2283
Benny Prijonoc97608e2007-03-23 16:34:20 +00002284 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002285 }
2286
2287 if (status != PJ_SUCCESS) {
2288 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2289 PJSUA_UNLOCK();
2290 return;
2291 }
2292
2293 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2294 if (status != PJ_SUCCESS) {
2295 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2296 PJSUA_UNLOCK();
2297 return;
2298 }
2299
2300 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002301}
2302
2303
2304/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002305 * Called to generate new offer.
2306 */
2307static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2308 pjmedia_sdp_session **offer)
2309{
2310 pjsua_call *call;
2311 pj_status_t status;
2312
2313 PJSUA_LOCK();
2314
2315 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2316
2317 /* See if we've put call on hold. */
2318 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2319 PJ_LOG(4,(THIS_FILE,
2320 "Call %d: call is on-hold locally, creating inactive SDP ",
2321 call->index));
2322 status = create_inactive_sdp( call, offer );
2323 } else {
2324
2325 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2326 call->index));
2327
2328 /* Init media channel */
2329 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
2330 if (status != PJ_SUCCESS) {
2331 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2332 PJSUA_UNLOCK();
2333 return;
2334 }
2335
2336 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, offer);
2337 }
2338
2339 if (status != PJ_SUCCESS) {
2340 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2341 PJSUA_UNLOCK();
2342 return;
2343 }
2344
2345
2346 PJSUA_UNLOCK();
2347}
2348
2349
2350/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002351 * Callback called by event framework when the xfer subscription state
2352 * has changed.
2353 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002354static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2355{
2356
2357 PJ_UNUSED_ARG(event);
2358
2359 /*
2360 * When subscription is accepted (got 200/OK to REFER), check if
2361 * subscription suppressed.
2362 */
2363 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2364
2365 pjsip_rx_data *rdata;
2366 pjsip_generic_string_hdr *refer_sub;
2367 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2368 pjsua_call *call;
2369
Benny Prijonoa1e69682007-05-11 15:14:34 +00002370 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002371
2372 /* Must be receipt of response message */
2373 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2374 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2375 rdata = event->body.tsx_state.src.rdata;
2376
2377 /* Find Refer-Sub header */
2378 refer_sub = (pjsip_generic_string_hdr*)
2379 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2380 &REFER_SUB, NULL);
2381
2382 /* Check if subscription is suppressed */
2383 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2384 /* Since no subscription is desired, assume that call has been
2385 * transfered successfully.
2386 */
2387 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2388 const pj_str_t ACCEPTED = { "Accepted", 8 };
2389 pj_bool_t cont = PJ_FALSE;
2390 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2391 200,
2392 &ACCEPTED,
2393 PJ_TRUE,
2394 &cont);
2395 }
2396
2397 /* Yes, subscription is suppressed.
2398 * Terminate our subscription now.
2399 */
2400 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2401 "event subcription..."));
2402 pjsip_evsub_terminate(sub, PJ_TRUE);
2403
2404 } else {
2405 /* Notify application about call transfer progress.
2406 * Initially notify with 100/Accepted status.
2407 */
2408 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2409 const pj_str_t ACCEPTED = { "Accepted", 8 };
2410 pj_bool_t cont = PJ_FALSE;
2411 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2412 100,
2413 &ACCEPTED,
2414 PJ_FALSE,
2415 &cont);
2416 }
2417 }
2418 }
2419 /*
2420 * On incoming NOTIFY, notify application about call transfer progress.
2421 */
2422 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2423 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2424 {
2425 pjsua_call *call;
2426 pjsip_msg *msg;
2427 pjsip_msg_body *body;
2428 pjsip_status_line status_line;
2429 pj_bool_t is_last;
2430 pj_bool_t cont;
2431 pj_status_t status;
2432
Benny Prijonoa1e69682007-05-11 15:14:34 +00002433 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002434
2435 /* When subscription is terminated, clear the xfer_sub member of
2436 * the inv_data.
2437 */
2438 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2439 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2440 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2441
2442 }
2443
2444 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2445 /* Application is not interested with call progress status */
2446 return;
2447 }
2448
2449 /* This better be a NOTIFY request */
2450 if (event->type == PJSIP_EVENT_TSX_STATE &&
2451 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2452 {
2453 pjsip_rx_data *rdata;
2454
2455 rdata = event->body.tsx_state.src.rdata;
2456
2457 /* Check if there's body */
2458 msg = rdata->msg_info.msg;
2459 body = msg->body;
2460 if (!body) {
2461 PJ_LOG(4,(THIS_FILE,
2462 "Warning: received NOTIFY without message body"));
2463 return;
2464 }
2465
2466 /* Check for appropriate content */
2467 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2468 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2469 {
2470 PJ_LOG(4,(THIS_FILE,
2471 "Warning: received NOTIFY with non message/sipfrag "
2472 "content"));
2473 return;
2474 }
2475
2476 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002477 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002478 &status_line);
2479 if (status != PJ_SUCCESS) {
2480 PJ_LOG(4,(THIS_FILE,
2481 "Warning: received NOTIFY with invalid "
2482 "message/sipfrag content"));
2483 return;
2484 }
2485
2486 } else {
2487 status_line.code = 500;
2488 status_line.reason = *pjsip_get_status_text(500);
2489 }
2490
2491 /* Notify application */
2492 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2493 cont = !is_last;
2494 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2495 status_line.code,
2496 &status_line.reason,
2497 is_last, &cont);
2498
2499 if (!cont) {
2500 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2501 }
2502 }
2503}
2504
2505
2506/*
2507 * Callback called by event framework when the xfer subscription state
2508 * has changed.
2509 */
2510static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002511{
2512
2513 PJ_UNUSED_ARG(event);
2514
2515 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002516 * When subscription is terminated, clear the xfer_sub member of
2517 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002518 */
2519 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002520 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002521
Benny Prijonoa1e69682007-05-11 15:14:34 +00002522 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002523 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002524 return;
2525
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002526 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002527 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002528
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002529 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002530 }
2531}
2532
2533
2534/*
2535 * Follow transfer (REFER) request.
2536 */
2537static void on_call_transfered( pjsip_inv_session *inv,
2538 pjsip_rx_data *rdata )
2539{
2540 pj_status_t status;
2541 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002542 pjsua_call *existing_call;
2543 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002544 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002545 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002546 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002547 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002548 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002549 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002550 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002551 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002552 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002553 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002554 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002555 pjsip_evsub *sub;
2556
Benny Prijonoa1e69682007-05-11 15:14:34 +00002557 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002558
Benny Prijono26ff9062006-02-21 23:47:00 +00002559 /* Find the Refer-To header */
2560 refer_to = (pjsip_generic_string_hdr*)
2561 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2562
2563 if (refer_to == NULL) {
2564 /* Invalid Request.
2565 * No Refer-To header!
2566 */
2567 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002568 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002569 return;
2570 }
2571
Benny Prijonoc8141a82006-08-20 09:12:19 +00002572 /* Find optional Refer-Sub header */
2573 refer_sub = (pjsip_generic_string_hdr*)
2574 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2575
2576 if (refer_sub) {
2577 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2578 no_refer_sub = PJ_TRUE;
2579 }
2580
Benny Prijono053f5222006-11-11 16:16:04 +00002581 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2582 * request.
2583 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002584 ref_by_hdr = (pjsip_hdr*)
2585 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002586 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002587
Benny Prijono9fc735d2006-05-28 14:58:12 +00002588 /* Notify callback */
2589 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002590 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2591 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2592 &refer_to->hvalue,
2593 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002594
2595 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002596 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002597 if (code >= 300) {
2598 /* Application rejects call transfer request */
2599 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2600 return;
2601 }
2602
Benny Prijono26ff9062006-02-21 23:47:00 +00002603 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2604 (int)inv->dlg->remote.info_str.slen,
2605 inv->dlg->remote.info_str.ptr,
2606 (int)refer_to->hvalue.slen,
2607 refer_to->hvalue.ptr));
2608
Benny Prijonoc8141a82006-08-20 09:12:19 +00002609 if (no_refer_sub) {
2610 /*
2611 * Always answer with 200.
2612 */
2613 pjsip_tx_data *tdata;
2614 const pj_str_t str_false = { "false", 5};
2615 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002616
Benny Prijonoc8141a82006-08-20 09:12:19 +00002617 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2618 if (status != PJ_SUCCESS) {
2619 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2620 status);
2621 return;
2622 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002623
Benny Prijonoc8141a82006-08-20 09:12:19 +00002624 /* Add Refer-Sub header */
2625 hdr = (pjsip_hdr*)
2626 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2627 &str_false);
2628 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002629
Benny Prijono26ff9062006-02-21 23:47:00 +00002630
Benny Prijonoc8141a82006-08-20 09:12:19 +00002631 /* Send answer */
2632 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2633 tdata);
2634 if (status != PJ_SUCCESS) {
2635 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2636 status);
2637 return;
2638 }
2639
2640 /* Don't have subscription */
2641 sub = NULL;
2642
2643 } else {
2644 struct pjsip_evsub_user xfer_cb;
2645 pjsip_hdr hdr_list;
2646
2647 /* Init callback */
2648 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002649 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002650
2651 /* Init additional header list to be sent with REFER response */
2652 pj_list_init(&hdr_list);
2653
2654 /* Create transferee event subscription */
2655 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2656 if (status != PJ_SUCCESS) {
2657 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2658 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2659 return;
2660 }
2661
2662 /* If there's Refer-Sub header and the value is "true", send back
2663 * Refer-Sub in the response with value "true" too.
2664 */
2665 if (refer_sub) {
2666 const pj_str_t str_true = { "true", 4 };
2667 pjsip_hdr *hdr;
2668
2669 hdr = (pjsip_hdr*)
2670 pjsip_generic_string_hdr_create(inv->dlg->pool,
2671 &str_refer_sub,
2672 &str_true);
2673 pj_list_push_back(&hdr_list, hdr);
2674
2675 }
2676
2677 /* Accept the REFER request, send 200 (OK). */
2678 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2679
2680 /* Create initial NOTIFY request */
2681 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2682 100, NULL, &tdata);
2683 if (status != PJ_SUCCESS) {
2684 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2685 status);
2686 return;
2687 }
2688
2689 /* Send initial NOTIFY request */
2690 status = pjsip_xfer_send_request( sub, tdata);
2691 if (status != PJ_SUCCESS) {
2692 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2693 return;
2694 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002695 }
2696
2697 /* We're cheating here.
2698 * We need to get a null terminated string from a pj_str_t.
2699 * So grab the pointer from the hvalue and NULL terminate it, knowing
2700 * that the NULL position will be occupied by a newline.
2701 */
2702 uri = refer_to->hvalue.ptr;
2703 uri[refer_to->hvalue.slen] = '\0';
2704
Benny Prijono053f5222006-11-11 16:16:04 +00002705 /* Init msg_data */
2706 pjsua_msg_data_init(&msg_data);
2707
2708 /* If Referred-By header is present in the REFER request, copy this
2709 * to the outgoing INVITE request.
2710 */
2711 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002712 pjsip_hdr *dup = (pjsip_hdr*)
2713 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00002714 pj_list_push_back(&msg_data.hdr_list, dup);
2715 }
2716
Benny Prijono26ff9062006-02-21 23:47:00 +00002717 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002718 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002719 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002720 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002721 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002722 if (status != PJ_SUCCESS) {
2723
Benny Prijonoc8141a82006-08-20 09:12:19 +00002724 /* Notify xferer about the error (if we have subscription) */
2725 if (sub) {
2726 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2727 500, NULL, &tdata);
2728 if (status != PJ_SUCCESS) {
2729 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2730 status);
2731 return;
2732 }
2733 status = pjsip_xfer_send_request(sub, tdata);
2734 if (status != PJ_SUCCESS) {
2735 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2736 status);
2737 return;
2738 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002739 }
2740 return;
2741 }
2742
Benny Prijonoc8141a82006-08-20 09:12:19 +00002743 if (sub) {
2744 /* Put the server subscription in inv_data.
2745 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2746 * reported back to the server subscription.
2747 */
2748 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002749
Benny Prijonoc8141a82006-08-20 09:12:19 +00002750 /* Put the invite_data in the subscription. */
2751 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2752 &pjsua_var.calls[new_call]);
2753 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002754}
2755
2756
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002757
Benny Prijono26ff9062006-02-21 23:47:00 +00002758/*
2759 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002760 * session. We use this to trap:
2761 * - incoming REFER request.
2762 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002763 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002764static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2765 pjsip_transaction *tsx,
2766 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002767{
Benny Prijonoa1e69682007-05-11 15:14:34 +00002768 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002769
2770 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002771
Benny Prijono26ff9062006-02-21 23:47:00 +00002772 if (tsx->role==PJSIP_ROLE_UAS &&
2773 tsx->state==PJSIP_TSX_STATE_TRYING &&
2774 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2775 {
2776 /*
2777 * Incoming REFER request.
2778 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002779 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002780
Benny Prijono26ff9062006-02-21 23:47:00 +00002781 }
Benny Prijonob0808372006-03-02 21:18:58 +00002782 else if (tsx->role==PJSIP_ROLE_UAS &&
2783 tsx->state==PJSIP_TSX_STATE_TRYING &&
2784 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2785 {
2786 /*
2787 * Incoming MESSAGE request!
2788 */
2789 pjsip_rx_data *rdata;
2790 pjsip_msg *msg;
2791 pjsip_accept_hdr *accept_hdr;
2792 pj_status_t status;
2793
2794 rdata = e->body.tsx_state.src.rdata;
2795 msg = rdata->msg_info.msg;
2796
2797 /* Request MUST have message body, with Content-Type equal to
2798 * "text/plain".
2799 */
2800 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2801
2802 pjsip_hdr hdr_list;
2803
2804 pj_list_init(&hdr_list);
2805 pj_list_push_back(&hdr_list, accept_hdr);
2806
2807 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2808 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002809 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002810 return;
2811 }
2812
2813 /* Respond with 200 first, so that remote doesn't retransmit in case
2814 * the UI takes too long to process the message.
2815 */
2816 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2817
2818 /* Process MESSAGE request */
2819 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2820 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002821
Benny Prijonob0808372006-03-02 21:18:58 +00002822 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002823 else if (tsx->role == PJSIP_ROLE_UAC &&
2824 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002825 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002826 /* Handle outgoing pager status */
2827 if (tsx->status_code >= 200) {
2828 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002829
Benny Prijonoa1e69682007-05-11 15:14:34 +00002830 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002831 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002832
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002833 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2834 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2835 &im_data->to,
2836 &im_data->body,
2837 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00002838 (pjsip_status_code)
2839 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002840 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002841 }
Benny Prijonofccab712006-02-22 22:23:22 +00002842 }
Benny Prijono834aee32006-02-19 01:38:06 +00002843 }
Benny Prijono834aee32006-02-19 01:38:06 +00002844
Benny Prijono26ff9062006-02-21 23:47:00 +00002845
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002846 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002847}