blob: 47dbaa725c6ffebbfc9dcecbd45a1ff108c5e006 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
53 * This callback is called when transaction state has changed in INVITE
54 * session. We use this to trap:
55 * - incoming REFER request.
56 * - incoming MESSAGE request.
57 */
58static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
59 pjsip_transaction *tsx,
60 pjsip_event *e);
61
62
Benny Prijonoeebe9af2006-06-13 22:57:13 +000063
64/* Create inactive SDP for call hold. */
65static pj_status_t create_inactive_sdp(pjsua_call *call,
66 pjmedia_sdp_session **p_answer);
67
Benny Prijonod524e822006-09-22 12:48:18 +000068/*
69 * Callback called by event framework when the xfer subscription state
70 * has changed.
71 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000072static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
73static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
74
75/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076 * Reset call descriptor.
77 */
78static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000079{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000080 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000081
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082 call->index = id;
83 call->inv = NULL;
84 call->user_data = NULL;
85 call->session = NULL;
86 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +000087 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000088 call->conf_slot = PJSUA_INVALID_ID;
89 call->last_text.ptr = call->last_text_buf_;
90 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +000091 call->conn_time.sec = 0;
92 call->conn_time.msec = 0;
93 call->res_time.sec = 0;
94 call->res_time.msec = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000095}
96
97
Benny Prijono275fd682006-03-22 11:59:11 +000098/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000100 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000102{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pjsip_inv_callback inv_cb;
104 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000105 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000107
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 /* Init calls array. */
109 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
110 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000111
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000112 /* Copy config */
113 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000114
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000115 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000116 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
118 inv_cb.on_new_session = &pjsua_call_on_forked;
119 inv_cb.on_media_update = &pjsua_call_on_media_update;
120 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
121 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000122
Benny Prijono275fd682006-03-22 11:59:11 +0000123
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 /* Initialize invite session module: */
125 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
126 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
127
Benny Prijonoc8141a82006-08-20 09:12:19 +0000128 /* Add "norefersub" in Supported header */
129 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
130 NULL, 1, &str_norefersub);
131
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000132 return status;
133}
134
135
136/*
137 * Start call subsystem.
138 */
139pj_status_t pjsua_call_subsys_start(void)
140{
141 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000142 return PJ_SUCCESS;
143}
144
145
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000147 * Get maximum number of calls configured in pjsua.
148 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000149PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000150{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000152}
153
154
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155/*
156 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000157 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000158PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000159{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000161}
162
163
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000164/*
165 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000166 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000167PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
168 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000169{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
177 if (!pjsua_var.calls[i].inv)
178 continue;
179 ids[c] = i;
180 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181 }
182
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186
187 return PJ_SUCCESS;
188}
189
190
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191/*
192 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
195 const pj_str_t *dest_uri,
196 unsigned options,
197 void *user_data,
198 const pjsua_msg_data *msg_data,
199 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000200{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000201 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000202 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000203 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 pjsua_acc *acc;
205 pjsua_call *call;
206 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000207 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000208 pjsip_tx_data *tdata;
209 pj_status_t status;
210
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 /* Check that account is valid */
213 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214 PJ_EINVAL);
215
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 /* Options must be zero for now */
217 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
218
Benny Prijono320fa4d2006-12-07 10:09:16 +0000219 /* Check arguments */
220 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
221
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 PJSUA_LOCK();
223
224 acc = &pjsua_var.acc[acc_id];
225 if (!acc->valid) {
226 pjsua_perror(THIS_FILE, "Unable to make call because account "
227 "is not valid", PJ_EINVALIDOP);
228 PJSUA_UNLOCK();
229 return PJ_EINVALIDOP;
230 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000231
Benny Prijonoa91a0032006-02-26 21:23:45 +0000232 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
234 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000235 break;
236 }
237
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 if (call_id == pjsua_var.ua_cfg.max_calls) {
239 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
240 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000241 return PJ_ETOOMANY;
242 }
243
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244 call = &pjsua_var.calls[call_id];
245
Benny Prijono320fa4d2006-12-07 10:09:16 +0000246 /* Verify that destination URI is valid before calling
247 * pjsua_acc_create_uac_contact, or otherwise there
248 * a misleading "Invalid Contact URI" error will be printed
249 * when pjsua_acc_create_uac_contact() fails.
250 */
251 if (1) {
252 pj_pool_t *pool;
253 pjsip_uri *uri;
254 pj_str_t dup;
255
256 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
257 if (!pool) {
258 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
259 PJSUA_UNLOCK();
260 return PJ_ENOMEM;
261 }
262
263 pj_strdup_with_null(pool, &dup, dest_uri);
264 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
265 pj_pool_release(pool);
266
267 if (uri == NULL) {
268 pjsua_perror(THIS_FILE, "Unable to make call",
269 PJSIP_EINVALIDREQURI);
270 PJSUA_UNLOCK();
271 return PJSIP_EINVALIDREQURI;
272 }
273 }
274
Benny Prijono093d3022006-09-24 00:07:11 +0000275 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
276 (int)dest_uri->slen, dest_uri->ptr));
277
Benny Prijonoe21e7842006-04-09 16:46:05 +0000278 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000279 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000280
Benny Prijonoe21e7842006-04-09 16:46:05 +0000281 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000282 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000283
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000284 /* Create suitable Contact header */
285 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
286 acc_id, dest_uri);
287 if (status != PJ_SUCCESS) {
288 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
289 PJSUA_UNLOCK();
290 return status;
291 }
292
Benny Prijonoe21e7842006-04-09 16:46:05 +0000293 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000294 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000295 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000296 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000297 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000298 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000299 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000300 return status;
301 }
302
Benny Prijonoc97608e2007-03-23 16:34:20 +0000303 /* Init media channel */
304 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
305 if (status != PJ_SUCCESS) {
306 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
307 goto on_error;
308 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000309
Benny Prijonoc97608e2007-03-23 16:34:20 +0000310 /* Create SDP offer */
311 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000312 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000313 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 goto on_error;
315 }
316
317 /* Create the INVITE session: */
318
319 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
320 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000321 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000322 goto on_error;
323 }
324
325
326 /* Create and associate our data in the session. */
327
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000329
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330 dlg->mod_data[pjsua_var.mod.id] = call;
331 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333 /* Attach user data */
334 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000335
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000336 /* If account is locked to specific transport, then lock dialog
337 * to this transport too.
338 */
339 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
340 pjsip_tpselector tp_sel;
341
342 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
343 pjsip_dlg_set_transport(dlg, &tp_sel);
344 }
345
Benny Prijono84126ab2006-02-09 09:30:09 +0000346 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 if (!pj_list_empty(&acc->route_set))
348 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000349
350
351 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000353 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000355 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000356
357
358 /* Create initial INVITE: */
359
360 status = pjsip_inv_invite(inv, &tdata);
361 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000362 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
363 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000364 goto on_error;
365 }
366
367
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 /* Add additional headers etc */
369
370 pjsua_process_msg_data( tdata, msg_data);
371
Benny Prijono093d3022006-09-24 00:07:11 +0000372 /* Must increment call counter now */
373 ++pjsua_var.call_cnt;
374
Benny Prijono84126ab2006-02-09 09:30:09 +0000375 /* Send initial INVITE: */
376
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000377 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000378 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000379 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
380 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000381
382 /* Upon failure to send first request, both dialog and invite
383 * session would have been cleared.
384 */
385 inv = NULL;
386 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000387 goto on_error;
388 }
389
Benny Prijono84126ab2006-02-09 09:30:09 +0000390 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 if (p_call_id)
393 *p_call_id = call_id;
394
395 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000396
397 return PJ_SUCCESS;
398
399
400on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000401 if (inv != NULL) {
402 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000403 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000404 pjsip_dlg_terminate(dlg);
405 }
406
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000407 if (call_id != -1) {
408 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000409 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000410 }
411
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000412 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000413 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000414}
415
416
417/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000418 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000420 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000421pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000422{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000423 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000424 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000425 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000426 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
427 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000428 pjsip_tx_data *response = NULL;
429 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000430 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 int acc_id;
432 pjsua_call *call;
433 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000434 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000435 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000436
Benny Prijono26ff9062006-02-21 23:47:00 +0000437 /* Don't want to handle anything but INVITE */
438 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
439 return PJ_FALSE;
440
441 /* Don't want to handle anything that's already associated with
442 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000443 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000444 if (dlg || tsx)
445 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000446
Benny Prijono148c9dd2006-09-19 13:37:53 +0000447 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000448
Benny Prijono26ff9062006-02-21 23:47:00 +0000449 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000450 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
451 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000452 break;
453 }
454
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000455 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
456 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000457 PJSIP_SC_BUSY_HERE, NULL,
458 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000459 PJ_LOG(2,(THIS_FILE,
460 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000461 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 return PJ_TRUE;
463 }
464
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 /* Clear call descriptor */
466 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000467
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468 call = &pjsua_var.calls[call_id];
469
470 /* Mark call start time. */
471 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000472
Benny Prijono053f5222006-11-11 16:16:04 +0000473 /* Check INVITE request for Replaces header. If Replaces header is
474 * present, the function will make sure that we can handle the request.
475 */
476 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
477 &response);
478 if (status != PJ_SUCCESS) {
479 /*
480 * Something wrong with the Replaces header.
481 */
482 if (response) {
483 pjsip_response_addr res_addr;
484
485 pjsip_get_response_addr(response->pool, rdata, &res_addr);
486 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
487 NULL, NULL);
488
489 } else {
490
491 /* Respond with 500 (Internal Server Error) */
492 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
493 NULL, NULL);
494 }
495
496 PJSUA_UNLOCK();
497 return PJ_TRUE;
498 }
499
500 /* If this INVITE request contains Replaces header, notify application
501 * about the request so that application can do subsequent checking
502 * if it wants to.
503 */
504 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
505 pjsua_call *replaced_call;
506 int st_code = 200;
507 pj_str_t st_text = { "OK", 2 };
508
509 /* Get the replaced call instance */
510 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
511
512 /* Notify application */
513 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
514 rdata, &st_code, &st_text);
515
516 /* Must specify final response */
517 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
518
519 /* Check if application rejects this request. */
520 if (st_code >= 300) {
521
522 if (st_text.slen == 2)
523 st_text = *pjsip_get_status_text(st_code);
524
525 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
526 st_code, &st_text, NULL, NULL, NULL);
527 PJSUA_UNLOCK();
528 return PJ_TRUE;
529 }
530 }
531
532
Benny Prijonoc97608e2007-03-23 16:34:20 +0000533 /* Init media channel */
534 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000535 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000537 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000538 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000539 return PJ_TRUE;
540 }
541
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000542
Benny Prijonoc97608e2007-03-23 16:34:20 +0000543 /* Get media capability from media endpoint: */
544 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
545 if (status != PJ_SUCCESS) {
546 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
547 NULL, NULL);
548 pjsua_media_channel_deinit(call->index);
549 PJSUA_UNLOCK();
550 return PJ_TRUE;
551 }
552
553
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000554 /* Verify that we can handle the request. */
555 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
556 pjsua_var.endpt, &response);
557 if (status != PJ_SUCCESS) {
558
559 /*
560 * No we can't handle the incoming INVITE request.
561 */
562
563 if (response) {
564 pjsip_response_addr res_addr;
565
566 pjsip_get_response_addr(response->pool, rdata, &res_addr);
567 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
568 NULL, NULL);
569
570 } else {
571
572 /* Respond with 500 (Internal Server Error) */
573 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
574 NULL, NULL);
575 }
576
Benny Prijonoc97608e2007-03-23 16:34:20 +0000577 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000578 PJSUA_UNLOCK();
579 return PJ_TRUE;
580 }
581
582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000584 * Get which account is most likely to be associated with this incoming
585 * call. We need the account to find which contact URI to put for
586 * the call.
587 */
Benny Prijono093d3022006-09-24 00:07:11 +0000588 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000589
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000590 /* Get suitable Contact header */
591 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
592 acc_id, rdata);
593 if (status != PJ_SUCCESS) {
594 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
595 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
596 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000597 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000598 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000599 return PJ_TRUE;
600 }
601
Benny Prijono26ff9062006-02-21 23:47:00 +0000602 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000603 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000604 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000605 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000606 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000607 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000608 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000609 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000610 return PJ_TRUE;
611 }
612
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613 /* Set credentials */
614 if (pjsua_var.acc[acc_id].cred_cnt) {
615 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
616 pjsua_var.acc[acc_id].cred_cnt,
617 pjsua_var.acc[acc_id].cred);
618 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000619
620 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000621 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
622 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000623 pjsip_hdr hdr_list;
624 pjsip_warning_hdr *w;
625
626 w = pjsip_warning_hdr_create_from_status(dlg->pool,
627 pjsip_endpt_name(pjsua_var.endpt),
628 status);
629 pj_list_init(&hdr_list);
630 pj_list_push_back(&hdr_list, w);
631
632 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
633
634 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000635 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000636 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000637 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000638 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000639 return PJ_TRUE;
640 }
641
642
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 /* Create and attach pjsua_var data to the dialog: */
644 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000645
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000646 dlg->mod_data[pjsua_var.mod.id] = call;
647 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000648
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000649 /* If account is locked to specific transport, then lock dialog
650 * to this transport too.
651 */
652 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
653 pjsip_tpselector tp_sel;
654
655 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
656 pjsip_dlg_set_transport(dlg, &tp_sel);
657 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000658
Benny Prijono64f851e2006-02-23 13:49:28 +0000659 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000660 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000661 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000662 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000663 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000664 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
665 status);
666
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000667 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
668 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000669 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000670 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000671 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000672
673 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000674 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000675 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000676 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000677 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000678 }
679
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000681
Benny Prijono105217f2006-03-06 16:25:59 +0000682
Benny Prijono053f5222006-11-11 16:16:04 +0000683 /* Check if this request should replace existing call */
684 if (replaced_dlg) {
685 pjsip_inv_session *replaced_inv;
686 struct pjsua_call *replaced_call;
687 pjsip_tx_data *tdata;
688
689 /* Get the invite session in the dialog */
690 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
691
692 /* Get the replaced call instance */
693 replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
694
695 /* Notify application */
696 if (pjsua_var.ua_cfg.cb.on_call_replaced)
697 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
698 call_id);
699
700 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
701 call_id));
702
703 /* Answer the new call with 200 response */
704 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
705 if (status == PJ_SUCCESS)
706 status = pjsip_inv_send_msg(inv, tdata);
707
708 if (status != PJ_SUCCESS)
709 pjsua_perror(THIS_FILE, "Error answering session", status);
710
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000711 /* Note that inv may be invalid if 200/OK has caused error in
712 * starting the media.
713 */
Benny Prijono053f5222006-11-11 16:16:04 +0000714
715 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
716 replaced_call->index));
717
718 /* Disconnect replaced invite session */
719 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
720 &tdata);
721 if (status == PJ_SUCCESS && tdata)
722 status = pjsip_inv_send_msg(replaced_inv, tdata);
723
724 if (status != PJ_SUCCESS)
725 pjsua_perror(THIS_FILE, "Error terminating session", status);
726
727
728 } else {
729
Benny Prijonob5388cf2007-01-04 22:45:08 +0000730 /* Notify application if on_incoming_call() is overriden,
731 * otherwise hangup the call with 480
732 */
733 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000734 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000735 } else {
736 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
737 NULL, NULL);
738 }
Benny Prijono053f5222006-11-11 16:16:04 +0000739 }
740
Benny Prijono8b1889b2006-06-06 18:40:40 +0000741
Benny Prijono26ff9062006-02-21 23:47:00 +0000742 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000743 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000744 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000745}
746
747
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000748
749/*
750 * Check if the specified call has active INVITE session and the INVITE
751 * session has not been disconnected.
752 */
753PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
754{
755 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
756 PJ_EINVAL);
757 return pjsua_var.calls[call_id].inv != NULL &&
758 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
759}
760
761
762/*
763 * Check if call has an active media session.
764 */
765PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
766{
767 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
768 PJ_EINVAL);
769 return pjsua_var.calls[call_id].session != NULL;
770}
771
772
Benny Prijono148c9dd2006-09-19 13:37:53 +0000773/* Acquire lock to the specified call_id */
774static pj_status_t acquire_call(const char *title,
775 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000776 pjsua_call **p_call,
777 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000778{
779 enum { MAX_RETRY=50 };
780 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000781 pjsua_call *call = NULL;
782 pj_bool_t has_pjsua_lock = PJ_FALSE;
783 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000784
785 for (retry=0; retry<MAX_RETRY; ++retry) {
786
787 has_pjsua_lock = PJ_FALSE;
788
789 status = PJSUA_TRY_LOCK();
790 if (status != PJ_SUCCESS) {
791 pj_thread_sleep(retry/10);
792 continue;
793 }
794
795 has_pjsua_lock = PJ_TRUE;
796 call = &pjsua_var.calls[call_id];
797
798 if (call->inv == NULL) {
799 PJSUA_UNLOCK();
800 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
801 return PJSIP_ESESSIONTERMINATED;
802 }
803
804 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
805 if (status != PJ_SUCCESS) {
806 PJSUA_UNLOCK();
807 pj_thread_sleep(retry/10);
808 continue;
809 }
810
811 PJSUA_UNLOCK();
812
813 break;
814 }
815
816 if (status != PJ_SUCCESS) {
817 if (has_pjsua_lock == PJ_FALSE)
818 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
819 "(possibly system has deadlocked) in %s",
820 title));
821 else
822 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
823 "(possibly system has deadlocked) in %s",
824 title));
825 return PJ_ETIMEDOUT;
826 }
827
828 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000829 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000830
831 return PJ_SUCCESS;
832}
833
834
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000835/*
836 * Get the conference port identification associated with the call.
837 */
838PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
839{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000840 pjsua_call *call;
841 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000842 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000843 pj_status_t status;
844
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000845 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
846 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000847
Benny Prijonodc752ca2006-09-22 16:55:42 +0000848 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000849 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000850 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000851
852 port_id = call->conf_slot;
853
Benny Prijonodc752ca2006-09-22 16:55:42 +0000854 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000855
856 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000857}
858
859
Benny Prijono148c9dd2006-09-19 13:37:53 +0000860
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000861/*
862 * Obtain detail information about the specified call.
863 */
864PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
865 pjsua_call_info *info)
866{
867 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000868 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000869 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000870
871 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
872 PJ_EINVAL);
873
Benny Prijonoac623b32006-07-03 15:19:31 +0000874 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000875
Benny Prijonodc752ca2006-09-22 16:55:42 +0000876 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000877 if (status != PJ_SUCCESS) {
878 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000879 }
880
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000881 /* id and role */
882 info->id = call_id;
883 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000884 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000885
886 /* local info */
887 info->local_info.ptr = info->buf_.local_info;
888 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
889 sizeof(info->buf_.local_info));
890
891 /* local contact */
892 info->local_contact.ptr = info->buf_.local_contact;
893 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
894 call->inv->dlg->local.contact->uri,
895 info->local_contact.ptr,
896 sizeof(info->buf_.local_contact));
897
898 /* remote info */
899 info->remote_info.ptr = info->buf_.remote_info;
900 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
901 sizeof(info->buf_.remote_info));
902
903 /* remote contact */
904 if (call->inv->dlg->remote.contact) {
905 int len;
906 info->remote_contact.ptr = info->buf_.remote_contact;
907 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
908 call->inv->dlg->remote.contact->uri,
909 info->remote_contact.ptr,
910 sizeof(info->buf_.remote_contact));
911 if (len < 0) len = 0;
912 info->remote_contact.slen = len;
913 } else {
914 info->remote_contact.slen = 0;
915 }
916
917 /* call id */
918 info->call_id.ptr = info->buf_.call_id;
919 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
920 sizeof(info->buf_.call_id));
921
922 /* state, state_text */
923 info->state = call->inv->state;
924 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
925
926 /* If call is disconnected, set the last_status from the cause code */
927 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
928 /* last_status, last_status_text */
929 info->last_status = call->inv->cause;
930
931 info->last_status_text.ptr = info->buf_.last_status_text;
932 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
933 sizeof(info->buf_.last_status_text));
934 } else {
935 /* last_status, last_status_text */
936 info->last_status = call->last_code;
937
938 info->last_status_text.ptr = info->buf_.last_status_text;
939 pj_strncpy(&info->last_status_text, &call->last_text,
940 sizeof(info->buf_.last_status_text));
941 }
942
943 /* media status and dir */
944 info->media_status = call->media_st;
945 info->media_dir = call->media_dir;
946
947
948 /* conference slot number */
949 info->conf_slot = call->conf_slot;
950
951 /* calculate duration */
952 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
953
954 info->total_duration = call->dis_time;
955 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
956
957 if (call->conn_time.sec) {
958 info->connect_duration = call->dis_time;
959 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
960 }
961
962 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
963
964 pj_gettimeofday(&info->total_duration);
965 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
966
967 pj_gettimeofday(&info->connect_duration);
968 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
969
970 } else {
971 pj_gettimeofday(&info->total_duration);
972 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
973 }
974
Benny Prijonodc752ca2006-09-22 16:55:42 +0000975 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000976
977 return PJ_SUCCESS;
978}
979
980
981/*
982 * Attach application specific data to the call.
983 */
984PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
985 void *user_data)
986{
987 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
988 PJ_EINVAL);
989 pjsua_var.calls[call_id].user_data = user_data;
990
991 return PJ_SUCCESS;
992}
993
994
995/*
996 * Get user data attached to the call.
997 */
998PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
999{
1000 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1001 NULL);
1002 return pjsua_var.calls[call_id].user_data;
1003}
1004
1005
1006/*
1007 * Send response to incoming INVITE request.
1008 */
1009PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1010 unsigned code,
1011 const pj_str_t *reason,
1012 const pjsua_msg_data *msg_data)
1013{
1014 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001015 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001016 pjsip_tx_data *tdata;
1017 pj_status_t status;
1018
1019 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1020 PJ_EINVAL);
1021
Benny Prijonodc752ca2006-09-22 16:55:42 +00001022 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001023 if (status != PJ_SUCCESS)
1024 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025
Benny Prijono2e507c22006-06-23 15:04:11 +00001026 if (call->res_time.sec == 0)
1027 pj_gettimeofday(&call->res_time);
1028
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001029 if (reason && reason->slen == 0)
1030 reason = NULL;
1031
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032 /* Create response message */
1033 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1034 if (status != PJ_SUCCESS) {
1035 pjsua_perror(THIS_FILE, "Error creating response",
1036 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001037 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001038 return status;
1039 }
1040
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001041 /* Call might have been disconnected if application is answering with
1042 * 200/OK and the media failed to start.
1043 */
1044 if (call->inv == NULL) {
1045 pjsip_dlg_dec_lock(dlg);
1046 return PJSIP_ESESSIONTERMINATED;
1047 }
1048
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001049 /* Add additional headers etc */
1050 pjsua_process_msg_data( tdata, msg_data);
1051
1052 /* Send the message */
1053 status = pjsip_inv_send_msg(call->inv, tdata);
1054 if (status != PJ_SUCCESS)
1055 pjsua_perror(THIS_FILE, "Error sending response",
1056 status);
1057
Benny Prijonodc752ca2006-09-22 16:55:42 +00001058 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001059
1060 return status;
1061}
1062
1063
1064/*
1065 * Hangup call by using method that is appropriate according to the
1066 * call state.
1067 */
1068PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1069 unsigned code,
1070 const pj_str_t *reason,
1071 const pjsua_msg_data *msg_data)
1072{
1073 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001074 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001075 pj_status_t status;
1076 pjsip_tx_data *tdata;
1077
1078
Benny Prijono148c9dd2006-09-19 13:37:53 +00001079 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1080 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1081 call_id));
1082 }
1083
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001084 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1085 PJ_EINVAL);
1086
Benny Prijonodc752ca2006-09-22 16:55:42 +00001087 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001088 if (status != PJ_SUCCESS)
1089 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001090
1091 if (code==0) {
1092 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1093 code = PJSIP_SC_OK;
1094 else if (call->inv->role == PJSIP_ROLE_UAS)
1095 code = PJSIP_SC_DECLINE;
1096 else
1097 code = PJSIP_SC_REQUEST_TERMINATED;
1098 }
1099
1100 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1101 if (status != PJ_SUCCESS) {
1102 pjsua_perror(THIS_FILE,
1103 "Failed to create end session message",
1104 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001105 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001106 return status;
1107 }
1108
1109 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1110 * as p_tdata when INVITE transaction has not been answered
1111 * with any provisional responses.
1112 */
1113 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001114 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001115 return PJ_SUCCESS;
1116 }
1117
1118 /* Add additional headers etc */
1119 pjsua_process_msg_data( tdata, msg_data);
1120
1121 /* Send the message */
1122 status = pjsip_inv_send_msg(call->inv, tdata);
1123 if (status != PJ_SUCCESS) {
1124 pjsua_perror(THIS_FILE,
1125 "Failed to send end session message",
1126 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001127 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001128 return status;
1129 }
1130
Benny Prijonodc752ca2006-09-22 16:55:42 +00001131 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001132
1133 return PJ_SUCCESS;
1134}
1135
1136
1137/*
1138 * Put the specified call on hold.
1139 */
1140PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1141 const pjsua_msg_data *msg_data)
1142{
1143 pjmedia_sdp_session *sdp;
1144 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001146 pjsip_tx_data *tdata;
1147 pj_status_t status;
1148
1149 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1150 PJ_EINVAL);
1151
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001153 if (status != PJ_SUCCESS)
1154 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001155
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001156
1157 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1158 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001160 return PJSIP_ESESSIONSTATE;
1161 }
1162
1163 status = create_inactive_sdp(call, &sdp);
1164 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001165 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166 return status;
1167 }
1168
1169 /* Create re-INVITE with new offer */
1170 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1171 if (status != PJ_SUCCESS) {
1172 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001173 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001174 return status;
1175 }
1176
1177 /* Add additional headers etc */
1178 pjsua_process_msg_data( tdata, msg_data);
1179
1180 /* Send the request */
1181 status = pjsip_inv_send_msg( call->inv, tdata);
1182 if (status != PJ_SUCCESS) {
1183 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001184 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 return status;
1186 }
1187
Benny Prijonodc752ca2006-09-22 16:55:42 +00001188 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001189
1190 return PJ_SUCCESS;
1191}
1192
1193
1194/*
1195 * Send re-INVITE (to release hold).
1196 */
1197PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1198 pj_bool_t unhold,
1199 const pjsua_msg_data *msg_data)
1200{
1201 pjmedia_sdp_session *sdp;
1202 pjsip_tx_data *tdata;
1203 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001204 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205 pj_status_t status;
1206
1207
1208 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1209 PJ_EINVAL);
1210
Benny Prijonodc752ca2006-09-22 16:55:42 +00001211 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001212 if (status != PJ_SUCCESS)
1213 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001214
1215 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1216 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001217 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001218 return PJSIP_ESESSIONSTATE;
1219 }
1220
Benny Prijono667952e2007-04-02 19:27:54 +00001221 /* Init media channel */
1222 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1223 if (status != PJ_SUCCESS) {
1224 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1225 pjsip_dlg_dec_lock(dlg);
1226 return PJSIP_ESESSIONSTATE;
1227 }
1228
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001229 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001230 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001231 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001232 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233 if (status != PJ_SUCCESS) {
1234 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1235 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001236 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237 return status;
1238 }
1239
1240 /* Create re-INVITE with new offer */
1241 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1242 if (status != PJ_SUCCESS) {
1243 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001244 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 return status;
1246 }
1247
1248 /* Add additional headers etc */
1249 pjsua_process_msg_data( tdata, msg_data);
1250
1251 /* Send the request */
1252 status = pjsip_inv_send_msg( call->inv, tdata);
1253 if (status != PJ_SUCCESS) {
1254 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001255 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001256 return status;
1257 }
1258
Benny Prijonodc752ca2006-09-22 16:55:42 +00001259 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001260
1261 return PJ_SUCCESS;
1262}
1263
1264
1265/*
1266 * Initiate call transfer to the specified address.
1267 */
1268PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1269 const pj_str_t *dest,
1270 const pjsua_msg_data *msg_data)
1271{
1272 pjsip_evsub *sub;
1273 pjsip_tx_data *tdata;
1274 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001275 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001276 pjsip_generic_string_hdr *gs_hdr;
1277 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001278 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279 pj_status_t status;
1280
1281
1282 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1283 PJ_EINVAL);
1284
Benny Prijonodc752ca2006-09-22 16:55:42 +00001285 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001286 if (status != PJ_SUCCESS)
1287 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001289
Benny Prijonod524e822006-09-22 12:48:18 +00001290 /* Create xfer client subscription. */
1291 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001292 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001293
1294 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001295 if (status != PJ_SUCCESS) {
1296 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001297 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298 return status;
1299 }
1300
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001301 /* Associate this call with the client subscription */
1302 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1303
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001304 /*
1305 * Create REFER request.
1306 */
1307 status = pjsip_xfer_initiate(sub, dest, &tdata);
1308 if (status != PJ_SUCCESS) {
1309 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001310 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001311 return status;
1312 }
1313
Benny Prijono053f5222006-11-11 16:16:04 +00001314 /* Add Referred-By header */
1315 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1316 &dlg->local.info_str);
1317 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1318
1319
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 /* Add additional headers etc */
1321 pjsua_process_msg_data( tdata, msg_data);
1322
1323 /* Send. */
1324 status = pjsip_xfer_send_request(sub, tdata);
1325 if (status != PJ_SUCCESS) {
1326 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001327 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001328 return status;
1329 }
1330
1331 /* For simplicity (that's what this program is intended to be!),
1332 * leave the original invite session as it is. More advanced application
1333 * may want to hold the INVITE, or terminate the invite, or whatever.
1334 */
1335
Benny Prijonodc752ca2006-09-22 16:55:42 +00001336 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001337
1338 return PJ_SUCCESS;
1339
1340}
1341
1342
1343/*
Benny Prijono053f5222006-11-11 16:16:04 +00001344 * Initiate attended call transfer to the specified address.
1345 */
1346PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1347 pjsua_call_id dest_call_id,
1348 unsigned options,
1349 const pjsua_msg_data *msg_data)
1350{
1351 pjsua_call *dest_call;
1352 pjsip_dialog *dest_dlg;
1353 char str_dest_buf[512];
1354 pj_str_t str_dest;
1355 int len;
1356 pjsip_uri *uri;
1357 pj_status_t status;
1358
1359
1360 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1361 PJ_EINVAL);
1362 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1363 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1364 PJ_EINVAL);
1365
1366 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1367 &dest_call, &dest_dlg);
1368 if (status != PJ_SUCCESS)
1369 return status;
1370
1371 /*
1372 * Create REFER destination URI with Replaces field.
1373 */
1374
1375 /* Make sure we have sufficient buffer's length */
1376 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1377 dest_dlg->call_id->id.slen +
1378 dest_dlg->remote.info->tag.slen +
1379 dest_dlg->local.info->tag.slen + 32
1380 < sizeof(str_dest_buf), PJSIP_EURITOOLONG);
1381
1382 /* Print URI */
1383 str_dest_buf[0] = '<';
1384 str_dest.slen = 1;
1385
1386 uri = pjsip_uri_get_uri(dest_dlg->remote.info->uri);
1387 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1388 str_dest_buf+1, sizeof(str_dest_buf)-1);
1389 if (len < 0)
1390 return PJSIP_EURITOOLONG;
1391
1392 str_dest.slen += len;
1393
1394
1395 /* Build the URI */
1396 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1397 sizeof(str_dest_buf) - str_dest.slen,
1398 "?%s"
1399 "Replaces=%.*s"
1400 "%%3Bto-tag%%3D%.*s"
1401 "%%3Bfrom-tag%%3D%.*s>",
1402 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1403 "" : "Require=replaces&"),
1404 (int)dest_dlg->call_id->id.slen,
1405 dest_dlg->call_id->id.ptr,
1406 (int)dest_dlg->remote.info->tag.slen,
1407 dest_dlg->remote.info->tag.ptr,
1408 (int)dest_dlg->local.info->tag.slen,
1409 dest_dlg->local.info->tag.ptr);
1410
1411 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1412 PJSIP_EURITOOLONG);
1413
1414 str_dest.ptr = str_dest_buf;
1415 str_dest.slen += len;
1416
1417 pjsip_dlg_dec_lock(dest_dlg);
1418
1419 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1420}
1421
1422
1423/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001424 * Send DTMF digits to remote using RFC 2833 payload formats.
1425 */
1426PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1427 const pj_str_t *digits)
1428{
1429 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001430 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 pj_status_t status;
1432
1433 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1434 PJ_EINVAL);
1435
Benny Prijonodc752ca2006-09-22 16:55:42 +00001436 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001437 if (status != PJ_SUCCESS)
1438 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440 if (!call->session) {
1441 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001442 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 return PJ_EINVALIDOP;
1444 }
1445
1446 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1447
Benny Prijonodc752ca2006-09-22 16:55:42 +00001448 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001449
1450 return status;
1451}
1452
1453
1454/**
1455 * Send instant messaging inside INVITE session.
1456 */
1457PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1458 const pj_str_t *mime_type,
1459 const pj_str_t *content,
1460 const pjsua_msg_data *msg_data,
1461 void *user_data)
1462{
1463 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001464 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001465 const pj_str_t mime_text_plain = pj_str("text/plain");
1466 pjsip_media_type ctype;
1467 pjsua_im_data *im_data;
1468 pjsip_tx_data *tdata;
1469 pj_status_t status;
1470
1471
1472 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1473 PJ_EINVAL);
1474
Benny Prijonodc752ca2006-09-22 16:55:42 +00001475 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001476 if (status != PJ_SUCCESS)
1477 return status;
1478
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479 /* Set default media type if none is specified */
1480 if (mime_type == NULL) {
1481 mime_type = &mime_text_plain;
1482 }
1483
1484 /* Create request message. */
1485 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1486 -1, &tdata);
1487 if (status != PJ_SUCCESS) {
1488 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1489 goto on_return;
1490 }
1491
1492 /* Add accept header. */
1493 pjsip_msg_add_hdr( tdata->msg,
1494 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1495
1496 /* Parse MIME type */
1497 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1498
1499 /* Create "text/plain" message body. */
1500 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1501 &ctype.subtype, content);
1502 if (tdata->msg->body == NULL) {
1503 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1504 pjsip_tx_data_dec_ref(tdata);
1505 goto on_return;
1506 }
1507
1508 /* Add additional headers etc */
1509 pjsua_process_msg_data( tdata, msg_data);
1510
1511 /* Create IM data and attach to the request. */
1512 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1513 im_data->acc_id = call->acc_id;
1514 im_data->call_id = call_id;
1515 im_data->to = call->inv->dlg->remote.info_str;
1516 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1517 im_data->user_data = user_data;
1518
1519
1520 /* Send the request. */
1521 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1522 pjsua_var.mod.id, im_data);
1523 if (status != PJ_SUCCESS) {
1524 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1525 goto on_return;
1526 }
1527
1528on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001529 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001530 return status;
1531}
1532
1533
1534/*
1535 * Send IM typing indication inside INVITE session.
1536 */
1537PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1538 pj_bool_t is_typing,
1539 const pjsua_msg_data*msg_data)
1540{
1541 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001542 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001543 pjsip_tx_data *tdata;
1544 pj_status_t status;
1545
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1547 PJ_EINVAL);
1548
Benny Prijonodc752ca2006-09-22 16:55:42 +00001549 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001550 if (status != PJ_SUCCESS)
1551 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553 /* Create request message. */
1554 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1555 -1, &tdata);
1556 if (status != PJ_SUCCESS) {
1557 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1558 goto on_return;
1559 }
1560
1561 /* Create "application/im-iscomposing+xml" msg body. */
1562 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1563 NULL, NULL, -1);
1564
1565 /* Add additional headers etc */
1566 pjsua_process_msg_data( tdata, msg_data);
1567
1568 /* Send the request. */
1569 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1570 if (status != PJ_SUCCESS) {
1571 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1572 goto on_return;
1573 }
1574
1575on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001576 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001577 return status;
1578}
1579
1580
1581/*
1582 * Terminate all calls.
1583 */
1584PJ_DEF(void) pjsua_call_hangup_all(void)
1585{
1586 unsigned i;
1587
1588 PJSUA_LOCK();
1589
1590 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1591 if (pjsua_var.calls[i].inv)
1592 pjsua_call_hangup(i, 0, NULL, NULL);
1593 }
1594
1595 PJSUA_UNLOCK();
1596}
1597
1598
1599static const char *good_number(char *buf, pj_int32_t val)
1600{
1601 if (val < 1000) {
1602 pj_ansi_sprintf(buf, "%d", val);
1603 } else if (val < 1000000) {
1604 pj_ansi_sprintf(buf, "%d.%dK",
1605 val / 1000,
1606 (val % 1000) / 100);
1607 } else {
1608 pj_ansi_sprintf(buf, "%d.%02dM",
1609 val / 1000000,
1610 (val % 1000000) / 10000);
1611 }
1612
1613 return buf;
1614}
1615
1616
1617/* Dump media session */
1618static void dump_media_session(const char *indent,
1619 char *buf, unsigned maxlen,
1620 pjmedia_session *session)
1621{
1622 unsigned i;
1623 char *p = buf, *end = buf+maxlen;
1624 int len;
1625 pjmedia_session_info info;
1626
1627 pjmedia_session_get_info(session, &info);
1628
1629 for (i=0; i<info.stream_cnt; ++i) {
1630 pjmedia_rtcp_stat stat;
1631 const char *rem_addr;
1632 int rem_port;
1633 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001634 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001635 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1636 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001637
1638 pjmedia_session_get_stream_stat(session, i, &stat);
1639 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1640 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1641
1642 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1643 dir = "sendonly";
1644 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1645 dir = "recvonly";
1646 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1647 dir = "sendrecv";
1648 else
1649 dir = "inactive";
1650
1651
1652 len = pj_ansi_snprintf(buf, end-p,
1653 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1654 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001655 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656 info.stream_info[i].fmt.encoding_name.ptr,
1657 info.stream_info[i].fmt.clock_rate / 1000,
1658 dir,
1659 rem_addr, rem_port);
1660 if (len < 1 || len > end-p) {
1661 *p = '\0';
1662 return;
1663 }
1664
1665 p += len;
1666 *p++ = '\n';
1667 *p = '\0';
1668
1669 if (stat.rx.update_cnt == 0)
1670 strcpy(last_update, "never");
1671 else {
1672 pj_gettimeofday(&now);
1673 PJ_TIME_VAL_SUB(now, stat.rx.update);
1674 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1675 now.sec / 3600,
1676 (now.sec % 3600) / 60,
1677 now.sec % 60,
1678 now.msec);
1679 }
1680
Benny Prijono80019eb2006-08-07 13:22:23 +00001681 pj_gettimeofday(&media_duration);
1682 PJ_TIME_VAL_SUB(media_duration, stat.start);
1683 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1684 media_duration.msec = 1;
1685
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001686 len = pj_ansi_snprintf(p, end-p,
1687 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001688 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001689 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1690 "%s (msec) min avg max last\n"
1691 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1692 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1693 indent, info.stream_info[i].fmt.pt,
1694 last_update,
1695 indent,
1696 good_number(packets, stat.rx.pkt),
1697 good_number(bytes, stat.rx.bytes),
1698 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001699 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 indent,
1701 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001702 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001704 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001705 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001706 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 indent, indent,
1708 stat.rx.loss_period.min / 1000.0,
1709 stat.rx.loss_period.avg / 1000.0,
1710 stat.rx.loss_period.max / 1000.0,
1711 stat.rx.loss_period.last / 1000.0,
1712 indent,
1713 stat.rx.jitter.min / 1000.0,
1714 stat.rx.jitter.avg / 1000.0,
1715 stat.rx.jitter.max / 1000.0,
1716 stat.rx.jitter.last / 1000.0,
1717 ""
1718 );
1719
1720 if (len < 1 || len > end-p) {
1721 *p = '\0';
1722 return;
1723 }
1724
1725 p += len;
1726 *p++ = '\n';
1727 *p = '\0';
1728
1729 if (stat.tx.update_cnt == 0)
1730 strcpy(last_update, "never");
1731 else {
1732 pj_gettimeofday(&now);
1733 PJ_TIME_VAL_SUB(now, stat.tx.update);
1734 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1735 now.sec / 3600,
1736 (now.sec % 3600) / 60,
1737 now.sec % 60,
1738 now.msec);
1739 }
1740
1741 len = pj_ansi_snprintf(p, end-p,
1742 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001743 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001744 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1745 "%s (msec) min avg max last\n"
1746 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1747 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1748 indent,
1749 info.stream_info[i].tx_pt,
1750 info.stream_info[i].param->info.frm_ptime *
1751 info.stream_info[i].param->setting.frm_per_pkt,
1752 last_update,
1753
1754 indent,
1755 good_number(packets, stat.tx.pkt),
1756 good_number(bytes, stat.tx.bytes),
1757 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001758 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759
1760 indent,
1761 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001762 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001763 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001764 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001765 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001766 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001767
1768 indent, indent,
1769 stat.tx.loss_period.min / 1000.0,
1770 stat.tx.loss_period.avg / 1000.0,
1771 stat.tx.loss_period.max / 1000.0,
1772 stat.tx.loss_period.last / 1000.0,
1773 indent,
1774 stat.tx.jitter.min / 1000.0,
1775 stat.tx.jitter.avg / 1000.0,
1776 stat.tx.jitter.max / 1000.0,
1777 stat.tx.jitter.last / 1000.0,
1778 ""
1779 );
1780
1781 if (len < 1 || len > end-p) {
1782 *p = '\0';
1783 return;
1784 }
1785
1786 p += len;
1787 *p++ = '\n';
1788 *p = '\0';
1789
1790 len = pj_ansi_snprintf(p, end-p,
1791 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1792 indent,
1793 stat.rtt.min / 1000.0,
1794 stat.rtt.avg / 1000.0,
1795 stat.rtt.max / 1000.0,
1796 stat.rtt.last / 1000.0
1797 );
1798 if (len < 1 || len > end-p) {
1799 *p = '\0';
1800 return;
1801 }
1802
1803 p += len;
1804 *p++ = '\n';
1805 *p = '\0';
1806 }
1807}
1808
1809
1810/* Print call info */
1811static void print_call(const char *title,
1812 int call_id,
1813 char *buf, pj_size_t size)
1814{
1815 int len;
1816 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1817 pjsip_dialog *dlg = inv->dlg;
1818 char userinfo[128];
1819
1820 /* Dump invite sesion info. */
1821
1822 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1823 if (len < 1)
1824 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1825 else
1826 userinfo[len] = '\0';
1827
1828 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1829 title,
1830 pjsip_inv_state_name(inv->state),
1831 userinfo);
1832 if (len < 1 || len >= (int)size) {
1833 pj_ansi_strcpy(buf, "<--uri too long-->");
1834 len = 18;
1835 } else
1836 buf[len] = '\0';
1837}
1838
1839
1840/*
1841 * Dump call and media statistics to string.
1842 */
1843PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1844 pj_bool_t with_media,
1845 char *buffer,
1846 unsigned maxlen,
1847 const char *indent)
1848{
1849 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001850 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 pj_time_val duration, res_delay, con_delay;
1852 char tmp[128];
1853 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001854 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001855 int len;
1856
1857 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1858 PJ_EINVAL);
1859
Benny Prijonodc752ca2006-09-22 16:55:42 +00001860 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001861 if (status != PJ_SUCCESS)
1862 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001863
1864 *buffer = '\0';
1865 p = buffer;
1866 end = buffer + maxlen;
1867 len = 0;
1868
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001869 print_call(indent, call_id, tmp, sizeof(tmp));
1870
1871 len = pj_ansi_strlen(tmp);
1872 pj_ansi_strcpy(buffer, tmp);
1873
1874 p += len;
1875 *p++ = '\r';
1876 *p++ = '\n';
1877
1878 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00001879 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001880 pj_gettimeofday(&duration);
1881 PJ_TIME_VAL_SUB(duration, call->conn_time);
1882 con_delay = call->conn_time;
1883 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1884 } else {
1885 duration.sec = duration.msec = 0;
1886 con_delay.sec = con_delay.msec = 0;
1887 }
1888
1889 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00001890 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001891 res_delay = call->res_time;
1892 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1893 } else {
1894 res_delay.sec = res_delay.msec = 0;
1895 }
1896
1897 /* Print duration */
1898 len = pj_ansi_snprintf(p, end-p,
1899 "%s Call time: %02dh:%02dm:%02ds, "
1900 "1st res in %d ms, conn in %dms",
1901 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001902 (int)(duration.sec / 3600),
1903 (int)((duration.sec % 3600)/60),
1904 (int)(duration.sec % 60),
1905 (int)PJ_TIME_VAL_MSEC(res_delay),
1906 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001907
1908 if (len > 0 && len < end-p) {
1909 p += len;
1910 *p++ = '\n';
1911 *p = '\0';
1912 }
1913
1914 /* Dump session statistics */
1915 if (with_media && call->session)
1916 dump_media_session(indent, p, end-p, call->session);
1917
Benny Prijonodc752ca2006-09-22 16:55:42 +00001918 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001919
1920 return PJ_SUCCESS;
1921}
1922
1923
1924/*
Benny Prijono84126ab2006-02-09 09:30:09 +00001925 * This callback receives notification from invite session when the
1926 * session state has changed.
1927 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001928static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1929 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001930{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001931 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001932
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001933 PJSUA_LOCK();
1934
1935 call = inv->dlg->mod_data[pjsua_var.mod.id];
1936
1937 if (!call) {
1938 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001939 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940 }
1941
Benny Prijonoe21e7842006-04-09 16:46:05 +00001942
1943 /* Get call times */
1944 switch (inv->state) {
1945 case PJSIP_INV_STATE_EARLY:
1946 case PJSIP_INV_STATE_CONNECTING:
1947 if (call->res_time.sec == 0)
1948 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00001949 call->last_code = (pjsip_status_code)
1950 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 pj_strncpy(&call->last_text,
1952 &e->body.tsx_state.tsx->status_text,
1953 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001954 break;
1955 case PJSIP_INV_STATE_CONFIRMED:
1956 pj_gettimeofday(&call->conn_time);
1957 break;
1958 case PJSIP_INV_STATE_DISCONNECTED:
1959 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001960 if (call->res_time.sec == 0)
1961 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001962 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00001963 call->last_code = (pjsip_status_code)
1964 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001965 pj_strncpy(&call->last_text,
1966 &e->body.tsx_state.tsx->status_text,
1967 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001968 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001969 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001970 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00001971 call->last_code = (pjsip_status_code)
1972 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001973 pj_strncpy(&call->last_text,
1974 &e->body.tsx_state.tsx->status_text,
1975 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001976 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001977 }
1978
Benny Prijono26ff9062006-02-21 23:47:00 +00001979 /* If this is an outgoing INVITE that was created because of
1980 * REFER/transfer, send NOTIFY to transferer.
1981 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001982 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001983 int st_code = -1;
1984 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1985
1986
Benny Prijonoa91a0032006-02-26 21:23:45 +00001987 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001988 case PJSIP_INV_STATE_NULL:
1989 case PJSIP_INV_STATE_CALLING:
1990 /* Do nothing */
1991 break;
1992
1993 case PJSIP_INV_STATE_EARLY:
1994 case PJSIP_INV_STATE_CONNECTING:
1995 st_code = e->body.tsx_state.tsx->status_code;
1996 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1997 break;
1998
1999 case PJSIP_INV_STATE_CONFIRMED:
2000 /* When state is confirmed, send the final 200/OK and terminate
2001 * subscription.
2002 */
2003 st_code = e->body.tsx_state.tsx->status_code;
2004 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2005 break;
2006
2007 case PJSIP_INV_STATE_DISCONNECTED:
2008 st_code = e->body.tsx_state.tsx->status_code;
2009 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2010 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002011
Benny Prijono8b1889b2006-06-06 18:40:40 +00002012 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002013 /* Nothing to do. Just to keep gcc from complaining about
2014 * unused enums.
2015 */
2016 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002017 }
2018
2019 if (st_code != -1) {
2020 pjsip_tx_data *tdata;
2021 pj_status_t status;
2022
Benny Prijonoa91a0032006-02-26 21:23:45 +00002023 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002024 ev_state, st_code,
2025 NULL, &tdata);
2026 if (status != PJ_SUCCESS) {
2027 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2028 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002029 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002030 if (status != PJ_SUCCESS) {
2031 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2032 }
2033 }
2034 }
2035 }
2036
Benny Prijono84126ab2006-02-09 09:30:09 +00002037
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002038 if (pjsua_var.ua_cfg.cb.on_call_state)
2039 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002040
2041 /* call->inv may be NULL now */
2042
Benny Prijono84126ab2006-02-09 09:30:09 +00002043 /* Destroy media session when invite session is disconnected. */
2044 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002045
Benny Prijonoa91a0032006-02-26 21:23:45 +00002046 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002047
Benny Prijono275fd682006-03-22 11:59:11 +00002048 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002049 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002050
Benny Prijono105217f2006-03-06 16:25:59 +00002051 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002052 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002053 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002054
2055 /* Reset call */
2056 reset_call(call->index);
2057
Benny Prijono84126ab2006-02-09 09:30:09 +00002058 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059
2060 PJSUA_UNLOCK();
2061}
2062
2063/*
2064 * This callback is called by invite session framework when UAC session
2065 * has forked.
2066 */
2067static void pjsua_call_on_forked( pjsip_inv_session *inv,
2068 pjsip_event *e)
2069{
2070 PJ_UNUSED_ARG(inv);
2071 PJ_UNUSED_ARG(e);
2072
2073 PJ_TODO(HANDLE_FORKED_DIALOG);
2074}
2075
2076
2077/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002078 * Disconnect call upon error.
2079 */
2080static void call_disconnect( pjsip_inv_session *inv,
2081 int code )
2082{
2083 pjsip_tx_data *tdata;
2084 pj_status_t status;
2085
2086 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2087 if (status == PJ_SUCCESS)
2088 pjsip_inv_send_msg(inv, tdata);
2089}
2090
2091/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002092 * Callback to be called when SDP offer/answer negotiation has just completed
2093 * in the session. This function will start/update media if negotiation
2094 * has succeeded.
2095 */
2096static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2097 pj_status_t status)
2098{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002099 pjsua_call *call;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002100 const pjmedia_sdp_session *local_sdp;
2101 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002102
2103 PJSUA_LOCK();
2104
2105 call = inv->dlg->mod_data[pjsua_var.mod.id];
2106
2107 if (status != PJ_SUCCESS) {
2108
2109 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2110
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002111 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002112 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002113
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002114 /* Disconnect call if we're not in the middle of initializing an
2115 * UAS dialog and if this is not a re-INVITE
2116 */
2117 if (inv->state != PJSIP_INV_STATE_NULL &&
2118 inv->state != PJSIP_INV_STATE_CONFIRMED)
2119 {
2120 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2121 }
2122
2123 PJSUA_UNLOCK();
2124 return;
2125 }
2126
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002127
2128 /* Get local and remote SDP */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002129 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2130 if (status != PJ_SUCCESS) {
2131 pjsua_perror(THIS_FILE,
2132 "Unable to retrieve currently active local SDP",
2133 status);
2134 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2135 PJSUA_UNLOCK();
2136 return;
2137 }
2138
2139
2140 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2141 if (status != PJ_SUCCESS) {
2142 pjsua_perror(THIS_FILE,
2143 "Unable to retrieve currently active remote SDP",
2144 status);
2145 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2146 PJSUA_UNLOCK();
2147 return;
2148 }
2149
Benny Prijonoc97608e2007-03-23 16:34:20 +00002150 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002151 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 Prijonoc97608e2007-03-23 16:34:20 +00002155 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 PJSUA_UNLOCK();
2157 return;
2158 }
2159
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002160
2161 /* Call application callback, if any */
2162 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2163 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2164
2165
2166 PJSUA_UNLOCK();
2167}
2168
2169
2170/*
2171 * Create inactive SDP for call hold.
2172 */
2173static pj_status_t create_inactive_sdp(pjsua_call *call,
2174 pjmedia_sdp_session **p_answer)
2175{
2176 pj_status_t status;
2177 pjmedia_sdp_conn *conn;
2178 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002179 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002180 pjmedia_sdp_session *sdp;
2181
Benny Prijono617c5bc2007-04-02 19:51:21 +00002182 /* Get media socket info */
2183 pjmedia_transport_get_info(call->med_tp, &skinfo);
2184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 /* Create new offer */
2186 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002187 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002188 if (status != PJ_SUCCESS) {
2189 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2190 return status;
2191 }
2192
2193 /* Get SDP media connection line */
2194 conn = sdp->media[0]->conn;
2195 if (!conn)
2196 conn = sdp->conn;
2197
2198 /* Modify address */
2199 conn->addr = pj_str("0.0.0.0");
2200
2201 /* Remove existing directions attributes */
2202 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2203 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2204 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2205 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2206
2207 /* Add inactive attribute */
2208 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2209 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2210
2211 *p_answer = sdp;
2212
2213 return status;
2214}
2215
2216
2217/*
2218 * Called when session received new offer.
2219 */
2220static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2221 const pjmedia_sdp_session *offer)
2222{
2223 const char *remote_state;
2224 pjsua_call *call;
2225 pjmedia_sdp_conn *conn;
2226 pjmedia_sdp_session *answer;
2227 pj_bool_t is_remote_active;
2228 pj_status_t status;
2229
2230 PJSUA_LOCK();
2231
2232 call = inv->dlg->mod_data[pjsua_var.mod.id];
2233
2234 /*
2235 * See if remote is offering active media (i.e. not on-hold)
2236 */
2237 is_remote_active = PJ_TRUE;
2238
2239 conn = offer->media[0]->conn;
2240 if (!conn)
2241 conn = offer->conn;
2242
2243 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2244 pj_strcmp2(&conn->addr, "0")==0)
2245 {
2246 is_remote_active = PJ_FALSE;
2247
2248 }
2249 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2250 {
2251 is_remote_active = PJ_FALSE;
2252 }
2253
2254 remote_state = (is_remote_active ? "active" : "inactive");
2255
2256 /* Supply candidate answer */
2257 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2258 PJ_LOG(4,(THIS_FILE,
2259 "Call %d: RX new media offer, creating inactive SDP "
2260 "(media in offer is %s)", call->index, remote_state));
2261 status = create_inactive_sdp( call, &answer );
2262 } else {
Benny Prijono667952e2007-04-02 19:27:54 +00002263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002264 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2265 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002266
2267 /* Init media channel */
2268 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
2269 if (status != PJ_SUCCESS) {
2270 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2271 PJSUA_UNLOCK();
2272 return;
2273 }
2274
Benny Prijonoc97608e2007-03-23 16:34:20 +00002275 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002276 }
2277
2278 if (status != PJ_SUCCESS) {
2279 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2280 PJSUA_UNLOCK();
2281 return;
2282 }
2283
2284 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2285 if (status != PJ_SUCCESS) {
2286 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2287 PJSUA_UNLOCK();
2288 return;
2289 }
2290
2291 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002292}
2293
2294
2295/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002296 * Callback called by event framework when the xfer subscription state
2297 * has changed.
2298 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002299static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2300{
2301
2302 PJ_UNUSED_ARG(event);
2303
2304 /*
2305 * When subscription is accepted (got 200/OK to REFER), check if
2306 * subscription suppressed.
2307 */
2308 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2309
2310 pjsip_rx_data *rdata;
2311 pjsip_generic_string_hdr *refer_sub;
2312 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2313 pjsua_call *call;
2314
2315 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2316
2317 /* Must be receipt of response message */
2318 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2319 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2320 rdata = event->body.tsx_state.src.rdata;
2321
2322 /* Find Refer-Sub header */
2323 refer_sub = (pjsip_generic_string_hdr*)
2324 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2325 &REFER_SUB, NULL);
2326
2327 /* Check if subscription is suppressed */
2328 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2329 /* Since no subscription is desired, assume that call has been
2330 * transfered successfully.
2331 */
2332 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2333 const pj_str_t ACCEPTED = { "Accepted", 8 };
2334 pj_bool_t cont = PJ_FALSE;
2335 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2336 200,
2337 &ACCEPTED,
2338 PJ_TRUE,
2339 &cont);
2340 }
2341
2342 /* Yes, subscription is suppressed.
2343 * Terminate our subscription now.
2344 */
2345 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2346 "event subcription..."));
2347 pjsip_evsub_terminate(sub, PJ_TRUE);
2348
2349 } else {
2350 /* Notify application about call transfer progress.
2351 * Initially notify with 100/Accepted status.
2352 */
2353 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2354 const pj_str_t ACCEPTED = { "Accepted", 8 };
2355 pj_bool_t cont = PJ_FALSE;
2356 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2357 100,
2358 &ACCEPTED,
2359 PJ_FALSE,
2360 &cont);
2361 }
2362 }
2363 }
2364 /*
2365 * On incoming NOTIFY, notify application about call transfer progress.
2366 */
2367 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2368 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2369 {
2370 pjsua_call *call;
2371 pjsip_msg *msg;
2372 pjsip_msg_body *body;
2373 pjsip_status_line status_line;
2374 pj_bool_t is_last;
2375 pj_bool_t cont;
2376 pj_status_t status;
2377
2378 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
2379
2380 /* When subscription is terminated, clear the xfer_sub member of
2381 * the inv_data.
2382 */
2383 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2384 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2385 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2386
2387 }
2388
2389 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2390 /* Application is not interested with call progress status */
2391 return;
2392 }
2393
2394 /* This better be a NOTIFY request */
2395 if (event->type == PJSIP_EVENT_TSX_STATE &&
2396 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2397 {
2398 pjsip_rx_data *rdata;
2399
2400 rdata = event->body.tsx_state.src.rdata;
2401
2402 /* Check if there's body */
2403 msg = rdata->msg_info.msg;
2404 body = msg->body;
2405 if (!body) {
2406 PJ_LOG(4,(THIS_FILE,
2407 "Warning: received NOTIFY without message body"));
2408 return;
2409 }
2410
2411 /* Check for appropriate content */
2412 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2413 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2414 {
2415 PJ_LOG(4,(THIS_FILE,
2416 "Warning: received NOTIFY with non message/sipfrag "
2417 "content"));
2418 return;
2419 }
2420
2421 /* Try to parse the content */
2422 status = pjsip_parse_status_line(body->data, body->len,
2423 &status_line);
2424 if (status != PJ_SUCCESS) {
2425 PJ_LOG(4,(THIS_FILE,
2426 "Warning: received NOTIFY with invalid "
2427 "message/sipfrag content"));
2428 return;
2429 }
2430
2431 } else {
2432 status_line.code = 500;
2433 status_line.reason = *pjsip_get_status_text(500);
2434 }
2435
2436 /* Notify application */
2437 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2438 cont = !is_last;
2439 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2440 status_line.code,
2441 &status_line.reason,
2442 is_last, &cont);
2443
2444 if (!cont) {
2445 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2446 }
2447 }
2448}
2449
2450
2451/*
2452 * Callback called by event framework when the xfer subscription state
2453 * has changed.
2454 */
2455static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002456{
2457
2458 PJ_UNUSED_ARG(event);
2459
2460 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002461 * When subscription is terminated, clear the xfer_sub member of
2462 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002463 */
2464 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002465 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002466
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002467 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002468 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002469 return;
2470
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002471 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002472 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002473
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002474 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002475 }
2476}
2477
2478
2479/*
2480 * Follow transfer (REFER) request.
2481 */
2482static void on_call_transfered( pjsip_inv_session *inv,
2483 pjsip_rx_data *rdata )
2484{
2485 pj_status_t status;
2486 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002487 pjsua_call *existing_call;
2488 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002489 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002490 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002491 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002492 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002493 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002494 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002495 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002496 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002497 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002498 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002499 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002500 pjsip_evsub *sub;
2501
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002502 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002503
Benny Prijono26ff9062006-02-21 23:47:00 +00002504 /* Find the Refer-To header */
2505 refer_to = (pjsip_generic_string_hdr*)
2506 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2507
2508 if (refer_to == NULL) {
2509 /* Invalid Request.
2510 * No Refer-To header!
2511 */
2512 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002513 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002514 return;
2515 }
2516
Benny Prijonoc8141a82006-08-20 09:12:19 +00002517 /* Find optional Refer-Sub header */
2518 refer_sub = (pjsip_generic_string_hdr*)
2519 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2520
2521 if (refer_sub) {
2522 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2523 no_refer_sub = PJ_TRUE;
2524 }
2525
Benny Prijono053f5222006-11-11 16:16:04 +00002526 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2527 * request.
2528 */
2529 ref_by_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
2530 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002531
Benny Prijono9fc735d2006-05-28 14:58:12 +00002532 /* Notify callback */
2533 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002534 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2535 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2536 &refer_to->hvalue,
2537 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002538
2539 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002540 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002541 if (code >= 300) {
2542 /* Application rejects call transfer request */
2543 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2544 return;
2545 }
2546
Benny Prijono26ff9062006-02-21 23:47:00 +00002547 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2548 (int)inv->dlg->remote.info_str.slen,
2549 inv->dlg->remote.info_str.ptr,
2550 (int)refer_to->hvalue.slen,
2551 refer_to->hvalue.ptr));
2552
Benny Prijonoc8141a82006-08-20 09:12:19 +00002553 if (no_refer_sub) {
2554 /*
2555 * Always answer with 200.
2556 */
2557 pjsip_tx_data *tdata;
2558 const pj_str_t str_false = { "false", 5};
2559 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002560
Benny Prijonoc8141a82006-08-20 09:12:19 +00002561 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2562 if (status != PJ_SUCCESS) {
2563 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2564 status);
2565 return;
2566 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002567
Benny Prijonoc8141a82006-08-20 09:12:19 +00002568 /* Add Refer-Sub header */
2569 hdr = (pjsip_hdr*)
2570 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2571 &str_false);
2572 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002573
Benny Prijono26ff9062006-02-21 23:47:00 +00002574
Benny Prijonoc8141a82006-08-20 09:12:19 +00002575 /* Send answer */
2576 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2577 tdata);
2578 if (status != PJ_SUCCESS) {
2579 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2580 status);
2581 return;
2582 }
2583
2584 /* Don't have subscription */
2585 sub = NULL;
2586
2587 } else {
2588 struct pjsip_evsub_user xfer_cb;
2589 pjsip_hdr hdr_list;
2590
2591 /* Init callback */
2592 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002593 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002594
2595 /* Init additional header list to be sent with REFER response */
2596 pj_list_init(&hdr_list);
2597
2598 /* Create transferee event subscription */
2599 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2600 if (status != PJ_SUCCESS) {
2601 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2602 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2603 return;
2604 }
2605
2606 /* If there's Refer-Sub header and the value is "true", send back
2607 * Refer-Sub in the response with value "true" too.
2608 */
2609 if (refer_sub) {
2610 const pj_str_t str_true = { "true", 4 };
2611 pjsip_hdr *hdr;
2612
2613 hdr = (pjsip_hdr*)
2614 pjsip_generic_string_hdr_create(inv->dlg->pool,
2615 &str_refer_sub,
2616 &str_true);
2617 pj_list_push_back(&hdr_list, hdr);
2618
2619 }
2620
2621 /* Accept the REFER request, send 200 (OK). */
2622 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2623
2624 /* Create initial NOTIFY request */
2625 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2626 100, NULL, &tdata);
2627 if (status != PJ_SUCCESS) {
2628 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2629 status);
2630 return;
2631 }
2632
2633 /* Send initial NOTIFY request */
2634 status = pjsip_xfer_send_request( sub, tdata);
2635 if (status != PJ_SUCCESS) {
2636 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2637 return;
2638 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002639 }
2640
2641 /* We're cheating here.
2642 * We need to get a null terminated string from a pj_str_t.
2643 * So grab the pointer from the hvalue and NULL terminate it, knowing
2644 * that the NULL position will be occupied by a newline.
2645 */
2646 uri = refer_to->hvalue.ptr;
2647 uri[refer_to->hvalue.slen] = '\0';
2648
Benny Prijono053f5222006-11-11 16:16:04 +00002649 /* Init msg_data */
2650 pjsua_msg_data_init(&msg_data);
2651
2652 /* If Referred-By header is present in the REFER request, copy this
2653 * to the outgoing INVITE request.
2654 */
2655 if (ref_by_hdr != NULL) {
2656 pjsip_hdr *dup = pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
2657 pj_list_push_back(&msg_data.hdr_list, dup);
2658 }
2659
Benny Prijono26ff9062006-02-21 23:47:00 +00002660 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002661 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002662 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002663 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002664 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002665 if (status != PJ_SUCCESS) {
2666
Benny Prijonoc8141a82006-08-20 09:12:19 +00002667 /* Notify xferer about the error (if we have subscription) */
2668 if (sub) {
2669 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2670 500, NULL, &tdata);
2671 if (status != PJ_SUCCESS) {
2672 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2673 status);
2674 return;
2675 }
2676 status = pjsip_xfer_send_request(sub, tdata);
2677 if (status != PJ_SUCCESS) {
2678 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2679 status);
2680 return;
2681 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002682 }
2683 return;
2684 }
2685
Benny Prijonoc8141a82006-08-20 09:12:19 +00002686 if (sub) {
2687 /* Put the server subscription in inv_data.
2688 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2689 * reported back to the server subscription.
2690 */
2691 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002692
Benny Prijonoc8141a82006-08-20 09:12:19 +00002693 /* Put the invite_data in the subscription. */
2694 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2695 &pjsua_var.calls[new_call]);
2696 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002697}
2698
2699
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002700
Benny Prijono26ff9062006-02-21 23:47:00 +00002701/*
2702 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002703 * session. We use this to trap:
2704 * - incoming REFER request.
2705 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002706 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002707static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2708 pjsip_transaction *tsx,
2709 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002710{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002711 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2712
2713 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002714
Benny Prijono26ff9062006-02-21 23:47:00 +00002715 if (tsx->role==PJSIP_ROLE_UAS &&
2716 tsx->state==PJSIP_TSX_STATE_TRYING &&
2717 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2718 {
2719 /*
2720 * Incoming REFER request.
2721 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002722 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002723
Benny Prijono26ff9062006-02-21 23:47:00 +00002724 }
Benny Prijonob0808372006-03-02 21:18:58 +00002725 else if (tsx->role==PJSIP_ROLE_UAS &&
2726 tsx->state==PJSIP_TSX_STATE_TRYING &&
2727 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2728 {
2729 /*
2730 * Incoming MESSAGE request!
2731 */
2732 pjsip_rx_data *rdata;
2733 pjsip_msg *msg;
2734 pjsip_accept_hdr *accept_hdr;
2735 pj_status_t status;
2736
2737 rdata = e->body.tsx_state.src.rdata;
2738 msg = rdata->msg_info.msg;
2739
2740 /* Request MUST have message body, with Content-Type equal to
2741 * "text/plain".
2742 */
2743 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2744
2745 pjsip_hdr hdr_list;
2746
2747 pj_list_init(&hdr_list);
2748 pj_list_push_back(&hdr_list, accept_hdr);
2749
2750 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2751 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002752 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002753 return;
2754 }
2755
2756 /* Respond with 200 first, so that remote doesn't retransmit in case
2757 * the UI takes too long to process the message.
2758 */
2759 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2760
2761 /* Process MESSAGE request */
2762 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2763 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002764
Benny Prijonob0808372006-03-02 21:18:58 +00002765 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002766 else if (tsx->role == PJSIP_ROLE_UAC &&
2767 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002768 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002769 /* Handle outgoing pager status */
2770 if (tsx->status_code >= 200) {
2771 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002772
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002773 im_data = tsx->mod_data[pjsua_var.mod.id];
2774 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002775
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002776 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2777 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2778 &im_data->to,
2779 &im_data->body,
2780 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00002781 (pjsip_status_code)
2782 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002783 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002784 }
Benny Prijonofccab712006-02-22 22:23:22 +00002785 }
Benny Prijono834aee32006-02-19 01:38:06 +00002786 }
Benny Prijono834aee32006-02-19 01:38:06 +00002787
Benny Prijono26ff9062006-02-21 23:47:00 +00002788
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002789 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002790}