blob: f7b7734826d69c998bf8e91826208bfc9a2efae9 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
53 * This callback is called when transaction state has changed in INVITE
54 * session. We use this to trap:
55 * - incoming REFER request.
56 * - incoming MESSAGE request.
57 */
58static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
59 pjsip_transaction *tsx,
60 pjsip_event *e);
61
62
63/* Destroy the call's media */
64static pj_status_t call_destroy_media(int call_id);
65
66/* Create inactive SDP for call hold. */
67static pj_status_t create_inactive_sdp(pjsua_call *call,
68 pjmedia_sdp_session **p_answer);
69
Benny Prijonod524e822006-09-22 12:48:18 +000070/*
71 * Callback called by event framework when the xfer subscription state
72 * has changed.
73 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000074static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
75static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
76
77/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078 * Reset call descriptor.
79 */
80static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000081{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000083
Benny Prijonoeebe9af2006-06-13 22:57:13 +000084 call->index = id;
85 call->inv = NULL;
86 call->user_data = NULL;
87 call->session = NULL;
88 call->xfer_sub = NULL;
89 call->last_code = 0;
90 call->conf_slot = PJSUA_INVALID_ID;
91 call->last_text.ptr = call->last_text_buf_;
92 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +000093 call->conn_time.sec = 0;
94 call->conn_time.msec = 0;
95 call->res_time.sec = 0;
96 call->res_time.msec = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000097}
98
99
Benny Prijono275fd682006-03-22 11:59:11 +0000100/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000102 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000104{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000105 pjsip_inv_callback inv_cb;
106 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000107 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000109
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 /* Init calls array. */
111 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
112 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000113
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000114 /* Copy config */
115 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000116
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000118 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000119 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
120 inv_cb.on_new_session = &pjsua_call_on_forked;
121 inv_cb.on_media_update = &pjsua_call_on_media_update;
122 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
123 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000124
Benny Prijono275fd682006-03-22 11:59:11 +0000125
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 /* Initialize invite session module: */
127 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
128 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
129
Benny Prijonoc8141a82006-08-20 09:12:19 +0000130 /* Add "norefersub" in Supported header */
131 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
132 NULL, 1, &str_norefersub);
133
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 return status;
135}
136
137
138/*
139 * Start call subsystem.
140 */
141pj_status_t pjsua_call_subsys_start(void)
142{
143 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000144 return PJ_SUCCESS;
145}
146
147
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000149 * Get maximum number of calls configured in pjsua.
150 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000151PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000152{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000154}
155
156
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157/*
158 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000159 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000160PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000161{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000163}
164
165
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000166/*
167 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
170 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
179 if (!pjsua_var.calls[i].inv)
180 continue;
181 ids[c] = i;
182 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183 }
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188
189 return PJ_SUCCESS;
190}
191
192
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000193/*
194 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
197 const pj_str_t *dest_uri,
198 unsigned options,
199 void *user_data,
200 const pjsua_msg_data *msg_data,
201 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000202{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000203 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000204 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000205 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 pjsua_acc *acc;
207 pjsua_call *call;
208 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000209 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000210 pjsip_tx_data *tdata;
211 pj_status_t status;
212
Benny Prijono9fc735d2006-05-28 14:58:12 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 /* Check that account is valid */
215 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216 PJ_EINVAL);
217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 /* Options must be zero for now */
219 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
220
Benny Prijono320fa4d2006-12-07 10:09:16 +0000221 /* Check arguments */
222 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
223
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 PJSUA_LOCK();
225
226 acc = &pjsua_var.acc[acc_id];
227 if (!acc->valid) {
228 pjsua_perror(THIS_FILE, "Unable to make call because account "
229 "is not valid", PJ_EINVALIDOP);
230 PJSUA_UNLOCK();
231 return PJ_EINVALIDOP;
232 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000233
Benny Prijonoa91a0032006-02-26 21:23:45 +0000234 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
236 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000237 break;
238 }
239
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 if (call_id == pjsua_var.ua_cfg.max_calls) {
241 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
242 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000243 return PJ_ETOOMANY;
244 }
245
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000246 call = &pjsua_var.calls[call_id];
247
Benny Prijono320fa4d2006-12-07 10:09:16 +0000248 /* Verify that destination URI is valid before calling
249 * pjsua_acc_create_uac_contact, or otherwise there
250 * a misleading "Invalid Contact URI" error will be printed
251 * when pjsua_acc_create_uac_contact() fails.
252 */
253 if (1) {
254 pj_pool_t *pool;
255 pjsip_uri *uri;
256 pj_str_t dup;
257
258 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
259 if (!pool) {
260 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
261 PJSUA_UNLOCK();
262 return PJ_ENOMEM;
263 }
264
265 pj_strdup_with_null(pool, &dup, dest_uri);
266 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
267 pj_pool_release(pool);
268
269 if (uri == NULL) {
270 pjsua_perror(THIS_FILE, "Unable to make call",
271 PJSIP_EINVALIDREQURI);
272 PJSUA_UNLOCK();
273 return PJSIP_EINVALIDREQURI;
274 }
275 }
276
Benny Prijono093d3022006-09-24 00:07:11 +0000277 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
278 (int)dest_uri->slen, dest_uri->ptr));
279
Benny Prijonoe21e7842006-04-09 16:46:05 +0000280 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000282
Benny Prijonoe21e7842006-04-09 16:46:05 +0000283 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000284 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000285
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000286 /* Create suitable Contact header */
287 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
288 acc_id, dest_uri);
289 if (status != PJ_SUCCESS) {
290 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
291 PJSUA_UNLOCK();
292 return status;
293 }
294
Benny Prijonoe21e7842006-04-09 16:46:05 +0000295 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000296 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000297 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000298 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000299 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000300 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000301 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000302 return status;
303 }
304
305 /* Get media capability from media endpoint: */
306
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000307 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
308 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000309 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000310 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000311 goto on_error;
312 }
313
314 /* Create the INVITE session: */
315
316 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
317 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000318 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000319 goto on_error;
320 }
321
322
323 /* Create and associate our data in the session. */
324
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000325 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327 dlg->mod_data[pjsua_var.mod.id] = call;
328 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000329
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330 /* Attach user data */
331 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
333 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000334 if (!pj_list_empty(&acc->route_set))
335 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000336
337
338 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000339 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000340 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000342 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000343
344
345 /* Create initial INVITE: */
346
347 status = pjsip_inv_invite(inv, &tdata);
348 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000349 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
350 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000351 goto on_error;
352 }
353
354
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000355 /* Add additional headers etc */
356
357 pjsua_process_msg_data( tdata, msg_data);
358
Benny Prijono093d3022006-09-24 00:07:11 +0000359 /* Must increment call counter now */
360 ++pjsua_var.call_cnt;
361
Benny Prijono84126ab2006-02-09 09:30:09 +0000362 /* Send initial INVITE: */
363
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000364 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000365 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000366 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
367 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000368
369 /* Upon failure to send first request, both dialog and invite
370 * session would have been cleared.
371 */
372 inv = NULL;
373 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000374 goto on_error;
375 }
376
Benny Prijono84126ab2006-02-09 09:30:09 +0000377 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000378
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000379 if (p_call_id)
380 *p_call_id = call_id;
381
382 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000383
384 return PJ_SUCCESS;
385
386
387on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000388 if (inv != NULL) {
389 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000390 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000391 pjsip_dlg_terminate(dlg);
392 }
393
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000394 if (call_id != -1) {
395 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000396 }
397
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000398 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000399 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000400}
401
402
403/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000404 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000405 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000406 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000407pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000408{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000409 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000410 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000411 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000412 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
413 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000414 pjsip_tx_data *response = NULL;
415 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000416 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 int acc_id;
418 pjsua_call *call;
419 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000420 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000421 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000422
Benny Prijono26ff9062006-02-21 23:47:00 +0000423 /* Don't want to handle anything but INVITE */
424 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
425 return PJ_FALSE;
426
427 /* Don't want to handle anything that's already associated with
428 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000429 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000430 if (dlg || tsx)
431 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000432
Benny Prijono148c9dd2006-09-19 13:37:53 +0000433 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000434
Benny Prijono26ff9062006-02-21 23:47:00 +0000435 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
437 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000438 break;
439 }
440
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000441 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
442 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000443 PJSIP_SC_BUSY_HERE, NULL,
444 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000445 PJ_LOG(2,(THIS_FILE,
446 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000447 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000448 return PJ_TRUE;
449 }
450
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 /* Clear call descriptor */
452 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000453
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000454 call = &pjsua_var.calls[call_id];
455
456 /* Mark call start time. */
457 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000458
Benny Prijono053f5222006-11-11 16:16:04 +0000459 /* Check INVITE request for Replaces header. If Replaces header is
460 * present, the function will make sure that we can handle the request.
461 */
462 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
463 &response);
464 if (status != PJ_SUCCESS) {
465 /*
466 * Something wrong with the Replaces header.
467 */
468 if (response) {
469 pjsip_response_addr res_addr;
470
471 pjsip_get_response_addr(response->pool, rdata, &res_addr);
472 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
473 NULL, NULL);
474
475 } else {
476
477 /* Respond with 500 (Internal Server Error) */
478 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
479 NULL, NULL);
480 }
481
482 PJSUA_UNLOCK();
483 return PJ_TRUE;
484 }
485
486 /* If this INVITE request contains Replaces header, notify application
487 * about the request so that application can do subsequent checking
488 * if it wants to.
489 */
490 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
491 pjsua_call *replaced_call;
492 int st_code = 200;
493 pj_str_t st_text = { "OK", 2 };
494
495 /* Get the replaced call instance */
496 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
497
498 /* Notify application */
499 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
500 rdata, &st_code, &st_text);
501
502 /* Must specify final response */
503 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
504
505 /* Check if application rejects this request. */
506 if (st_code >= 300) {
507
508 if (st_text.slen == 2)
509 st_text = *pjsip_get_status_text(st_code);
510
511 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
512 st_code, &st_text, NULL, NULL, NULL);
513 PJSUA_UNLOCK();
514 return PJ_TRUE;
515 }
516 }
517
518
Benny Prijono26ff9062006-02-21 23:47:00 +0000519 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000520 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
521 rdata->tp_info.pool, 1,
522 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000523 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000524 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000525 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000526 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000527 return PJ_TRUE;
528 }
529
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000530
531 /* Verify that we can handle the request. */
532 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
533 pjsua_var.endpt, &response);
534 if (status != PJ_SUCCESS) {
535
536 /*
537 * No we can't handle the incoming INVITE request.
538 */
539
540 if (response) {
541 pjsip_response_addr res_addr;
542
543 pjsip_get_response_addr(response->pool, rdata, &res_addr);
544 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
545 NULL, NULL);
546
547 } else {
548
549 /* Respond with 500 (Internal Server Error) */
550 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
551 NULL, NULL);
552 }
553
554 PJSUA_UNLOCK();
555 return PJ_TRUE;
556 }
557
558
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000560 * Get which account is most likely to be associated with this incoming
561 * call. We need the account to find which contact URI to put for
562 * the call.
563 */
Benny Prijono093d3022006-09-24 00:07:11 +0000564 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000565
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000566 /* Get suitable Contact header */
567 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
568 acc_id, rdata);
569 if (status != PJ_SUCCESS) {
570 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
571 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
572 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000573 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000574 return PJ_TRUE;
575 }
576
Benny Prijono26ff9062006-02-21 23:47:00 +0000577 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000578 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000579 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000580 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000582 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000583 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000584 return PJ_TRUE;
585 }
586
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587 /* Set credentials */
588 if (pjsua_var.acc[acc_id].cred_cnt) {
589 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
590 pjsua_var.acc[acc_id].cred_cnt,
591 pjsua_var.acc[acc_id].cred);
592 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000593
594 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000595 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
596 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000597 pjsip_hdr hdr_list;
598 pjsip_warning_hdr *w;
599
600 w = pjsip_warning_hdr_create_from_status(dlg->pool,
601 pjsip_endpt_name(pjsua_var.endpt),
602 status);
603 pj_list_init(&hdr_list);
604 pj_list_push_back(&hdr_list, w);
605
606 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
607
608 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000609 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000610 */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000611 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000612 return PJ_TRUE;
613 }
614
615
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000616 /* Create and attach pjsua_var data to the dialog: */
617 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000618
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 dlg->mod_data[pjsua_var.mod.id] = call;
620 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000621
622
Benny Prijono64f851e2006-02-23 13:49:28 +0000623 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000624 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000625 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000626 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000627 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000628 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
629 status);
630
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
632 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000633 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000634 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000635
636 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000637 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000638 if (status != PJ_SUCCESS)
639 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000640 }
641
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000642 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000643
Benny Prijono105217f2006-03-06 16:25:59 +0000644
Benny Prijono053f5222006-11-11 16:16:04 +0000645 /* Check if this request should replace existing call */
646 if (replaced_dlg) {
647 pjsip_inv_session *replaced_inv;
648 struct pjsua_call *replaced_call;
649 pjsip_tx_data *tdata;
650
651 /* Get the invite session in the dialog */
652 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
653
654 /* Get the replaced call instance */
655 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
656
657 /* Notify application */
658 if (pjsua_var.ua_cfg.cb.on_call_replaced)
659 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
660 call_id);
661
662 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
663 call_id));
664
665 /* Answer the new call with 200 response */
666 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
667 if (status == PJ_SUCCESS)
668 status = pjsip_inv_send_msg(inv, tdata);
669
670 if (status != PJ_SUCCESS)
671 pjsua_perror(THIS_FILE, "Error answering session", status);
672
673
674 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
675 replaced_call->index));
676
677 /* Disconnect replaced invite session */
678 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
679 &tdata);
680 if (status == PJ_SUCCESS && tdata)
681 status = pjsip_inv_send_msg(replaced_inv, tdata);
682
683 if (status != PJ_SUCCESS)
684 pjsua_perror(THIS_FILE, "Error terminating session", status);
685
686
687 } else {
688
Benny Prijonob5388cf2007-01-04 22:45:08 +0000689 /* Notify application if on_incoming_call() is overriden,
690 * otherwise hangup the call with 480
691 */
692 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000693 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000694 } else {
695 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
696 NULL, NULL);
697 }
Benny Prijono053f5222006-11-11 16:16:04 +0000698 }
699
Benny Prijono8b1889b2006-06-06 18:40:40 +0000700
Benny Prijono26ff9062006-02-21 23:47:00 +0000701 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000702 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000703 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000704}
705
706
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000707
708/*
709 * Check if the specified call has active INVITE session and the INVITE
710 * session has not been disconnected.
711 */
712PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
713{
714 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
715 PJ_EINVAL);
716 return pjsua_var.calls[call_id].inv != NULL &&
717 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
718}
719
720
721/*
722 * Check if call has an active media session.
723 */
724PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
725{
726 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
727 PJ_EINVAL);
728 return pjsua_var.calls[call_id].session != NULL;
729}
730
731
Benny Prijono148c9dd2006-09-19 13:37:53 +0000732/* Acquire lock to the specified call_id */
733static pj_status_t acquire_call(const char *title,
734 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000735 pjsua_call **p_call,
736 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000737{
738 enum { MAX_RETRY=50 };
739 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000740 pjsua_call *call = NULL;
741 pj_bool_t has_pjsua_lock = PJ_FALSE;
742 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000743
744 for (retry=0; retry<MAX_RETRY; ++retry) {
745
746 has_pjsua_lock = PJ_FALSE;
747
748 status = PJSUA_TRY_LOCK();
749 if (status != PJ_SUCCESS) {
750 pj_thread_sleep(retry/10);
751 continue;
752 }
753
754 has_pjsua_lock = PJ_TRUE;
755 call = &pjsua_var.calls[call_id];
756
757 if (call->inv == NULL) {
758 PJSUA_UNLOCK();
759 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
760 return PJSIP_ESESSIONTERMINATED;
761 }
762
763 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
764 if (status != PJ_SUCCESS) {
765 PJSUA_UNLOCK();
766 pj_thread_sleep(retry/10);
767 continue;
768 }
769
770 PJSUA_UNLOCK();
771
772 break;
773 }
774
775 if (status != PJ_SUCCESS) {
776 if (has_pjsua_lock == PJ_FALSE)
777 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
778 "(possibly system has deadlocked) in %s",
779 title));
780 else
781 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
782 "(possibly system has deadlocked) in %s",
783 title));
784 return PJ_ETIMEDOUT;
785 }
786
787 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000788 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000789
790 return PJ_SUCCESS;
791}
792
793
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000794/*
795 * Get the conference port identification associated with the call.
796 */
797PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
798{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000799 pjsua_call *call;
800 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000801 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000802 pj_status_t status;
803
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000804 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
805 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000806
Benny Prijonodc752ca2006-09-22 16:55:42 +0000807 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000808 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000809 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000810
811 port_id = call->conf_slot;
812
Benny Prijonodc752ca2006-09-22 16:55:42 +0000813 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000814
815 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000816}
817
818
Benny Prijono148c9dd2006-09-19 13:37:53 +0000819
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000820/*
821 * Obtain detail information about the specified call.
822 */
823PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
824 pjsua_call_info *info)
825{
826 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000827 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000828 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000829
830 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
831 PJ_EINVAL);
832
Benny Prijonoac623b32006-07-03 15:19:31 +0000833 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000834
Benny Prijonodc752ca2006-09-22 16:55:42 +0000835 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000836 if (status != PJ_SUCCESS) {
837 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000838 }
839
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000840 /* id and role */
841 info->id = call_id;
842 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000843 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000844
845 /* local info */
846 info->local_info.ptr = info->buf_.local_info;
847 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
848 sizeof(info->buf_.local_info));
849
850 /* local contact */
851 info->local_contact.ptr = info->buf_.local_contact;
852 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
853 call->inv->dlg->local.contact->uri,
854 info->local_contact.ptr,
855 sizeof(info->buf_.local_contact));
856
857 /* remote info */
858 info->remote_info.ptr = info->buf_.remote_info;
859 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
860 sizeof(info->buf_.remote_info));
861
862 /* remote contact */
863 if (call->inv->dlg->remote.contact) {
864 int len;
865 info->remote_contact.ptr = info->buf_.remote_contact;
866 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
867 call->inv->dlg->remote.contact->uri,
868 info->remote_contact.ptr,
869 sizeof(info->buf_.remote_contact));
870 if (len < 0) len = 0;
871 info->remote_contact.slen = len;
872 } else {
873 info->remote_contact.slen = 0;
874 }
875
876 /* call id */
877 info->call_id.ptr = info->buf_.call_id;
878 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
879 sizeof(info->buf_.call_id));
880
881 /* state, state_text */
882 info->state = call->inv->state;
883 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
884
885 /* If call is disconnected, set the last_status from the cause code */
886 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
887 /* last_status, last_status_text */
888 info->last_status = call->inv->cause;
889
890 info->last_status_text.ptr = info->buf_.last_status_text;
891 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
892 sizeof(info->buf_.last_status_text));
893 } else {
894 /* last_status, last_status_text */
895 info->last_status = call->last_code;
896
897 info->last_status_text.ptr = info->buf_.last_status_text;
898 pj_strncpy(&info->last_status_text, &call->last_text,
899 sizeof(info->buf_.last_status_text));
900 }
901
902 /* media status and dir */
903 info->media_status = call->media_st;
904 info->media_dir = call->media_dir;
905
906
907 /* conference slot number */
908 info->conf_slot = call->conf_slot;
909
910 /* calculate duration */
911 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
912
913 info->total_duration = call->dis_time;
914 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
915
916 if (call->conn_time.sec) {
917 info->connect_duration = call->dis_time;
918 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
919 }
920
921 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
922
923 pj_gettimeofday(&info->total_duration);
924 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
925
926 pj_gettimeofday(&info->connect_duration);
927 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
928
929 } else {
930 pj_gettimeofday(&info->total_duration);
931 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
932 }
933
Benny Prijonodc752ca2006-09-22 16:55:42 +0000934 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000935
936 return PJ_SUCCESS;
937}
938
939
940/*
941 * Attach application specific data to the call.
942 */
943PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
944 void *user_data)
945{
946 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
947 PJ_EINVAL);
948 pjsua_var.calls[call_id].user_data = user_data;
949
950 return PJ_SUCCESS;
951}
952
953
954/*
955 * Get user data attached to the call.
956 */
957PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
958{
959 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
960 NULL);
961 return pjsua_var.calls[call_id].user_data;
962}
963
964
965/*
966 * Send response to incoming INVITE request.
967 */
968PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
969 unsigned code,
970 const pj_str_t *reason,
971 const pjsua_msg_data *msg_data)
972{
973 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000974 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000975 pjsip_tx_data *tdata;
976 pj_status_t status;
977
978 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
979 PJ_EINVAL);
980
Benny Prijonodc752ca2006-09-22 16:55:42 +0000981 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000982 if (status != PJ_SUCCESS)
983 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000984
Benny Prijono2e507c22006-06-23 15:04:11 +0000985 if (call->res_time.sec == 0)
986 pj_gettimeofday(&call->res_time);
987
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000988 /* Create response message */
989 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
990 if (status != PJ_SUCCESS) {
991 pjsua_perror(THIS_FILE, "Error creating response",
992 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000993 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000994 return status;
995 }
996
997 /* Add additional headers etc */
998 pjsua_process_msg_data( tdata, msg_data);
999
1000 /* Send the message */
1001 status = pjsip_inv_send_msg(call->inv, tdata);
1002 if (status != PJ_SUCCESS)
1003 pjsua_perror(THIS_FILE, "Error sending response",
1004 status);
1005
Benny Prijonodc752ca2006-09-22 16:55:42 +00001006 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001007
1008 return status;
1009}
1010
1011
1012/*
1013 * Hangup call by using method that is appropriate according to the
1014 * call state.
1015 */
1016PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1017 unsigned code,
1018 const pj_str_t *reason,
1019 const pjsua_msg_data *msg_data)
1020{
1021 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001022 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001023 pj_status_t status;
1024 pjsip_tx_data *tdata;
1025
1026
Benny Prijono148c9dd2006-09-19 13:37:53 +00001027 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1028 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1029 call_id));
1030 }
1031
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1033 PJ_EINVAL);
1034
Benny Prijonodc752ca2006-09-22 16:55:42 +00001035 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001036 if (status != PJ_SUCCESS)
1037 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001038
1039 if (code==0) {
1040 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1041 code = PJSIP_SC_OK;
1042 else if (call->inv->role == PJSIP_ROLE_UAS)
1043 code = PJSIP_SC_DECLINE;
1044 else
1045 code = PJSIP_SC_REQUEST_TERMINATED;
1046 }
1047
1048 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1049 if (status != PJ_SUCCESS) {
1050 pjsua_perror(THIS_FILE,
1051 "Failed to create end session message",
1052 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001053 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001054 return status;
1055 }
1056
1057 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1058 * as p_tdata when INVITE transaction has not been answered
1059 * with any provisional responses.
1060 */
1061 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001062 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001063 return PJ_SUCCESS;
1064 }
1065
1066 /* Add additional headers etc */
1067 pjsua_process_msg_data( tdata, msg_data);
1068
1069 /* Send the message */
1070 status = pjsip_inv_send_msg(call->inv, tdata);
1071 if (status != PJ_SUCCESS) {
1072 pjsua_perror(THIS_FILE,
1073 "Failed to send 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
Benny Prijonodc752ca2006-09-22 16:55:42 +00001079 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001080
1081 return PJ_SUCCESS;
1082}
1083
1084
1085/*
1086 * Put the specified call on hold.
1087 */
1088PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1089 const pjsua_msg_data *msg_data)
1090{
1091 pjmedia_sdp_session *sdp;
1092 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001093 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001094 pjsip_tx_data *tdata;
1095 pj_status_t status;
1096
1097 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1098 PJ_EINVAL);
1099
Benny Prijonodc752ca2006-09-22 16:55:42 +00001100 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001101 if (status != PJ_SUCCESS)
1102 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001103
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001104
1105 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1106 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001107 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001108 return PJSIP_ESESSIONSTATE;
1109 }
1110
1111 status = create_inactive_sdp(call, &sdp);
1112 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001113 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001114 return status;
1115 }
1116
1117 /* Create re-INVITE with new offer */
1118 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1119 if (status != PJ_SUCCESS) {
1120 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001121 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001122 return status;
1123 }
1124
1125 /* Add additional headers etc */
1126 pjsua_process_msg_data( tdata, msg_data);
1127
1128 /* Send the request */
1129 status = pjsip_inv_send_msg( call->inv, tdata);
1130 if (status != PJ_SUCCESS) {
1131 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001132 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 return status;
1134 }
1135
Benny Prijonodc752ca2006-09-22 16:55:42 +00001136 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001137
1138 return PJ_SUCCESS;
1139}
1140
1141
1142/*
1143 * Send re-INVITE (to release hold).
1144 */
1145PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1146 pj_bool_t unhold,
1147 const pjsua_msg_data *msg_data)
1148{
1149 pjmedia_sdp_session *sdp;
1150 pjsip_tx_data *tdata;
1151 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001153 pj_status_t status;
1154
1155
1156 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1157 PJ_EINVAL);
1158
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160 if (status != PJ_SUCCESS)
1161 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001162
1163 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1164 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001165 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166 return PJSIP_ESESSIONSTATE;
1167 }
1168
1169 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001170 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001171 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
1173 1, &call->skinfo, &sdp);
1174 if (status != PJ_SUCCESS) {
1175 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1176 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001177 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001178 return status;
1179 }
1180
1181 /* Create re-INVITE with new offer */
1182 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1183 if (status != PJ_SUCCESS) {
1184 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001185 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001186 return status;
1187 }
1188
1189 /* Add additional headers etc */
1190 pjsua_process_msg_data( tdata, msg_data);
1191
1192 /* Send the request */
1193 status = pjsip_inv_send_msg( call->inv, tdata);
1194 if (status != PJ_SUCCESS) {
1195 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001196 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001197 return status;
1198 }
1199
Benny Prijonodc752ca2006-09-22 16:55:42 +00001200 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001201
1202 return PJ_SUCCESS;
1203}
1204
1205
1206/*
1207 * Initiate call transfer to the specified address.
1208 */
1209PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1210 const pj_str_t *dest,
1211 const pjsua_msg_data *msg_data)
1212{
1213 pjsip_evsub *sub;
1214 pjsip_tx_data *tdata;
1215 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001216 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001217 pjsip_generic_string_hdr *gs_hdr;
1218 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001219 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001220 pj_status_t status;
1221
1222
1223 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1224 PJ_EINVAL);
1225
Benny Prijonodc752ca2006-09-22 16:55:42 +00001226 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001227 if (status != PJ_SUCCESS)
1228 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001229
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230
Benny Prijonod524e822006-09-22 12:48:18 +00001231 /* Create xfer client subscription. */
1232 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001233 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001234
1235 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236 if (status != PJ_SUCCESS) {
1237 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001238 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239 return status;
1240 }
1241
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001242 /* Associate this call with the client subscription */
1243 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1244
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 /*
1246 * Create REFER request.
1247 */
1248 status = pjsip_xfer_initiate(sub, dest, &tdata);
1249 if (status != PJ_SUCCESS) {
1250 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001251 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252 return status;
1253 }
1254
Benny Prijono053f5222006-11-11 16:16:04 +00001255 /* Add Referred-By header */
1256 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1257 &dlg->local.info_str);
1258 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1259
1260
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261 /* Add additional headers etc */
1262 pjsua_process_msg_data( tdata, msg_data);
1263
1264 /* Send. */
1265 status = pjsip_xfer_send_request(sub, tdata);
1266 if (status != PJ_SUCCESS) {
1267 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001268 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 return status;
1270 }
1271
1272 /* For simplicity (that's what this program is intended to be!),
1273 * leave the original invite session as it is. More advanced application
1274 * may want to hold the INVITE, or terminate the invite, or whatever.
1275 */
1276
Benny Prijonodc752ca2006-09-22 16:55:42 +00001277 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001278
1279 return PJ_SUCCESS;
1280
1281}
1282
1283
1284/*
Benny Prijono053f5222006-11-11 16:16:04 +00001285 * Initiate attended call transfer to the specified address.
1286 */
1287PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1288 pjsua_call_id dest_call_id,
1289 unsigned options,
1290 const pjsua_msg_data *msg_data)
1291{
1292 pjsua_call *dest_call;
1293 pjsip_dialog *dest_dlg;
1294 char str_dest_buf[512];
1295 pj_str_t str_dest;
1296 int len;
1297 pjsip_uri *uri;
1298 pj_status_t status;
1299
1300
1301 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1302 PJ_EINVAL);
1303 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1304 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1305 PJ_EINVAL);
1306
1307 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1308 &dest_call, &dest_dlg);
1309 if (status != PJ_SUCCESS)
1310 return status;
1311
1312 /*
1313 * Create REFER destination URI with Replaces field.
1314 */
1315
1316 /* Make sure we have sufficient buffer's length */
1317 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1318 dest_dlg->call_id->id.slen +
1319 dest_dlg->remote.info->tag.slen +
1320 dest_dlg->local.info->tag.slen + 32
1321 < sizeof(str_dest_buf), PJSIP_EURITOOLONG);
1322
1323 /* Print URI */
1324 str_dest_buf[0] = '<';
1325 str_dest.slen = 1;
1326
1327 uri = pjsip_uri_get_uri(dest_dlg->remote.info->uri);
1328 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1329 str_dest_buf+1, sizeof(str_dest_buf)-1);
1330 if (len < 0)
1331 return PJSIP_EURITOOLONG;
1332
1333 str_dest.slen += len;
1334
1335
1336 /* Build the URI */
1337 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1338 sizeof(str_dest_buf) - str_dest.slen,
1339 "?%s"
1340 "Replaces=%.*s"
1341 "%%3Bto-tag%%3D%.*s"
1342 "%%3Bfrom-tag%%3D%.*s>",
1343 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1344 "" : "Require=replaces&"),
1345 (int)dest_dlg->call_id->id.slen,
1346 dest_dlg->call_id->id.ptr,
1347 (int)dest_dlg->remote.info->tag.slen,
1348 dest_dlg->remote.info->tag.ptr,
1349 (int)dest_dlg->local.info->tag.slen,
1350 dest_dlg->local.info->tag.ptr);
1351
1352 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1353 PJSIP_EURITOOLONG);
1354
1355 str_dest.ptr = str_dest_buf;
1356 str_dest.slen += len;
1357
1358 pjsip_dlg_dec_lock(dest_dlg);
1359
1360 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1361}
1362
1363
1364/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365 * Send DTMF digits to remote using RFC 2833 payload formats.
1366 */
1367PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1368 const pj_str_t *digits)
1369{
1370 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001371 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001372 pj_status_t status;
1373
1374 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1375 PJ_EINVAL);
1376
Benny Prijonodc752ca2006-09-22 16:55:42 +00001377 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001378 if (status != PJ_SUCCESS)
1379 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001380
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 if (!call->session) {
1382 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001383 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 return PJ_EINVALIDOP;
1385 }
1386
1387 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1388
Benny Prijonodc752ca2006-09-22 16:55:42 +00001389 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001390
1391 return status;
1392}
1393
1394
1395/**
1396 * Send instant messaging inside INVITE session.
1397 */
1398PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1399 const pj_str_t *mime_type,
1400 const pj_str_t *content,
1401 const pjsua_msg_data *msg_data,
1402 void *user_data)
1403{
1404 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001405 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 const pj_str_t mime_text_plain = pj_str("text/plain");
1407 pjsip_media_type ctype;
1408 pjsua_im_data *im_data;
1409 pjsip_tx_data *tdata;
1410 pj_status_t status;
1411
1412
1413 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1414 PJ_EINVAL);
1415
Benny Prijonodc752ca2006-09-22 16:55:42 +00001416 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001417 if (status != PJ_SUCCESS)
1418 return status;
1419
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001420 /* Set default media type if none is specified */
1421 if (mime_type == NULL) {
1422 mime_type = &mime_text_plain;
1423 }
1424
1425 /* Create request message. */
1426 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1427 -1, &tdata);
1428 if (status != PJ_SUCCESS) {
1429 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1430 goto on_return;
1431 }
1432
1433 /* Add accept header. */
1434 pjsip_msg_add_hdr( tdata->msg,
1435 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1436
1437 /* Parse MIME type */
1438 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1439
1440 /* Create "text/plain" message body. */
1441 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1442 &ctype.subtype, content);
1443 if (tdata->msg->body == NULL) {
1444 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1445 pjsip_tx_data_dec_ref(tdata);
1446 goto on_return;
1447 }
1448
1449 /* Add additional headers etc */
1450 pjsua_process_msg_data( tdata, msg_data);
1451
1452 /* Create IM data and attach to the request. */
1453 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1454 im_data->acc_id = call->acc_id;
1455 im_data->call_id = call_id;
1456 im_data->to = call->inv->dlg->remote.info_str;
1457 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1458 im_data->user_data = user_data;
1459
1460
1461 /* Send the request. */
1462 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1463 pjsua_var.mod.id, im_data);
1464 if (status != PJ_SUCCESS) {
1465 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1466 goto on_return;
1467 }
1468
1469on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001470 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471 return status;
1472}
1473
1474
1475/*
1476 * Send IM typing indication inside INVITE session.
1477 */
1478PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1479 pj_bool_t is_typing,
1480 const pjsua_msg_data*msg_data)
1481{
1482 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001483 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001484 pjsip_tx_data *tdata;
1485 pj_status_t status;
1486
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1488 PJ_EINVAL);
1489
Benny Prijonodc752ca2006-09-22 16:55:42 +00001490 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001491 if (status != PJ_SUCCESS)
1492 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494 /* Create request message. */
1495 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1496 -1, &tdata);
1497 if (status != PJ_SUCCESS) {
1498 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1499 goto on_return;
1500 }
1501
1502 /* Create "application/im-iscomposing+xml" msg body. */
1503 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1504 NULL, NULL, -1);
1505
1506 /* Add additional headers etc */
1507 pjsua_process_msg_data( tdata, msg_data);
1508
1509 /* Send the request. */
1510 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1511 if (status != PJ_SUCCESS) {
1512 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1513 goto on_return;
1514 }
1515
1516on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001517 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 return status;
1519}
1520
1521
1522/*
1523 * Terminate all calls.
1524 */
1525PJ_DEF(void) pjsua_call_hangup_all(void)
1526{
1527 unsigned i;
1528
1529 PJSUA_LOCK();
1530
1531 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1532 if (pjsua_var.calls[i].inv)
1533 pjsua_call_hangup(i, 0, NULL, NULL);
1534 }
1535
1536 PJSUA_UNLOCK();
1537}
1538
1539
1540static const char *good_number(char *buf, pj_int32_t val)
1541{
1542 if (val < 1000) {
1543 pj_ansi_sprintf(buf, "%d", val);
1544 } else if (val < 1000000) {
1545 pj_ansi_sprintf(buf, "%d.%dK",
1546 val / 1000,
1547 (val % 1000) / 100);
1548 } else {
1549 pj_ansi_sprintf(buf, "%d.%02dM",
1550 val / 1000000,
1551 (val % 1000000) / 10000);
1552 }
1553
1554 return buf;
1555}
1556
1557
1558/* Dump media session */
1559static void dump_media_session(const char *indent,
1560 char *buf, unsigned maxlen,
1561 pjmedia_session *session)
1562{
1563 unsigned i;
1564 char *p = buf, *end = buf+maxlen;
1565 int len;
1566 pjmedia_session_info info;
1567
1568 pjmedia_session_get_info(session, &info);
1569
1570 for (i=0; i<info.stream_cnt; ++i) {
1571 pjmedia_rtcp_stat stat;
1572 const char *rem_addr;
1573 int rem_port;
1574 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001575 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001576 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1577 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578
1579 pjmedia_session_get_stream_stat(session, i, &stat);
1580 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1581 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1582
1583 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1584 dir = "sendonly";
1585 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1586 dir = "recvonly";
1587 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1588 dir = "sendrecv";
1589 else
1590 dir = "inactive";
1591
1592
1593 len = pj_ansi_snprintf(buf, end-p,
1594 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1595 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001596 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597 info.stream_info[i].fmt.encoding_name.ptr,
1598 info.stream_info[i].fmt.clock_rate / 1000,
1599 dir,
1600 rem_addr, rem_port);
1601 if (len < 1 || len > end-p) {
1602 *p = '\0';
1603 return;
1604 }
1605
1606 p += len;
1607 *p++ = '\n';
1608 *p = '\0';
1609
1610 if (stat.rx.update_cnt == 0)
1611 strcpy(last_update, "never");
1612 else {
1613 pj_gettimeofday(&now);
1614 PJ_TIME_VAL_SUB(now, stat.rx.update);
1615 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1616 now.sec / 3600,
1617 (now.sec % 3600) / 60,
1618 now.sec % 60,
1619 now.msec);
1620 }
1621
Benny Prijono80019eb2006-08-07 13:22:23 +00001622 pj_gettimeofday(&media_duration);
1623 PJ_TIME_VAL_SUB(media_duration, stat.start);
1624 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1625 media_duration.msec = 1;
1626
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627 len = pj_ansi_snprintf(p, end-p,
1628 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001629 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001630 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1631 "%s (msec) min avg max last\n"
1632 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1633 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1634 indent, info.stream_info[i].fmt.pt,
1635 last_update,
1636 indent,
1637 good_number(packets, stat.rx.pkt),
1638 good_number(bytes, stat.rx.bytes),
1639 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001640 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641 indent,
1642 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001643 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001645 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001647 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001648 indent, indent,
1649 stat.rx.loss_period.min / 1000.0,
1650 stat.rx.loss_period.avg / 1000.0,
1651 stat.rx.loss_period.max / 1000.0,
1652 stat.rx.loss_period.last / 1000.0,
1653 indent,
1654 stat.rx.jitter.min / 1000.0,
1655 stat.rx.jitter.avg / 1000.0,
1656 stat.rx.jitter.max / 1000.0,
1657 stat.rx.jitter.last / 1000.0,
1658 ""
1659 );
1660
1661 if (len < 1 || len > end-p) {
1662 *p = '\0';
1663 return;
1664 }
1665
1666 p += len;
1667 *p++ = '\n';
1668 *p = '\0';
1669
1670 if (stat.tx.update_cnt == 0)
1671 strcpy(last_update, "never");
1672 else {
1673 pj_gettimeofday(&now);
1674 PJ_TIME_VAL_SUB(now, stat.tx.update);
1675 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1676 now.sec / 3600,
1677 (now.sec % 3600) / 60,
1678 now.sec % 60,
1679 now.msec);
1680 }
1681
1682 len = pj_ansi_snprintf(p, end-p,
1683 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001684 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1686 "%s (msec) min avg max last\n"
1687 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1688 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1689 indent,
1690 info.stream_info[i].tx_pt,
1691 info.stream_info[i].param->info.frm_ptime *
1692 info.stream_info[i].param->setting.frm_per_pkt,
1693 last_update,
1694
1695 indent,
1696 good_number(packets, stat.tx.pkt),
1697 good_number(bytes, stat.tx.bytes),
1698 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001699 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700
1701 indent,
1702 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001703 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001704 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001705 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001706 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001707 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001708
1709 indent, indent,
1710 stat.tx.loss_period.min / 1000.0,
1711 stat.tx.loss_period.avg / 1000.0,
1712 stat.tx.loss_period.max / 1000.0,
1713 stat.tx.loss_period.last / 1000.0,
1714 indent,
1715 stat.tx.jitter.min / 1000.0,
1716 stat.tx.jitter.avg / 1000.0,
1717 stat.tx.jitter.max / 1000.0,
1718 stat.tx.jitter.last / 1000.0,
1719 ""
1720 );
1721
1722 if (len < 1 || len > end-p) {
1723 *p = '\0';
1724 return;
1725 }
1726
1727 p += len;
1728 *p++ = '\n';
1729 *p = '\0';
1730
1731 len = pj_ansi_snprintf(p, end-p,
1732 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1733 indent,
1734 stat.rtt.min / 1000.0,
1735 stat.rtt.avg / 1000.0,
1736 stat.rtt.max / 1000.0,
1737 stat.rtt.last / 1000.0
1738 );
1739 if (len < 1 || len > end-p) {
1740 *p = '\0';
1741 return;
1742 }
1743
1744 p += len;
1745 *p++ = '\n';
1746 *p = '\0';
1747 }
1748}
1749
1750
1751/* Print call info */
1752static void print_call(const char *title,
1753 int call_id,
1754 char *buf, pj_size_t size)
1755{
1756 int len;
1757 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1758 pjsip_dialog *dlg = inv->dlg;
1759 char userinfo[128];
1760
1761 /* Dump invite sesion info. */
1762
1763 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1764 if (len < 1)
1765 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1766 else
1767 userinfo[len] = '\0';
1768
1769 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1770 title,
1771 pjsip_inv_state_name(inv->state),
1772 userinfo);
1773 if (len < 1 || len >= (int)size) {
1774 pj_ansi_strcpy(buf, "<--uri too long-->");
1775 len = 18;
1776 } else
1777 buf[len] = '\0';
1778}
1779
1780
1781/*
1782 * Dump call and media statistics to string.
1783 */
1784PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1785 pj_bool_t with_media,
1786 char *buffer,
1787 unsigned maxlen,
1788 const char *indent)
1789{
1790 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001791 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001792 pj_time_val duration, res_delay, con_delay;
1793 char tmp[128];
1794 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001795 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001796 int len;
1797
1798 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1799 PJ_EINVAL);
1800
Benny Prijonodc752ca2006-09-22 16:55:42 +00001801 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001802 if (status != PJ_SUCCESS)
1803 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001804
1805 *buffer = '\0';
1806 p = buffer;
1807 end = buffer + maxlen;
1808 len = 0;
1809
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001810 print_call(indent, call_id, tmp, sizeof(tmp));
1811
1812 len = pj_ansi_strlen(tmp);
1813 pj_ansi_strcpy(buffer, tmp);
1814
1815 p += len;
1816 *p++ = '\r';
1817 *p++ = '\n';
1818
1819 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001820 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001821 pj_gettimeofday(&duration);
1822 PJ_TIME_VAL_SUB(duration, call->conn_time);
1823 con_delay = call->conn_time;
1824 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1825 } else {
1826 duration.sec = duration.msec = 0;
1827 con_delay.sec = con_delay.msec = 0;
1828 }
1829
1830 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001831 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001832 res_delay = call->res_time;
1833 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1834 } else {
1835 res_delay.sec = res_delay.msec = 0;
1836 }
1837
1838 /* Print duration */
1839 len = pj_ansi_snprintf(p, end-p,
1840 "%s Call time: %02dh:%02dm:%02ds, "
1841 "1st res in %d ms, conn in %dms",
1842 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001843 (int)(duration.sec / 3600),
1844 (int)((duration.sec % 3600)/60),
1845 (int)(duration.sec % 60),
1846 (int)PJ_TIME_VAL_MSEC(res_delay),
1847 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001848
1849 if (len > 0 && len < end-p) {
1850 p += len;
1851 *p++ = '\n';
1852 *p = '\0';
1853 }
1854
1855 /* Dump session statistics */
1856 if (with_media && call->session)
1857 dump_media_session(indent, p, end-p, call->session);
1858
Benny Prijonodc752ca2006-09-22 16:55:42 +00001859 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860
1861 return PJ_SUCCESS;
1862}
1863
1864
1865/*
1866 * Destroy the call's media
1867 */
1868static pj_status_t call_destroy_media(int call_id)
1869{
1870 pjsua_call *call = &pjsua_var.calls[call_id];
1871
1872 if (call->conf_slot != PJSUA_INVALID_ID) {
1873 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1874 call->conf_slot = PJSUA_INVALID_ID;
1875 }
1876
1877 if (call->session) {
1878 /* Destroy session (this will also close RTP/RTCP sockets). */
1879 pjmedia_session_destroy(call->session);
1880 call->session = NULL;
1881
1882 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1883 call_id));
1884
1885 }
1886
1887 call->media_st = PJSUA_CALL_MEDIA_NONE;
1888
1889 return PJ_SUCCESS;
1890}
1891
1892
Benny Prijono84126ab2006-02-09 09:30:09 +00001893/*
1894 * This callback receives notification from invite session when the
1895 * session state has changed.
1896 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001897static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1898 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001899{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001900 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001901
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001902 PJSUA_LOCK();
1903
1904 call = inv->dlg->mod_data[pjsua_var.mod.id];
1905
1906 if (!call) {
1907 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001908 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001909 }
1910
Benny Prijonoe21e7842006-04-09 16:46:05 +00001911
1912 /* Get call times */
1913 switch (inv->state) {
1914 case PJSIP_INV_STATE_EARLY:
1915 case PJSIP_INV_STATE_CONNECTING:
1916 if (call->res_time.sec == 0)
1917 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001918 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001919 pj_strncpy(&call->last_text,
1920 &e->body.tsx_state.tsx->status_text,
1921 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001922 break;
1923 case PJSIP_INV_STATE_CONFIRMED:
1924 pj_gettimeofday(&call->conn_time);
1925 break;
1926 case PJSIP_INV_STATE_DISCONNECTED:
1927 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001928 if (call->res_time.sec == 0)
1929 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001930 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1931 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001932 pj_strncpy(&call->last_text,
1933 &e->body.tsx_state.tsx->status_text,
1934 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001935 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001936 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001937 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001938 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939 pj_strncpy(&call->last_text,
1940 &e->body.tsx_state.tsx->status_text,
1941 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001942 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001943 }
1944
Benny Prijono26ff9062006-02-21 23:47:00 +00001945 /* If this is an outgoing INVITE that was created because of
1946 * REFER/transfer, send NOTIFY to transferer.
1947 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001948 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001949 int st_code = -1;
1950 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1951
1952
Benny Prijonoa91a0032006-02-26 21:23:45 +00001953 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001954 case PJSIP_INV_STATE_NULL:
1955 case PJSIP_INV_STATE_CALLING:
1956 /* Do nothing */
1957 break;
1958
1959 case PJSIP_INV_STATE_EARLY:
1960 case PJSIP_INV_STATE_CONNECTING:
1961 st_code = e->body.tsx_state.tsx->status_code;
1962 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1963 break;
1964
1965 case PJSIP_INV_STATE_CONFIRMED:
1966 /* When state is confirmed, send the final 200/OK and terminate
1967 * subscription.
1968 */
1969 st_code = e->body.tsx_state.tsx->status_code;
1970 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1971 break;
1972
1973 case PJSIP_INV_STATE_DISCONNECTED:
1974 st_code = e->body.tsx_state.tsx->status_code;
1975 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1976 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001977
Benny Prijono8b1889b2006-06-06 18:40:40 +00001978 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001979 /* Nothing to do. Just to keep gcc from complaining about
1980 * unused enums.
1981 */
1982 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001983 }
1984
1985 if (st_code != -1) {
1986 pjsip_tx_data *tdata;
1987 pj_status_t status;
1988
Benny Prijonoa91a0032006-02-26 21:23:45 +00001989 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001990 ev_state, st_code,
1991 NULL, &tdata);
1992 if (status != PJ_SUCCESS) {
1993 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1994 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001995 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001996 if (status != PJ_SUCCESS) {
1997 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1998 }
1999 }
2000 }
2001 }
2002
Benny Prijono84126ab2006-02-09 09:30:09 +00002003
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002004 if (pjsua_var.ua_cfg.cb.on_call_state)
2005 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002006
2007 /* call->inv may be NULL now */
2008
Benny Prijono84126ab2006-02-09 09:30:09 +00002009 /* Destroy media session when invite session is disconnected. */
2010 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002011
Benny Prijonoa91a0032006-02-26 21:23:45 +00002012 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002013
Benny Prijono275fd682006-03-22 11:59:11 +00002014 if (call)
2015 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002016
Benny Prijono105217f2006-03-06 16:25:59 +00002017 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002018 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002019 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002020
2021 /* Reset call */
2022 reset_call(call->index);
2023
Benny Prijono84126ab2006-02-09 09:30:09 +00002024 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002025
2026 PJSUA_UNLOCK();
2027}
2028
2029/*
2030 * This callback is called by invite session framework when UAC session
2031 * has forked.
2032 */
2033static void pjsua_call_on_forked( pjsip_inv_session *inv,
2034 pjsip_event *e)
2035{
2036 PJ_UNUSED_ARG(inv);
2037 PJ_UNUSED_ARG(e);
2038
2039 PJ_TODO(HANDLE_FORKED_DIALOG);
2040}
2041
2042
2043/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002044 * Disconnect call upon error.
2045 */
2046static void call_disconnect( pjsip_inv_session *inv,
2047 int code )
2048{
2049 pjsip_tx_data *tdata;
2050 pj_status_t status;
2051
2052 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2053 if (status == PJ_SUCCESS)
2054 pjsip_inv_send_msg(inv, tdata);
2055}
2056
2057/*
Benny Prijono0875ae82006-12-26 00:11:48 +00002058 * DTMF callback from the stream.
2059 */
2060static void dtmf_callback(pjmedia_stream *strm, void *user_data,
2061 int digit)
2062{
2063 PJ_UNUSED_ARG(strm);
2064
2065 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2066 pjsua_call_id call_id;
2067
2068 call_id = (pjsua_call_id)user_data;
2069 pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
2070 }
2071}
2072
2073/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 * Callback to be called when SDP offer/answer negotiation has just completed
2075 * in the session. This function will start/update media if negotiation
2076 * has succeeded.
2077 */
2078static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2079 pj_status_t status)
2080{
2081 int prev_media_st = 0;
2082 pjsua_call *call;
2083 pjmedia_session_info sess_info;
2084 const pjmedia_sdp_session *local_sdp;
2085 const pjmedia_sdp_session *remote_sdp;
2086 pjmedia_port *media_port;
2087 pj_str_t port_name;
2088 char tmp[PJSIP_MAX_URL_SIZE];
2089
2090 PJSUA_LOCK();
2091
2092 call = inv->dlg->mod_data[pjsua_var.mod.id];
2093
2094 if (status != PJ_SUCCESS) {
2095
2096 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2097
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002098 /* Stop/destroy media, if any */
2099 call_destroy_media(call->index);
2100
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002101 /* Disconnect call if we're not in the middle of initializing an
2102 * UAS dialog and if this is not a re-INVITE
2103 */
2104 if (inv->state != PJSIP_INV_STATE_NULL &&
2105 inv->state != PJSIP_INV_STATE_CONFIRMED)
2106 {
2107 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2108 }
2109
2110 PJSUA_UNLOCK();
2111 return;
2112 }
2113
2114 /* Destroy existing media session, if any. */
2115
2116 if (call) {
2117 prev_media_st = call->media_st;
2118 call_destroy_media(call->index);
2119 }
2120
2121 /* Get local and remote SDP */
2122
2123 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2124 if (status != PJ_SUCCESS) {
2125 pjsua_perror(THIS_FILE,
2126 "Unable to retrieve currently active local SDP",
2127 status);
2128 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2129 PJSUA_UNLOCK();
2130 return;
2131 }
2132
2133
2134 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2135 if (status != PJ_SUCCESS) {
2136 pjsua_perror(THIS_FILE,
2137 "Unable to retrieve currently active remote SDP",
2138 status);
2139 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2140 PJSUA_UNLOCK();
2141 return;
2142 }
2143
2144 /* Create media session info based on SDP parameters.
2145 * We only support one stream per session at the moment
2146 */
2147 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
2148 pjsua_var.med_endpt,
2149 1,&sess_info,
2150 local_sdp, remote_sdp);
2151 if (status != PJ_SUCCESS) {
2152 pjsua_perror(THIS_FILE, "Unable to create media session",
2153 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002154 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002155 PJSUA_UNLOCK();
2156 return;
2157 }
2158
2159 /* Check if media is put on-hold */
2160 if (sess_info.stream_cnt == 0 ||
2161 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
2162 {
2163
2164 /* Determine who puts the call on-hold */
2165 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
2166 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
2167 /* It was local who offer hold */
2168 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2169 } else {
2170 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2171 }
2172 }
2173
2174 call->media_dir = PJMEDIA_DIR_NONE;
2175
2176 } else {
2177
2178 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00002179 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00002180 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
2181 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00002182 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
2183 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
2184 }
2185
2186 /* Disable VAD, if this option is specified. */
2187 if (pjsua_var.media_cfg.no_vad) {
2188 sess_info.stream_info[0].param->setting.vad = 0;
2189 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002190
2191
2192 /* Optionally, application may modify other stream settings here
2193 * (such as jitter buffer parameters, codec ptime, etc.)
2194 */
Benny Prijono1d0ca0c2006-11-21 09:06:47 +00002195 sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
2196 sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
2197 sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
2198 sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199
2200 /* Create session based on session info. */
2201 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
2202 &call->med_tp,
2203 call, &call->session );
2204 if (status != PJ_SUCCESS) {
2205 pjsua_perror(THIS_FILE, "Unable to create media session",
2206 status);
2207 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2208 PJSUA_UNLOCK();
2209 return;
2210 }
2211
Benny Prijono0875ae82006-12-26 00:11:48 +00002212 /* If DTMF callback is installed by application, install our
2213 * callback to the session.
2214 */
2215 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
2216 pjmedia_session_set_dtmf_callback(call->session, 0,
2217 &dtmf_callback,
2218 (void*)(call->index));
2219 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002220
2221 /* Get the port interface of the first stream in the session.
2222 * We need the port interface to add to the conference bridge.
2223 */
2224 pjmedia_session_get_port(call->session, 0, &media_port);
2225
2226
2227 /*
2228 * Add the call to conference bridge.
2229 */
2230 port_name.ptr = tmp;
2231 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
2232 call->inv->dlg->remote.info->uri,
2233 tmp, sizeof(tmp));
2234 if (port_name.slen < 1) {
2235 port_name = pj_str("call");
2236 }
2237 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
2238 media_port,
2239 &port_name,
2240 (unsigned*)&call->conf_slot);
2241 if (status != PJ_SUCCESS) {
2242 pjsua_perror(THIS_FILE, "Unable to create conference slot",
2243 status);
2244 call_destroy_media(call->index);
2245 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
2246 PJSUA_UNLOCK();
2247 return;
2248 }
2249
2250 /* Call's media state is active */
2251 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
2252 call->media_dir = sess_info.stream_info[0].dir;
2253 }
2254
2255 /* Print info. */
2256 {
2257 char info[80];
2258 int info_len = 0;
2259 unsigned i;
2260
2261 for (i=0; i<sess_info.stream_cnt; ++i) {
2262 int len;
2263 const char *dir;
2264 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
2265
2266 switch (strm_info->dir) {
2267 case PJMEDIA_DIR_NONE:
2268 dir = "inactive";
2269 break;
2270 case PJMEDIA_DIR_ENCODING:
2271 dir = "sendonly";
2272 break;
2273 case PJMEDIA_DIR_DECODING:
2274 dir = "recvonly";
2275 break;
2276 case PJMEDIA_DIR_ENCODING_DECODING:
2277 dir = "sendrecv";
2278 break;
2279 default:
2280 dir = "unknown";
2281 break;
2282 }
2283 len = pj_ansi_sprintf( info+info_len,
2284 ", stream #%d: %.*s (%s)", i,
2285 (int)strm_info->fmt.encoding_name.slen,
2286 strm_info->fmt.encoding_name.ptr,
2287 dir);
2288 if (len > 0)
2289 info_len += len;
2290 }
2291 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
2292 }
2293
2294 /* Call application callback, if any */
2295 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2296 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2297
2298
2299 PJSUA_UNLOCK();
2300}
2301
2302
2303/*
2304 * Create inactive SDP for call hold.
2305 */
2306static pj_status_t create_inactive_sdp(pjsua_call *call,
2307 pjmedia_sdp_session **p_answer)
2308{
2309 pj_status_t status;
2310 pjmedia_sdp_conn *conn;
2311 pjmedia_sdp_attr *attr;
2312 pjmedia_sdp_session *sdp;
2313
2314 /* Create new offer */
2315 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
2316 &call->skinfo, &sdp);
2317 if (status != PJ_SUCCESS) {
2318 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2319 return status;
2320 }
2321
2322 /* Get SDP media connection line */
2323 conn = sdp->media[0]->conn;
2324 if (!conn)
2325 conn = sdp->conn;
2326
2327 /* Modify address */
2328 conn->addr = pj_str("0.0.0.0");
2329
2330 /* Remove existing directions attributes */
2331 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2332 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2333 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2334 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2335
2336 /* Add inactive attribute */
2337 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2338 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2339
2340 *p_answer = sdp;
2341
2342 return status;
2343}
2344
2345
2346/*
2347 * Called when session received new offer.
2348 */
2349static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2350 const pjmedia_sdp_session *offer)
2351{
2352 const char *remote_state;
2353 pjsua_call *call;
2354 pjmedia_sdp_conn *conn;
2355 pjmedia_sdp_session *answer;
2356 pj_bool_t is_remote_active;
2357 pj_status_t status;
2358
2359 PJSUA_LOCK();
2360
2361 call = inv->dlg->mod_data[pjsua_var.mod.id];
2362
2363 /*
2364 * See if remote is offering active media (i.e. not on-hold)
2365 */
2366 is_remote_active = PJ_TRUE;
2367
2368 conn = offer->media[0]->conn;
2369 if (!conn)
2370 conn = offer->conn;
2371
2372 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2373 pj_strcmp2(&conn->addr, "0")==0)
2374 {
2375 is_remote_active = PJ_FALSE;
2376
2377 }
2378 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2379 {
2380 is_remote_active = PJ_FALSE;
2381 }
2382
2383 remote_state = (is_remote_active ? "active" : "inactive");
2384
2385 /* Supply candidate answer */
2386 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2387 PJ_LOG(4,(THIS_FILE,
2388 "Call %d: RX new media offer, creating inactive SDP "
2389 "(media in offer is %s)", call->index, remote_state));
2390 status = create_inactive_sdp( call, &answer );
2391 } else {
2392 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2393 call->index));
2394 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2395 call->inv->pool, 1,
2396 &call->skinfo, &answer);
2397 }
2398
2399 if (status != PJ_SUCCESS) {
2400 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2401 PJSUA_UNLOCK();
2402 return;
2403 }
2404
2405 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2406 if (status != PJ_SUCCESS) {
2407 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2408 PJSUA_UNLOCK();
2409 return;
2410 }
2411
2412 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002413}
2414
2415
2416/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002417 * Callback called by event framework when the xfer subscription state
2418 * has changed.
2419 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002420static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2421{
2422
2423 PJ_UNUSED_ARG(event);
2424
2425 /*
2426 * When subscription is accepted (got 200/OK to REFER), check if
2427 * subscription suppressed.
2428 */
2429 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2430
2431 pjsip_rx_data *rdata;
2432 pjsip_generic_string_hdr *refer_sub;
2433 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2434 pjsua_call *call;
2435
2436 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2437
2438 /* Must be receipt of response message */
2439 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2440 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2441 rdata = event->body.tsx_state.src.rdata;
2442
2443 /* Find Refer-Sub header */
2444 refer_sub = (pjsip_generic_string_hdr*)
2445 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2446 &REFER_SUB, NULL);
2447
2448 /* Check if subscription is suppressed */
2449 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2450 /* Since no subscription is desired, assume that call has been
2451 * transfered successfully.
2452 */
2453 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2454 const pj_str_t ACCEPTED = { "Accepted", 8 };
2455 pj_bool_t cont = PJ_FALSE;
2456 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2457 200,
2458 &ACCEPTED,
2459 PJ_TRUE,
2460 &cont);
2461 }
2462
2463 /* Yes, subscription is suppressed.
2464 * Terminate our subscription now.
2465 */
2466 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2467 "event subcription..."));
2468 pjsip_evsub_terminate(sub, PJ_TRUE);
2469
2470 } else {
2471 /* Notify application about call transfer progress.
2472 * Initially notify with 100/Accepted status.
2473 */
2474 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2475 const pj_str_t ACCEPTED = { "Accepted", 8 };
2476 pj_bool_t cont = PJ_FALSE;
2477 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2478 100,
2479 &ACCEPTED,
2480 PJ_FALSE,
2481 &cont);
2482 }
2483 }
2484 }
2485 /*
2486 * On incoming NOTIFY, notify application about call transfer progress.
2487 */
2488 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2489 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2490 {
2491 pjsua_call *call;
2492 pjsip_msg *msg;
2493 pjsip_msg_body *body;
2494 pjsip_status_line status_line;
2495 pj_bool_t is_last;
2496 pj_bool_t cont;
2497 pj_status_t status;
2498
2499 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2500
2501 /* When subscription is terminated, clear the xfer_sub member of
2502 * the inv_data.
2503 */
2504 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2505 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2506 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2507
2508 }
2509
2510 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2511 /* Application is not interested with call progress status */
2512 return;
2513 }
2514
2515 /* This better be a NOTIFY request */
2516 if (event->type == PJSIP_EVENT_TSX_STATE &&
2517 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2518 {
2519 pjsip_rx_data *rdata;
2520
2521 rdata = event->body.tsx_state.src.rdata;
2522
2523 /* Check if there's body */
2524 msg = rdata->msg_info.msg;
2525 body = msg->body;
2526 if (!body) {
2527 PJ_LOG(4,(THIS_FILE,
2528 "Warning: received NOTIFY without message body"));
2529 return;
2530 }
2531
2532 /* Check for appropriate content */
2533 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2534 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2535 {
2536 PJ_LOG(4,(THIS_FILE,
2537 "Warning: received NOTIFY with non message/sipfrag "
2538 "content"));
2539 return;
2540 }
2541
2542 /* Try to parse the content */
2543 status = pjsip_parse_status_line(body->data, body->len,
2544 &status_line);
2545 if (status != PJ_SUCCESS) {
2546 PJ_LOG(4,(THIS_FILE,
2547 "Warning: received NOTIFY with invalid "
2548 "message/sipfrag content"));
2549 return;
2550 }
2551
2552 } else {
2553 status_line.code = 500;
2554 status_line.reason = *pjsip_get_status_text(500);
2555 }
2556
2557 /* Notify application */
2558 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2559 cont = !is_last;
2560 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2561 status_line.code,
2562 &status_line.reason,
2563 is_last, &cont);
2564
2565 if (!cont) {
2566 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2567 }
2568 }
2569}
2570
2571
2572/*
2573 * Callback called by event framework when the xfer subscription state
2574 * has changed.
2575 */
2576static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002577{
2578
2579 PJ_UNUSED_ARG(event);
2580
2581 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002582 * When subscription is terminated, clear the xfer_sub member of
2583 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002584 */
2585 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002586 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002587
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002588 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002589 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002590 return;
2591
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002592 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002593 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002594
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002595 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002596 }
2597}
2598
2599
2600/*
2601 * Follow transfer (REFER) request.
2602 */
2603static void on_call_transfered( pjsip_inv_session *inv,
2604 pjsip_rx_data *rdata )
2605{
2606 pj_status_t status;
2607 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002608 pjsua_call *existing_call;
2609 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002610 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002611 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002612 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002613 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002614 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002615 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002616 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002617 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002618 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002619 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002620 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002621 pjsip_evsub *sub;
2622
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002623 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002624
Benny Prijono26ff9062006-02-21 23:47:00 +00002625 /* Find the Refer-To header */
2626 refer_to = (pjsip_generic_string_hdr*)
2627 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2628
2629 if (refer_to == NULL) {
2630 /* Invalid Request.
2631 * No Refer-To header!
2632 */
2633 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002634 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002635 return;
2636 }
2637
Benny Prijonoc8141a82006-08-20 09:12:19 +00002638 /* Find optional Refer-Sub header */
2639 refer_sub = (pjsip_generic_string_hdr*)
2640 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2641
2642 if (refer_sub) {
2643 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2644 no_refer_sub = PJ_TRUE;
2645 }
2646
Benny Prijono053f5222006-11-11 16:16:04 +00002647 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2648 * request.
2649 */
2650 ref_by_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
2651 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002652
Benny Prijono9fc735d2006-05-28 14:58:12 +00002653 /* Notify callback */
2654 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002655 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2656 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2657 &refer_to->hvalue,
2658 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002659
2660 if (code < 200)
2661 code = 200;
2662 if (code >= 300) {
2663 /* Application rejects call transfer request */
2664 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2665 return;
2666 }
2667
Benny Prijono26ff9062006-02-21 23:47:00 +00002668 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2669 (int)inv->dlg->remote.info_str.slen,
2670 inv->dlg->remote.info_str.ptr,
2671 (int)refer_to->hvalue.slen,
2672 refer_to->hvalue.ptr));
2673
Benny Prijonoc8141a82006-08-20 09:12:19 +00002674 if (no_refer_sub) {
2675 /*
2676 * Always answer with 200.
2677 */
2678 pjsip_tx_data *tdata;
2679 const pj_str_t str_false = { "false", 5};
2680 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002681
Benny Prijonoc8141a82006-08-20 09:12:19 +00002682 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2683 if (status != PJ_SUCCESS) {
2684 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2685 status);
2686 return;
2687 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002688
Benny Prijonoc8141a82006-08-20 09:12:19 +00002689 /* Add Refer-Sub header */
2690 hdr = (pjsip_hdr*)
2691 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2692 &str_false);
2693 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002694
Benny Prijono26ff9062006-02-21 23:47:00 +00002695
Benny Prijonoc8141a82006-08-20 09:12:19 +00002696 /* Send answer */
2697 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2698 tdata);
2699 if (status != PJ_SUCCESS) {
2700 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2701 status);
2702 return;
2703 }
2704
2705 /* Don't have subscription */
2706 sub = NULL;
2707
2708 } else {
2709 struct pjsip_evsub_user xfer_cb;
2710 pjsip_hdr hdr_list;
2711
2712 /* Init callback */
2713 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002714 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002715
2716 /* Init additional header list to be sent with REFER response */
2717 pj_list_init(&hdr_list);
2718
2719 /* Create transferee event subscription */
2720 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2721 if (status != PJ_SUCCESS) {
2722 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2723 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2724 return;
2725 }
2726
2727 /* If there's Refer-Sub header and the value is "true", send back
2728 * Refer-Sub in the response with value "true" too.
2729 */
2730 if (refer_sub) {
2731 const pj_str_t str_true = { "true", 4 };
2732 pjsip_hdr *hdr;
2733
2734 hdr = (pjsip_hdr*)
2735 pjsip_generic_string_hdr_create(inv->dlg->pool,
2736 &str_refer_sub,
2737 &str_true);
2738 pj_list_push_back(&hdr_list, hdr);
2739
2740 }
2741
2742 /* Accept the REFER request, send 200 (OK). */
2743 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2744
2745 /* Create initial NOTIFY request */
2746 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2747 100, NULL, &tdata);
2748 if (status != PJ_SUCCESS) {
2749 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2750 status);
2751 return;
2752 }
2753
2754 /* Send initial NOTIFY request */
2755 status = pjsip_xfer_send_request( sub, tdata);
2756 if (status != PJ_SUCCESS) {
2757 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2758 return;
2759 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002760 }
2761
2762 /* We're cheating here.
2763 * We need to get a null terminated string from a pj_str_t.
2764 * So grab the pointer from the hvalue and NULL terminate it, knowing
2765 * that the NULL position will be occupied by a newline.
2766 */
2767 uri = refer_to->hvalue.ptr;
2768 uri[refer_to->hvalue.slen] = '\0';
2769
Benny Prijono053f5222006-11-11 16:16:04 +00002770 /* Init msg_data */
2771 pjsua_msg_data_init(&msg_data);
2772
2773 /* If Referred-By header is present in the REFER request, copy this
2774 * to the outgoing INVITE request.
2775 */
2776 if (ref_by_hdr != NULL) {
2777 pjsip_hdr *dup = pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
2778 pj_list_push_back(&msg_data.hdr_list, dup);
2779 }
2780
Benny Prijono26ff9062006-02-21 23:47:00 +00002781 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002782 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002783 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002784 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002785 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002786 if (status != PJ_SUCCESS) {
2787
Benny Prijonoc8141a82006-08-20 09:12:19 +00002788 /* Notify xferer about the error (if we have subscription) */
2789 if (sub) {
2790 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2791 500, NULL, &tdata);
2792 if (status != PJ_SUCCESS) {
2793 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2794 status);
2795 return;
2796 }
2797 status = pjsip_xfer_send_request(sub, tdata);
2798 if (status != PJ_SUCCESS) {
2799 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2800 status);
2801 return;
2802 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002803 }
2804 return;
2805 }
2806
Benny Prijonoc8141a82006-08-20 09:12:19 +00002807 if (sub) {
2808 /* Put the server subscription in inv_data.
2809 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2810 * reported back to the server subscription.
2811 */
2812 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002813
Benny Prijonoc8141a82006-08-20 09:12:19 +00002814 /* Put the invite_data in the subscription. */
2815 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2816 &pjsua_var.calls[new_call]);
2817 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002818}
2819
2820
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002821
Benny Prijono26ff9062006-02-21 23:47:00 +00002822/*
2823 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002824 * session. We use this to trap:
2825 * - incoming REFER request.
2826 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002827 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002828static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2829 pjsip_transaction *tsx,
2830 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002831{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002832 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2833
2834 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002835
Benny Prijono26ff9062006-02-21 23:47:00 +00002836 if (tsx->role==PJSIP_ROLE_UAS &&
2837 tsx->state==PJSIP_TSX_STATE_TRYING &&
2838 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2839 {
2840 /*
2841 * Incoming REFER request.
2842 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002843 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002844
Benny Prijono26ff9062006-02-21 23:47:00 +00002845 }
Benny Prijonob0808372006-03-02 21:18:58 +00002846 else if (tsx->role==PJSIP_ROLE_UAS &&
2847 tsx->state==PJSIP_TSX_STATE_TRYING &&
2848 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2849 {
2850 /*
2851 * Incoming MESSAGE request!
2852 */
2853 pjsip_rx_data *rdata;
2854 pjsip_msg *msg;
2855 pjsip_accept_hdr *accept_hdr;
2856 pj_status_t status;
2857
2858 rdata = e->body.tsx_state.src.rdata;
2859 msg = rdata->msg_info.msg;
2860
2861 /* Request MUST have message body, with Content-Type equal to
2862 * "text/plain".
2863 */
2864 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2865
2866 pjsip_hdr hdr_list;
2867
2868 pj_list_init(&hdr_list);
2869 pj_list_push_back(&hdr_list, accept_hdr);
2870
2871 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2872 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002873 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002874 return;
2875 }
2876
2877 /* Respond with 200 first, so that remote doesn't retransmit in case
2878 * the UI takes too long to process the message.
2879 */
2880 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2881
2882 /* Process MESSAGE request */
2883 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2884 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002885
Benny Prijonob0808372006-03-02 21:18:58 +00002886 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887 else if (tsx->role == PJSIP_ROLE_UAC &&
2888 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002889 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002890 /* Handle outgoing pager status */
2891 if (tsx->status_code >= 200) {
2892 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002893
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002894 im_data = tsx->mod_data[pjsua_var.mod.id];
2895 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002896
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002897 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2898 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2899 &im_data->to,
2900 &im_data->body,
2901 im_data->user_data,
2902 tsx->status_code,
2903 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002904 }
Benny Prijonofccab712006-02-22 22:23:22 +00002905 }
Benny Prijono834aee32006-02-19 01:38:06 +00002906 }
Benny Prijono834aee32006-02-19 01:38:06 +00002907
Benny Prijono26ff9062006-02-21 23:47:00 +00002908
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002909 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002910}