blob: 68eba7780b6fd250ccc6ceb0224d1c3743c273e5 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
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 Prijonoed7a5a72007-01-29 18:36:38 +00001007 if (reason && reason->slen == 0)
1008 reason = NULL;
1009
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001010 /* Create response message */
1011 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1012 if (status != PJ_SUCCESS) {
1013 pjsua_perror(THIS_FILE, "Error creating response",
1014 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001015 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001016 return status;
1017 }
1018
1019 /* Add additional headers etc */
1020 pjsua_process_msg_data( tdata, msg_data);
1021
1022 /* Send the message */
1023 status = pjsip_inv_send_msg(call->inv, tdata);
1024 if (status != PJ_SUCCESS)
1025 pjsua_perror(THIS_FILE, "Error sending response",
1026 status);
1027
Benny Prijonodc752ca2006-09-22 16:55:42 +00001028 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001029
1030 return status;
1031}
1032
1033
1034/*
1035 * Hangup call by using method that is appropriate according to the
1036 * call state.
1037 */
1038PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1039 unsigned code,
1040 const pj_str_t *reason,
1041 const pjsua_msg_data *msg_data)
1042{
1043 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001044 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001045 pj_status_t status;
1046 pjsip_tx_data *tdata;
1047
1048
Benny Prijono148c9dd2006-09-19 13:37:53 +00001049 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1050 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1051 call_id));
1052 }
1053
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001054 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1055 PJ_EINVAL);
1056
Benny Prijonodc752ca2006-09-22 16:55:42 +00001057 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001058 if (status != PJ_SUCCESS)
1059 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001060
1061 if (code==0) {
1062 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1063 code = PJSIP_SC_OK;
1064 else if (call->inv->role == PJSIP_ROLE_UAS)
1065 code = PJSIP_SC_DECLINE;
1066 else
1067 code = PJSIP_SC_REQUEST_TERMINATED;
1068 }
1069
1070 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1071 if (status != PJ_SUCCESS) {
1072 pjsua_perror(THIS_FILE,
1073 "Failed to create end session message",
1074 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001075 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001076 return status;
1077 }
1078
1079 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1080 * as p_tdata when INVITE transaction has not been answered
1081 * with any provisional responses.
1082 */
1083 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001084 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001085 return PJ_SUCCESS;
1086 }
1087
1088 /* Add additional headers etc */
1089 pjsua_process_msg_data( tdata, msg_data);
1090
1091 /* Send the message */
1092 status = pjsip_inv_send_msg(call->inv, tdata);
1093 if (status != PJ_SUCCESS) {
1094 pjsua_perror(THIS_FILE,
1095 "Failed to send end session message",
1096 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001097 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001098 return status;
1099 }
1100
Benny Prijonodc752ca2006-09-22 16:55:42 +00001101 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001102
1103 return PJ_SUCCESS;
1104}
1105
1106
1107/*
1108 * Put the specified call on hold.
1109 */
1110PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1111 const pjsua_msg_data *msg_data)
1112{
1113 pjmedia_sdp_session *sdp;
1114 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001115 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116 pjsip_tx_data *tdata;
1117 pj_status_t status;
1118
1119 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1120 PJ_EINVAL);
1121
Benny Prijonodc752ca2006-09-22 16:55:42 +00001122 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001123 if (status != PJ_SUCCESS)
1124 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001125
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126
1127 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1128 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001129 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130 return PJSIP_ESESSIONSTATE;
1131 }
1132
1133 status = create_inactive_sdp(call, &sdp);
1134 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001135 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136 return status;
1137 }
1138
1139 /* Create re-INVITE with new offer */
1140 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1141 if (status != PJ_SUCCESS) {
1142 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001143 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001144 return status;
1145 }
1146
1147 /* Add additional headers etc */
1148 pjsua_process_msg_data( tdata, msg_data);
1149
1150 /* Send the request */
1151 status = pjsip_inv_send_msg( call->inv, tdata);
1152 if (status != PJ_SUCCESS) {
1153 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001154 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155 return status;
1156 }
1157
Benny Prijonodc752ca2006-09-22 16:55:42 +00001158 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159
1160 return PJ_SUCCESS;
1161}
1162
1163
1164/*
1165 * Send re-INVITE (to release hold).
1166 */
1167PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1168 pj_bool_t unhold,
1169 const pjsua_msg_data *msg_data)
1170{
1171 pjmedia_sdp_session *sdp;
1172 pjsip_tx_data *tdata;
1173 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001174 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001175 pj_status_t status;
1176
1177
1178 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1179 PJ_EINVAL);
1180
Benny Prijonodc752ca2006-09-22 16:55:42 +00001181 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001182 if (status != PJ_SUCCESS)
1183 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001184
1185 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1186 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001187 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001188 return PJSIP_ESESSIONSTATE;
1189 }
1190
1191 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001192 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001193 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001194 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
1195 1, &call->skinfo, &sdp);
1196 if (status != PJ_SUCCESS) {
1197 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1198 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001199 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001200 return status;
1201 }
1202
1203 /* Create re-INVITE with new offer */
1204 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1205 if (status != PJ_SUCCESS) {
1206 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001207 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208 return status;
1209 }
1210
1211 /* Add additional headers etc */
1212 pjsua_process_msg_data( tdata, msg_data);
1213
1214 /* Send the request */
1215 status = pjsip_inv_send_msg( call->inv, tdata);
1216 if (status != PJ_SUCCESS) {
1217 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001218 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001219 return status;
1220 }
1221
Benny Prijonodc752ca2006-09-22 16:55:42 +00001222 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001223
1224 return PJ_SUCCESS;
1225}
1226
1227
1228/*
1229 * Initiate call transfer to the specified address.
1230 */
1231PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1232 const pj_str_t *dest,
1233 const pjsua_msg_data *msg_data)
1234{
1235 pjsip_evsub *sub;
1236 pjsip_tx_data *tdata;
1237 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001238 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001239 pjsip_generic_string_hdr *gs_hdr;
1240 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001241 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 pj_status_t status;
1243
1244
1245 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1246 PJ_EINVAL);
1247
Benny Prijonodc752ca2006-09-22 16:55:42 +00001248 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001249 if (status != PJ_SUCCESS)
1250 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252
Benny Prijonod524e822006-09-22 12:48:18 +00001253 /* Create xfer client subscription. */
1254 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001255 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001256
1257 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258 if (status != PJ_SUCCESS) {
1259 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001260 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261 return status;
1262 }
1263
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001264 /* Associate this call with the client subscription */
1265 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1266
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267 /*
1268 * Create REFER request.
1269 */
1270 status = pjsip_xfer_initiate(sub, dest, &tdata);
1271 if (status != PJ_SUCCESS) {
1272 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001273 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274 return status;
1275 }
1276
Benny Prijono053f5222006-11-11 16:16:04 +00001277 /* Add Referred-By header */
1278 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1279 &dlg->local.info_str);
1280 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1281
1282
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001283 /* Add additional headers etc */
1284 pjsua_process_msg_data( tdata, msg_data);
1285
1286 /* Send. */
1287 status = pjsip_xfer_send_request(sub, tdata);
1288 if (status != PJ_SUCCESS) {
1289 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001290 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001291 return status;
1292 }
1293
1294 /* For simplicity (that's what this program is intended to be!),
1295 * leave the original invite session as it is. More advanced application
1296 * may want to hold the INVITE, or terminate the invite, or whatever.
1297 */
1298
Benny Prijonodc752ca2006-09-22 16:55:42 +00001299 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001300
1301 return PJ_SUCCESS;
1302
1303}
1304
1305
1306/*
Benny Prijono053f5222006-11-11 16:16:04 +00001307 * Initiate attended call transfer to the specified address.
1308 */
1309PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1310 pjsua_call_id dest_call_id,
1311 unsigned options,
1312 const pjsua_msg_data *msg_data)
1313{
1314 pjsua_call *dest_call;
1315 pjsip_dialog *dest_dlg;
1316 char str_dest_buf[512];
1317 pj_str_t str_dest;
1318 int len;
1319 pjsip_uri *uri;
1320 pj_status_t status;
1321
1322
1323 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1324 PJ_EINVAL);
1325 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1326 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1327 PJ_EINVAL);
1328
1329 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1330 &dest_call, &dest_dlg);
1331 if (status != PJ_SUCCESS)
1332 return status;
1333
1334 /*
1335 * Create REFER destination URI with Replaces field.
1336 */
1337
1338 /* Make sure we have sufficient buffer's length */
1339 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1340 dest_dlg->call_id->id.slen +
1341 dest_dlg->remote.info->tag.slen +
1342 dest_dlg->local.info->tag.slen + 32
1343 < sizeof(str_dest_buf), PJSIP_EURITOOLONG);
1344
1345 /* Print URI */
1346 str_dest_buf[0] = '<';
1347 str_dest.slen = 1;
1348
1349 uri = pjsip_uri_get_uri(dest_dlg->remote.info->uri);
1350 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1351 str_dest_buf+1, sizeof(str_dest_buf)-1);
1352 if (len < 0)
1353 return PJSIP_EURITOOLONG;
1354
1355 str_dest.slen += len;
1356
1357
1358 /* Build the URI */
1359 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1360 sizeof(str_dest_buf) - str_dest.slen,
1361 "?%s"
1362 "Replaces=%.*s"
1363 "%%3Bto-tag%%3D%.*s"
1364 "%%3Bfrom-tag%%3D%.*s>",
1365 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1366 "" : "Require=replaces&"),
1367 (int)dest_dlg->call_id->id.slen,
1368 dest_dlg->call_id->id.ptr,
1369 (int)dest_dlg->remote.info->tag.slen,
1370 dest_dlg->remote.info->tag.ptr,
1371 (int)dest_dlg->local.info->tag.slen,
1372 dest_dlg->local.info->tag.ptr);
1373
1374 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1375 PJSIP_EURITOOLONG);
1376
1377 str_dest.ptr = str_dest_buf;
1378 str_dest.slen += len;
1379
1380 pjsip_dlg_dec_lock(dest_dlg);
1381
1382 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1383}
1384
1385
1386/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001387 * Send DTMF digits to remote using RFC 2833 payload formats.
1388 */
1389PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1390 const pj_str_t *digits)
1391{
1392 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001393 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394 pj_status_t status;
1395
1396 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1397 PJ_EINVAL);
1398
Benny Prijonodc752ca2006-09-22 16:55:42 +00001399 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001400 if (status != PJ_SUCCESS)
1401 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001403 if (!call->session) {
1404 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001405 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 return PJ_EINVALIDOP;
1407 }
1408
1409 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1410
Benny Prijonodc752ca2006-09-22 16:55:42 +00001411 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412
1413 return status;
1414}
1415
1416
1417/**
1418 * Send instant messaging inside INVITE session.
1419 */
1420PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1421 const pj_str_t *mime_type,
1422 const pj_str_t *content,
1423 const pjsua_msg_data *msg_data,
1424 void *user_data)
1425{
1426 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001427 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001428 const pj_str_t mime_text_plain = pj_str("text/plain");
1429 pjsip_media_type ctype;
1430 pjsua_im_data *im_data;
1431 pjsip_tx_data *tdata;
1432 pj_status_t status;
1433
1434
1435 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1436 PJ_EINVAL);
1437
Benny Prijonodc752ca2006-09-22 16:55:42 +00001438 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001439 if (status != PJ_SUCCESS)
1440 return status;
1441
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 /* Set default media type if none is specified */
1443 if (mime_type == NULL) {
1444 mime_type = &mime_text_plain;
1445 }
1446
1447 /* Create request message. */
1448 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1449 -1, &tdata);
1450 if (status != PJ_SUCCESS) {
1451 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1452 goto on_return;
1453 }
1454
1455 /* Add accept header. */
1456 pjsip_msg_add_hdr( tdata->msg,
1457 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1458
1459 /* Parse MIME type */
1460 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1461
1462 /* Create "text/plain" message body. */
1463 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1464 &ctype.subtype, content);
1465 if (tdata->msg->body == NULL) {
1466 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1467 pjsip_tx_data_dec_ref(tdata);
1468 goto on_return;
1469 }
1470
1471 /* Add additional headers etc */
1472 pjsua_process_msg_data( tdata, msg_data);
1473
1474 /* Create IM data and attach to the request. */
1475 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1476 im_data->acc_id = call->acc_id;
1477 im_data->call_id = call_id;
1478 im_data->to = call->inv->dlg->remote.info_str;
1479 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1480 im_data->user_data = user_data;
1481
1482
1483 /* Send the request. */
1484 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1485 pjsua_var.mod.id, im_data);
1486 if (status != PJ_SUCCESS) {
1487 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1488 goto on_return;
1489 }
1490
1491on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001492 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493 return status;
1494}
1495
1496
1497/*
1498 * Send IM typing indication inside INVITE session.
1499 */
1500PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1501 pj_bool_t is_typing,
1502 const pjsua_msg_data*msg_data)
1503{
1504 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001505 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506 pjsip_tx_data *tdata;
1507 pj_status_t status;
1508
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1510 PJ_EINVAL);
1511
Benny Prijonodc752ca2006-09-22 16:55:42 +00001512 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001513 if (status != PJ_SUCCESS)
1514 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001515
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001516 /* Create request message. */
1517 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1518 -1, &tdata);
1519 if (status != PJ_SUCCESS) {
1520 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1521 goto on_return;
1522 }
1523
1524 /* Create "application/im-iscomposing+xml" msg body. */
1525 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1526 NULL, NULL, -1);
1527
1528 /* Add additional headers etc */
1529 pjsua_process_msg_data( tdata, msg_data);
1530
1531 /* Send the request. */
1532 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1533 if (status != PJ_SUCCESS) {
1534 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1535 goto on_return;
1536 }
1537
1538on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001539 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001540 return status;
1541}
1542
1543
1544/*
1545 * Terminate all calls.
1546 */
1547PJ_DEF(void) pjsua_call_hangup_all(void)
1548{
1549 unsigned i;
1550
1551 PJSUA_LOCK();
1552
1553 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1554 if (pjsua_var.calls[i].inv)
1555 pjsua_call_hangup(i, 0, NULL, NULL);
1556 }
1557
1558 PJSUA_UNLOCK();
1559}
1560
1561
1562static const char *good_number(char *buf, pj_int32_t val)
1563{
1564 if (val < 1000) {
1565 pj_ansi_sprintf(buf, "%d", val);
1566 } else if (val < 1000000) {
1567 pj_ansi_sprintf(buf, "%d.%dK",
1568 val / 1000,
1569 (val % 1000) / 100);
1570 } else {
1571 pj_ansi_sprintf(buf, "%d.%02dM",
1572 val / 1000000,
1573 (val % 1000000) / 10000);
1574 }
1575
1576 return buf;
1577}
1578
1579
1580/* Dump media session */
1581static void dump_media_session(const char *indent,
1582 char *buf, unsigned maxlen,
1583 pjmedia_session *session)
1584{
1585 unsigned i;
1586 char *p = buf, *end = buf+maxlen;
1587 int len;
1588 pjmedia_session_info info;
1589
1590 pjmedia_session_get_info(session, &info);
1591
1592 for (i=0; i<info.stream_cnt; ++i) {
1593 pjmedia_rtcp_stat stat;
1594 const char *rem_addr;
1595 int rem_port;
1596 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001597 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001598 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1599 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001600
1601 pjmedia_session_get_stream_stat(session, i, &stat);
1602 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1603 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1604
1605 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1606 dir = "sendonly";
1607 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1608 dir = "recvonly";
1609 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1610 dir = "sendrecv";
1611 else
1612 dir = "inactive";
1613
1614
1615 len = pj_ansi_snprintf(buf, end-p,
1616 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1617 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001618 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619 info.stream_info[i].fmt.encoding_name.ptr,
1620 info.stream_info[i].fmt.clock_rate / 1000,
1621 dir,
1622 rem_addr, rem_port);
1623 if (len < 1 || len > end-p) {
1624 *p = '\0';
1625 return;
1626 }
1627
1628 p += len;
1629 *p++ = '\n';
1630 *p = '\0';
1631
1632 if (stat.rx.update_cnt == 0)
1633 strcpy(last_update, "never");
1634 else {
1635 pj_gettimeofday(&now);
1636 PJ_TIME_VAL_SUB(now, stat.rx.update);
1637 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1638 now.sec / 3600,
1639 (now.sec % 3600) / 60,
1640 now.sec % 60,
1641 now.msec);
1642 }
1643
Benny Prijono80019eb2006-08-07 13:22:23 +00001644 pj_gettimeofday(&media_duration);
1645 PJ_TIME_VAL_SUB(media_duration, stat.start);
1646 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1647 media_duration.msec = 1;
1648
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649 len = pj_ansi_snprintf(p, end-p,
1650 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001651 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001652 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1653 "%s (msec) min avg max last\n"
1654 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1655 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1656 indent, info.stream_info[i].fmt.pt,
1657 last_update,
1658 indent,
1659 good_number(packets, stat.rx.pkt),
1660 good_number(bytes, stat.rx.bytes),
1661 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001662 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 indent,
1664 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001665 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001666 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001667 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001668 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001669 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670 indent, indent,
1671 stat.rx.loss_period.min / 1000.0,
1672 stat.rx.loss_period.avg / 1000.0,
1673 stat.rx.loss_period.max / 1000.0,
1674 stat.rx.loss_period.last / 1000.0,
1675 indent,
1676 stat.rx.jitter.min / 1000.0,
1677 stat.rx.jitter.avg / 1000.0,
1678 stat.rx.jitter.max / 1000.0,
1679 stat.rx.jitter.last / 1000.0,
1680 ""
1681 );
1682
1683 if (len < 1 || len > end-p) {
1684 *p = '\0';
1685 return;
1686 }
1687
1688 p += len;
1689 *p++ = '\n';
1690 *p = '\0';
1691
1692 if (stat.tx.update_cnt == 0)
1693 strcpy(last_update, "never");
1694 else {
1695 pj_gettimeofday(&now);
1696 PJ_TIME_VAL_SUB(now, stat.tx.update);
1697 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1698 now.sec / 3600,
1699 (now.sec % 3600) / 60,
1700 now.sec % 60,
1701 now.msec);
1702 }
1703
1704 len = pj_ansi_snprintf(p, end-p,
1705 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001706 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1708 "%s (msec) min avg max last\n"
1709 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1710 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1711 indent,
1712 info.stream_info[i].tx_pt,
1713 info.stream_info[i].param->info.frm_ptime *
1714 info.stream_info[i].param->setting.frm_per_pkt,
1715 last_update,
1716
1717 indent,
1718 good_number(packets, stat.tx.pkt),
1719 good_number(bytes, stat.tx.bytes),
1720 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001721 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001722
1723 indent,
1724 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001725 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001726 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001727 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001728 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001729 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001730
1731 indent, indent,
1732 stat.tx.loss_period.min / 1000.0,
1733 stat.tx.loss_period.avg / 1000.0,
1734 stat.tx.loss_period.max / 1000.0,
1735 stat.tx.loss_period.last / 1000.0,
1736 indent,
1737 stat.tx.jitter.min / 1000.0,
1738 stat.tx.jitter.avg / 1000.0,
1739 stat.tx.jitter.max / 1000.0,
1740 stat.tx.jitter.last / 1000.0,
1741 ""
1742 );
1743
1744 if (len < 1 || len > end-p) {
1745 *p = '\0';
1746 return;
1747 }
1748
1749 p += len;
1750 *p++ = '\n';
1751 *p = '\0';
1752
1753 len = pj_ansi_snprintf(p, end-p,
1754 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1755 indent,
1756 stat.rtt.min / 1000.0,
1757 stat.rtt.avg / 1000.0,
1758 stat.rtt.max / 1000.0,
1759 stat.rtt.last / 1000.0
1760 );
1761 if (len < 1 || len > end-p) {
1762 *p = '\0';
1763 return;
1764 }
1765
1766 p += len;
1767 *p++ = '\n';
1768 *p = '\0';
1769 }
1770}
1771
1772
1773/* Print call info */
1774static void print_call(const char *title,
1775 int call_id,
1776 char *buf, pj_size_t size)
1777{
1778 int len;
1779 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1780 pjsip_dialog *dlg = inv->dlg;
1781 char userinfo[128];
1782
1783 /* Dump invite sesion info. */
1784
1785 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1786 if (len < 1)
1787 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1788 else
1789 userinfo[len] = '\0';
1790
1791 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1792 title,
1793 pjsip_inv_state_name(inv->state),
1794 userinfo);
1795 if (len < 1 || len >= (int)size) {
1796 pj_ansi_strcpy(buf, "<--uri too long-->");
1797 len = 18;
1798 } else
1799 buf[len] = '\0';
1800}
1801
1802
1803/*
1804 * Dump call and media statistics to string.
1805 */
1806PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1807 pj_bool_t with_media,
1808 char *buffer,
1809 unsigned maxlen,
1810 const char *indent)
1811{
1812 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001813 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001814 pj_time_val duration, res_delay, con_delay;
1815 char tmp[128];
1816 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001817 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818 int len;
1819
1820 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1821 PJ_EINVAL);
1822
Benny Prijonodc752ca2006-09-22 16:55:42 +00001823 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001824 if (status != PJ_SUCCESS)
1825 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001826
1827 *buffer = '\0';
1828 p = buffer;
1829 end = buffer + maxlen;
1830 len = 0;
1831
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001832 print_call(indent, call_id, tmp, sizeof(tmp));
1833
1834 len = pj_ansi_strlen(tmp);
1835 pj_ansi_strcpy(buffer, tmp);
1836
1837 p += len;
1838 *p++ = '\r';
1839 *p++ = '\n';
1840
1841 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001842 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001843 pj_gettimeofday(&duration);
1844 PJ_TIME_VAL_SUB(duration, call->conn_time);
1845 con_delay = call->conn_time;
1846 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1847 } else {
1848 duration.sec = duration.msec = 0;
1849 con_delay.sec = con_delay.msec = 0;
1850 }
1851
1852 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001853 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001854 res_delay = call->res_time;
1855 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1856 } else {
1857 res_delay.sec = res_delay.msec = 0;
1858 }
1859
1860 /* Print duration */
1861 len = pj_ansi_snprintf(p, end-p,
1862 "%s Call time: %02dh:%02dm:%02ds, "
1863 "1st res in %d ms, conn in %dms",
1864 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001865 (int)(duration.sec / 3600),
1866 (int)((duration.sec % 3600)/60),
1867 (int)(duration.sec % 60),
1868 (int)PJ_TIME_VAL_MSEC(res_delay),
1869 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870
1871 if (len > 0 && len < end-p) {
1872 p += len;
1873 *p++ = '\n';
1874 *p = '\0';
1875 }
1876
1877 /* Dump session statistics */
1878 if (with_media && call->session)
1879 dump_media_session(indent, p, end-p, call->session);
1880
Benny Prijonodc752ca2006-09-22 16:55:42 +00001881 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882
1883 return PJ_SUCCESS;
1884}
1885
1886
1887/*
1888 * Destroy the call's media
1889 */
1890static pj_status_t call_destroy_media(int call_id)
1891{
1892 pjsua_call *call = &pjsua_var.calls[call_id];
1893
1894 if (call->conf_slot != PJSUA_INVALID_ID) {
1895 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1896 call->conf_slot = PJSUA_INVALID_ID;
1897 }
1898
1899 if (call->session) {
1900 /* Destroy session (this will also close RTP/RTCP sockets). */
1901 pjmedia_session_destroy(call->session);
1902 call->session = NULL;
1903
1904 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1905 call_id));
1906
1907 }
1908
1909 call->media_st = PJSUA_CALL_MEDIA_NONE;
1910
1911 return PJ_SUCCESS;
1912}
1913
1914
Benny Prijono84126ab2006-02-09 09:30:09 +00001915/*
1916 * This callback receives notification from invite session when the
1917 * session state has changed.
1918 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001919static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1920 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001921{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001922 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001923
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001924 PJSUA_LOCK();
1925
1926 call = inv->dlg->mod_data[pjsua_var.mod.id];
1927
1928 if (!call) {
1929 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001930 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001931 }
1932
Benny Prijonoe21e7842006-04-09 16:46:05 +00001933
1934 /* Get call times */
1935 switch (inv->state) {
1936 case PJSIP_INV_STATE_EARLY:
1937 case PJSIP_INV_STATE_CONNECTING:
1938 if (call->res_time.sec == 0)
1939 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001940 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001941 pj_strncpy(&call->last_text,
1942 &e->body.tsx_state.tsx->status_text,
1943 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001944 break;
1945 case PJSIP_INV_STATE_CONFIRMED:
1946 pj_gettimeofday(&call->conn_time);
1947 break;
1948 case PJSIP_INV_STATE_DISCONNECTED:
1949 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001950 if (call->res_time.sec == 0)
1951 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001952 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1953 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001954 pj_strncpy(&call->last_text,
1955 &e->body.tsx_state.tsx->status_text,
1956 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001957 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001958 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001959 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001960 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961 pj_strncpy(&call->last_text,
1962 &e->body.tsx_state.tsx->status_text,
1963 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001964 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001965 }
1966
Benny Prijono26ff9062006-02-21 23:47:00 +00001967 /* If this is an outgoing INVITE that was created because of
1968 * REFER/transfer, send NOTIFY to transferer.
1969 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001970 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001971 int st_code = -1;
1972 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1973
1974
Benny Prijonoa91a0032006-02-26 21:23:45 +00001975 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001976 case PJSIP_INV_STATE_NULL:
1977 case PJSIP_INV_STATE_CALLING:
1978 /* Do nothing */
1979 break;
1980
1981 case PJSIP_INV_STATE_EARLY:
1982 case PJSIP_INV_STATE_CONNECTING:
1983 st_code = e->body.tsx_state.tsx->status_code;
1984 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1985 break;
1986
1987 case PJSIP_INV_STATE_CONFIRMED:
1988 /* When state is confirmed, send the final 200/OK and terminate
1989 * subscription.
1990 */
1991 st_code = e->body.tsx_state.tsx->status_code;
1992 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1993 break;
1994
1995 case PJSIP_INV_STATE_DISCONNECTED:
1996 st_code = e->body.tsx_state.tsx->status_code;
1997 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1998 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001999
Benny Prijono8b1889b2006-06-06 18:40:40 +00002000 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002001 /* Nothing to do. Just to keep gcc from complaining about
2002 * unused enums.
2003 */
2004 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002005 }
2006
2007 if (st_code != -1) {
2008 pjsip_tx_data *tdata;
2009 pj_status_t status;
2010
Benny Prijonoa91a0032006-02-26 21:23:45 +00002011 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002012 ev_state, st_code,
2013 NULL, &tdata);
2014 if (status != PJ_SUCCESS) {
2015 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2016 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002017 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002018 if (status != PJ_SUCCESS) {
2019 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2020 }
2021 }
2022 }
2023 }
2024
Benny Prijono84126ab2006-02-09 09:30:09 +00002025
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002026 if (pjsua_var.ua_cfg.cb.on_call_state)
2027 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002028
2029 /* call->inv may be NULL now */
2030
Benny Prijono84126ab2006-02-09 09:30:09 +00002031 /* Destroy media session when invite session is disconnected. */
2032 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002033
Benny Prijonoa91a0032006-02-26 21:23:45 +00002034 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002035
Benny Prijono275fd682006-03-22 11:59:11 +00002036 if (call)
2037 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002038
Benny Prijono105217f2006-03-06 16:25:59 +00002039 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002040 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002041 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002042
2043 /* Reset call */
2044 reset_call(call->index);
2045
Benny Prijono84126ab2006-02-09 09:30:09 +00002046 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002047
2048 PJSUA_UNLOCK();
2049}
2050
2051/*
2052 * This callback is called by invite session framework when UAC session
2053 * has forked.
2054 */
2055static void pjsua_call_on_forked( pjsip_inv_session *inv,
2056 pjsip_event *e)
2057{
2058 PJ_UNUSED_ARG(inv);
2059 PJ_UNUSED_ARG(e);
2060
2061 PJ_TODO(HANDLE_FORKED_DIALOG);
2062}
2063
2064
2065/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002066 * Disconnect call upon error.
2067 */
2068static void call_disconnect( pjsip_inv_session *inv,
2069 int code )
2070{
2071 pjsip_tx_data *tdata;
2072 pj_status_t status;
2073
2074 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2075 if (status == PJ_SUCCESS)
2076 pjsip_inv_send_msg(inv, tdata);
2077}
2078
2079/*
Benny Prijono0875ae82006-12-26 00:11:48 +00002080 * DTMF callback from the stream.
2081 */
2082static void dtmf_callback(pjmedia_stream *strm, void *user_data,
2083 int digit)
2084{
2085 PJ_UNUSED_ARG(strm);
2086
2087 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2088 pjsua_call_id call_id;
2089
2090 call_id = (pjsua_call_id)user_data;
2091 pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
2092 }
2093}
2094
2095/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 * Callback to be called when SDP offer/answer negotiation has just completed
2097 * in the session. This function will start/update media if negotiation
2098 * has succeeded.
2099 */
2100static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2101 pj_status_t status)
2102{
2103 int prev_media_st = 0;
2104 pjsua_call *call;
2105 pjmedia_session_info sess_info;
2106 const pjmedia_sdp_session *local_sdp;
2107 const pjmedia_sdp_session *remote_sdp;
2108 pjmedia_port *media_port;
2109 pj_str_t port_name;
2110 char tmp[PJSIP_MAX_URL_SIZE];
2111
2112 PJSUA_LOCK();
2113
2114 call = inv->dlg->mod_data[pjsua_var.mod.id];
2115
2116 if (status != PJ_SUCCESS) {
2117
2118 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2119
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002120 /* Stop/destroy media, if any */
2121 call_destroy_media(call->index);
2122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002123 /* Disconnect call if we're not in the middle of initializing an
2124 * UAS dialog and if this is not a re-INVITE
2125 */
2126 if (inv->state != PJSIP_INV_STATE_NULL &&
2127 inv->state != PJSIP_INV_STATE_CONFIRMED)
2128 {
2129 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2130 }
2131
2132 PJSUA_UNLOCK();
2133 return;
2134 }
2135
2136 /* Destroy existing media session, if any. */
2137
2138 if (call) {
2139 prev_media_st = call->media_st;
2140 call_destroy_media(call->index);
2141 }
2142
2143 /* Get local and remote SDP */
2144
2145 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2146 if (status != PJ_SUCCESS) {
2147 pjsua_perror(THIS_FILE,
2148 "Unable to retrieve currently active local SDP",
2149 status);
2150 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2151 PJSUA_UNLOCK();
2152 return;
2153 }
2154
2155
2156 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2157 if (status != PJ_SUCCESS) {
2158 pjsua_perror(THIS_FILE,
2159 "Unable to retrieve currently active remote SDP",
2160 status);
2161 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2162 PJSUA_UNLOCK();
2163 return;
2164 }
2165
2166 /* Create media session info based on SDP parameters.
2167 * We only support one stream per session at the moment
2168 */
2169 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
2170 pjsua_var.med_endpt,
2171 1,&sess_info,
2172 local_sdp, remote_sdp);
2173 if (status != PJ_SUCCESS) {
2174 pjsua_perror(THIS_FILE, "Unable to create media session",
2175 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002176 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177 PJSUA_UNLOCK();
2178 return;
2179 }
2180
2181 /* Check if media is put on-hold */
2182 if (sess_info.stream_cnt == 0 ||
2183 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
2184 {
2185
2186 /* Determine who puts the call on-hold */
2187 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
2188 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
2189 /* It was local who offer hold */
2190 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2191 } else {
2192 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2193 }
2194 }
2195
2196 call->media_dir = PJMEDIA_DIR_NONE;
2197
2198 } else {
2199
2200 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00002201 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00002202 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
2203 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00002204 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
2205 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
2206 }
2207
2208 /* Disable VAD, if this option is specified. */
2209 if (pjsua_var.media_cfg.no_vad) {
2210 sess_info.stream_info[0].param->setting.vad = 0;
2211 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212
2213
2214 /* Optionally, application may modify other stream settings here
2215 * (such as jitter buffer parameters, codec ptime, etc.)
2216 */
Benny Prijono1d0ca0c2006-11-21 09:06:47 +00002217 sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
2218 sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
2219 sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
2220 sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002221
2222 /* Create session based on session info. */
2223 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
2224 &call->med_tp,
2225 call, &call->session );
2226 if (status != PJ_SUCCESS) {
2227 pjsua_perror(THIS_FILE, "Unable to create media session",
2228 status);
2229 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2230 PJSUA_UNLOCK();
2231 return;
2232 }
2233
Benny Prijono0875ae82006-12-26 00:11:48 +00002234 /* If DTMF callback is installed by application, install our
2235 * callback to the session.
2236 */
2237 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2238 pjmedia_session_set_dtmf_callback(call->session, 0,
2239 &dtmf_callback,
2240 (void*)(call->index));
2241 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242
2243 /* Get the port interface of the first stream in the session.
2244 * We need the port interface to add to the conference bridge.
2245 */
2246 pjmedia_session_get_port(call->session, 0, &media_port);
2247
2248
2249 /*
2250 * Add the call to conference bridge.
2251 */
2252 port_name.ptr = tmp;
2253 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
2254 call->inv->dlg->remote.info->uri,
2255 tmp, sizeof(tmp));
2256 if (port_name.slen < 1) {
2257 port_name = pj_str("call");
2258 }
2259 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
2260 media_port,
2261 &port_name,
2262 (unsigned*)&call->conf_slot);
2263 if (status != PJ_SUCCESS) {
2264 pjsua_perror(THIS_FILE, "Unable to create conference slot",
2265 status);
2266 call_destroy_media(call->index);
2267 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
2268 PJSUA_UNLOCK();
2269 return;
2270 }
2271
2272 /* Call's media state is active */
2273 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
2274 call->media_dir = sess_info.stream_info[0].dir;
2275 }
2276
2277 /* Print info. */
2278 {
2279 char info[80];
2280 int info_len = 0;
2281 unsigned i;
2282
2283 for (i=0; i<sess_info.stream_cnt; ++i) {
2284 int len;
2285 const char *dir;
2286 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
2287
2288 switch (strm_info->dir) {
2289 case PJMEDIA_DIR_NONE:
2290 dir = "inactive";
2291 break;
2292 case PJMEDIA_DIR_ENCODING:
2293 dir = "sendonly";
2294 break;
2295 case PJMEDIA_DIR_DECODING:
2296 dir = "recvonly";
2297 break;
2298 case PJMEDIA_DIR_ENCODING_DECODING:
2299 dir = "sendrecv";
2300 break;
2301 default:
2302 dir = "unknown";
2303 break;
2304 }
2305 len = pj_ansi_sprintf( info+info_len,
2306 ", stream #%d: %.*s (%s)", i,
2307 (int)strm_info->fmt.encoding_name.slen,
2308 strm_info->fmt.encoding_name.ptr,
2309 dir);
2310 if (len > 0)
2311 info_len += len;
2312 }
2313 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
2314 }
2315
2316 /* Call application callback, if any */
2317 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2318 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2319
2320
2321 PJSUA_UNLOCK();
2322}
2323
2324
2325/*
2326 * Create inactive SDP for call hold.
2327 */
2328static pj_status_t create_inactive_sdp(pjsua_call *call,
2329 pjmedia_sdp_session **p_answer)
2330{
2331 pj_status_t status;
2332 pjmedia_sdp_conn *conn;
2333 pjmedia_sdp_attr *attr;
2334 pjmedia_sdp_session *sdp;
2335
2336 /* Create new offer */
2337 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
2338 &call->skinfo, &sdp);
2339 if (status != PJ_SUCCESS) {
2340 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2341 return status;
2342 }
2343
2344 /* Get SDP media connection line */
2345 conn = sdp->media[0]->conn;
2346 if (!conn)
2347 conn = sdp->conn;
2348
2349 /* Modify address */
2350 conn->addr = pj_str("0.0.0.0");
2351
2352 /* Remove existing directions attributes */
2353 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2354 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2355 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2356 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2357
2358 /* Add inactive attribute */
2359 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2360 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2361
2362 *p_answer = sdp;
2363
2364 return status;
2365}
2366
2367
2368/*
2369 * Called when session received new offer.
2370 */
2371static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2372 const pjmedia_sdp_session *offer)
2373{
2374 const char *remote_state;
2375 pjsua_call *call;
2376 pjmedia_sdp_conn *conn;
2377 pjmedia_sdp_session *answer;
2378 pj_bool_t is_remote_active;
2379 pj_status_t status;
2380
2381 PJSUA_LOCK();
2382
2383 call = inv->dlg->mod_data[pjsua_var.mod.id];
2384
2385 /*
2386 * See if remote is offering active media (i.e. not on-hold)
2387 */
2388 is_remote_active = PJ_TRUE;
2389
2390 conn = offer->media[0]->conn;
2391 if (!conn)
2392 conn = offer->conn;
2393
2394 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2395 pj_strcmp2(&conn->addr, "0")==0)
2396 {
2397 is_remote_active = PJ_FALSE;
2398
2399 }
2400 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2401 {
2402 is_remote_active = PJ_FALSE;
2403 }
2404
2405 remote_state = (is_remote_active ? "active" : "inactive");
2406
2407 /* Supply candidate answer */
2408 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2409 PJ_LOG(4,(THIS_FILE,
2410 "Call %d: RX new media offer, creating inactive SDP "
2411 "(media in offer is %s)", call->index, remote_state));
2412 status = create_inactive_sdp( call, &answer );
2413 } else {
2414 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2415 call->index));
2416 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2417 call->inv->pool, 1,
2418 &call->skinfo, &answer);
2419 }
2420
2421 if (status != PJ_SUCCESS) {
2422 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2423 PJSUA_UNLOCK();
2424 return;
2425 }
2426
2427 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2428 if (status != PJ_SUCCESS) {
2429 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2430 PJSUA_UNLOCK();
2431 return;
2432 }
2433
2434 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002435}
2436
2437
2438/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002439 * Callback called by event framework when the xfer subscription state
2440 * has changed.
2441 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002442static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2443{
2444
2445 PJ_UNUSED_ARG(event);
2446
2447 /*
2448 * When subscription is accepted (got 200/OK to REFER), check if
2449 * subscription suppressed.
2450 */
2451 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2452
2453 pjsip_rx_data *rdata;
2454 pjsip_generic_string_hdr *refer_sub;
2455 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2456 pjsua_call *call;
2457
2458 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2459
2460 /* Must be receipt of response message */
2461 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2462 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2463 rdata = event->body.tsx_state.src.rdata;
2464
2465 /* Find Refer-Sub header */
2466 refer_sub = (pjsip_generic_string_hdr*)
2467 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2468 &REFER_SUB, NULL);
2469
2470 /* Check if subscription is suppressed */
2471 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2472 /* Since no subscription is desired, assume that call has been
2473 * transfered successfully.
2474 */
2475 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2476 const pj_str_t ACCEPTED = { "Accepted", 8 };
2477 pj_bool_t cont = PJ_FALSE;
2478 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2479 200,
2480 &ACCEPTED,
2481 PJ_TRUE,
2482 &cont);
2483 }
2484
2485 /* Yes, subscription is suppressed.
2486 * Terminate our subscription now.
2487 */
2488 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2489 "event subcription..."));
2490 pjsip_evsub_terminate(sub, PJ_TRUE);
2491
2492 } else {
2493 /* Notify application about call transfer progress.
2494 * Initially notify with 100/Accepted status.
2495 */
2496 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2497 const pj_str_t ACCEPTED = { "Accepted", 8 };
2498 pj_bool_t cont = PJ_FALSE;
2499 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2500 100,
2501 &ACCEPTED,
2502 PJ_FALSE,
2503 &cont);
2504 }
2505 }
2506 }
2507 /*
2508 * On incoming NOTIFY, notify application about call transfer progress.
2509 */
2510 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2511 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2512 {
2513 pjsua_call *call;
2514 pjsip_msg *msg;
2515 pjsip_msg_body *body;
2516 pjsip_status_line status_line;
2517 pj_bool_t is_last;
2518 pj_bool_t cont;
2519 pj_status_t status;
2520
2521 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2522
2523 /* When subscription is terminated, clear the xfer_sub member of
2524 * the inv_data.
2525 */
2526 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2527 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2528 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2529
2530 }
2531
2532 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2533 /* Application is not interested with call progress status */
2534 return;
2535 }
2536
2537 /* This better be a NOTIFY request */
2538 if (event->type == PJSIP_EVENT_TSX_STATE &&
2539 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2540 {
2541 pjsip_rx_data *rdata;
2542
2543 rdata = event->body.tsx_state.src.rdata;
2544
2545 /* Check if there's body */
2546 msg = rdata->msg_info.msg;
2547 body = msg->body;
2548 if (!body) {
2549 PJ_LOG(4,(THIS_FILE,
2550 "Warning: received NOTIFY without message body"));
2551 return;
2552 }
2553
2554 /* Check for appropriate content */
2555 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2556 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2557 {
2558 PJ_LOG(4,(THIS_FILE,
2559 "Warning: received NOTIFY with non message/sipfrag "
2560 "content"));
2561 return;
2562 }
2563
2564 /* Try to parse the content */
2565 status = pjsip_parse_status_line(body->data, body->len,
2566 &status_line);
2567 if (status != PJ_SUCCESS) {
2568 PJ_LOG(4,(THIS_FILE,
2569 "Warning: received NOTIFY with invalid "
2570 "message/sipfrag content"));
2571 return;
2572 }
2573
2574 } else {
2575 status_line.code = 500;
2576 status_line.reason = *pjsip_get_status_text(500);
2577 }
2578
2579 /* Notify application */
2580 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2581 cont = !is_last;
2582 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2583 status_line.code,
2584 &status_line.reason,
2585 is_last, &cont);
2586
2587 if (!cont) {
2588 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2589 }
2590 }
2591}
2592
2593
2594/*
2595 * Callback called by event framework when the xfer subscription state
2596 * has changed.
2597 */
2598static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002599{
2600
2601 PJ_UNUSED_ARG(event);
2602
2603 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002604 * When subscription is terminated, clear the xfer_sub member of
2605 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002606 */
2607 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002608 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002609
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002610 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002611 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002612 return;
2613
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002614 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002615 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002616
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002617 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002618 }
2619}
2620
2621
2622/*
2623 * Follow transfer (REFER) request.
2624 */
2625static void on_call_transfered( pjsip_inv_session *inv,
2626 pjsip_rx_data *rdata )
2627{
2628 pj_status_t status;
2629 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002630 pjsua_call *existing_call;
2631 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002632 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002633 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002634 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002635 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002636 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002637 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002638 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002639 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002640 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002641 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002642 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002643 pjsip_evsub *sub;
2644
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002645 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002646
Benny Prijono26ff9062006-02-21 23:47:00 +00002647 /* Find the Refer-To header */
2648 refer_to = (pjsip_generic_string_hdr*)
2649 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2650
2651 if (refer_to == NULL) {
2652 /* Invalid Request.
2653 * No Refer-To header!
2654 */
2655 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002656 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002657 return;
2658 }
2659
Benny Prijonoc8141a82006-08-20 09:12:19 +00002660 /* Find optional Refer-Sub header */
2661 refer_sub = (pjsip_generic_string_hdr*)
2662 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2663
2664 if (refer_sub) {
2665 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2666 no_refer_sub = PJ_TRUE;
2667 }
2668
Benny Prijono053f5222006-11-11 16:16:04 +00002669 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2670 * request.
2671 */
2672 ref_by_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
2673 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002674
Benny Prijono9fc735d2006-05-28 14:58:12 +00002675 /* Notify callback */
2676 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002677 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2678 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2679 &refer_to->hvalue,
2680 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002681
2682 if (code < 200)
2683 code = 200;
2684 if (code >= 300) {
2685 /* Application rejects call transfer request */
2686 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2687 return;
2688 }
2689
Benny Prijono26ff9062006-02-21 23:47:00 +00002690 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2691 (int)inv->dlg->remote.info_str.slen,
2692 inv->dlg->remote.info_str.ptr,
2693 (int)refer_to->hvalue.slen,
2694 refer_to->hvalue.ptr));
2695
Benny Prijonoc8141a82006-08-20 09:12:19 +00002696 if (no_refer_sub) {
2697 /*
2698 * Always answer with 200.
2699 */
2700 pjsip_tx_data *tdata;
2701 const pj_str_t str_false = { "false", 5};
2702 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002703
Benny Prijonoc8141a82006-08-20 09:12:19 +00002704 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2705 if (status != PJ_SUCCESS) {
2706 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2707 status);
2708 return;
2709 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002710
Benny Prijonoc8141a82006-08-20 09:12:19 +00002711 /* Add Refer-Sub header */
2712 hdr = (pjsip_hdr*)
2713 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2714 &str_false);
2715 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002716
Benny Prijono26ff9062006-02-21 23:47:00 +00002717
Benny Prijonoc8141a82006-08-20 09:12:19 +00002718 /* Send answer */
2719 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2720 tdata);
2721 if (status != PJ_SUCCESS) {
2722 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2723 status);
2724 return;
2725 }
2726
2727 /* Don't have subscription */
2728 sub = NULL;
2729
2730 } else {
2731 struct pjsip_evsub_user xfer_cb;
2732 pjsip_hdr hdr_list;
2733
2734 /* Init callback */
2735 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002736 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002737
2738 /* Init additional header list to be sent with REFER response */
2739 pj_list_init(&hdr_list);
2740
2741 /* Create transferee event subscription */
2742 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2743 if (status != PJ_SUCCESS) {
2744 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2745 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2746 return;
2747 }
2748
2749 /* If there's Refer-Sub header and the value is "true", send back
2750 * Refer-Sub in the response with value "true" too.
2751 */
2752 if (refer_sub) {
2753 const pj_str_t str_true = { "true", 4 };
2754 pjsip_hdr *hdr;
2755
2756 hdr = (pjsip_hdr*)
2757 pjsip_generic_string_hdr_create(inv->dlg->pool,
2758 &str_refer_sub,
2759 &str_true);
2760 pj_list_push_back(&hdr_list, hdr);
2761
2762 }
2763
2764 /* Accept the REFER request, send 200 (OK). */
2765 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2766
2767 /* Create initial NOTIFY request */
2768 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2769 100, NULL, &tdata);
2770 if (status != PJ_SUCCESS) {
2771 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2772 status);
2773 return;
2774 }
2775
2776 /* Send initial NOTIFY request */
2777 status = pjsip_xfer_send_request( sub, tdata);
2778 if (status != PJ_SUCCESS) {
2779 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2780 return;
2781 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002782 }
2783
2784 /* We're cheating here.
2785 * We need to get a null terminated string from a pj_str_t.
2786 * So grab the pointer from the hvalue and NULL terminate it, knowing
2787 * that the NULL position will be occupied by a newline.
2788 */
2789 uri = refer_to->hvalue.ptr;
2790 uri[refer_to->hvalue.slen] = '\0';
2791
Benny Prijono053f5222006-11-11 16:16:04 +00002792 /* Init msg_data */
2793 pjsua_msg_data_init(&msg_data);
2794
2795 /* If Referred-By header is present in the REFER request, copy this
2796 * to the outgoing INVITE request.
2797 */
2798 if (ref_by_hdr != NULL) {
2799 pjsip_hdr *dup = pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
2800 pj_list_push_back(&msg_data.hdr_list, dup);
2801 }
2802
Benny Prijono26ff9062006-02-21 23:47:00 +00002803 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002804 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002805 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002806 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002807 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002808 if (status != PJ_SUCCESS) {
2809
Benny Prijonoc8141a82006-08-20 09:12:19 +00002810 /* Notify xferer about the error (if we have subscription) */
2811 if (sub) {
2812 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2813 500, NULL, &tdata);
2814 if (status != PJ_SUCCESS) {
2815 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2816 status);
2817 return;
2818 }
2819 status = pjsip_xfer_send_request(sub, tdata);
2820 if (status != PJ_SUCCESS) {
2821 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2822 status);
2823 return;
2824 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002825 }
2826 return;
2827 }
2828
Benny Prijonoc8141a82006-08-20 09:12:19 +00002829 if (sub) {
2830 /* Put the server subscription in inv_data.
2831 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2832 * reported back to the server subscription.
2833 */
2834 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002835
Benny Prijonoc8141a82006-08-20 09:12:19 +00002836 /* Put the invite_data in the subscription. */
2837 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2838 &pjsua_var.calls[new_call]);
2839 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002840}
2841
2842
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002843
Benny Prijono26ff9062006-02-21 23:47:00 +00002844/*
2845 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002846 * session. We use this to trap:
2847 * - incoming REFER request.
2848 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002849 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002850static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2851 pjsip_transaction *tsx,
2852 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002853{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002854 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2855
2856 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002857
Benny Prijono26ff9062006-02-21 23:47:00 +00002858 if (tsx->role==PJSIP_ROLE_UAS &&
2859 tsx->state==PJSIP_TSX_STATE_TRYING &&
2860 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2861 {
2862 /*
2863 * Incoming REFER request.
2864 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002865 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002866
Benny Prijono26ff9062006-02-21 23:47:00 +00002867 }
Benny Prijonob0808372006-03-02 21:18:58 +00002868 else if (tsx->role==PJSIP_ROLE_UAS &&
2869 tsx->state==PJSIP_TSX_STATE_TRYING &&
2870 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2871 {
2872 /*
2873 * Incoming MESSAGE request!
2874 */
2875 pjsip_rx_data *rdata;
2876 pjsip_msg *msg;
2877 pjsip_accept_hdr *accept_hdr;
2878 pj_status_t status;
2879
2880 rdata = e->body.tsx_state.src.rdata;
2881 msg = rdata->msg_info.msg;
2882
2883 /* Request MUST have message body, with Content-Type equal to
2884 * "text/plain".
2885 */
2886 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2887
2888 pjsip_hdr hdr_list;
2889
2890 pj_list_init(&hdr_list);
2891 pj_list_push_back(&hdr_list, accept_hdr);
2892
2893 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2894 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002895 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002896 return;
2897 }
2898
2899 /* Respond with 200 first, so that remote doesn't retransmit in case
2900 * the UI takes too long to process the message.
2901 */
2902 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2903
2904 /* Process MESSAGE request */
2905 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2906 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002907
Benny Prijonob0808372006-03-02 21:18:58 +00002908 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002909 else if (tsx->role == PJSIP_ROLE_UAC &&
2910 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002911 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002912 /* Handle outgoing pager status */
2913 if (tsx->status_code >= 200) {
2914 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002915
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916 im_data = tsx->mod_data[pjsua_var.mod.id];
2917 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002918
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002919 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2920 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2921 &im_data->to,
2922 &im_data->body,
2923 im_data->user_data,
2924 tsx->status_code,
2925 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002926 }
Benny Prijonofccab712006-02-22 22:23:22 +00002927 }
Benny Prijono834aee32006-02-19 01:38:06 +00002928 }
Benny Prijono834aee32006-02-19 01:38:06 +00002929
Benny Prijono26ff9062006-02-21 23:47:00 +00002930
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002931 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002932}