blob: 6b40becf14cb17f18be40132ef0f8f47318ae940 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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/*
53 * This callback is called when transaction state has changed in INVITE
54 * session. We use this to trap:
55 * - incoming REFER request.
56 * - incoming MESSAGE request.
57 */
58static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
59 pjsip_transaction *tsx,
60 pjsip_event *e);
61
62
63/* Destroy the call's media */
64static pj_status_t call_destroy_media(int call_id);
65
66/* Create inactive SDP for call hold. */
67static pj_status_t create_inactive_sdp(pjsua_call *call,
68 pjmedia_sdp_session **p_answer);
69
Benny Prijonod524e822006-09-22 12:48:18 +000070/*
71 * Callback called by event framework when the xfer subscription state
72 * has changed.
73 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000074static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
75static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
76
77/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078 * Reset call descriptor.
79 */
80static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000081{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000083
Benny Prijonoeebe9af2006-06-13 22:57:13 +000084 call->index = id;
85 call->inv = NULL;
86 call->user_data = NULL;
87 call->session = NULL;
88 call->xfer_sub = NULL;
89 call->last_code = 0;
90 call->conf_slot = PJSUA_INVALID_ID;
91 call->last_text.ptr = call->last_text_buf_;
92 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +000093 call->conn_time.sec = 0;
94 call->conn_time.msec = 0;
95 call->res_time.sec = 0;
96 call->res_time.msec = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000097}
98
99
Benny Prijono275fd682006-03-22 11:59:11 +0000100/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000102 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000104{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000105 pjsip_inv_callback inv_cb;
106 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000107 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000109
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 /* Init calls array. */
111 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
112 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000113
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000114 /* Copy config */
115 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000116
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000118 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000119 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
120 inv_cb.on_new_session = &pjsua_call_on_forked;
121 inv_cb.on_media_update = &pjsua_call_on_media_update;
122 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
123 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000124
Benny Prijono275fd682006-03-22 11:59:11 +0000125
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 /* Initialize invite session module: */
127 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
128 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
129
Benny Prijonoc8141a82006-08-20 09:12:19 +0000130 /* Add "norefersub" in Supported header */
131 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
132 NULL, 1, &str_norefersub);
133
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 return status;
135}
136
137
138/*
139 * Start call subsystem.
140 */
141pj_status_t pjsua_call_subsys_start(void)
142{
143 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000144 return PJ_SUCCESS;
145}
146
147
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000149 * Get maximum number of calls configured in pjsua.
150 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000151PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000152{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000154}
155
156
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157/*
158 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000159 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000160PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000161{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000163}
164
165
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000166/*
167 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
170 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
179 if (!pjsua_var.calls[i].inv)
180 continue;
181 ids[c] = i;
182 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183 }
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188
189 return PJ_SUCCESS;
190}
191
192
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193/*
194 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
197 const pj_str_t *dest_uri,
198 unsigned options,
199 void *user_data,
200 const pjsua_msg_data *msg_data,
201 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000202{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000203 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000204 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000205 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 pjsua_acc *acc;
207 pjsua_call *call;
208 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000209 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000210 pjsip_tx_data *tdata;
211 pj_status_t status;
212
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 /* Check that account is valid */
215 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216 PJ_EINVAL);
217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 /* Options must be zero for now */
219 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
220
Benny Prijono320fa4d2006-12-07 10:09:16 +0000221 /* Check arguments */
222 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
223
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 PJSUA_LOCK();
225
226 acc = &pjsua_var.acc[acc_id];
227 if (!acc->valid) {
228 pjsua_perror(THIS_FILE, "Unable to make call because account "
229 "is not valid", PJ_EINVALIDOP);
230 PJSUA_UNLOCK();
231 return PJ_EINVALIDOP;
232 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000233
Benny Prijonoa91a0032006-02-26 21:23:45 +0000234 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
236 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000237 break;
238 }
239
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 if (call_id == pjsua_var.ua_cfg.max_calls) {
241 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
242 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000243 return PJ_ETOOMANY;
244 }
245
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000246 call = &pjsua_var.calls[call_id];
247
Benny Prijono320fa4d2006-12-07 10:09:16 +0000248 /* Verify that destination URI is valid before calling
249 * pjsua_acc_create_uac_contact, or otherwise there
250 * a misleading "Invalid Contact URI" error will be printed
251 * when pjsua_acc_create_uac_contact() fails.
252 */
253 if (1) {
254 pj_pool_t *pool;
255 pjsip_uri *uri;
256 pj_str_t dup;
257
258 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
259 if (!pool) {
260 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
261 PJSUA_UNLOCK();
262 return PJ_ENOMEM;
263 }
264
265 pj_strdup_with_null(pool, &dup, dest_uri);
266 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
267 pj_pool_release(pool);
268
269 if (uri == NULL) {
270 pjsua_perror(THIS_FILE, "Unable to make call",
271 PJSIP_EINVALIDREQURI);
272 PJSUA_UNLOCK();
273 return PJSIP_EINVALIDREQURI;
274 }
275 }
276
Benny Prijono093d3022006-09-24 00:07:11 +0000277 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
278 (int)dest_uri->slen, dest_uri->ptr));
279
Benny Prijonoe21e7842006-04-09 16:46:05 +0000280 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000282
Benny Prijonoe21e7842006-04-09 16:46:05 +0000283 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000284 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000285
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000286 /* Create suitable Contact header */
287 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
288 acc_id, dest_uri);
289 if (status != PJ_SUCCESS) {
290 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
291 PJSUA_UNLOCK();
292 return status;
293 }
294
Benny Prijonoe21e7842006-04-09 16:46:05 +0000295 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000296 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000297 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000298 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000299 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000300 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000301 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000302 return status;
303 }
304
305 /* Get media capability from media endpoint: */
306
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000307 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
308 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000309 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000310 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000311 goto on_error;
312 }
313
314 /* Create the INVITE session: */
315
316 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
317 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000318 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000319 goto on_error;
320 }
321
322
323 /* Create and associate our data in the session. */
324
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000325 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327 dlg->mod_data[pjsua_var.mod.id] = call;
328 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000329
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330 /* Attach user data */
331 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000333 /* If account is locked to specific transport, then lock dialog
334 * to this transport too.
335 */
336 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
337 pjsip_tpselector tp_sel;
338
339 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
340 pjsip_dlg_set_transport(dlg, &tp_sel);
341 }
342
Benny Prijono84126ab2006-02-09 09:30:09 +0000343 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344 if (!pj_list_empty(&acc->route_set))
345 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000346
347
348 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000350 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000351 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000352 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000353
354
355 /* Create initial INVITE: */
356
357 status = pjsip_inv_invite(inv, &tdata);
358 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000359 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
360 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000361 goto on_error;
362 }
363
364
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 /* Add additional headers etc */
366
367 pjsua_process_msg_data( tdata, msg_data);
368
Benny Prijono093d3022006-09-24 00:07:11 +0000369 /* Must increment call counter now */
370 ++pjsua_var.call_cnt;
371
Benny Prijono84126ab2006-02-09 09:30:09 +0000372 /* Send initial INVITE: */
373
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000374 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000375 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000376 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
377 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000378
379 /* Upon failure to send first request, both dialog and invite
380 * session would have been cleared.
381 */
382 inv = NULL;
383 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000384 goto on_error;
385 }
386
Benny Prijono84126ab2006-02-09 09:30:09 +0000387 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000388
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000389 if (p_call_id)
390 *p_call_id = call_id;
391
392 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000393
394 return PJ_SUCCESS;
395
396
397on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000398 if (inv != NULL) {
399 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000400 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000401 pjsip_dlg_terminate(dlg);
402 }
403
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 if (call_id != -1) {
405 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000406 }
407
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000408 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000409 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000410}
411
412
413/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000414 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000415 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000416 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000417pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000418{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000419 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000420 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000421 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000422 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
423 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000424 pjsip_tx_data *response = NULL;
425 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000426 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 int acc_id;
428 pjsua_call *call;
429 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000430 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000431 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000432
Benny Prijono26ff9062006-02-21 23:47:00 +0000433 /* Don't want to handle anything but INVITE */
434 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
435 return PJ_FALSE;
436
437 /* Don't want to handle anything that's already associated with
438 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000439 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000440 if (dlg || tsx)
441 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000442
Benny Prijono148c9dd2006-09-19 13:37:53 +0000443 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000444
Benny Prijono26ff9062006-02-21 23:47:00 +0000445 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
447 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000448 break;
449 }
450
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
452 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000453 PJSIP_SC_BUSY_HERE, NULL,
454 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000455 PJ_LOG(2,(THIS_FILE,
456 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000457 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 return PJ_TRUE;
459 }
460
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 /* Clear call descriptor */
462 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000463
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000464 call = &pjsua_var.calls[call_id];
465
466 /* Mark call start time. */
467 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000468
Benny Prijono053f5222006-11-11 16:16:04 +0000469 /* Check INVITE request for Replaces header. If Replaces header is
470 * present, the function will make sure that we can handle the request.
471 */
472 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
473 &response);
474 if (status != PJ_SUCCESS) {
475 /*
476 * Something wrong with the Replaces header.
477 */
478 if (response) {
479 pjsip_response_addr res_addr;
480
481 pjsip_get_response_addr(response->pool, rdata, &res_addr);
482 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
483 NULL, NULL);
484
485 } else {
486
487 /* Respond with 500 (Internal Server Error) */
488 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
489 NULL, NULL);
490 }
491
492 PJSUA_UNLOCK();
493 return PJ_TRUE;
494 }
495
496 /* If this INVITE request contains Replaces header, notify application
497 * about the request so that application can do subsequent checking
498 * if it wants to.
499 */
500 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
501 pjsua_call *replaced_call;
502 int st_code = 200;
503 pj_str_t st_text = { "OK", 2 };
504
505 /* Get the replaced call instance */
506 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
507
508 /* Notify application */
509 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
510 rdata, &st_code, &st_text);
511
512 /* Must specify final response */
513 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
514
515 /* Check if application rejects this request. */
516 if (st_code >= 300) {
517
518 if (st_text.slen == 2)
519 st_text = *pjsip_get_status_text(st_code);
520
521 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
522 st_code, &st_text, NULL, NULL, NULL);
523 PJSUA_UNLOCK();
524 return PJ_TRUE;
525 }
526 }
527
528
Benny Prijono26ff9062006-02-21 23:47:00 +0000529 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000530 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
531 rdata->tp_info.pool, 1,
532 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000533 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000534 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000535 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000536 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000537 return PJ_TRUE;
538 }
539
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000540
541 /* Verify that we can handle the request. */
542 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
543 pjsua_var.endpt, &response);
544 if (status != PJ_SUCCESS) {
545
546 /*
547 * No we can't handle the incoming INVITE request.
548 */
549
550 if (response) {
551 pjsip_response_addr res_addr;
552
553 pjsip_get_response_addr(response->pool, rdata, &res_addr);
554 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
555 NULL, NULL);
556
557 } else {
558
559 /* Respond with 500 (Internal Server Error) */
560 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
561 NULL, NULL);
562 }
563
564 PJSUA_UNLOCK();
565 return PJ_TRUE;
566 }
567
568
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000569 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000570 * Get which account is most likely to be associated with this incoming
571 * call. We need the account to find which contact URI to put for
572 * the call.
573 */
Benny Prijono093d3022006-09-24 00:07:11 +0000574 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000575
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000576 /* Get suitable Contact header */
577 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
578 acc_id, rdata);
579 if (status != PJ_SUCCESS) {
580 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
581 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
582 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000583 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000584 return PJ_TRUE;
585 }
586
Benny Prijono26ff9062006-02-21 23:47:00 +0000587 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000588 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000589 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000590 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000592 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000593 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000594 return PJ_TRUE;
595 }
596
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000597 /* Set credentials */
598 if (pjsua_var.acc[acc_id].cred_cnt) {
599 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
600 pjsua_var.acc[acc_id].cred_cnt,
601 pjsua_var.acc[acc_id].cred);
602 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000603
604 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000605 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
606 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000607 pjsip_hdr hdr_list;
608 pjsip_warning_hdr *w;
609
610 w = pjsip_warning_hdr_create_from_status(dlg->pool,
611 pjsip_endpt_name(pjsua_var.endpt),
612 status);
613 pj_list_init(&hdr_list);
614 pj_list_push_back(&hdr_list, w);
615
616 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
617
618 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000619 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000620 */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000621 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000622 return PJ_TRUE;
623 }
624
625
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000626 /* Create and attach pjsua_var data to the dialog: */
627 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000628
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000629 dlg->mod_data[pjsua_var.mod.id] = call;
630 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000631
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000632 /* If account is locked to specific transport, then lock dialog
633 * to this transport too.
634 */
635 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
636 pjsip_tpselector tp_sel;
637
638 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
639 pjsip_dlg_set_transport(dlg, &tp_sel);
640 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000641
Benny Prijono64f851e2006-02-23 13:49:28 +0000642 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000643 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000644 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000645 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000646 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000647 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
648 status);
649
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000650 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
651 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000652 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000653 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000654
655 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000656 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000657 if (status != PJ_SUCCESS)
658 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000659 }
660
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000661 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000662
Benny Prijono105217f2006-03-06 16:25:59 +0000663
Benny Prijono053f5222006-11-11 16:16:04 +0000664 /* Check if this request should replace existing call */
665 if (replaced_dlg) {
666 pjsip_inv_session *replaced_inv;
667 struct pjsua_call *replaced_call;
668 pjsip_tx_data *tdata;
669
670 /* Get the invite session in the dialog */
671 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
672
673 /* Get the replaced call instance */
674 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
675
676 /* Notify application */
677 if (pjsua_var.ua_cfg.cb.on_call_replaced)
678 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
679 call_id);
680
681 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
682 call_id));
683
684 /* Answer the new call with 200 response */
685 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
686 if (status == PJ_SUCCESS)
687 status = pjsip_inv_send_msg(inv, tdata);
688
689 if (status != PJ_SUCCESS)
690 pjsua_perror(THIS_FILE, "Error answering session", status);
691
692
693 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
694 replaced_call->index));
695
696 /* Disconnect replaced invite session */
697 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
698 &tdata);
699 if (status == PJ_SUCCESS && tdata)
700 status = pjsip_inv_send_msg(replaced_inv, tdata);
701
702 if (status != PJ_SUCCESS)
703 pjsua_perror(THIS_FILE, "Error terminating session", status);
704
705
706 } else {
707
Benny Prijonob5388cf2007-01-04 22:45:08 +0000708 /* Notify application if on_incoming_call() is overriden,
709 * otherwise hangup the call with 480
710 */
711 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000712 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000713 } else {
714 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
715 NULL, NULL);
716 }
Benny Prijono053f5222006-11-11 16:16:04 +0000717 }
718
Benny Prijono8b1889b2006-06-06 18:40:40 +0000719
Benny Prijono26ff9062006-02-21 23:47:00 +0000720 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000721 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000722 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000723}
724
725
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000726
727/*
728 * Check if the specified call has active INVITE session and the INVITE
729 * session has not been disconnected.
730 */
731PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
732{
733 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
734 PJ_EINVAL);
735 return pjsua_var.calls[call_id].inv != NULL &&
736 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
737}
738
739
740/*
741 * Check if call has an active media session.
742 */
743PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
744{
745 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
746 PJ_EINVAL);
747 return pjsua_var.calls[call_id].session != NULL;
748}
749
750
Benny Prijono148c9dd2006-09-19 13:37:53 +0000751/* Acquire lock to the specified call_id */
752static pj_status_t acquire_call(const char *title,
753 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000754 pjsua_call **p_call,
755 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000756{
757 enum { MAX_RETRY=50 };
758 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000759 pjsua_call *call = NULL;
760 pj_bool_t has_pjsua_lock = PJ_FALSE;
761 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000762
763 for (retry=0; retry<MAX_RETRY; ++retry) {
764
765 has_pjsua_lock = PJ_FALSE;
766
767 status = PJSUA_TRY_LOCK();
768 if (status != PJ_SUCCESS) {
769 pj_thread_sleep(retry/10);
770 continue;
771 }
772
773 has_pjsua_lock = PJ_TRUE;
774 call = &pjsua_var.calls[call_id];
775
776 if (call->inv == NULL) {
777 PJSUA_UNLOCK();
778 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
779 return PJSIP_ESESSIONTERMINATED;
780 }
781
782 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
783 if (status != PJ_SUCCESS) {
784 PJSUA_UNLOCK();
785 pj_thread_sleep(retry/10);
786 continue;
787 }
788
789 PJSUA_UNLOCK();
790
791 break;
792 }
793
794 if (status != PJ_SUCCESS) {
795 if (has_pjsua_lock == PJ_FALSE)
796 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
797 "(possibly system has deadlocked) in %s",
798 title));
799 else
800 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
801 "(possibly system has deadlocked) in %s",
802 title));
803 return PJ_ETIMEDOUT;
804 }
805
806 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000807 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000808
809 return PJ_SUCCESS;
810}
811
812
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000813/*
814 * Get the conference port identification associated with the call.
815 */
816PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
817{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000818 pjsua_call *call;
819 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000820 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000821 pj_status_t status;
822
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000823 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
824 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000825
Benny Prijonodc752ca2006-09-22 16:55:42 +0000826 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000827 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000828 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000829
830 port_id = call->conf_slot;
831
Benny Prijonodc752ca2006-09-22 16:55:42 +0000832 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000833
834 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000835}
836
837
Benny Prijono148c9dd2006-09-19 13:37:53 +0000838
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000839/*
840 * Obtain detail information about the specified call.
841 */
842PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
843 pjsua_call_info *info)
844{
845 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000846 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000847 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000848
849 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
850 PJ_EINVAL);
851
Benny Prijonoac623b32006-07-03 15:19:31 +0000852 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000853
Benny Prijonodc752ca2006-09-22 16:55:42 +0000854 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000855 if (status != PJ_SUCCESS) {
856 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000857 }
858
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000859 /* id and role */
860 info->id = call_id;
861 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000862 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000863
864 /* local info */
865 info->local_info.ptr = info->buf_.local_info;
866 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
867 sizeof(info->buf_.local_info));
868
869 /* local contact */
870 info->local_contact.ptr = info->buf_.local_contact;
871 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
872 call->inv->dlg->local.contact->uri,
873 info->local_contact.ptr,
874 sizeof(info->buf_.local_contact));
875
876 /* remote info */
877 info->remote_info.ptr = info->buf_.remote_info;
878 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
879 sizeof(info->buf_.remote_info));
880
881 /* remote contact */
882 if (call->inv->dlg->remote.contact) {
883 int len;
884 info->remote_contact.ptr = info->buf_.remote_contact;
885 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
886 call->inv->dlg->remote.contact->uri,
887 info->remote_contact.ptr,
888 sizeof(info->buf_.remote_contact));
889 if (len < 0) len = 0;
890 info->remote_contact.slen = len;
891 } else {
892 info->remote_contact.slen = 0;
893 }
894
895 /* call id */
896 info->call_id.ptr = info->buf_.call_id;
897 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
898 sizeof(info->buf_.call_id));
899
900 /* state, state_text */
901 info->state = call->inv->state;
902 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
903
904 /* If call is disconnected, set the last_status from the cause code */
905 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
906 /* last_status, last_status_text */
907 info->last_status = call->inv->cause;
908
909 info->last_status_text.ptr = info->buf_.last_status_text;
910 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
911 sizeof(info->buf_.last_status_text));
912 } else {
913 /* last_status, last_status_text */
914 info->last_status = call->last_code;
915
916 info->last_status_text.ptr = info->buf_.last_status_text;
917 pj_strncpy(&info->last_status_text, &call->last_text,
918 sizeof(info->buf_.last_status_text));
919 }
920
921 /* media status and dir */
922 info->media_status = call->media_st;
923 info->media_dir = call->media_dir;
924
925
926 /* conference slot number */
927 info->conf_slot = call->conf_slot;
928
929 /* calculate duration */
930 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
931
932 info->total_duration = call->dis_time;
933 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
934
935 if (call->conn_time.sec) {
936 info->connect_duration = call->dis_time;
937 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
938 }
939
940 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
941
942 pj_gettimeofday(&info->total_duration);
943 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
944
945 pj_gettimeofday(&info->connect_duration);
946 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
947
948 } else {
949 pj_gettimeofday(&info->total_duration);
950 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
951 }
952
Benny Prijonodc752ca2006-09-22 16:55:42 +0000953 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000954
955 return PJ_SUCCESS;
956}
957
958
959/*
960 * Attach application specific data to the call.
961 */
962PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
963 void *user_data)
964{
965 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
966 PJ_EINVAL);
967 pjsua_var.calls[call_id].user_data = user_data;
968
969 return PJ_SUCCESS;
970}
971
972
973/*
974 * Get user data attached to the call.
975 */
976PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
977{
978 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
979 NULL);
980 return pjsua_var.calls[call_id].user_data;
981}
982
983
984/*
985 * Send response to incoming INVITE request.
986 */
987PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
988 unsigned code,
989 const pj_str_t *reason,
990 const pjsua_msg_data *msg_data)
991{
992 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000993 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000994 pjsip_tx_data *tdata;
995 pj_status_t status;
996
997 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
998 PJ_EINVAL);
999
Benny Prijonodc752ca2006-09-22 16:55:42 +00001000 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001001 if (status != PJ_SUCCESS)
1002 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001003
Benny Prijono2e507c22006-06-23 15:04:11 +00001004 if (call->res_time.sec == 0)
1005 pj_gettimeofday(&call->res_time);
1006
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001007 /* Create response message */
1008 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1009 if (status != PJ_SUCCESS) {
1010 pjsua_perror(THIS_FILE, "Error creating response",
1011 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001012 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001013 return status;
1014 }
1015
1016 /* Add additional headers etc */
1017 pjsua_process_msg_data( tdata, msg_data);
1018
1019 /* Send the message */
1020 status = pjsip_inv_send_msg(call->inv, tdata);
1021 if (status != PJ_SUCCESS)
1022 pjsua_perror(THIS_FILE, "Error sending response",
1023 status);
1024
Benny Prijonodc752ca2006-09-22 16:55:42 +00001025 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001026
1027 return status;
1028}
1029
1030
1031/*
1032 * Hangup call by using method that is appropriate according to the
1033 * call state.
1034 */
1035PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1036 unsigned code,
1037 const pj_str_t *reason,
1038 const pjsua_msg_data *msg_data)
1039{
1040 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001041 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001042 pj_status_t status;
1043 pjsip_tx_data *tdata;
1044
1045
Benny Prijono148c9dd2006-09-19 13:37:53 +00001046 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1047 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1048 call_id));
1049 }
1050
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001051 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1052 PJ_EINVAL);
1053
Benny Prijonodc752ca2006-09-22 16:55:42 +00001054 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001055 if (status != PJ_SUCCESS)
1056 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001057
1058 if (code==0) {
1059 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1060 code = PJSIP_SC_OK;
1061 else if (call->inv->role == PJSIP_ROLE_UAS)
1062 code = PJSIP_SC_DECLINE;
1063 else
1064 code = PJSIP_SC_REQUEST_TERMINATED;
1065 }
1066
1067 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1068 if (status != PJ_SUCCESS) {
1069 pjsua_perror(THIS_FILE,
1070 "Failed to create end session message",
1071 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001072 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001073 return status;
1074 }
1075
1076 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1077 * as p_tdata when INVITE transaction has not been answered
1078 * with any provisional responses.
1079 */
1080 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001081 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001082 return PJ_SUCCESS;
1083 }
1084
1085 /* Add additional headers etc */
1086 pjsua_process_msg_data( tdata, msg_data);
1087
1088 /* Send the message */
1089 status = pjsip_inv_send_msg(call->inv, tdata);
1090 if (status != PJ_SUCCESS) {
1091 pjsua_perror(THIS_FILE,
1092 "Failed to send end session message",
1093 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001094 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095 return status;
1096 }
1097
Benny Prijonodc752ca2006-09-22 16:55:42 +00001098 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001099
1100 return PJ_SUCCESS;
1101}
1102
1103
1104/*
1105 * Put the specified call on hold.
1106 */
1107PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1108 const pjsua_msg_data *msg_data)
1109{
1110 pjmedia_sdp_session *sdp;
1111 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001112 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 pjsip_tx_data *tdata;
1114 pj_status_t status;
1115
1116 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1117 PJ_EINVAL);
1118
Benny Prijonodc752ca2006-09-22 16:55:42 +00001119 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001120 if (status != PJ_SUCCESS)
1121 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001123
1124 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1125 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001126 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001127 return PJSIP_ESESSIONSTATE;
1128 }
1129
1130 status = create_inactive_sdp(call, &sdp);
1131 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001132 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 return status;
1134 }
1135
1136 /* Create re-INVITE with new offer */
1137 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1138 if (status != PJ_SUCCESS) {
1139 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001140 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001141 return status;
1142 }
1143
1144 /* Add additional headers etc */
1145 pjsua_process_msg_data( tdata, msg_data);
1146
1147 /* Send the request */
1148 status = pjsip_inv_send_msg( call->inv, tdata);
1149 if (status != PJ_SUCCESS) {
1150 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001151 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152 return status;
1153 }
1154
Benny Prijonodc752ca2006-09-22 16:55:42 +00001155 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001156
1157 return PJ_SUCCESS;
1158}
1159
1160
1161/*
1162 * Send re-INVITE (to release hold).
1163 */
1164PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1165 pj_bool_t unhold,
1166 const pjsua_msg_data *msg_data)
1167{
1168 pjmedia_sdp_session *sdp;
1169 pjsip_tx_data *tdata;
1170 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001171 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172 pj_status_t status;
1173
1174
1175 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1176 PJ_EINVAL);
1177
Benny Prijonodc752ca2006-09-22 16:55:42 +00001178 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001179 if (status != PJ_SUCCESS)
1180 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001181
1182 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1183 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001184 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 return PJSIP_ESESSIONSTATE;
1186 }
1187
1188 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001189 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001190 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001191 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
1192 1, &call->skinfo, &sdp);
1193 if (status != PJ_SUCCESS) {
1194 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1195 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001196 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001197 return status;
1198 }
1199
1200 /* Create re-INVITE with new offer */
1201 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1202 if (status != PJ_SUCCESS) {
1203 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001204 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205 return status;
1206 }
1207
1208 /* Add additional headers etc */
1209 pjsua_process_msg_data( tdata, msg_data);
1210
1211 /* Send the request */
1212 status = pjsip_inv_send_msg( call->inv, tdata);
1213 if (status != PJ_SUCCESS) {
1214 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001215 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001216 return status;
1217 }
1218
Benny Prijonodc752ca2006-09-22 16:55:42 +00001219 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001220
1221 return PJ_SUCCESS;
1222}
1223
1224
1225/*
1226 * Initiate call transfer to the specified address.
1227 */
1228PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1229 const pj_str_t *dest,
1230 const pjsua_msg_data *msg_data)
1231{
1232 pjsip_evsub *sub;
1233 pjsip_tx_data *tdata;
1234 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001235 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001236 pjsip_generic_string_hdr *gs_hdr;
1237 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001238 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239 pj_status_t status;
1240
1241
1242 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1243 PJ_EINVAL);
1244
Benny Prijonodc752ca2006-09-22 16:55:42 +00001245 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001246 if (status != PJ_SUCCESS)
1247 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001249
Benny Prijonod524e822006-09-22 12:48:18 +00001250 /* Create xfer client subscription. */
1251 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001252 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001253
1254 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001255 if (status != PJ_SUCCESS) {
1256 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001257 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258 return status;
1259 }
1260
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001261 /* Associate this call with the client subscription */
1262 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 /*
1265 * Create REFER request.
1266 */
1267 status = pjsip_xfer_initiate(sub, dest, &tdata);
1268 if (status != PJ_SUCCESS) {
1269 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001270 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001271 return status;
1272 }
1273
Benny Prijono053f5222006-11-11 16:16:04 +00001274 /* Add Referred-By header */
1275 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1276 &dlg->local.info_str);
1277 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1278
1279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280 /* Add additional headers etc */
1281 pjsua_process_msg_data( tdata, msg_data);
1282
1283 /* Send. */
1284 status = pjsip_xfer_send_request(sub, tdata);
1285 if (status != PJ_SUCCESS) {
1286 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001287 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288 return status;
1289 }
1290
1291 /* For simplicity (that's what this program is intended to be!),
1292 * leave the original invite session as it is. More advanced application
1293 * may want to hold the INVITE, or terminate the invite, or whatever.
1294 */
1295
Benny Prijonodc752ca2006-09-22 16:55:42 +00001296 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001297
1298 return PJ_SUCCESS;
1299
1300}
1301
1302
1303/*
Benny Prijono053f5222006-11-11 16:16:04 +00001304 * Initiate attended call transfer to the specified address.
1305 */
1306PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1307 pjsua_call_id dest_call_id,
1308 unsigned options,
1309 const pjsua_msg_data *msg_data)
1310{
1311 pjsua_call *dest_call;
1312 pjsip_dialog *dest_dlg;
1313 char str_dest_buf[512];
1314 pj_str_t str_dest;
1315 int len;
1316 pjsip_uri *uri;
1317 pj_status_t status;
1318
1319
1320 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1321 PJ_EINVAL);
1322 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1323 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1324 PJ_EINVAL);
1325
1326 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1327 &dest_call, &dest_dlg);
1328 if (status != PJ_SUCCESS)
1329 return status;
1330
1331 /*
1332 * Create REFER destination URI with Replaces field.
1333 */
1334
1335 /* Make sure we have sufficient buffer's length */
1336 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1337 dest_dlg->call_id->id.slen +
1338 dest_dlg->remote.info->tag.slen +
1339 dest_dlg->local.info->tag.slen + 32
1340 < sizeof(str_dest_buf), PJSIP_EURITOOLONG);
1341
1342 /* Print URI */
1343 str_dest_buf[0] = '<';
1344 str_dest.slen = 1;
1345
1346 uri = pjsip_uri_get_uri(dest_dlg->remote.info->uri);
1347 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1348 str_dest_buf+1, sizeof(str_dest_buf)-1);
1349 if (len < 0)
1350 return PJSIP_EURITOOLONG;
1351
1352 str_dest.slen += len;
1353
1354
1355 /* Build the URI */
1356 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1357 sizeof(str_dest_buf) - str_dest.slen,
1358 "?%s"
1359 "Replaces=%.*s"
1360 "%%3Bto-tag%%3D%.*s"
1361 "%%3Bfrom-tag%%3D%.*s>",
1362 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1363 "" : "Require=replaces&"),
1364 (int)dest_dlg->call_id->id.slen,
1365 dest_dlg->call_id->id.ptr,
1366 (int)dest_dlg->remote.info->tag.slen,
1367 dest_dlg->remote.info->tag.ptr,
1368 (int)dest_dlg->local.info->tag.slen,
1369 dest_dlg->local.info->tag.ptr);
1370
1371 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1372 PJSIP_EURITOOLONG);
1373
1374 str_dest.ptr = str_dest_buf;
1375 str_dest.slen += len;
1376
1377 pjsip_dlg_dec_lock(dest_dlg);
1378
1379 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1380}
1381
1382
1383/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 * Send DTMF digits to remote using RFC 2833 payload formats.
1385 */
1386PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1387 const pj_str_t *digits)
1388{
1389 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001390 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391 pj_status_t status;
1392
1393 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1394 PJ_EINVAL);
1395
Benny Prijonodc752ca2006-09-22 16:55:42 +00001396 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001397 if (status != PJ_SUCCESS)
1398 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001399
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 if (!call->session) {
1401 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001402 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001403 return PJ_EINVALIDOP;
1404 }
1405
1406 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1407
Benny Prijonodc752ca2006-09-22 16:55:42 +00001408 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409
1410 return status;
1411}
1412
1413
1414/**
1415 * Send instant messaging inside INVITE session.
1416 */
1417PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1418 const pj_str_t *mime_type,
1419 const pj_str_t *content,
1420 const pjsua_msg_data *msg_data,
1421 void *user_data)
1422{
1423 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001424 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001425 const pj_str_t mime_text_plain = pj_str("text/plain");
1426 pjsip_media_type ctype;
1427 pjsua_im_data *im_data;
1428 pjsip_tx_data *tdata;
1429 pj_status_t status;
1430
1431
1432 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1433 PJ_EINVAL);
1434
Benny Prijonodc752ca2006-09-22 16:55:42 +00001435 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001436 if (status != PJ_SUCCESS)
1437 return status;
1438
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439 /* Set default media type if none is specified */
1440 if (mime_type == NULL) {
1441 mime_type = &mime_text_plain;
1442 }
1443
1444 /* Create request message. */
1445 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1446 -1, &tdata);
1447 if (status != PJ_SUCCESS) {
1448 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1449 goto on_return;
1450 }
1451
1452 /* Add accept header. */
1453 pjsip_msg_add_hdr( tdata->msg,
1454 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1455
1456 /* Parse MIME type */
1457 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1458
1459 /* Create "text/plain" message body. */
1460 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1461 &ctype.subtype, content);
1462 if (tdata->msg->body == NULL) {
1463 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1464 pjsip_tx_data_dec_ref(tdata);
1465 goto on_return;
1466 }
1467
1468 /* Add additional headers etc */
1469 pjsua_process_msg_data( tdata, msg_data);
1470
1471 /* Create IM data and attach to the request. */
1472 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1473 im_data->acc_id = call->acc_id;
1474 im_data->call_id = call_id;
1475 im_data->to = call->inv->dlg->remote.info_str;
1476 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1477 im_data->user_data = user_data;
1478
1479
1480 /* Send the request. */
1481 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1482 pjsua_var.mod.id, im_data);
1483 if (status != PJ_SUCCESS) {
1484 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1485 goto on_return;
1486 }
1487
1488on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001489 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001490 return status;
1491}
1492
1493
1494/*
1495 * Send IM typing indication inside INVITE session.
1496 */
1497PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1498 pj_bool_t is_typing,
1499 const pjsua_msg_data*msg_data)
1500{
1501 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001502 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001503 pjsip_tx_data *tdata;
1504 pj_status_t status;
1505
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1507 PJ_EINVAL);
1508
Benny Prijonodc752ca2006-09-22 16:55:42 +00001509 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001510 if (status != PJ_SUCCESS)
1511 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 /* Create request message. */
1514 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1515 -1, &tdata);
1516 if (status != PJ_SUCCESS) {
1517 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1518 goto on_return;
1519 }
1520
1521 /* Create "application/im-iscomposing+xml" msg body. */
1522 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1523 NULL, NULL, -1);
1524
1525 /* Add additional headers etc */
1526 pjsua_process_msg_data( tdata, msg_data);
1527
1528 /* Send the request. */
1529 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
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 * Terminate all calls.
1543 */
1544PJ_DEF(void) pjsua_call_hangup_all(void)
1545{
1546 unsigned i;
1547
1548 PJSUA_LOCK();
1549
1550 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1551 if (pjsua_var.calls[i].inv)
1552 pjsua_call_hangup(i, 0, NULL, NULL);
1553 }
1554
1555 PJSUA_UNLOCK();
1556}
1557
1558
1559static const char *good_number(char *buf, pj_int32_t val)
1560{
1561 if (val < 1000) {
1562 pj_ansi_sprintf(buf, "%d", val);
1563 } else if (val < 1000000) {
1564 pj_ansi_sprintf(buf, "%d.%dK",
1565 val / 1000,
1566 (val % 1000) / 100);
1567 } else {
1568 pj_ansi_sprintf(buf, "%d.%02dM",
1569 val / 1000000,
1570 (val % 1000000) / 10000);
1571 }
1572
1573 return buf;
1574}
1575
1576
1577/* Dump media session */
1578static void dump_media_session(const char *indent,
1579 char *buf, unsigned maxlen,
1580 pjmedia_session *session)
1581{
1582 unsigned i;
1583 char *p = buf, *end = buf+maxlen;
1584 int len;
1585 pjmedia_session_info info;
1586
1587 pjmedia_session_get_info(session, &info);
1588
1589 for (i=0; i<info.stream_cnt; ++i) {
1590 pjmedia_rtcp_stat stat;
1591 const char *rem_addr;
1592 int rem_port;
1593 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001594 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001595 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1596 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597
1598 pjmedia_session_get_stream_stat(session, i, &stat);
1599 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1600 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1601
1602 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1603 dir = "sendonly";
1604 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1605 dir = "recvonly";
1606 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1607 dir = "sendrecv";
1608 else
1609 dir = "inactive";
1610
1611
1612 len = pj_ansi_snprintf(buf, end-p,
1613 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1614 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001615 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 info.stream_info[i].fmt.encoding_name.ptr,
1617 info.stream_info[i].fmt.clock_rate / 1000,
1618 dir,
1619 rem_addr, rem_port);
1620 if (len < 1 || len > end-p) {
1621 *p = '\0';
1622 return;
1623 }
1624
1625 p += len;
1626 *p++ = '\n';
1627 *p = '\0';
1628
1629 if (stat.rx.update_cnt == 0)
1630 strcpy(last_update, "never");
1631 else {
1632 pj_gettimeofday(&now);
1633 PJ_TIME_VAL_SUB(now, stat.rx.update);
1634 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1635 now.sec / 3600,
1636 (now.sec % 3600) / 60,
1637 now.sec % 60,
1638 now.msec);
1639 }
1640
Benny Prijono80019eb2006-08-07 13:22:23 +00001641 pj_gettimeofday(&media_duration);
1642 PJ_TIME_VAL_SUB(media_duration, stat.start);
1643 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1644 media_duration.msec = 1;
1645
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646 len = pj_ansi_snprintf(p, end-p,
1647 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001648 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1650 "%s (msec) min avg max last\n"
1651 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1652 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1653 indent, info.stream_info[i].fmt.pt,
1654 last_update,
1655 indent,
1656 good_number(packets, stat.rx.pkt),
1657 good_number(bytes, stat.rx.bytes),
1658 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001659 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001660 indent,
1661 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001662 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001664 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001666 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667 indent, indent,
1668 stat.rx.loss_period.min / 1000.0,
1669 stat.rx.loss_period.avg / 1000.0,
1670 stat.rx.loss_period.max / 1000.0,
1671 stat.rx.loss_period.last / 1000.0,
1672 indent,
1673 stat.rx.jitter.min / 1000.0,
1674 stat.rx.jitter.avg / 1000.0,
1675 stat.rx.jitter.max / 1000.0,
1676 stat.rx.jitter.last / 1000.0,
1677 ""
1678 );
1679
1680 if (len < 1 || len > end-p) {
1681 *p = '\0';
1682 return;
1683 }
1684
1685 p += len;
1686 *p++ = '\n';
1687 *p = '\0';
1688
1689 if (stat.tx.update_cnt == 0)
1690 strcpy(last_update, "never");
1691 else {
1692 pj_gettimeofday(&now);
1693 PJ_TIME_VAL_SUB(now, stat.tx.update);
1694 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1695 now.sec / 3600,
1696 (now.sec % 3600) / 60,
1697 now.sec % 60,
1698 now.msec);
1699 }
1700
1701 len = pj_ansi_snprintf(p, end-p,
1702 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001703 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001704 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1705 "%s (msec) min avg max last\n"
1706 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1707 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1708 indent,
1709 info.stream_info[i].tx_pt,
1710 info.stream_info[i].param->info.frm_ptime *
1711 info.stream_info[i].param->setting.frm_per_pkt,
1712 last_update,
1713
1714 indent,
1715 good_number(packets, stat.tx.pkt),
1716 good_number(bytes, stat.tx.bytes),
1717 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001718 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001719
1720 indent,
1721 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001722 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001723 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001724 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001726 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727
1728 indent, indent,
1729 stat.tx.loss_period.min / 1000.0,
1730 stat.tx.loss_period.avg / 1000.0,
1731 stat.tx.loss_period.max / 1000.0,
1732 stat.tx.loss_period.last / 1000.0,
1733 indent,
1734 stat.tx.jitter.min / 1000.0,
1735 stat.tx.jitter.avg / 1000.0,
1736 stat.tx.jitter.max / 1000.0,
1737 stat.tx.jitter.last / 1000.0,
1738 ""
1739 );
1740
1741 if (len < 1 || len > end-p) {
1742 *p = '\0';
1743 return;
1744 }
1745
1746 p += len;
1747 *p++ = '\n';
1748 *p = '\0';
1749
1750 len = pj_ansi_snprintf(p, end-p,
1751 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1752 indent,
1753 stat.rtt.min / 1000.0,
1754 stat.rtt.avg / 1000.0,
1755 stat.rtt.max / 1000.0,
1756 stat.rtt.last / 1000.0
1757 );
1758 if (len < 1 || len > end-p) {
1759 *p = '\0';
1760 return;
1761 }
1762
1763 p += len;
1764 *p++ = '\n';
1765 *p = '\0';
1766 }
1767}
1768
1769
1770/* Print call info */
1771static void print_call(const char *title,
1772 int call_id,
1773 char *buf, pj_size_t size)
1774{
1775 int len;
1776 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1777 pjsip_dialog *dlg = inv->dlg;
1778 char userinfo[128];
1779
1780 /* Dump invite sesion info. */
1781
1782 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1783 if (len < 1)
1784 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1785 else
1786 userinfo[len] = '\0';
1787
1788 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1789 title,
1790 pjsip_inv_state_name(inv->state),
1791 userinfo);
1792 if (len < 1 || len >= (int)size) {
1793 pj_ansi_strcpy(buf, "<--uri too long-->");
1794 len = 18;
1795 } else
1796 buf[len] = '\0';
1797}
1798
1799
1800/*
1801 * Dump call and media statistics to string.
1802 */
1803PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1804 pj_bool_t with_media,
1805 char *buffer,
1806 unsigned maxlen,
1807 const char *indent)
1808{
1809 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001810 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811 pj_time_val duration, res_delay, con_delay;
1812 char tmp[128];
1813 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001814 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001815 int len;
1816
1817 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1818 PJ_EINVAL);
1819
Benny Prijonodc752ca2006-09-22 16:55:42 +00001820 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001821 if (status != PJ_SUCCESS)
1822 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001823
1824 *buffer = '\0';
1825 p = buffer;
1826 end = buffer + maxlen;
1827 len = 0;
1828
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001829 print_call(indent, call_id, tmp, sizeof(tmp));
1830
1831 len = pj_ansi_strlen(tmp);
1832 pj_ansi_strcpy(buffer, tmp);
1833
1834 p += len;
1835 *p++ = '\r';
1836 *p++ = '\n';
1837
1838 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001839 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001840 pj_gettimeofday(&duration);
1841 PJ_TIME_VAL_SUB(duration, call->conn_time);
1842 con_delay = call->conn_time;
1843 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1844 } else {
1845 duration.sec = duration.msec = 0;
1846 con_delay.sec = con_delay.msec = 0;
1847 }
1848
1849 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001850 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 res_delay = call->res_time;
1852 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1853 } else {
1854 res_delay.sec = res_delay.msec = 0;
1855 }
1856
1857 /* Print duration */
1858 len = pj_ansi_snprintf(p, end-p,
1859 "%s Call time: %02dh:%02dm:%02ds, "
1860 "1st res in %d ms, conn in %dms",
1861 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001862 (int)(duration.sec / 3600),
1863 (int)((duration.sec % 3600)/60),
1864 (int)(duration.sec % 60),
1865 (int)PJ_TIME_VAL_MSEC(res_delay),
1866 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001867
1868 if (len > 0 && len < end-p) {
1869 p += len;
1870 *p++ = '\n';
1871 *p = '\0';
1872 }
1873
1874 /* Dump session statistics */
1875 if (with_media && call->session)
1876 dump_media_session(indent, p, end-p, call->session);
1877
Benny Prijonodc752ca2006-09-22 16:55:42 +00001878 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001879
1880 return PJ_SUCCESS;
1881}
1882
1883
1884/*
1885 * Destroy the call's media
1886 */
1887static pj_status_t call_destroy_media(int call_id)
1888{
1889 pjsua_call *call = &pjsua_var.calls[call_id];
1890
1891 if (call->conf_slot != PJSUA_INVALID_ID) {
1892 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1893 call->conf_slot = PJSUA_INVALID_ID;
1894 }
1895
1896 if (call->session) {
1897 /* Destroy session (this will also close RTP/RTCP sockets). */
1898 pjmedia_session_destroy(call->session);
1899 call->session = NULL;
1900
1901 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1902 call_id));
1903
1904 }
1905
1906 call->media_st = PJSUA_CALL_MEDIA_NONE;
1907
1908 return PJ_SUCCESS;
1909}
1910
1911
Benny Prijono84126ab2006-02-09 09:30:09 +00001912/*
1913 * This callback receives notification from invite session when the
1914 * session state has changed.
1915 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001916static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1917 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001918{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001919 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001920
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001921 PJSUA_LOCK();
1922
1923 call = inv->dlg->mod_data[pjsua_var.mod.id];
1924
1925 if (!call) {
1926 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001927 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001928 }
1929
Benny Prijonoe21e7842006-04-09 16:46:05 +00001930
1931 /* Get call times */
1932 switch (inv->state) {
1933 case PJSIP_INV_STATE_EARLY:
1934 case PJSIP_INV_STATE_CONNECTING:
1935 if (call->res_time.sec == 0)
1936 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001937 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001938 pj_strncpy(&call->last_text,
1939 &e->body.tsx_state.tsx->status_text,
1940 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001941 break;
1942 case PJSIP_INV_STATE_CONFIRMED:
1943 pj_gettimeofday(&call->conn_time);
1944 break;
1945 case PJSIP_INV_STATE_DISCONNECTED:
1946 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001947 if (call->res_time.sec == 0)
1948 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001949 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1950 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 pj_strncpy(&call->last_text,
1952 &e->body.tsx_state.tsx->status_text,
1953 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001954 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001955 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001956 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001957 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001958 pj_strncpy(&call->last_text,
1959 &e->body.tsx_state.tsx->status_text,
1960 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001961 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001962 }
1963
Benny Prijono26ff9062006-02-21 23:47:00 +00001964 /* If this is an outgoing INVITE that was created because of
1965 * REFER/transfer, send NOTIFY to transferer.
1966 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001967 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001968 int st_code = -1;
1969 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1970
1971
Benny Prijonoa91a0032006-02-26 21:23:45 +00001972 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001973 case PJSIP_INV_STATE_NULL:
1974 case PJSIP_INV_STATE_CALLING:
1975 /* Do nothing */
1976 break;
1977
1978 case PJSIP_INV_STATE_EARLY:
1979 case PJSIP_INV_STATE_CONNECTING:
1980 st_code = e->body.tsx_state.tsx->status_code;
1981 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1982 break;
1983
1984 case PJSIP_INV_STATE_CONFIRMED:
1985 /* When state is confirmed, send the final 200/OK and terminate
1986 * subscription.
1987 */
1988 st_code = e->body.tsx_state.tsx->status_code;
1989 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1990 break;
1991
1992 case PJSIP_INV_STATE_DISCONNECTED:
1993 st_code = e->body.tsx_state.tsx->status_code;
1994 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1995 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001996
Benny Prijono8b1889b2006-06-06 18:40:40 +00001997 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001998 /* Nothing to do. Just to keep gcc from complaining about
1999 * unused enums.
2000 */
2001 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002002 }
2003
2004 if (st_code != -1) {
2005 pjsip_tx_data *tdata;
2006 pj_status_t status;
2007
Benny Prijonoa91a0032006-02-26 21:23:45 +00002008 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002009 ev_state, st_code,
2010 NULL, &tdata);
2011 if (status != PJ_SUCCESS) {
2012 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2013 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002014 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002015 if (status != PJ_SUCCESS) {
2016 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2017 }
2018 }
2019 }
2020 }
2021
Benny Prijono84126ab2006-02-09 09:30:09 +00002022
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002023 if (pjsua_var.ua_cfg.cb.on_call_state)
2024 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002025
2026 /* call->inv may be NULL now */
2027
Benny Prijono84126ab2006-02-09 09:30:09 +00002028 /* Destroy media session when invite session is disconnected. */
2029 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002030
Benny Prijonoa91a0032006-02-26 21:23:45 +00002031 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002032
Benny Prijono275fd682006-03-22 11:59:11 +00002033 if (call)
2034 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002035
Benny Prijono105217f2006-03-06 16:25:59 +00002036 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002037 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002038 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002039
2040 /* Reset call */
2041 reset_call(call->index);
2042
Benny Prijono84126ab2006-02-09 09:30:09 +00002043 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002044
2045 PJSUA_UNLOCK();
2046}
2047
2048/*
2049 * This callback is called by invite session framework when UAC session
2050 * has forked.
2051 */
2052static void pjsua_call_on_forked( pjsip_inv_session *inv,
2053 pjsip_event *e)
2054{
2055 PJ_UNUSED_ARG(inv);
2056 PJ_UNUSED_ARG(e);
2057
2058 PJ_TODO(HANDLE_FORKED_DIALOG);
2059}
2060
2061
2062/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002063 * Disconnect call upon error.
2064 */
2065static void call_disconnect( pjsip_inv_session *inv,
2066 int code )
2067{
2068 pjsip_tx_data *tdata;
2069 pj_status_t status;
2070
2071 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2072 if (status == PJ_SUCCESS)
2073 pjsip_inv_send_msg(inv, tdata);
2074}
2075
2076/*
Benny Prijono0875ae82006-12-26 00:11:48 +00002077 * DTMF callback from the stream.
2078 */
2079static void dtmf_callback(pjmedia_stream *strm, void *user_data,
2080 int digit)
2081{
2082 PJ_UNUSED_ARG(strm);
2083
2084 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2085 pjsua_call_id call_id;
2086
2087 call_id = (pjsua_call_id)user_data;
2088 pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
2089 }
2090}
2091
2092/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002093 * Callback to be called when SDP offer/answer negotiation has just completed
2094 * in the session. This function will start/update media if negotiation
2095 * has succeeded.
2096 */
2097static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2098 pj_status_t status)
2099{
2100 int prev_media_st = 0;
2101 pjsua_call *call;
2102 pjmedia_session_info sess_info;
2103 const pjmedia_sdp_session *local_sdp;
2104 const pjmedia_sdp_session *remote_sdp;
2105 pjmedia_port *media_port;
2106 pj_str_t port_name;
2107 char tmp[PJSIP_MAX_URL_SIZE];
2108
2109 PJSUA_LOCK();
2110
2111 call = inv->dlg->mod_data[pjsua_var.mod.id];
2112
2113 if (status != PJ_SUCCESS) {
2114
2115 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2116
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002117 /* Stop/destroy media, if any */
2118 call_destroy_media(call->index);
2119
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002120 /* Disconnect call if we're not in the middle of initializing an
2121 * UAS dialog and if this is not a re-INVITE
2122 */
2123 if (inv->state != PJSIP_INV_STATE_NULL &&
2124 inv->state != PJSIP_INV_STATE_CONFIRMED)
2125 {
2126 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2127 }
2128
2129 PJSUA_UNLOCK();
2130 return;
2131 }
2132
2133 /* Destroy existing media session, if any. */
2134
2135 if (call) {
2136 prev_media_st = call->media_st;
2137 call_destroy_media(call->index);
2138 }
2139
2140 /* Get local and remote SDP */
2141
2142 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2143 if (status != PJ_SUCCESS) {
2144 pjsua_perror(THIS_FILE,
2145 "Unable to retrieve currently active local SDP",
2146 status);
2147 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2148 PJSUA_UNLOCK();
2149 return;
2150 }
2151
2152
2153 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2154 if (status != PJ_SUCCESS) {
2155 pjsua_perror(THIS_FILE,
2156 "Unable to retrieve currently active remote SDP",
2157 status);
2158 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2159 PJSUA_UNLOCK();
2160 return;
2161 }
2162
2163 /* Create media session info based on SDP parameters.
2164 * We only support one stream per session at the moment
2165 */
2166 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
2167 pjsua_var.med_endpt,
2168 1,&sess_info,
2169 local_sdp, remote_sdp);
2170 if (status != PJ_SUCCESS) {
2171 pjsua_perror(THIS_FILE, "Unable to create media session",
2172 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002173 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002174 PJSUA_UNLOCK();
2175 return;
2176 }
2177
2178 /* Check if media is put on-hold */
2179 if (sess_info.stream_cnt == 0 ||
2180 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
2181 {
2182
2183 /* Determine who puts the call on-hold */
2184 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
2185 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
2186 /* It was local who offer hold */
2187 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2188 } else {
2189 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2190 }
2191 }
2192
2193 call->media_dir = PJMEDIA_DIR_NONE;
2194
2195 } else {
2196
2197 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00002198 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00002199 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
2200 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00002201 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
2202 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
2203 }
2204
2205 /* Disable VAD, if this option is specified. */
2206 if (pjsua_var.media_cfg.no_vad) {
2207 sess_info.stream_info[0].param->setting.vad = 0;
2208 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209
2210
2211 /* Optionally, application may modify other stream settings here
2212 * (such as jitter buffer parameters, codec ptime, etc.)
2213 */
Benny Prijono1d0ca0c2006-11-21 09:06:47 +00002214 sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
2215 sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
2216 sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
2217 sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002218
2219 /* Create session based on session info. */
2220 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
2221 &call->med_tp,
2222 call, &call->session );
2223 if (status != PJ_SUCCESS) {
2224 pjsua_perror(THIS_FILE, "Unable to create media session",
2225 status);
2226 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2227 PJSUA_UNLOCK();
2228 return;
2229 }
2230
Benny Prijono0875ae82006-12-26 00:11:48 +00002231 /* If DTMF callback is installed by application, install our
2232 * callback to the session.
2233 */
2234 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2235 pjmedia_session_set_dtmf_callback(call->session, 0,
2236 &dtmf_callback,
2237 (void*)(call->index));
2238 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002239
2240 /* Get the port interface of the first stream in the session.
2241 * We need the port interface to add to the conference bridge.
2242 */
2243 pjmedia_session_get_port(call->session, 0, &media_port);
2244
2245
2246 /*
2247 * Add the call to conference bridge.
2248 */
2249 port_name.ptr = tmp;
2250 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
2251 call->inv->dlg->remote.info->uri,
2252 tmp, sizeof(tmp));
2253 if (port_name.slen < 1) {
2254 port_name = pj_str("call");
2255 }
2256 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
2257 media_port,
2258 &port_name,
2259 (unsigned*)&call->conf_slot);
2260 if (status != PJ_SUCCESS) {
2261 pjsua_perror(THIS_FILE, "Unable to create conference slot",
2262 status);
2263 call_destroy_media(call->index);
2264 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
2265 PJSUA_UNLOCK();
2266 return;
2267 }
2268
2269 /* Call's media state is active */
2270 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
2271 call->media_dir = sess_info.stream_info[0].dir;
2272 }
2273
2274 /* Print info. */
2275 {
2276 char info[80];
2277 int info_len = 0;
2278 unsigned i;
2279
2280 for (i=0; i<sess_info.stream_cnt; ++i) {
2281 int len;
2282 const char *dir;
2283 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
2284
2285 switch (strm_info->dir) {
2286 case PJMEDIA_DIR_NONE:
2287 dir = "inactive";
2288 break;
2289 case PJMEDIA_DIR_ENCODING:
2290 dir = "sendonly";
2291 break;
2292 case PJMEDIA_DIR_DECODING:
2293 dir = "recvonly";
2294 break;
2295 case PJMEDIA_DIR_ENCODING_DECODING:
2296 dir = "sendrecv";
2297 break;
2298 default:
2299 dir = "unknown";
2300 break;
2301 }
2302 len = pj_ansi_sprintf( info+info_len,
2303 ", stream #%d: %.*s (%s)", i,
2304 (int)strm_info->fmt.encoding_name.slen,
2305 strm_info->fmt.encoding_name.ptr,
2306 dir);
2307 if (len > 0)
2308 info_len += len;
2309 }
2310 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
2311 }
2312
2313 /* Call application callback, if any */
2314 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2315 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2316
2317
2318 PJSUA_UNLOCK();
2319}
2320
2321
2322/*
2323 * Create inactive SDP for call hold.
2324 */
2325static pj_status_t create_inactive_sdp(pjsua_call *call,
2326 pjmedia_sdp_session **p_answer)
2327{
2328 pj_status_t status;
2329 pjmedia_sdp_conn *conn;
2330 pjmedia_sdp_attr *attr;
2331 pjmedia_sdp_session *sdp;
2332
2333 /* Create new offer */
2334 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
2335 &call->skinfo, &sdp);
2336 if (status != PJ_SUCCESS) {
2337 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2338 return status;
2339 }
2340
2341 /* Get SDP media connection line */
2342 conn = sdp->media[0]->conn;
2343 if (!conn)
2344 conn = sdp->conn;
2345
2346 /* Modify address */
2347 conn->addr = pj_str("0.0.0.0");
2348
2349 /* Remove existing directions attributes */
2350 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2351 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2352 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2353 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2354
2355 /* Add inactive attribute */
2356 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2357 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2358
2359 *p_answer = sdp;
2360
2361 return status;
2362}
2363
2364
2365/*
2366 * Called when session received new offer.
2367 */
2368static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2369 const pjmedia_sdp_session *offer)
2370{
2371 const char *remote_state;
2372 pjsua_call *call;
2373 pjmedia_sdp_conn *conn;
2374 pjmedia_sdp_session *answer;
2375 pj_bool_t is_remote_active;
2376 pj_status_t status;
2377
2378 PJSUA_LOCK();
2379
2380 call = inv->dlg->mod_data[pjsua_var.mod.id];
2381
2382 /*
2383 * See if remote is offering active media (i.e. not on-hold)
2384 */
2385 is_remote_active = PJ_TRUE;
2386
2387 conn = offer->media[0]->conn;
2388 if (!conn)
2389 conn = offer->conn;
2390
2391 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2392 pj_strcmp2(&conn->addr, "0")==0)
2393 {
2394 is_remote_active = PJ_FALSE;
2395
2396 }
2397 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2398 {
2399 is_remote_active = PJ_FALSE;
2400 }
2401
2402 remote_state = (is_remote_active ? "active" : "inactive");
2403
2404 /* Supply candidate answer */
2405 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2406 PJ_LOG(4,(THIS_FILE,
2407 "Call %d: RX new media offer, creating inactive SDP "
2408 "(media in offer is %s)", call->index, remote_state));
2409 status = create_inactive_sdp( call, &answer );
2410 } else {
2411 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2412 call->index));
2413 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2414 call->inv->pool, 1,
2415 &call->skinfo, &answer);
2416 }
2417
2418 if (status != PJ_SUCCESS) {
2419 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2420 PJSUA_UNLOCK();
2421 return;
2422 }
2423
2424 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2425 if (status != PJ_SUCCESS) {
2426 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2427 PJSUA_UNLOCK();
2428 return;
2429 }
2430
2431 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002432}
2433
2434
2435/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002436 * Callback called by event framework when the xfer subscription state
2437 * has changed.
2438 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002439static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2440{
2441
2442 PJ_UNUSED_ARG(event);
2443
2444 /*
2445 * When subscription is accepted (got 200/OK to REFER), check if
2446 * subscription suppressed.
2447 */
2448 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2449
2450 pjsip_rx_data *rdata;
2451 pjsip_generic_string_hdr *refer_sub;
2452 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2453 pjsua_call *call;
2454
2455 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2456
2457 /* Must be receipt of response message */
2458 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2459 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2460 rdata = event->body.tsx_state.src.rdata;
2461
2462 /* Find Refer-Sub header */
2463 refer_sub = (pjsip_generic_string_hdr*)
2464 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2465 &REFER_SUB, NULL);
2466
2467 /* Check if subscription is suppressed */
2468 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2469 /* Since no subscription is desired, assume that call has been
2470 * transfered successfully.
2471 */
2472 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2473 const pj_str_t ACCEPTED = { "Accepted", 8 };
2474 pj_bool_t cont = PJ_FALSE;
2475 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2476 200,
2477 &ACCEPTED,
2478 PJ_TRUE,
2479 &cont);
2480 }
2481
2482 /* Yes, subscription is suppressed.
2483 * Terminate our subscription now.
2484 */
2485 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2486 "event subcription..."));
2487 pjsip_evsub_terminate(sub, PJ_TRUE);
2488
2489 } else {
2490 /* Notify application about call transfer progress.
2491 * Initially notify with 100/Accepted status.
2492 */
2493 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2494 const pj_str_t ACCEPTED = { "Accepted", 8 };
2495 pj_bool_t cont = PJ_FALSE;
2496 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2497 100,
2498 &ACCEPTED,
2499 PJ_FALSE,
2500 &cont);
2501 }
2502 }
2503 }
2504 /*
2505 * On incoming NOTIFY, notify application about call transfer progress.
2506 */
2507 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2508 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2509 {
2510 pjsua_call *call;
2511 pjsip_msg *msg;
2512 pjsip_msg_body *body;
2513 pjsip_status_line status_line;
2514 pj_bool_t is_last;
2515 pj_bool_t cont;
2516 pj_status_t status;
2517
2518 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2519
2520 /* When subscription is terminated, clear the xfer_sub member of
2521 * the inv_data.
2522 */
2523 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2524 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2525 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2526
2527 }
2528
2529 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2530 /* Application is not interested with call progress status */
2531 return;
2532 }
2533
2534 /* This better be a NOTIFY request */
2535 if (event->type == PJSIP_EVENT_TSX_STATE &&
2536 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2537 {
2538 pjsip_rx_data *rdata;
2539
2540 rdata = event->body.tsx_state.src.rdata;
2541
2542 /* Check if there's body */
2543 msg = rdata->msg_info.msg;
2544 body = msg->body;
2545 if (!body) {
2546 PJ_LOG(4,(THIS_FILE,
2547 "Warning: received NOTIFY without message body"));
2548 return;
2549 }
2550
2551 /* Check for appropriate content */
2552 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2553 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2554 {
2555 PJ_LOG(4,(THIS_FILE,
2556 "Warning: received NOTIFY with non message/sipfrag "
2557 "content"));
2558 return;
2559 }
2560
2561 /* Try to parse the content */
2562 status = pjsip_parse_status_line(body->data, body->len,
2563 &status_line);
2564 if (status != PJ_SUCCESS) {
2565 PJ_LOG(4,(THIS_FILE,
2566 "Warning: received NOTIFY with invalid "
2567 "message/sipfrag content"));
2568 return;
2569 }
2570
2571 } else {
2572 status_line.code = 500;
2573 status_line.reason = *pjsip_get_status_text(500);
2574 }
2575
2576 /* Notify application */
2577 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2578 cont = !is_last;
2579 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2580 status_line.code,
2581 &status_line.reason,
2582 is_last, &cont);
2583
2584 if (!cont) {
2585 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2586 }
2587 }
2588}
2589
2590
2591/*
2592 * Callback called by event framework when the xfer subscription state
2593 * has changed.
2594 */
2595static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002596{
2597
2598 PJ_UNUSED_ARG(event);
2599
2600 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002601 * When subscription is terminated, clear the xfer_sub member of
2602 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002603 */
2604 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002605 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002606
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002607 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002608 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002609 return;
2610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002611 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002612 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002613
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002614 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002615 }
2616}
2617
2618
2619/*
2620 * Follow transfer (REFER) request.
2621 */
2622static void on_call_transfered( pjsip_inv_session *inv,
2623 pjsip_rx_data *rdata )
2624{
2625 pj_status_t status;
2626 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002627 pjsua_call *existing_call;
2628 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002629 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002630 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002631 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002632 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002633 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002634 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002635 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002636 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002637 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002638 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002639 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002640 pjsip_evsub *sub;
2641
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002642 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002643
Benny Prijono26ff9062006-02-21 23:47:00 +00002644 /* Find the Refer-To header */
2645 refer_to = (pjsip_generic_string_hdr*)
2646 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2647
2648 if (refer_to == NULL) {
2649 /* Invalid Request.
2650 * No Refer-To header!
2651 */
2652 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002653 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002654 return;
2655 }
2656
Benny Prijonoc8141a82006-08-20 09:12:19 +00002657 /* Find optional Refer-Sub header */
2658 refer_sub = (pjsip_generic_string_hdr*)
2659 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2660
2661 if (refer_sub) {
2662 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2663 no_refer_sub = PJ_TRUE;
2664 }
2665
Benny Prijono053f5222006-11-11 16:16:04 +00002666 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2667 * request.
2668 */
2669 ref_by_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
2670 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002671
Benny Prijono9fc735d2006-05-28 14:58:12 +00002672 /* Notify callback */
2673 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002674 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2675 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2676 &refer_to->hvalue,
2677 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002678
2679 if (code < 200)
2680 code = 200;
2681 if (code >= 300) {
2682 /* Application rejects call transfer request */
2683 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2684 return;
2685 }
2686
Benny Prijono26ff9062006-02-21 23:47:00 +00002687 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2688 (int)inv->dlg->remote.info_str.slen,
2689 inv->dlg->remote.info_str.ptr,
2690 (int)refer_to->hvalue.slen,
2691 refer_to->hvalue.ptr));
2692
Benny Prijonoc8141a82006-08-20 09:12:19 +00002693 if (no_refer_sub) {
2694 /*
2695 * Always answer with 200.
2696 */
2697 pjsip_tx_data *tdata;
2698 const pj_str_t str_false = { "false", 5};
2699 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002700
Benny Prijonoc8141a82006-08-20 09:12:19 +00002701 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2702 if (status != PJ_SUCCESS) {
2703 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2704 status);
2705 return;
2706 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002707
Benny Prijonoc8141a82006-08-20 09:12:19 +00002708 /* Add Refer-Sub header */
2709 hdr = (pjsip_hdr*)
2710 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2711 &str_false);
2712 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002713
Benny Prijono26ff9062006-02-21 23:47:00 +00002714
Benny Prijonoc8141a82006-08-20 09:12:19 +00002715 /* Send answer */
2716 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2717 tdata);
2718 if (status != PJ_SUCCESS) {
2719 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2720 status);
2721 return;
2722 }
2723
2724 /* Don't have subscription */
2725 sub = NULL;
2726
2727 } else {
2728 struct pjsip_evsub_user xfer_cb;
2729 pjsip_hdr hdr_list;
2730
2731 /* Init callback */
2732 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002733 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002734
2735 /* Init additional header list to be sent with REFER response */
2736 pj_list_init(&hdr_list);
2737
2738 /* Create transferee event subscription */
2739 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2740 if (status != PJ_SUCCESS) {
2741 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2742 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2743 return;
2744 }
2745
2746 /* If there's Refer-Sub header and the value is "true", send back
2747 * Refer-Sub in the response with value "true" too.
2748 */
2749 if (refer_sub) {
2750 const pj_str_t str_true = { "true", 4 };
2751 pjsip_hdr *hdr;
2752
2753 hdr = (pjsip_hdr*)
2754 pjsip_generic_string_hdr_create(inv->dlg->pool,
2755 &str_refer_sub,
2756 &str_true);
2757 pj_list_push_back(&hdr_list, hdr);
2758
2759 }
2760
2761 /* Accept the REFER request, send 200 (OK). */
2762 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2763
2764 /* Create initial NOTIFY request */
2765 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2766 100, NULL, &tdata);
2767 if (status != PJ_SUCCESS) {
2768 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2769 status);
2770 return;
2771 }
2772
2773 /* Send initial NOTIFY request */
2774 status = pjsip_xfer_send_request( sub, tdata);
2775 if (status != PJ_SUCCESS) {
2776 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2777 return;
2778 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002779 }
2780
2781 /* We're cheating here.
2782 * We need to get a null terminated string from a pj_str_t.
2783 * So grab the pointer from the hvalue and NULL terminate it, knowing
2784 * that the NULL position will be occupied by a newline.
2785 */
2786 uri = refer_to->hvalue.ptr;
2787 uri[refer_to->hvalue.slen] = '\0';
2788
Benny Prijono053f5222006-11-11 16:16:04 +00002789 /* Init msg_data */
2790 pjsua_msg_data_init(&msg_data);
2791
2792 /* If Referred-By header is present in the REFER request, copy this
2793 * to the outgoing INVITE request.
2794 */
2795 if (ref_by_hdr != NULL) {
2796 pjsip_hdr *dup = pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
2797 pj_list_push_back(&msg_data.hdr_list, dup);
2798 }
2799
Benny Prijono26ff9062006-02-21 23:47:00 +00002800 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002801 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002802 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002803 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002804 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002805 if (status != PJ_SUCCESS) {
2806
Benny Prijonoc8141a82006-08-20 09:12:19 +00002807 /* Notify xferer about the error (if we have subscription) */
2808 if (sub) {
2809 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2810 500, NULL, &tdata);
2811 if (status != PJ_SUCCESS) {
2812 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2813 status);
2814 return;
2815 }
2816 status = pjsip_xfer_send_request(sub, tdata);
2817 if (status != PJ_SUCCESS) {
2818 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2819 status);
2820 return;
2821 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002822 }
2823 return;
2824 }
2825
Benny Prijonoc8141a82006-08-20 09:12:19 +00002826 if (sub) {
2827 /* Put the server subscription in inv_data.
2828 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2829 * reported back to the server subscription.
2830 */
2831 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002832
Benny Prijonoc8141a82006-08-20 09:12:19 +00002833 /* Put the invite_data in the subscription. */
2834 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2835 &pjsua_var.calls[new_call]);
2836 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002837}
2838
2839
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002840
Benny Prijono26ff9062006-02-21 23:47:00 +00002841/*
2842 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002843 * session. We use this to trap:
2844 * - incoming REFER request.
2845 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002846 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002847static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2848 pjsip_transaction *tsx,
2849 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002850{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002851 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2852
2853 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002854
Benny Prijono26ff9062006-02-21 23:47:00 +00002855 if (tsx->role==PJSIP_ROLE_UAS &&
2856 tsx->state==PJSIP_TSX_STATE_TRYING &&
2857 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2858 {
2859 /*
2860 * Incoming REFER request.
2861 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002862 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002863
Benny Prijono26ff9062006-02-21 23:47:00 +00002864 }
Benny Prijonob0808372006-03-02 21:18:58 +00002865 else if (tsx->role==PJSIP_ROLE_UAS &&
2866 tsx->state==PJSIP_TSX_STATE_TRYING &&
2867 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2868 {
2869 /*
2870 * Incoming MESSAGE request!
2871 */
2872 pjsip_rx_data *rdata;
2873 pjsip_msg *msg;
2874 pjsip_accept_hdr *accept_hdr;
2875 pj_status_t status;
2876
2877 rdata = e->body.tsx_state.src.rdata;
2878 msg = rdata->msg_info.msg;
2879
2880 /* Request MUST have message body, with Content-Type equal to
2881 * "text/plain".
2882 */
2883 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2884
2885 pjsip_hdr hdr_list;
2886
2887 pj_list_init(&hdr_list);
2888 pj_list_push_back(&hdr_list, accept_hdr);
2889
2890 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2891 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002892 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002893 return;
2894 }
2895
2896 /* Respond with 200 first, so that remote doesn't retransmit in case
2897 * the UI takes too long to process the message.
2898 */
2899 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2900
2901 /* Process MESSAGE request */
2902 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2903 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002904
Benny Prijonob0808372006-03-02 21:18:58 +00002905 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002906 else if (tsx->role == PJSIP_ROLE_UAC &&
2907 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002908 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002909 /* Handle outgoing pager status */
2910 if (tsx->status_code >= 200) {
2911 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002912
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002913 im_data = tsx->mod_data[pjsua_var.mod.id];
2914 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002915
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2917 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2918 &im_data->to,
2919 &im_data->body,
2920 im_data->user_data,
2921 tsx->status_code,
2922 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002923 }
Benny Prijonofccab712006-02-22 22:23:22 +00002924 }
Benny Prijono834aee32006-02-19 01:38:06 +00002925 }
Benny Prijono834aee32006-02-19 01:38:06 +00002926
Benny Prijono26ff9062006-02-21 23:47:00 +00002927
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002928 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002929}