blob: a7e0dd477d581c83a1688f747483509fc6f9be0e [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
53 * This callback is called when transaction state has changed in INVITE
54 * session. We use this to trap:
55 * - incoming REFER request.
56 * - incoming MESSAGE request.
57 */
58static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
59 pjsip_transaction *tsx,
60 pjsip_event *e);
61
62
63/* Destroy the call's media */
64static pj_status_t call_destroy_media(int call_id);
65
66/* Create inactive SDP for call hold. */
67static pj_status_t create_inactive_sdp(pjsua_call *call,
68 pjmedia_sdp_session **p_answer);
69
70
71/*
72 * Reset call descriptor.
73 */
74static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000075{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000077
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078 call->index = id;
79 call->inv = NULL;
80 call->user_data = NULL;
81 call->session = NULL;
82 call->xfer_sub = NULL;
83 call->last_code = 0;
84 call->conf_slot = PJSUA_INVALID_ID;
85 call->last_text.ptr = call->last_text_buf_;
86 call->last_text.slen = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000087}
88
89
Benny Prijono275fd682006-03-22 11:59:11 +000090/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000091 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +000092 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +000094{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 pjsip_inv_callback inv_cb;
96 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +000097 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +000098 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +000099
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000100 /* Init calls array. */
101 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
102 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000103
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000104 /* Copy config */
105 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000106
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000108 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
110 inv_cb.on_new_session = &pjsua_call_on_forked;
111 inv_cb.on_media_update = &pjsua_call_on_media_update;
112 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
113 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000114
Benny Prijono275fd682006-03-22 11:59:11 +0000115
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000116 /* Initialize invite session module: */
117 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
118 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
119
Benny Prijonoc8141a82006-08-20 09:12:19 +0000120 /* Add "norefersub" in Supported header */
121 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
122 NULL, 1, &str_norefersub);
123
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 return status;
125}
126
127
128/*
129 * Start call subsystem.
130 */
131pj_status_t pjsua_call_subsys_start(void)
132{
133 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000134 return PJ_SUCCESS;
135}
136
137
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000139 * Get maximum number of calls configured in pjsua.
140 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000141PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000142{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000144}
145
146
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147/*
148 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000149 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000150PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000151{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000153}
154
155
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156/*
157 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000158 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
160 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000161{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000163
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000164 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000165
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000166 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
169 if (!pjsua_var.calls[i].inv)
170 continue;
171 ids[c] = i;
172 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173 }
174
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000175 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000178
179 return PJ_SUCCESS;
180}
181
182
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183/*
184 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
187 const pj_str_t *dest_uri,
188 unsigned options,
189 void *user_data,
190 const pjsua_msg_data *msg_data,
191 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000192{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000193 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000194 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000195 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 pjsua_acc *acc;
197 pjsua_call *call;
198 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000199 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000200 pjsip_tx_data *tdata;
201 pj_status_t status;
202
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 /* Check that account is valid */
205 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000206 PJ_EINVAL);
207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 /* Options must be zero for now */
209 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
210
211 PJSUA_LOCK();
212
213 acc = &pjsua_var.acc[acc_id];
214 if (!acc->valid) {
215 pjsua_perror(THIS_FILE, "Unable to make call because account "
216 "is not valid", PJ_EINVALIDOP);
217 PJSUA_UNLOCK();
218 return PJ_EINVALIDOP;
219 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000220
Benny Prijonoa91a0032006-02-26 21:23:45 +0000221 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
223 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000224 break;
225 }
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 if (call_id == pjsua_var.ua_cfg.max_calls) {
228 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
229 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000230 return PJ_ETOOMANY;
231 }
232
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 call = &pjsua_var.calls[call_id];
234
Benny Prijonoe21e7842006-04-09 16:46:05 +0000235 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000236 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000237
Benny Prijonoe21e7842006-04-09 16:46:05 +0000238 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000239 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000240
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000241 /* Create suitable Contact header */
242 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
243 acc_id, dest_uri);
244 if (status != PJ_SUCCESS) {
245 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
246 PJSUA_UNLOCK();
247 return status;
248 }
249
Benny Prijonoe21e7842006-04-09 16:46:05 +0000250 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000251 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000252 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000253 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000254 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000255 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000256 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000257 return status;
258 }
259
260 /* Get media capability from media endpoint: */
261
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000262 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
263 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000264 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000265 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000266 goto on_error;
267 }
268
269 /* Create the INVITE session: */
270
271 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
272 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000273 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000274 goto on_error;
275 }
276
277
278 /* Create and associate our data in the session. */
279
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000280 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000281
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000282 dlg->mod_data[pjsua_var.mod.id] = call;
283 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000284
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000285 /* Attach user data */
286 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000287
288 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000289 if (!pj_list_empty(&acc->route_set))
290 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000291
292
293 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000294 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000295 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000296 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000297 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000298
299
300 /* Create initial INVITE: */
301
302 status = pjsip_inv_invite(inv, &tdata);
303 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000304 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
305 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000306 goto on_error;
307 }
308
309
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000310 /* Add additional headers etc */
311
312 pjsua_process_msg_data( tdata, msg_data);
313
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 /* Send initial INVITE: */
315
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000316 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000317 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000318 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
319 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000320
321 /* Upon failure to send first request, both dialog and invite
322 * session would have been cleared.
323 */
324 inv = NULL;
325 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000326 goto on_error;
327 }
328
Benny Prijono84126ab2006-02-09 09:30:09 +0000329 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000330
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000331 ++pjsua_var.call_cnt;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333 if (p_call_id)
334 *p_call_id = call_id;
335
336 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000337
338 return PJ_SUCCESS;
339
340
341on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000342 if (inv != NULL) {
343 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000344 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000345 pjsip_dlg_terminate(dlg);
346 }
347
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000348 if (call_id != -1) {
349 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000350 }
351
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000353 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000354}
355
356
357/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000358 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000359 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000360 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000361pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000362{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000363 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000364 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
365 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
366 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000367 pjsip_tx_data *response = NULL;
368 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000369 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 int acc_id;
371 pjsua_call *call;
372 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000373 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000374 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000375
Benny Prijono26ff9062006-02-21 23:47:00 +0000376 /* Don't want to handle anything but INVITE */
377 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
378 return PJ_FALSE;
379
380 /* Don't want to handle anything that's already associated with
381 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000382 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000383 if (dlg || tsx)
384 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000385
Benny Prijono84126ab2006-02-09 09:30:09 +0000386
Benny Prijono26ff9062006-02-21 23:47:00 +0000387 /* Verify that we can handle the request. */
388 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000389 pjsua_var.endpt, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000390 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000391
Benny Prijono26ff9062006-02-21 23:47:00 +0000392 /*
393 * No we can't handle the incoming INVITE request.
394 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000395
Benny Prijono26ff9062006-02-21 23:47:00 +0000396 if (response) {
397 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000398
Benny Prijono26ff9062006-02-21 23:47:00 +0000399 pjsip_get_response_addr(response->pool, rdata, &res_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000400 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
Benny Prijono26ff9062006-02-21 23:47:00 +0000401 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000402
403 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000404
Benny Prijono26ff9062006-02-21 23:47:00 +0000405 /* Respond with 500 (Internal Server Error) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000406 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000407 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000408 }
409
Benny Prijono26ff9062006-02-21 23:47:00 +0000410 return PJ_TRUE;
411 }
412
413
414 /*
415 * Yes we can handle the incoming INVITE request.
416 */
417
418 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
420 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000421 break;
422 }
423
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
425 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000426 PJSIP_SC_BUSY_HERE, NULL,
427 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000428 PJ_LOG(2,(THIS_FILE,
429 "Unable to accept incoming call (too many calls)"));
Benny Prijono84126ab2006-02-09 09:30:09 +0000430 return PJ_TRUE;
431 }
432
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 /* Clear call descriptor */
434 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000435
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 call = &pjsua_var.calls[call_id];
437
438 /* Mark call start time. */
439 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000440
Benny Prijono26ff9062006-02-21 23:47:00 +0000441 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
443 rdata->tp_info.pool, 1,
444 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000445 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000447 NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000448 return PJ_TRUE;
449 }
450
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000452 * Get which account is most likely to be associated with this incoming
453 * call. We need the account to find which contact URI to put for
454 * the call.
455 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000456 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000457
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000458 /* Get suitable Contact header */
459 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
460 acc_id, rdata);
461 if (status != PJ_SUCCESS) {
462 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
463 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
464 NULL, NULL);
465 return PJ_TRUE;
466 }
467
Benny Prijono26ff9062006-02-21 23:47:00 +0000468 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000469 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000470 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000471 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000472 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000473 NULL, NULL);
474
Benny Prijono26ff9062006-02-21 23:47:00 +0000475 return PJ_TRUE;
476 }
477
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478 /* Set credentials */
479 if (pjsua_var.acc[acc_id].cred_cnt) {
480 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
481 pjsua_var.acc[acc_id].cred_cnt,
482 pjsua_var.acc[acc_id].cred);
483 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000484
485 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000486 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
487 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000488 pjsip_hdr hdr_list;
489 pjsip_warning_hdr *w;
490
491 w = pjsip_warning_hdr_create_from_status(dlg->pool,
492 pjsip_endpt_name(pjsua_var.endpt),
493 status);
494 pj_list_init(&hdr_list);
495 pj_list_push_back(&hdr_list, w);
496
497 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
498
499 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000500 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000501 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000502 return PJ_TRUE;
503 }
504
505
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000506 /* Create and attach pjsua_var data to the dialog: */
507 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000508
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000509 dlg->mod_data[pjsua_var.mod.id] = call;
510 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000511
512
Benny Prijono64f851e2006-02-23 13:49:28 +0000513 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000514 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000515 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000517 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000518 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
519 status);
520
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
522 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000523 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000524
525 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000526 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000527 if (status != PJ_SUCCESS)
528 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000529 }
530
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000531 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000532
Benny Prijono105217f2006-03-06 16:25:59 +0000533
Benny Prijono8b1889b2006-06-06 18:40:40 +0000534 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 if (pjsua_var.ua_cfg.cb.on_incoming_call)
536 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000537
Benny Prijono26ff9062006-02-21 23:47:00 +0000538 /* This INVITE request has been handled. */
539 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000540}
541
542
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000543
544/*
545 * Check if the specified call has active INVITE session and the INVITE
546 * session has not been disconnected.
547 */
548PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
549{
550 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
551 PJ_EINVAL);
552 return pjsua_var.calls[call_id].inv != NULL &&
553 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
554}
555
556
557/*
558 * Check if call has an active media session.
559 */
560PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
561{
562 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
563 PJ_EINVAL);
564 return pjsua_var.calls[call_id].session != NULL;
565}
566
567
568/*
569 * Get the conference port identification associated with the call.
570 */
571PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
572{
573 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
574 PJ_EINVAL);
575 return pjsua_var.calls[call_id].conf_slot;
576}
577
578
579/*
580 * Obtain detail information about the specified call.
581 */
582PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
583 pjsua_call_info *info)
584{
585 pjsua_call *call;
586
587 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
588 PJ_EINVAL);
589
Benny Prijonoac623b32006-07-03 15:19:31 +0000590 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591
592 PJSUA_LOCK();
593
594 call = &pjsua_var.calls[call_id];
595
596 if (call->inv == NULL) {
597 PJSUA_UNLOCK();
598 return PJ_SUCCESS;
599 }
600
601 pjsip_dlg_inc_lock(call->inv->dlg);
602
603
604 /* id and role */
605 info->id = call_id;
606 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000607 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000608
609 /* local info */
610 info->local_info.ptr = info->buf_.local_info;
611 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
612 sizeof(info->buf_.local_info));
613
614 /* local contact */
615 info->local_contact.ptr = info->buf_.local_contact;
616 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
617 call->inv->dlg->local.contact->uri,
618 info->local_contact.ptr,
619 sizeof(info->buf_.local_contact));
620
621 /* remote info */
622 info->remote_info.ptr = info->buf_.remote_info;
623 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
624 sizeof(info->buf_.remote_info));
625
626 /* remote contact */
627 if (call->inv->dlg->remote.contact) {
628 int len;
629 info->remote_contact.ptr = info->buf_.remote_contact;
630 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
631 call->inv->dlg->remote.contact->uri,
632 info->remote_contact.ptr,
633 sizeof(info->buf_.remote_contact));
634 if (len < 0) len = 0;
635 info->remote_contact.slen = len;
636 } else {
637 info->remote_contact.slen = 0;
638 }
639
640 /* call id */
641 info->call_id.ptr = info->buf_.call_id;
642 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
643 sizeof(info->buf_.call_id));
644
645 /* state, state_text */
646 info->state = call->inv->state;
647 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
648
649 /* If call is disconnected, set the last_status from the cause code */
650 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
651 /* last_status, last_status_text */
652 info->last_status = call->inv->cause;
653
654 info->last_status_text.ptr = info->buf_.last_status_text;
655 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
656 sizeof(info->buf_.last_status_text));
657 } else {
658 /* last_status, last_status_text */
659 info->last_status = call->last_code;
660
661 info->last_status_text.ptr = info->buf_.last_status_text;
662 pj_strncpy(&info->last_status_text, &call->last_text,
663 sizeof(info->buf_.last_status_text));
664 }
665
666 /* media status and dir */
667 info->media_status = call->media_st;
668 info->media_dir = call->media_dir;
669
670
671 /* conference slot number */
672 info->conf_slot = call->conf_slot;
673
674 /* calculate duration */
675 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
676
677 info->total_duration = call->dis_time;
678 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
679
680 if (call->conn_time.sec) {
681 info->connect_duration = call->dis_time;
682 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
683 }
684
685 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
686
687 pj_gettimeofday(&info->total_duration);
688 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
689
690 pj_gettimeofday(&info->connect_duration);
691 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
692
693 } else {
694 pj_gettimeofday(&info->total_duration);
695 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
696 }
697
698 pjsip_dlg_dec_lock(call->inv->dlg);
699 PJSUA_UNLOCK();
700
701 return PJ_SUCCESS;
702}
703
704
705/*
706 * Attach application specific data to the call.
707 */
708PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
709 void *user_data)
710{
711 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
712 PJ_EINVAL);
713 pjsua_var.calls[call_id].user_data = user_data;
714
715 return PJ_SUCCESS;
716}
717
718
719/*
720 * Get user data attached to the call.
721 */
722PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
723{
724 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
725 NULL);
726 return pjsua_var.calls[call_id].user_data;
727}
728
729
730/*
731 * Send response to incoming INVITE request.
732 */
733PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
734 unsigned code,
735 const pj_str_t *reason,
736 const pjsua_msg_data *msg_data)
737{
738 pjsua_call *call;
739 pjsip_tx_data *tdata;
740 pj_status_t status;
741
742 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
743 PJ_EINVAL);
744
745 PJSUA_LOCK();
746
747 call = &pjsua_var.calls[call_id];
748
749 if (call->inv == NULL) {
750 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id));
751 PJSUA_UNLOCK();
752 return PJSIP_ESESSIONTERMINATED;
753 }
754
Benny Prijono2e507c22006-06-23 15:04:11 +0000755 if (call->res_time.sec == 0)
756 pj_gettimeofday(&call->res_time);
757
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000758 /* Create response message */
759 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
760 if (status != PJ_SUCCESS) {
761 pjsua_perror(THIS_FILE, "Error creating response",
762 status);
763 PJSUA_UNLOCK();
764 return status;
765 }
766
767 /* Add additional headers etc */
768 pjsua_process_msg_data( tdata, msg_data);
769
770 /* Send the message */
771 status = pjsip_inv_send_msg(call->inv, tdata);
772 if (status != PJ_SUCCESS)
773 pjsua_perror(THIS_FILE, "Error sending response",
774 status);
775
776 PJSUA_UNLOCK();
777
778 return status;
779}
780
781
782/*
783 * Hangup call by using method that is appropriate according to the
784 * call state.
785 */
786PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
787 unsigned code,
788 const pj_str_t *reason,
789 const pjsua_msg_data *msg_data)
790{
791 pjsua_call *call;
792 pj_status_t status;
793 pjsip_tx_data *tdata;
794
795
796 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
797 PJ_EINVAL);
798
799 PJSUA_LOCK();
800
801 call = &pjsua_var.calls[call_id];
802
803 if (!call->inv) {
Benny Prijono70972992006-08-05 11:13:58 +0000804 PJ_LOG(3,(THIS_FILE,"Invalid call or call has been disconnected"));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000805 PJSUA_UNLOCK();
806 return PJ_EINVAL;
807 }
808
809 if (code==0) {
810 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
811 code = PJSIP_SC_OK;
812 else if (call->inv->role == PJSIP_ROLE_UAS)
813 code = PJSIP_SC_DECLINE;
814 else
815 code = PJSIP_SC_REQUEST_TERMINATED;
816 }
817
818 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
819 if (status != PJ_SUCCESS) {
820 pjsua_perror(THIS_FILE,
821 "Failed to create end session message",
822 status);
823 PJSUA_UNLOCK();
824 return status;
825 }
826
827 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
828 * as p_tdata when INVITE transaction has not been answered
829 * with any provisional responses.
830 */
831 if (tdata == NULL) {
832 PJSUA_UNLOCK();
833 return PJ_SUCCESS;
834 }
835
836 /* Add additional headers etc */
837 pjsua_process_msg_data( tdata, msg_data);
838
839 /* Send the message */
840 status = pjsip_inv_send_msg(call->inv, tdata);
841 if (status != PJ_SUCCESS) {
842 pjsua_perror(THIS_FILE,
843 "Failed to send end session message",
844 status);
845 PJSUA_UNLOCK();
846 return status;
847 }
848
849 PJSUA_UNLOCK();
850
851 return PJ_SUCCESS;
852}
853
854
855/*
856 * Put the specified call on hold.
857 */
858PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
859 const pjsua_msg_data *msg_data)
860{
861 pjmedia_sdp_session *sdp;
862 pjsua_call *call;
863 pjsip_tx_data *tdata;
864 pj_status_t status;
865
866 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
867 PJ_EINVAL);
868
869 PJSUA_LOCK();
870
871 call = &pjsua_var.calls[call_id];
872
873 if (!call->inv) {
874 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
875 PJSUA_UNLOCK();
876 return PJSIP_ESESSIONTERMINATED;
877 }
878
879 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
880 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
881 PJSUA_UNLOCK();
882 return PJSIP_ESESSIONSTATE;
883 }
884
885 status = create_inactive_sdp(call, &sdp);
886 if (status != PJ_SUCCESS) {
887 PJSUA_UNLOCK();
888 return status;
889 }
890
891 /* Create re-INVITE with new offer */
892 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
893 if (status != PJ_SUCCESS) {
894 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
895 PJSUA_UNLOCK();
896 return status;
897 }
898
899 /* Add additional headers etc */
900 pjsua_process_msg_data( tdata, msg_data);
901
902 /* Send the request */
903 status = pjsip_inv_send_msg( call->inv, tdata);
904 if (status != PJ_SUCCESS) {
905 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
906 PJSUA_UNLOCK();
907 return status;
908 }
909
910 PJSUA_UNLOCK();
911
912 return PJ_SUCCESS;
913}
914
915
916/*
917 * Send re-INVITE (to release hold).
918 */
919PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
920 pj_bool_t unhold,
921 const pjsua_msg_data *msg_data)
922{
923 pjmedia_sdp_session *sdp;
924 pjsip_tx_data *tdata;
925 pjsua_call *call;
926 pj_status_t status;
927
928
929 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
930 PJ_EINVAL);
931
932 PJSUA_LOCK();
933
934 call = &pjsua_var.calls[call_id];
935
936 if (!call->inv) {
937 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
938 PJSUA_UNLOCK();
939 return PJSIP_ESESSIONTERMINATED;
940 }
941
942
943 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
944 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
945 PJSUA_UNLOCK();
946 return PJSIP_ESESSIONSTATE;
947 }
948
949 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +0000950 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000951 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000952 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
953 1, &call->skinfo, &sdp);
954 if (status != PJ_SUCCESS) {
955 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
956 status);
957 PJSUA_UNLOCK();
958 return status;
959 }
960
961 /* Create re-INVITE with new offer */
962 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
963 if (status != PJ_SUCCESS) {
964 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
965 PJSUA_UNLOCK();
966 return status;
967 }
968
969 /* Add additional headers etc */
970 pjsua_process_msg_data( tdata, msg_data);
971
972 /* Send the request */
973 status = pjsip_inv_send_msg( call->inv, tdata);
974 if (status != PJ_SUCCESS) {
975 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
976 PJSUA_UNLOCK();
977 return status;
978 }
979
980 PJSUA_UNLOCK();
981
982 return PJ_SUCCESS;
983}
984
985
986/*
987 * Initiate call transfer to the specified address.
988 */
989PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
990 const pj_str_t *dest,
991 const pjsua_msg_data *msg_data)
992{
993 pjsip_evsub *sub;
994 pjsip_tx_data *tdata;
995 pjsua_call *call;
996 pj_status_t status;
997
998
999 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1000 PJ_EINVAL);
1001
1002 PJSUA_LOCK();
1003
1004 call = &pjsua_var.calls[call_id];
1005
1006 if (!call->inv) {
1007 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1008 PJSUA_UNLOCK();
1009 return PJSIP_ESESSIONTERMINATED;
1010 }
1011
1012 /* Create xfer client subscription.
1013 * We're not interested in knowing the transfer result, so we
1014 * put NULL as the callback.
1015 */
1016 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
1017 if (status != PJ_SUCCESS) {
1018 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
1019 PJSUA_UNLOCK();
1020 return status;
1021 }
1022
1023 /*
1024 * Create REFER request.
1025 */
1026 status = pjsip_xfer_initiate(sub, dest, &tdata);
1027 if (status != PJ_SUCCESS) {
1028 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1029 PJSUA_UNLOCK();
1030 return status;
1031 }
1032
1033 /* Add additional headers etc */
1034 pjsua_process_msg_data( tdata, msg_data);
1035
1036 /* Send. */
1037 status = pjsip_xfer_send_request(sub, tdata);
1038 if (status != PJ_SUCCESS) {
1039 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1040 PJSUA_UNLOCK();
1041 return status;
1042 }
1043
1044 /* For simplicity (that's what this program is intended to be!),
1045 * leave the original invite session as it is. More advanced application
1046 * may want to hold the INVITE, or terminate the invite, or whatever.
1047 */
1048
1049 PJSUA_UNLOCK();
1050
1051 return PJ_SUCCESS;
1052
1053}
1054
1055
1056/*
1057 * Send DTMF digits to remote using RFC 2833 payload formats.
1058 */
1059PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1060 const pj_str_t *digits)
1061{
1062 pjsua_call *call;
1063 pj_status_t status;
1064
1065 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1066 PJ_EINVAL);
1067
1068 PJSUA_LOCK();
1069
1070 call = &pjsua_var.calls[call_id];
1071
1072 if (!call->session) {
1073 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1074 PJSUA_UNLOCK();
1075 return PJ_EINVALIDOP;
1076 }
1077
1078 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1079
1080 PJSUA_UNLOCK();
1081
1082 return status;
1083}
1084
1085
1086/**
1087 * Send instant messaging inside INVITE session.
1088 */
1089PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1090 const pj_str_t *mime_type,
1091 const pj_str_t *content,
1092 const pjsua_msg_data *msg_data,
1093 void *user_data)
1094{
1095 pjsua_call *call;
1096 const pj_str_t mime_text_plain = pj_str("text/plain");
1097 pjsip_media_type ctype;
1098 pjsua_im_data *im_data;
1099 pjsip_tx_data *tdata;
1100 pj_status_t status;
1101
1102
1103 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1104 PJ_EINVAL);
1105
1106 PJSUA_LOCK();
1107
1108 call = &pjsua_var.calls[call_id];
1109
1110 if (!call->inv) {
1111 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1112 PJSUA_UNLOCK();
1113 return PJSIP_ESESSIONTERMINATED;
1114 }
1115
1116 /* Lock dialog. */
1117 pjsip_dlg_inc_lock(call->inv->dlg);
1118
1119 /* Set default media type if none is specified */
1120 if (mime_type == NULL) {
1121 mime_type = &mime_text_plain;
1122 }
1123
1124 /* Create request message. */
1125 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1126 -1, &tdata);
1127 if (status != PJ_SUCCESS) {
1128 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1129 goto on_return;
1130 }
1131
1132 /* Add accept header. */
1133 pjsip_msg_add_hdr( tdata->msg,
1134 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1135
1136 /* Parse MIME type */
1137 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1138
1139 /* Create "text/plain" message body. */
1140 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1141 &ctype.subtype, content);
1142 if (tdata->msg->body == NULL) {
1143 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1144 pjsip_tx_data_dec_ref(tdata);
1145 goto on_return;
1146 }
1147
1148 /* Add additional headers etc */
1149 pjsua_process_msg_data( tdata, msg_data);
1150
1151 /* Create IM data and attach to the request. */
1152 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1153 im_data->acc_id = call->acc_id;
1154 im_data->call_id = call_id;
1155 im_data->to = call->inv->dlg->remote.info_str;
1156 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1157 im_data->user_data = user_data;
1158
1159
1160 /* Send the request. */
1161 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1162 pjsua_var.mod.id, im_data);
1163 if (status != PJ_SUCCESS) {
1164 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1165 goto on_return;
1166 }
1167
1168on_return:
1169 pjsip_dlg_dec_lock(call->inv->dlg);
1170 PJSUA_UNLOCK();
1171 return status;
1172}
1173
1174
1175/*
1176 * Send IM typing indication inside INVITE session.
1177 */
1178PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1179 pj_bool_t is_typing,
1180 const pjsua_msg_data*msg_data)
1181{
1182 pjsua_call *call;
1183 pjsip_tx_data *tdata;
1184 pj_status_t status;
1185
1186
1187 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1188 PJ_EINVAL);
1189
1190 PJSUA_LOCK();
1191
1192 call = &pjsua_var.calls[call_id];
1193
1194 if (!call->inv) {
1195 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1196 PJSUA_UNLOCK();
1197 return PJSIP_ESESSIONTERMINATED;
1198 }
1199
1200 /* Lock dialog. */
1201 pjsip_dlg_inc_lock(call->inv->dlg);
1202
1203 /* Create request message. */
1204 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1205 -1, &tdata);
1206 if (status != PJ_SUCCESS) {
1207 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1208 goto on_return;
1209 }
1210
1211 /* Create "application/im-iscomposing+xml" msg body. */
1212 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1213 NULL, NULL, -1);
1214
1215 /* Add additional headers etc */
1216 pjsua_process_msg_data( tdata, msg_data);
1217
1218 /* Send the request. */
1219 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1220 if (status != PJ_SUCCESS) {
1221 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1222 goto on_return;
1223 }
1224
1225on_return:
1226 pjsip_dlg_dec_lock(call->inv->dlg);
1227 PJSUA_UNLOCK();
1228 return status;
1229}
1230
1231
1232/*
1233 * Terminate all calls.
1234 */
1235PJ_DEF(void) pjsua_call_hangup_all(void)
1236{
1237 unsigned i;
1238
1239 PJSUA_LOCK();
1240
1241 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1242 if (pjsua_var.calls[i].inv)
1243 pjsua_call_hangup(i, 0, NULL, NULL);
1244 }
1245
1246 PJSUA_UNLOCK();
1247}
1248
1249
1250static const char *good_number(char *buf, pj_int32_t val)
1251{
1252 if (val < 1000) {
1253 pj_ansi_sprintf(buf, "%d", val);
1254 } else if (val < 1000000) {
1255 pj_ansi_sprintf(buf, "%d.%dK",
1256 val / 1000,
1257 (val % 1000) / 100);
1258 } else {
1259 pj_ansi_sprintf(buf, "%d.%02dM",
1260 val / 1000000,
1261 (val % 1000000) / 10000);
1262 }
1263
1264 return buf;
1265}
1266
1267
1268/* Dump media session */
1269static void dump_media_session(const char *indent,
1270 char *buf, unsigned maxlen,
1271 pjmedia_session *session)
1272{
1273 unsigned i;
1274 char *p = buf, *end = buf+maxlen;
1275 int len;
1276 pjmedia_session_info info;
1277
1278 pjmedia_session_get_info(session, &info);
1279
1280 for (i=0; i<info.stream_cnt; ++i) {
1281 pjmedia_rtcp_stat stat;
1282 const char *rem_addr;
1283 int rem_port;
1284 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001285 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001286 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1287 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001288
1289 pjmedia_session_get_stream_stat(session, i, &stat);
1290 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1291 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1292
1293 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1294 dir = "sendonly";
1295 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1296 dir = "recvonly";
1297 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1298 dir = "sendrecv";
1299 else
1300 dir = "inactive";
1301
1302
1303 len = pj_ansi_snprintf(buf, end-p,
1304 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1305 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001306 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 info.stream_info[i].fmt.encoding_name.ptr,
1308 info.stream_info[i].fmt.clock_rate / 1000,
1309 dir,
1310 rem_addr, rem_port);
1311 if (len < 1 || len > end-p) {
1312 *p = '\0';
1313 return;
1314 }
1315
1316 p += len;
1317 *p++ = '\n';
1318 *p = '\0';
1319
1320 if (stat.rx.update_cnt == 0)
1321 strcpy(last_update, "never");
1322 else {
1323 pj_gettimeofday(&now);
1324 PJ_TIME_VAL_SUB(now, stat.rx.update);
1325 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1326 now.sec / 3600,
1327 (now.sec % 3600) / 60,
1328 now.sec % 60,
1329 now.msec);
1330 }
1331
Benny Prijono80019eb2006-08-07 13:22:23 +00001332 pj_gettimeofday(&media_duration);
1333 PJ_TIME_VAL_SUB(media_duration, stat.start);
1334 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1335 media_duration.msec = 1;
1336
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001337 len = pj_ansi_snprintf(p, end-p,
1338 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001339 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1341 "%s (msec) min avg max last\n"
1342 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1343 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1344 indent, info.stream_info[i].fmt.pt,
1345 last_update,
1346 indent,
1347 good_number(packets, stat.rx.pkt),
1348 good_number(bytes, stat.rx.bytes),
1349 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001350 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351 indent,
1352 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001353 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001354 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001355 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001357 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358 indent, indent,
1359 stat.rx.loss_period.min / 1000.0,
1360 stat.rx.loss_period.avg / 1000.0,
1361 stat.rx.loss_period.max / 1000.0,
1362 stat.rx.loss_period.last / 1000.0,
1363 indent,
1364 stat.rx.jitter.min / 1000.0,
1365 stat.rx.jitter.avg / 1000.0,
1366 stat.rx.jitter.max / 1000.0,
1367 stat.rx.jitter.last / 1000.0,
1368 ""
1369 );
1370
1371 if (len < 1 || len > end-p) {
1372 *p = '\0';
1373 return;
1374 }
1375
1376 p += len;
1377 *p++ = '\n';
1378 *p = '\0';
1379
1380 if (stat.tx.update_cnt == 0)
1381 strcpy(last_update, "never");
1382 else {
1383 pj_gettimeofday(&now);
1384 PJ_TIME_VAL_SUB(now, stat.tx.update);
1385 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1386 now.sec / 3600,
1387 (now.sec % 3600) / 60,
1388 now.sec % 60,
1389 now.msec);
1390 }
1391
1392 len = pj_ansi_snprintf(p, end-p,
1393 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001394 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1396 "%s (msec) min avg max last\n"
1397 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1398 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1399 indent,
1400 info.stream_info[i].tx_pt,
1401 info.stream_info[i].param->info.frm_ptime *
1402 info.stream_info[i].param->setting.frm_per_pkt,
1403 last_update,
1404
1405 indent,
1406 good_number(packets, stat.tx.pkt),
1407 good_number(bytes, stat.tx.bytes),
1408 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001409 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410
1411 indent,
1412 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001413 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001414 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001415 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001416 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001417 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001418
1419 indent, indent,
1420 stat.tx.loss_period.min / 1000.0,
1421 stat.tx.loss_period.avg / 1000.0,
1422 stat.tx.loss_period.max / 1000.0,
1423 stat.tx.loss_period.last / 1000.0,
1424 indent,
1425 stat.tx.jitter.min / 1000.0,
1426 stat.tx.jitter.avg / 1000.0,
1427 stat.tx.jitter.max / 1000.0,
1428 stat.tx.jitter.last / 1000.0,
1429 ""
1430 );
1431
1432 if (len < 1 || len > end-p) {
1433 *p = '\0';
1434 return;
1435 }
1436
1437 p += len;
1438 *p++ = '\n';
1439 *p = '\0';
1440
1441 len = pj_ansi_snprintf(p, end-p,
1442 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1443 indent,
1444 stat.rtt.min / 1000.0,
1445 stat.rtt.avg / 1000.0,
1446 stat.rtt.max / 1000.0,
1447 stat.rtt.last / 1000.0
1448 );
1449 if (len < 1 || len > end-p) {
1450 *p = '\0';
1451 return;
1452 }
1453
1454 p += len;
1455 *p++ = '\n';
1456 *p = '\0';
1457 }
1458}
1459
1460
1461/* Print call info */
1462static void print_call(const char *title,
1463 int call_id,
1464 char *buf, pj_size_t size)
1465{
1466 int len;
1467 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1468 pjsip_dialog *dlg = inv->dlg;
1469 char userinfo[128];
1470
1471 /* Dump invite sesion info. */
1472
1473 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1474 if (len < 1)
1475 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1476 else
1477 userinfo[len] = '\0';
1478
1479 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1480 title,
1481 pjsip_inv_state_name(inv->state),
1482 userinfo);
1483 if (len < 1 || len >= (int)size) {
1484 pj_ansi_strcpy(buf, "<--uri too long-->");
1485 len = 18;
1486 } else
1487 buf[len] = '\0';
1488}
1489
1490
1491/*
1492 * Dump call and media statistics to string.
1493 */
1494PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1495 pj_bool_t with_media,
1496 char *buffer,
1497 unsigned maxlen,
1498 const char *indent)
1499{
1500 pjsua_call *call;
1501 pj_time_val duration, res_delay, con_delay;
1502 char tmp[128];
1503 char *p, *end;
1504 int len;
1505
1506 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1507 PJ_EINVAL);
1508
1509 PJSUA_LOCK();
1510
1511 call = &pjsua_var.calls[call_id];
1512
1513 *buffer = '\0';
1514 p = buffer;
1515 end = buffer + maxlen;
1516 len = 0;
1517
1518 if (call->inv == NULL) {
1519 PJSUA_UNLOCK();
1520 return PJ_EINVALIDOP;
1521 }
1522
1523 print_call(indent, call_id, tmp, sizeof(tmp));
1524
1525 len = pj_ansi_strlen(tmp);
1526 pj_ansi_strcpy(buffer, tmp);
1527
1528 p += len;
1529 *p++ = '\r';
1530 *p++ = '\n';
1531
1532 /* Calculate call duration */
1533 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1534 pj_gettimeofday(&duration);
1535 PJ_TIME_VAL_SUB(duration, call->conn_time);
1536 con_delay = call->conn_time;
1537 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1538 } else {
1539 duration.sec = duration.msec = 0;
1540 con_delay.sec = con_delay.msec = 0;
1541 }
1542
1543 /* Calculate first response delay */
1544 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1545 res_delay = call->res_time;
1546 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1547 } else {
1548 res_delay.sec = res_delay.msec = 0;
1549 }
1550
1551 /* Print duration */
1552 len = pj_ansi_snprintf(p, end-p,
1553 "%s Call time: %02dh:%02dm:%02ds, "
1554 "1st res in %d ms, conn in %dms",
1555 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001556 (int)(duration.sec / 3600),
1557 (int)((duration.sec % 3600)/60),
1558 (int)(duration.sec % 60),
1559 (int)PJ_TIME_VAL_MSEC(res_delay),
1560 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001561
1562 if (len > 0 && len < end-p) {
1563 p += len;
1564 *p++ = '\n';
1565 *p = '\0';
1566 }
1567
1568 /* Dump session statistics */
1569 if (with_media && call->session)
1570 dump_media_session(indent, p, end-p, call->session);
1571
1572 PJSUA_UNLOCK();
1573
1574 return PJ_SUCCESS;
1575}
1576
1577
1578/*
1579 * Destroy the call's media
1580 */
1581static pj_status_t call_destroy_media(int call_id)
1582{
1583 pjsua_call *call = &pjsua_var.calls[call_id];
1584
1585 if (call->conf_slot != PJSUA_INVALID_ID) {
1586 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1587 call->conf_slot = PJSUA_INVALID_ID;
1588 }
1589
1590 if (call->session) {
1591 /* Destroy session (this will also close RTP/RTCP sockets). */
1592 pjmedia_session_destroy(call->session);
1593 call->session = NULL;
1594
1595 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1596 call_id));
1597
1598 }
1599
1600 call->media_st = PJSUA_CALL_MEDIA_NONE;
1601
1602 return PJ_SUCCESS;
1603}
1604
1605
Benny Prijono84126ab2006-02-09 09:30:09 +00001606/*
1607 * This callback receives notification from invite session when the
1608 * session state has changed.
1609 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001610static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1611 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001612{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001614
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001615 PJSUA_LOCK();
1616
1617 call = inv->dlg->mod_data[pjsua_var.mod.id];
1618
1619 if (!call) {
1620 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001621 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001622 }
1623
Benny Prijonoe21e7842006-04-09 16:46:05 +00001624
1625 /* Get call times */
1626 switch (inv->state) {
1627 case PJSIP_INV_STATE_EARLY:
1628 case PJSIP_INV_STATE_CONNECTING:
1629 if (call->res_time.sec == 0)
1630 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001631 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001632 pj_strncpy(&call->last_text,
1633 &e->body.tsx_state.tsx->status_text,
1634 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001635 break;
1636 case PJSIP_INV_STATE_CONFIRMED:
1637 pj_gettimeofday(&call->conn_time);
1638 break;
1639 case PJSIP_INV_STATE_DISCONNECTED:
1640 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001641 if (call->res_time.sec == 0)
1642 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001643 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1644 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001645 pj_strncpy(&call->last_text,
1646 &e->body.tsx_state.tsx->status_text,
1647 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001648 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001649 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001650 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001651 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001652 pj_strncpy(&call->last_text,
1653 &e->body.tsx_state.tsx->status_text,
1654 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001655 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001656 }
1657
Benny Prijono26ff9062006-02-21 23:47:00 +00001658 /* If this is an outgoing INVITE that was created because of
1659 * REFER/transfer, send NOTIFY to transferer.
1660 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001661 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001662 int st_code = -1;
1663 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1664
1665
Benny Prijonoa91a0032006-02-26 21:23:45 +00001666 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001667 case PJSIP_INV_STATE_NULL:
1668 case PJSIP_INV_STATE_CALLING:
1669 /* Do nothing */
1670 break;
1671
1672 case PJSIP_INV_STATE_EARLY:
1673 case PJSIP_INV_STATE_CONNECTING:
1674 st_code = e->body.tsx_state.tsx->status_code;
1675 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1676 break;
1677
1678 case PJSIP_INV_STATE_CONFIRMED:
1679 /* When state is confirmed, send the final 200/OK and terminate
1680 * subscription.
1681 */
1682 st_code = e->body.tsx_state.tsx->status_code;
1683 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1684 break;
1685
1686 case PJSIP_INV_STATE_DISCONNECTED:
1687 st_code = e->body.tsx_state.tsx->status_code;
1688 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1689 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001690
Benny Prijono8b1889b2006-06-06 18:40:40 +00001691 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001692 /* Nothing to do. Just to keep gcc from complaining about
1693 * unused enums.
1694 */
1695 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001696 }
1697
1698 if (st_code != -1) {
1699 pjsip_tx_data *tdata;
1700 pj_status_t status;
1701
Benny Prijonoa91a0032006-02-26 21:23:45 +00001702 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001703 ev_state, st_code,
1704 NULL, &tdata);
1705 if (status != PJ_SUCCESS) {
1706 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1707 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001708 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001709 if (status != PJ_SUCCESS) {
1710 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1711 }
1712 }
1713 }
1714 }
1715
Benny Prijono84126ab2006-02-09 09:30:09 +00001716
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001717 if (pjsua_var.ua_cfg.cb.on_call_state)
1718 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001719
1720 /* call->inv may be NULL now */
1721
Benny Prijono84126ab2006-02-09 09:30:09 +00001722 /* Destroy media session when invite session is disconnected. */
1723 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001724
Benny Prijonoa91a0032006-02-26 21:23:45 +00001725 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001726
Benny Prijono275fd682006-03-22 11:59:11 +00001727 if (call)
1728 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001729
Benny Prijono105217f2006-03-06 16:25:59 +00001730 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001731 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001732 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001733 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001734
1735 PJSUA_UNLOCK();
1736}
1737
1738/*
1739 * This callback is called by invite session framework when UAC session
1740 * has forked.
1741 */
1742static void pjsua_call_on_forked( pjsip_inv_session *inv,
1743 pjsip_event *e)
1744{
1745 PJ_UNUSED_ARG(inv);
1746 PJ_UNUSED_ARG(e);
1747
1748 PJ_TODO(HANDLE_FORKED_DIALOG);
1749}
1750
1751
1752/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00001753 * Disconnect call upon error.
1754 */
1755static void call_disconnect( pjsip_inv_session *inv,
1756 int code )
1757{
1758 pjsip_tx_data *tdata;
1759 pj_status_t status;
1760
1761 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
1762 if (status == PJ_SUCCESS)
1763 pjsip_inv_send_msg(inv, tdata);
1764}
1765
1766/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001767 * Callback to be called when SDP offer/answer negotiation has just completed
1768 * in the session. This function will start/update media if negotiation
1769 * has succeeded.
1770 */
1771static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1772 pj_status_t status)
1773{
1774 int prev_media_st = 0;
1775 pjsua_call *call;
1776 pjmedia_session_info sess_info;
1777 const pjmedia_sdp_session *local_sdp;
1778 const pjmedia_sdp_session *remote_sdp;
1779 pjmedia_port *media_port;
1780 pj_str_t port_name;
1781 char tmp[PJSIP_MAX_URL_SIZE];
1782
1783 PJSUA_LOCK();
1784
1785 call = inv->dlg->mod_data[pjsua_var.mod.id];
1786
1787 if (status != PJ_SUCCESS) {
1788
1789 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1790
1791 /* Disconnect call if we're not in the middle of initializing an
1792 * UAS dialog and if this is not a re-INVITE
1793 */
1794 if (inv->state != PJSIP_INV_STATE_NULL &&
1795 inv->state != PJSIP_INV_STATE_CONFIRMED)
1796 {
1797 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1798 }
1799
1800 PJSUA_UNLOCK();
1801 return;
1802 }
1803
1804 /* Destroy existing media session, if any. */
1805
1806 if (call) {
1807 prev_media_st = call->media_st;
1808 call_destroy_media(call->index);
1809 }
1810
1811 /* Get local and remote SDP */
1812
1813 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1814 if (status != PJ_SUCCESS) {
1815 pjsua_perror(THIS_FILE,
1816 "Unable to retrieve currently active local SDP",
1817 status);
1818 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1819 PJSUA_UNLOCK();
1820 return;
1821 }
1822
1823
1824 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1825 if (status != PJ_SUCCESS) {
1826 pjsua_perror(THIS_FILE,
1827 "Unable to retrieve currently active remote SDP",
1828 status);
1829 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1830 PJSUA_UNLOCK();
1831 return;
1832 }
1833
1834 /* Create media session info based on SDP parameters.
1835 * We only support one stream per session at the moment
1836 */
1837 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1838 pjsua_var.med_endpt,
1839 1,&sess_info,
1840 local_sdp, remote_sdp);
1841 if (status != PJ_SUCCESS) {
1842 pjsua_perror(THIS_FILE, "Unable to create media session",
1843 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001844 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845 PJSUA_UNLOCK();
1846 return;
1847 }
1848
1849 /* Check if media is put on-hold */
1850 if (sess_info.stream_cnt == 0 ||
1851 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1852 {
1853
1854 /* Determine who puts the call on-hold */
1855 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1856 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1857 /* It was local who offer hold */
1858 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1859 } else {
1860 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1861 }
1862 }
1863
1864 call->media_dir = PJMEDIA_DIR_NONE;
1865
1866 } else {
1867
1868 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00001869 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00001870 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
1871 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00001872 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
1873 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1874 }
1875
1876 /* Disable VAD, if this option is specified. */
1877 if (pjsua_var.media_cfg.no_vad) {
1878 sess_info.stream_info[0].param->setting.vad = 0;
1879 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001880
1881
1882 /* Optionally, application may modify other stream settings here
1883 * (such as jitter buffer parameters, codec ptime, etc.)
1884 */
1885
1886 /* Create session based on session info. */
1887 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1888 &call->med_tp,
1889 call, &call->session );
1890 if (status != PJ_SUCCESS) {
1891 pjsua_perror(THIS_FILE, "Unable to create media session",
1892 status);
1893 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1894 PJSUA_UNLOCK();
1895 return;
1896 }
1897
1898
1899 /* Get the port interface of the first stream in the session.
1900 * We need the port interface to add to the conference bridge.
1901 */
1902 pjmedia_session_get_port(call->session, 0, &media_port);
1903
1904
1905 /*
1906 * Add the call to conference bridge.
1907 */
1908 port_name.ptr = tmp;
1909 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1910 call->inv->dlg->remote.info->uri,
1911 tmp, sizeof(tmp));
1912 if (port_name.slen < 1) {
1913 port_name = pj_str("call");
1914 }
1915 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1916 media_port,
1917 &port_name,
1918 (unsigned*)&call->conf_slot);
1919 if (status != PJ_SUCCESS) {
1920 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1921 status);
1922 call_destroy_media(call->index);
1923 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1924 PJSUA_UNLOCK();
1925 return;
1926 }
1927
1928 /* Call's media state is active */
1929 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1930 call->media_dir = sess_info.stream_info[0].dir;
1931 }
1932
1933 /* Print info. */
1934 {
1935 char info[80];
1936 int info_len = 0;
1937 unsigned i;
1938
1939 for (i=0; i<sess_info.stream_cnt; ++i) {
1940 int len;
1941 const char *dir;
1942 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1943
1944 switch (strm_info->dir) {
1945 case PJMEDIA_DIR_NONE:
1946 dir = "inactive";
1947 break;
1948 case PJMEDIA_DIR_ENCODING:
1949 dir = "sendonly";
1950 break;
1951 case PJMEDIA_DIR_DECODING:
1952 dir = "recvonly";
1953 break;
1954 case PJMEDIA_DIR_ENCODING_DECODING:
1955 dir = "sendrecv";
1956 break;
1957 default:
1958 dir = "unknown";
1959 break;
1960 }
1961 len = pj_ansi_sprintf( info+info_len,
1962 ", stream #%d: %.*s (%s)", i,
1963 (int)strm_info->fmt.encoding_name.slen,
1964 strm_info->fmt.encoding_name.ptr,
1965 dir);
1966 if (len > 0)
1967 info_len += len;
1968 }
1969 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1970 }
1971
1972 /* Call application callback, if any */
1973 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1974 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1975
1976
1977 PJSUA_UNLOCK();
1978}
1979
1980
1981/*
1982 * Create inactive SDP for call hold.
1983 */
1984static pj_status_t create_inactive_sdp(pjsua_call *call,
1985 pjmedia_sdp_session **p_answer)
1986{
1987 pj_status_t status;
1988 pjmedia_sdp_conn *conn;
1989 pjmedia_sdp_attr *attr;
1990 pjmedia_sdp_session *sdp;
1991
1992 /* Create new offer */
1993 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1994 &call->skinfo, &sdp);
1995 if (status != PJ_SUCCESS) {
1996 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1997 return status;
1998 }
1999
2000 /* Get SDP media connection line */
2001 conn = sdp->media[0]->conn;
2002 if (!conn)
2003 conn = sdp->conn;
2004
2005 /* Modify address */
2006 conn->addr = pj_str("0.0.0.0");
2007
2008 /* Remove existing directions attributes */
2009 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2010 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2011 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2012 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2013
2014 /* Add inactive attribute */
2015 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2016 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2017
2018 *p_answer = sdp;
2019
2020 return status;
2021}
2022
2023
2024/*
2025 * Called when session received new offer.
2026 */
2027static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2028 const pjmedia_sdp_session *offer)
2029{
2030 const char *remote_state;
2031 pjsua_call *call;
2032 pjmedia_sdp_conn *conn;
2033 pjmedia_sdp_session *answer;
2034 pj_bool_t is_remote_active;
2035 pj_status_t status;
2036
2037 PJSUA_LOCK();
2038
2039 call = inv->dlg->mod_data[pjsua_var.mod.id];
2040
2041 /*
2042 * See if remote is offering active media (i.e. not on-hold)
2043 */
2044 is_remote_active = PJ_TRUE;
2045
2046 conn = offer->media[0]->conn;
2047 if (!conn)
2048 conn = offer->conn;
2049
2050 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2051 pj_strcmp2(&conn->addr, "0")==0)
2052 {
2053 is_remote_active = PJ_FALSE;
2054
2055 }
2056 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2057 {
2058 is_remote_active = PJ_FALSE;
2059 }
2060
2061 remote_state = (is_remote_active ? "active" : "inactive");
2062
2063 /* Supply candidate answer */
2064 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2065 PJ_LOG(4,(THIS_FILE,
2066 "Call %d: RX new media offer, creating inactive SDP "
2067 "(media in offer is %s)", call->index, remote_state));
2068 status = create_inactive_sdp( call, &answer );
2069 } else {
2070 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2071 call->index));
2072 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2073 call->inv->pool, 1,
2074 &call->skinfo, &answer);
2075 }
2076
2077 if (status != PJ_SUCCESS) {
2078 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2079 PJSUA_UNLOCK();
2080 return;
2081 }
2082
2083 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2084 if (status != PJ_SUCCESS) {
2085 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2086 PJSUA_UNLOCK();
2087 return;
2088 }
2089
2090 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002091}
2092
2093
2094/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002095 * Callback called by event framework when the xfer subscription state
2096 * has changed.
2097 */
2098static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2099{
2100
2101 PJ_UNUSED_ARG(event);
2102
2103 /*
2104 * We're only interested when subscription is terminated, to
2105 * clear the xfer_sub member of the inv_data.
2106 */
2107 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002108 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002109
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002110 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002111 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002112 return;
2113
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002114 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002115 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002116
2117 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2118 }
2119}
2120
2121
2122/*
2123 * Follow transfer (REFER) request.
2124 */
2125static void on_call_transfered( pjsip_inv_session *inv,
2126 pjsip_rx_data *rdata )
2127{
2128 pj_status_t status;
2129 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002130 pjsua_call *existing_call;
2131 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002132 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002133 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002134 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002135 pjsip_generic_string_hdr *refer_sub;
2136 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002137 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002138 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002139 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002140 pjsip_evsub *sub;
2141
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002142 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002143
Benny Prijono26ff9062006-02-21 23:47:00 +00002144 /* Find the Refer-To header */
2145 refer_to = (pjsip_generic_string_hdr*)
2146 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2147
2148 if (refer_to == NULL) {
2149 /* Invalid Request.
2150 * No Refer-To header!
2151 */
2152 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002153 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002154 return;
2155 }
2156
Benny Prijonoc8141a82006-08-20 09:12:19 +00002157 /* Find optional Refer-Sub header */
2158 refer_sub = (pjsip_generic_string_hdr*)
2159 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2160
2161 if (refer_sub) {
2162 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2163 no_refer_sub = PJ_TRUE;
2164 }
2165
2166
Benny Prijono9fc735d2006-05-28 14:58:12 +00002167 /* Notify callback */
2168 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2170 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2171 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002172
2173 if (code < 200)
2174 code = 200;
2175 if (code >= 300) {
2176 /* Application rejects call transfer request */
2177 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2178 return;
2179 }
2180
Benny Prijono26ff9062006-02-21 23:47:00 +00002181 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2182 (int)inv->dlg->remote.info_str.slen,
2183 inv->dlg->remote.info_str.ptr,
2184 (int)refer_to->hvalue.slen,
2185 refer_to->hvalue.ptr));
2186
Benny Prijonoc8141a82006-08-20 09:12:19 +00002187 if (no_refer_sub) {
2188 /*
2189 * Always answer with 200.
2190 */
2191 pjsip_tx_data *tdata;
2192 const pj_str_t str_false = { "false", 5};
2193 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002194
Benny Prijonoc8141a82006-08-20 09:12:19 +00002195 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2196 if (status != PJ_SUCCESS) {
2197 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2198 status);
2199 return;
2200 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002201
Benny Prijonoc8141a82006-08-20 09:12:19 +00002202 /* Add Refer-Sub header */
2203 hdr = (pjsip_hdr*)
2204 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2205 &str_false);
2206 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002207
Benny Prijono26ff9062006-02-21 23:47:00 +00002208
Benny Prijonoc8141a82006-08-20 09:12:19 +00002209 /* Send answer */
2210 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2211 tdata);
2212 if (status != PJ_SUCCESS) {
2213 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2214 status);
2215 return;
2216 }
2217
2218 /* Don't have subscription */
2219 sub = NULL;
2220
2221 } else {
2222 struct pjsip_evsub_user xfer_cb;
2223 pjsip_hdr hdr_list;
2224
2225 /* Init callback */
2226 pj_bzero(&xfer_cb, sizeof(xfer_cb));
2227 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2228
2229 /* Init additional header list to be sent with REFER response */
2230 pj_list_init(&hdr_list);
2231
2232 /* Create transferee event subscription */
2233 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2234 if (status != PJ_SUCCESS) {
2235 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2236 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2237 return;
2238 }
2239
2240 /* If there's Refer-Sub header and the value is "true", send back
2241 * Refer-Sub in the response with value "true" too.
2242 */
2243 if (refer_sub) {
2244 const pj_str_t str_true = { "true", 4 };
2245 pjsip_hdr *hdr;
2246
2247 hdr = (pjsip_hdr*)
2248 pjsip_generic_string_hdr_create(inv->dlg->pool,
2249 &str_refer_sub,
2250 &str_true);
2251 pj_list_push_back(&hdr_list, hdr);
2252
2253 }
2254
2255 /* Accept the REFER request, send 200 (OK). */
2256 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2257
2258 /* Create initial NOTIFY request */
2259 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2260 100, NULL, &tdata);
2261 if (status != PJ_SUCCESS) {
2262 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2263 status);
2264 return;
2265 }
2266
2267 /* Send initial NOTIFY request */
2268 status = pjsip_xfer_send_request( sub, tdata);
2269 if (status != PJ_SUCCESS) {
2270 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2271 return;
2272 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002273 }
2274
2275 /* We're cheating here.
2276 * We need to get a null terminated string from a pj_str_t.
2277 * So grab the pointer from the hvalue and NULL terminate it, knowing
2278 * that the NULL position will be occupied by a newline.
2279 */
2280 uri = refer_to->hvalue.ptr;
2281 uri[refer_to->hvalue.slen] = '\0';
2282
2283 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002284 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002285 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2286 existing_call->user_data, NULL,
2287 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002288 if (status != PJ_SUCCESS) {
2289
Benny Prijonoc8141a82006-08-20 09:12:19 +00002290 /* Notify xferer about the error (if we have subscription) */
2291 if (sub) {
2292 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2293 500, NULL, &tdata);
2294 if (status != PJ_SUCCESS) {
2295 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2296 status);
2297 return;
2298 }
2299 status = pjsip_xfer_send_request(sub, tdata);
2300 if (status != PJ_SUCCESS) {
2301 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2302 status);
2303 return;
2304 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002305 }
2306 return;
2307 }
2308
Benny Prijonoc8141a82006-08-20 09:12:19 +00002309 if (sub) {
2310 /* Put the server subscription in inv_data.
2311 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2312 * reported back to the server subscription.
2313 */
2314 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002315
Benny Prijonoc8141a82006-08-20 09:12:19 +00002316 /* Put the invite_data in the subscription. */
2317 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2318 &pjsua_var.calls[new_call]);
2319 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002320}
2321
2322
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002323
Benny Prijono26ff9062006-02-21 23:47:00 +00002324/*
2325 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002326 * session. We use this to trap:
2327 * - incoming REFER request.
2328 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002329 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002330static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2331 pjsip_transaction *tsx,
2332 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002333{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002334 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2335
2336 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002337
Benny Prijono26ff9062006-02-21 23:47:00 +00002338 if (tsx->role==PJSIP_ROLE_UAS &&
2339 tsx->state==PJSIP_TSX_STATE_TRYING &&
2340 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2341 {
2342 /*
2343 * Incoming REFER request.
2344 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002345 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002346
Benny Prijono26ff9062006-02-21 23:47:00 +00002347 }
Benny Prijonob0808372006-03-02 21:18:58 +00002348 else if (tsx->role==PJSIP_ROLE_UAS &&
2349 tsx->state==PJSIP_TSX_STATE_TRYING &&
2350 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2351 {
2352 /*
2353 * Incoming MESSAGE request!
2354 */
2355 pjsip_rx_data *rdata;
2356 pjsip_msg *msg;
2357 pjsip_accept_hdr *accept_hdr;
2358 pj_status_t status;
2359
2360 rdata = e->body.tsx_state.src.rdata;
2361 msg = rdata->msg_info.msg;
2362
2363 /* Request MUST have message body, with Content-Type equal to
2364 * "text/plain".
2365 */
2366 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2367
2368 pjsip_hdr hdr_list;
2369
2370 pj_list_init(&hdr_list);
2371 pj_list_push_back(&hdr_list, accept_hdr);
2372
2373 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2374 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002375 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002376 return;
2377 }
2378
2379 /* Respond with 200 first, so that remote doesn't retransmit in case
2380 * the UI takes too long to process the message.
2381 */
2382 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2383
2384 /* Process MESSAGE request */
2385 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2386 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002387
Benny Prijonob0808372006-03-02 21:18:58 +00002388 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002389 else if (tsx->role == PJSIP_ROLE_UAC &&
2390 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002391 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002392 /* Handle outgoing pager status */
2393 if (tsx->status_code >= 200) {
2394 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002395
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002396 im_data = tsx->mod_data[pjsua_var.mod.id];
2397 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002398
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002399 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2400 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2401 &im_data->to,
2402 &im_data->body,
2403 im_data->user_data,
2404 tsx->status_code,
2405 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002406 }
Benny Prijonofccab712006-02-22 22:23:22 +00002407 }
Benny Prijono834aee32006-02-19 01:38:06 +00002408 }
Benny Prijono834aee32006-02-19 01:38:06 +00002409
Benny Prijono26ff9062006-02-21 23:47:00 +00002410
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002411 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002412}