blob: e904267097c82475f10827f7082a3cdb81041396 [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;
97 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +000098
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 /* Init calls array. */
100 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
101 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 /* Copy config */
104 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000105
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000107 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
109 inv_cb.on_new_session = &pjsua_call_on_forked;
110 inv_cb.on_media_update = &pjsua_call_on_media_update;
111 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
112 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000113
Benny Prijono275fd682006-03-22 11:59:11 +0000114
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000115 /* Initialize invite session module: */
116 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
117 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
118
119 return status;
120}
121
122
123/*
124 * Start call subsystem.
125 */
126pj_status_t pjsua_call_subsys_start(void)
127{
128 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000129 return PJ_SUCCESS;
130}
131
132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000134 * Get maximum number of calls configured in pjsua.
135 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000136PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000137{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000139}
140
141
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000142/*
143 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000144 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000145PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000146{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000148}
149
150
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151/*
152 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000153 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
155 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000156{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000158
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000160
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000162
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000163 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
164 if (!pjsua_var.calls[i].inv)
165 continue;
166 ids[c] = i;
167 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168 }
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173
174 return PJ_SUCCESS;
175}
176
177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178/*
179 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
182 const pj_str_t *dest_uri,
183 unsigned options,
184 void *user_data,
185 const pjsua_msg_data *msg_data,
186 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000187{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000188 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000189 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000190 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191 pjsua_acc *acc;
192 pjsua_call *call;
193 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000194 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000195 pjsip_tx_data *tdata;
196 pj_status_t status;
197
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 /* Check that account is valid */
200 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201 PJ_EINVAL);
202
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 /* Options must be zero for now */
204 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
205
206 PJSUA_LOCK();
207
208 acc = &pjsua_var.acc[acc_id];
209 if (!acc->valid) {
210 pjsua_perror(THIS_FILE, "Unable to make call because account "
211 "is not valid", PJ_EINVALIDOP);
212 PJSUA_UNLOCK();
213 return PJ_EINVALIDOP;
214 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000215
Benny Prijonoa91a0032006-02-26 21:23:45 +0000216 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
218 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000219 break;
220 }
221
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 if (call_id == pjsua_var.ua_cfg.max_calls) {
223 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
224 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000225 return PJ_ETOOMANY;
226 }
227
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228 call = &pjsua_var.calls[call_id];
229
Benny Prijonoe21e7842006-04-09 16:46:05 +0000230 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000232
Benny Prijonoe21e7842006-04-09 16:46:05 +0000233 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000234 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000235
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000236 /* Create suitable Contact header */
237 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
238 acc_id, dest_uri);
239 if (status != PJ_SUCCESS) {
240 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
241 PJSUA_UNLOCK();
242 return status;
243 }
244
Benny Prijonoe21e7842006-04-09 16:46:05 +0000245 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000246 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000247 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000248 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000249 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000250 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000251 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000252 return status;
253 }
254
255 /* Get media capability from media endpoint: */
256
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000257 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
258 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000259 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000260 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000261 goto on_error;
262 }
263
264 /* Create the INVITE session: */
265
266 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
267 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000268 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000269 goto on_error;
270 }
271
272
273 /* Create and associate our data in the session. */
274
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000275 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000276
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000277 dlg->mod_data[pjsua_var.mod.id] = call;
278 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000279
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000280 /* Attach user data */
281 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000282
283 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000284 if (!pj_list_empty(&acc->route_set))
285 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000286
287
288 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000289 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000290 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000291 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000292 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000293
294
295 /* Create initial INVITE: */
296
297 status = pjsip_inv_invite(inv, &tdata);
298 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000299 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
300 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000301 goto on_error;
302 }
303
304
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000305 /* Add additional headers etc */
306
307 pjsua_process_msg_data( tdata, msg_data);
308
Benny Prijono84126ab2006-02-09 09:30:09 +0000309 /* Send initial INVITE: */
310
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000311 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000312 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000313 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
314 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000315
316 /* Upon failure to send first request, both dialog and invite
317 * session would have been cleared.
318 */
319 inv = NULL;
320 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000321 goto on_error;
322 }
323
Benny Prijono84126ab2006-02-09 09:30:09 +0000324 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000325
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326 ++pjsua_var.call_cnt;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000327
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 if (p_call_id)
329 *p_call_id = call_id;
330
331 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
333 return PJ_SUCCESS;
334
335
336on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000337 if (inv != NULL) {
338 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000339 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000340 pjsip_dlg_terminate(dlg);
341 }
342
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000343 if (call_id != -1) {
344 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000345 }
346
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000348 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000349}
350
351
352/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000353 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000355 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000356pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000357{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000358 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000359 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
360 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
361 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000362 pjsip_tx_data *response = NULL;
363 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000364 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 int acc_id;
366 pjsua_call *call;
367 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000368 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000369 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000370
Benny Prijono26ff9062006-02-21 23:47:00 +0000371 /* Don't want to handle anything but INVITE */
372 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
373 return PJ_FALSE;
374
375 /* Don't want to handle anything that's already associated with
376 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000377 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000378 if (dlg || tsx)
379 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000380
Benny Prijono84126ab2006-02-09 09:30:09 +0000381
Benny Prijono26ff9062006-02-21 23:47:00 +0000382 /* Verify that we can handle the request. */
383 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 pjsua_var.endpt, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000385 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000386
Benny Prijono26ff9062006-02-21 23:47:00 +0000387 /*
388 * No we can't handle the incoming INVITE request.
389 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000390
Benny Prijono26ff9062006-02-21 23:47:00 +0000391 if (response) {
392 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000393
Benny Prijono26ff9062006-02-21 23:47:00 +0000394 pjsip_get_response_addr(response->pool, rdata, &res_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000395 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
Benny Prijono26ff9062006-02-21 23:47:00 +0000396 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000397
398 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000399
Benny Prijono26ff9062006-02-21 23:47:00 +0000400 /* Respond with 500 (Internal Server Error) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000401 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000402 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000403 }
404
Benny Prijono26ff9062006-02-21 23:47:00 +0000405 return PJ_TRUE;
406 }
407
408
409 /*
410 * Yes we can handle the incoming INVITE request.
411 */
412
413 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000414 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
415 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000416 break;
417 }
418
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000419 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
420 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000421 PJSIP_SC_BUSY_HERE, NULL,
422 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000423 PJ_LOG(2,(THIS_FILE,
424 "Unable to accept incoming call (too many calls)"));
Benny Prijono84126ab2006-02-09 09:30:09 +0000425 return PJ_TRUE;
426 }
427
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 /* Clear call descriptor */
429 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000430
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 call = &pjsua_var.calls[call_id];
432
433 /* Mark call start time. */
434 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000435
Benny Prijono26ff9062006-02-21 23:47:00 +0000436 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
438 rdata->tp_info.pool, 1,
439 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000440 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000441 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000442 NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000443 return PJ_TRUE;
444 }
445
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000447 * Get which account is most likely to be associated with this incoming
448 * call. We need the account to find which contact URI to put for
449 * the call.
450 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000452
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000453 /* Get suitable Contact header */
454 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
455 acc_id, rdata);
456 if (status != PJ_SUCCESS) {
457 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
458 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
459 NULL, NULL);
460 return PJ_TRUE;
461 }
462
Benny Prijono26ff9062006-02-21 23:47:00 +0000463 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000464 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000465 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000466 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000468 NULL, NULL);
469
Benny Prijono26ff9062006-02-21 23:47:00 +0000470 return PJ_TRUE;
471 }
472
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000473 /* Set credentials */
474 if (pjsua_var.acc[acc_id].cred_cnt) {
475 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
476 pjsua_var.acc[acc_id].cred_cnt,
477 pjsua_var.acc[acc_id].cred);
478 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000479
480 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000481 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
482 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000483 pjsip_hdr hdr_list;
484 pjsip_warning_hdr *w;
485
486 w = pjsip_warning_hdr_create_from_status(dlg->pool,
487 pjsip_endpt_name(pjsua_var.endpt),
488 status);
489 pj_list_init(&hdr_list);
490 pj_list_push_back(&hdr_list, w);
491
492 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
493
494 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000495 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000496 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000497 return PJ_TRUE;
498 }
499
500
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000501 /* Create and attach pjsua_var data to the dialog: */
502 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 dlg->mod_data[pjsua_var.mod.id] = call;
505 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000506
507
Benny Prijono64f851e2006-02-23 13:49:28 +0000508 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000509 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000510 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000511 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000512 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000513 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
514 status);
515
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
517 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000518 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000519
520 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000521 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000522 if (status != PJ_SUCCESS)
523 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000524 }
525
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000527
Benny Prijono105217f2006-03-06 16:25:59 +0000528
Benny Prijono8b1889b2006-06-06 18:40:40 +0000529 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000530 if (pjsua_var.ua_cfg.cb.on_incoming_call)
531 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000532
Benny Prijono26ff9062006-02-21 23:47:00 +0000533 /* This INVITE request has been handled. */
534 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000535}
536
537
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538
539/*
540 * Check if the specified call has active INVITE session and the INVITE
541 * session has not been disconnected.
542 */
543PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
544{
545 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
546 PJ_EINVAL);
547 return pjsua_var.calls[call_id].inv != NULL &&
548 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
549}
550
551
552/*
553 * Check if call has an active media session.
554 */
555PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
556{
557 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
558 PJ_EINVAL);
559 return pjsua_var.calls[call_id].session != NULL;
560}
561
562
563/*
564 * Get the conference port identification associated with the call.
565 */
566PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
567{
568 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
569 PJ_EINVAL);
570 return pjsua_var.calls[call_id].conf_slot;
571}
572
573
574/*
575 * Obtain detail information about the specified call.
576 */
577PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
578 pjsua_call_info *info)
579{
580 pjsua_call *call;
581
582 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
583 PJ_EINVAL);
584
Benny Prijonoac623b32006-07-03 15:19:31 +0000585 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586
587 PJSUA_LOCK();
588
589 call = &pjsua_var.calls[call_id];
590
591 if (call->inv == NULL) {
592 PJSUA_UNLOCK();
593 return PJ_SUCCESS;
594 }
595
596 pjsip_dlg_inc_lock(call->inv->dlg);
597
598
599 /* id and role */
600 info->id = call_id;
601 info->role = call->inv->role;
602
603 /* local info */
604 info->local_info.ptr = info->buf_.local_info;
605 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
606 sizeof(info->buf_.local_info));
607
608 /* local contact */
609 info->local_contact.ptr = info->buf_.local_contact;
610 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
611 call->inv->dlg->local.contact->uri,
612 info->local_contact.ptr,
613 sizeof(info->buf_.local_contact));
614
615 /* remote info */
616 info->remote_info.ptr = info->buf_.remote_info;
617 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
618 sizeof(info->buf_.remote_info));
619
620 /* remote contact */
621 if (call->inv->dlg->remote.contact) {
622 int len;
623 info->remote_contact.ptr = info->buf_.remote_contact;
624 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
625 call->inv->dlg->remote.contact->uri,
626 info->remote_contact.ptr,
627 sizeof(info->buf_.remote_contact));
628 if (len < 0) len = 0;
629 info->remote_contact.slen = len;
630 } else {
631 info->remote_contact.slen = 0;
632 }
633
634 /* call id */
635 info->call_id.ptr = info->buf_.call_id;
636 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
637 sizeof(info->buf_.call_id));
638
639 /* state, state_text */
640 info->state = call->inv->state;
641 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
642
643 /* If call is disconnected, set the last_status from the cause code */
644 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
645 /* last_status, last_status_text */
646 info->last_status = call->inv->cause;
647
648 info->last_status_text.ptr = info->buf_.last_status_text;
649 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
650 sizeof(info->buf_.last_status_text));
651 } else {
652 /* last_status, last_status_text */
653 info->last_status = call->last_code;
654
655 info->last_status_text.ptr = info->buf_.last_status_text;
656 pj_strncpy(&info->last_status_text, &call->last_text,
657 sizeof(info->buf_.last_status_text));
658 }
659
660 /* media status and dir */
661 info->media_status = call->media_st;
662 info->media_dir = call->media_dir;
663
664
665 /* conference slot number */
666 info->conf_slot = call->conf_slot;
667
668 /* calculate duration */
669 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
670
671 info->total_duration = call->dis_time;
672 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
673
674 if (call->conn_time.sec) {
675 info->connect_duration = call->dis_time;
676 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
677 }
678
679 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
680
681 pj_gettimeofday(&info->total_duration);
682 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
683
684 pj_gettimeofday(&info->connect_duration);
685 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
686
687 } else {
688 pj_gettimeofday(&info->total_duration);
689 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
690 }
691
692 pjsip_dlg_dec_lock(call->inv->dlg);
693 PJSUA_UNLOCK();
694
695 return PJ_SUCCESS;
696}
697
698
699/*
700 * Attach application specific data to the call.
701 */
702PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
703 void *user_data)
704{
705 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
706 PJ_EINVAL);
707 pjsua_var.calls[call_id].user_data = user_data;
708
709 return PJ_SUCCESS;
710}
711
712
713/*
714 * Get user data attached to the call.
715 */
716PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
717{
718 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
719 NULL);
720 return pjsua_var.calls[call_id].user_data;
721}
722
723
724/*
725 * Send response to incoming INVITE request.
726 */
727PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
728 unsigned code,
729 const pj_str_t *reason,
730 const pjsua_msg_data *msg_data)
731{
732 pjsua_call *call;
733 pjsip_tx_data *tdata;
734 pj_status_t status;
735
736 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
737 PJ_EINVAL);
738
739 PJSUA_LOCK();
740
741 call = &pjsua_var.calls[call_id];
742
743 if (call->inv == NULL) {
744 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id));
745 PJSUA_UNLOCK();
746 return PJSIP_ESESSIONTERMINATED;
747 }
748
Benny Prijono2e507c22006-06-23 15:04:11 +0000749 if (call->res_time.sec == 0)
750 pj_gettimeofday(&call->res_time);
751
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000752 /* Create response message */
753 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
754 if (status != PJ_SUCCESS) {
755 pjsua_perror(THIS_FILE, "Error creating response",
756 status);
757 PJSUA_UNLOCK();
758 return status;
759 }
760
761 /* Add additional headers etc */
762 pjsua_process_msg_data( tdata, msg_data);
763
764 /* Send the message */
765 status = pjsip_inv_send_msg(call->inv, tdata);
766 if (status != PJ_SUCCESS)
767 pjsua_perror(THIS_FILE, "Error sending response",
768 status);
769
770 PJSUA_UNLOCK();
771
772 return status;
773}
774
775
776/*
777 * Hangup call by using method that is appropriate according to the
778 * call state.
779 */
780PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
781 unsigned code,
782 const pj_str_t *reason,
783 const pjsua_msg_data *msg_data)
784{
785 pjsua_call *call;
786 pj_status_t status;
787 pjsip_tx_data *tdata;
788
789
790 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
791 PJ_EINVAL);
792
793 PJSUA_LOCK();
794
795 call = &pjsua_var.calls[call_id];
796
797 if (!call->inv) {
798 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
799 PJSUA_UNLOCK();
800 return PJ_EINVAL;
801 }
802
803 if (code==0) {
804 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
805 code = PJSIP_SC_OK;
806 else if (call->inv->role == PJSIP_ROLE_UAS)
807 code = PJSIP_SC_DECLINE;
808 else
809 code = PJSIP_SC_REQUEST_TERMINATED;
810 }
811
812 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
813 if (status != PJ_SUCCESS) {
814 pjsua_perror(THIS_FILE,
815 "Failed to create end session message",
816 status);
817 PJSUA_UNLOCK();
818 return status;
819 }
820
821 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
822 * as p_tdata when INVITE transaction has not been answered
823 * with any provisional responses.
824 */
825 if (tdata == NULL) {
826 PJSUA_UNLOCK();
827 return PJ_SUCCESS;
828 }
829
830 /* Add additional headers etc */
831 pjsua_process_msg_data( tdata, msg_data);
832
833 /* Send the message */
834 status = pjsip_inv_send_msg(call->inv, tdata);
835 if (status != PJ_SUCCESS) {
836 pjsua_perror(THIS_FILE,
837 "Failed to send end session message",
838 status);
839 PJSUA_UNLOCK();
840 return status;
841 }
842
843 PJSUA_UNLOCK();
844
845 return PJ_SUCCESS;
846}
847
848
849/*
850 * Put the specified call on hold.
851 */
852PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
853 const pjsua_msg_data *msg_data)
854{
855 pjmedia_sdp_session *sdp;
856 pjsua_call *call;
857 pjsip_tx_data *tdata;
858 pj_status_t status;
859
860 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
861 PJ_EINVAL);
862
863 PJSUA_LOCK();
864
865 call = &pjsua_var.calls[call_id];
866
867 if (!call->inv) {
868 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
869 PJSUA_UNLOCK();
870 return PJSIP_ESESSIONTERMINATED;
871 }
872
873 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
874 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
875 PJSUA_UNLOCK();
876 return PJSIP_ESESSIONSTATE;
877 }
878
879 status = create_inactive_sdp(call, &sdp);
880 if (status != PJ_SUCCESS) {
881 PJSUA_UNLOCK();
882 return status;
883 }
884
885 /* Create re-INVITE with new offer */
886 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
887 if (status != PJ_SUCCESS) {
888 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
889 PJSUA_UNLOCK();
890 return status;
891 }
892
893 /* Add additional headers etc */
894 pjsua_process_msg_data( tdata, msg_data);
895
896 /* Send the request */
897 status = pjsip_inv_send_msg( call->inv, tdata);
898 if (status != PJ_SUCCESS) {
899 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
900 PJSUA_UNLOCK();
901 return status;
902 }
903
904 PJSUA_UNLOCK();
905
906 return PJ_SUCCESS;
907}
908
909
910/*
911 * Send re-INVITE (to release hold).
912 */
913PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
914 pj_bool_t unhold,
915 const pjsua_msg_data *msg_data)
916{
917 pjmedia_sdp_session *sdp;
918 pjsip_tx_data *tdata;
919 pjsua_call *call;
920 pj_status_t status;
921
922
923 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
924 PJ_EINVAL);
925
926 PJSUA_LOCK();
927
928 call = &pjsua_var.calls[call_id];
929
930 if (!call->inv) {
931 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
932 PJSUA_UNLOCK();
933 return PJSIP_ESESSIONTERMINATED;
934 }
935
936
937 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
938 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
939 PJSUA_UNLOCK();
940 return PJSIP_ESESSIONSTATE;
941 }
942
943 /* Create SDP */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000944 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000945 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
946 1, &call->skinfo, &sdp);
947 if (status != PJ_SUCCESS) {
948 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
949 status);
950 PJSUA_UNLOCK();
951 return status;
952 }
953
954 /* Create re-INVITE with new offer */
955 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
956 if (status != PJ_SUCCESS) {
957 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
958 PJSUA_UNLOCK();
959 return status;
960 }
961
962 /* Add additional headers etc */
963 pjsua_process_msg_data( tdata, msg_data);
964
965 /* Send the request */
966 status = pjsip_inv_send_msg( call->inv, tdata);
967 if (status != PJ_SUCCESS) {
968 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
969 PJSUA_UNLOCK();
970 return status;
971 }
972
973 PJSUA_UNLOCK();
974
975 return PJ_SUCCESS;
976}
977
978
979/*
980 * Initiate call transfer to the specified address.
981 */
982PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
983 const pj_str_t *dest,
984 const pjsua_msg_data *msg_data)
985{
986 pjsip_evsub *sub;
987 pjsip_tx_data *tdata;
988 pjsua_call *call;
989 pj_status_t status;
990
991
992 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
993 PJ_EINVAL);
994
995 PJSUA_LOCK();
996
997 call = &pjsua_var.calls[call_id];
998
999 if (!call->inv) {
1000 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1001 PJSUA_UNLOCK();
1002 return PJSIP_ESESSIONTERMINATED;
1003 }
1004
1005 /* Create xfer client subscription.
1006 * We're not interested in knowing the transfer result, so we
1007 * put NULL as the callback.
1008 */
1009 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
1010 if (status != PJ_SUCCESS) {
1011 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
1012 PJSUA_UNLOCK();
1013 return status;
1014 }
1015
1016 /*
1017 * Create REFER request.
1018 */
1019 status = pjsip_xfer_initiate(sub, dest, &tdata);
1020 if (status != PJ_SUCCESS) {
1021 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1022 PJSUA_UNLOCK();
1023 return status;
1024 }
1025
1026 /* Add additional headers etc */
1027 pjsua_process_msg_data( tdata, msg_data);
1028
1029 /* Send. */
1030 status = pjsip_xfer_send_request(sub, tdata);
1031 if (status != PJ_SUCCESS) {
1032 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1033 PJSUA_UNLOCK();
1034 return status;
1035 }
1036
1037 /* For simplicity (that's what this program is intended to be!),
1038 * leave the original invite session as it is. More advanced application
1039 * may want to hold the INVITE, or terminate the invite, or whatever.
1040 */
1041
1042 PJSUA_UNLOCK();
1043
1044 return PJ_SUCCESS;
1045
1046}
1047
1048
1049/*
1050 * Send DTMF digits to remote using RFC 2833 payload formats.
1051 */
1052PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1053 const pj_str_t *digits)
1054{
1055 pjsua_call *call;
1056 pj_status_t status;
1057
1058 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1059 PJ_EINVAL);
1060
1061 PJSUA_LOCK();
1062
1063 call = &pjsua_var.calls[call_id];
1064
1065 if (!call->session) {
1066 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1067 PJSUA_UNLOCK();
1068 return PJ_EINVALIDOP;
1069 }
1070
1071 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1072
1073 PJSUA_UNLOCK();
1074
1075 return status;
1076}
1077
1078
1079/**
1080 * Send instant messaging inside INVITE session.
1081 */
1082PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1083 const pj_str_t *mime_type,
1084 const pj_str_t *content,
1085 const pjsua_msg_data *msg_data,
1086 void *user_data)
1087{
1088 pjsua_call *call;
1089 const pj_str_t mime_text_plain = pj_str("text/plain");
1090 pjsip_media_type ctype;
1091 pjsua_im_data *im_data;
1092 pjsip_tx_data *tdata;
1093 pj_status_t status;
1094
1095
1096 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1097 PJ_EINVAL);
1098
1099 PJSUA_LOCK();
1100
1101 call = &pjsua_var.calls[call_id];
1102
1103 if (!call->inv) {
1104 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1105 PJSUA_UNLOCK();
1106 return PJSIP_ESESSIONTERMINATED;
1107 }
1108
1109 /* Lock dialog. */
1110 pjsip_dlg_inc_lock(call->inv->dlg);
1111
1112 /* Set default media type if none is specified */
1113 if (mime_type == NULL) {
1114 mime_type = &mime_text_plain;
1115 }
1116
1117 /* Create request message. */
1118 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1119 -1, &tdata);
1120 if (status != PJ_SUCCESS) {
1121 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1122 goto on_return;
1123 }
1124
1125 /* Add accept header. */
1126 pjsip_msg_add_hdr( tdata->msg,
1127 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1128
1129 /* Parse MIME type */
1130 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1131
1132 /* Create "text/plain" message body. */
1133 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1134 &ctype.subtype, content);
1135 if (tdata->msg->body == NULL) {
1136 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1137 pjsip_tx_data_dec_ref(tdata);
1138 goto on_return;
1139 }
1140
1141 /* Add additional headers etc */
1142 pjsua_process_msg_data( tdata, msg_data);
1143
1144 /* Create IM data and attach to the request. */
1145 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1146 im_data->acc_id = call->acc_id;
1147 im_data->call_id = call_id;
1148 im_data->to = call->inv->dlg->remote.info_str;
1149 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1150 im_data->user_data = user_data;
1151
1152
1153 /* Send the request. */
1154 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1155 pjsua_var.mod.id, im_data);
1156 if (status != PJ_SUCCESS) {
1157 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1158 goto on_return;
1159 }
1160
1161on_return:
1162 pjsip_dlg_dec_lock(call->inv->dlg);
1163 PJSUA_UNLOCK();
1164 return status;
1165}
1166
1167
1168/*
1169 * Send IM typing indication inside INVITE session.
1170 */
1171PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1172 pj_bool_t is_typing,
1173 const pjsua_msg_data*msg_data)
1174{
1175 pjsua_call *call;
1176 pjsip_tx_data *tdata;
1177 pj_status_t status;
1178
1179
1180 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1181 PJ_EINVAL);
1182
1183 PJSUA_LOCK();
1184
1185 call = &pjsua_var.calls[call_id];
1186
1187 if (!call->inv) {
1188 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1189 PJSUA_UNLOCK();
1190 return PJSIP_ESESSIONTERMINATED;
1191 }
1192
1193 /* Lock dialog. */
1194 pjsip_dlg_inc_lock(call->inv->dlg);
1195
1196 /* Create request message. */
1197 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1198 -1, &tdata);
1199 if (status != PJ_SUCCESS) {
1200 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1201 goto on_return;
1202 }
1203
1204 /* Create "application/im-iscomposing+xml" msg body. */
1205 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1206 NULL, NULL, -1);
1207
1208 /* Add additional headers etc */
1209 pjsua_process_msg_data( tdata, msg_data);
1210
1211 /* Send the request. */
1212 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1213 if (status != PJ_SUCCESS) {
1214 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1215 goto on_return;
1216 }
1217
1218on_return:
1219 pjsip_dlg_dec_lock(call->inv->dlg);
1220 PJSUA_UNLOCK();
1221 return status;
1222}
1223
1224
1225/*
1226 * Terminate all calls.
1227 */
1228PJ_DEF(void) pjsua_call_hangup_all(void)
1229{
1230 unsigned i;
1231
1232 PJSUA_LOCK();
1233
1234 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1235 if (pjsua_var.calls[i].inv)
1236 pjsua_call_hangup(i, 0, NULL, NULL);
1237 }
1238
1239 PJSUA_UNLOCK();
1240}
1241
1242
1243static const char *good_number(char *buf, pj_int32_t val)
1244{
1245 if (val < 1000) {
1246 pj_ansi_sprintf(buf, "%d", val);
1247 } else if (val < 1000000) {
1248 pj_ansi_sprintf(buf, "%d.%dK",
1249 val / 1000,
1250 (val % 1000) / 100);
1251 } else {
1252 pj_ansi_sprintf(buf, "%d.%02dM",
1253 val / 1000000,
1254 (val % 1000000) / 10000);
1255 }
1256
1257 return buf;
1258}
1259
1260
1261/* Dump media session */
1262static void dump_media_session(const char *indent,
1263 char *buf, unsigned maxlen,
1264 pjmedia_session *session)
1265{
1266 unsigned i;
1267 char *p = buf, *end = buf+maxlen;
1268 int len;
1269 pjmedia_session_info info;
1270
1271 pjmedia_session_get_info(session, &info);
1272
1273 for (i=0; i<info.stream_cnt; ++i) {
1274 pjmedia_rtcp_stat stat;
1275 const char *rem_addr;
1276 int rem_port;
1277 const char *dir;
1278 char last_update[40];
1279 char packets[16], bytes[16], ipbytes[16];
1280 pj_time_val now;
1281
1282 pjmedia_session_get_stream_stat(session, i, &stat);
1283 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1284 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1285
1286 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1287 dir = "sendonly";
1288 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1289 dir = "recvonly";
1290 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1291 dir = "sendrecv";
1292 else
1293 dir = "inactive";
1294
1295
1296 len = pj_ansi_snprintf(buf, end-p,
1297 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1298 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001299 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001300 info.stream_info[i].fmt.encoding_name.ptr,
1301 info.stream_info[i].fmt.clock_rate / 1000,
1302 dir,
1303 rem_addr, rem_port);
1304 if (len < 1 || len > end-p) {
1305 *p = '\0';
1306 return;
1307 }
1308
1309 p += len;
1310 *p++ = '\n';
1311 *p = '\0';
1312
1313 if (stat.rx.update_cnt == 0)
1314 strcpy(last_update, "never");
1315 else {
1316 pj_gettimeofday(&now);
1317 PJ_TIME_VAL_SUB(now, stat.rx.update);
1318 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1319 now.sec / 3600,
1320 (now.sec % 3600) / 60,
1321 now.sec % 60,
1322 now.msec);
1323 }
1324
1325 len = pj_ansi_snprintf(p, end-p,
1326 "%s RX pt=%d, stat last update: %s\n"
1327 "%s total %spkt %sB (%sB +IP hdr)\n"
1328 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1329 "%s (msec) min avg max last\n"
1330 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1331 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1332 indent, info.stream_info[i].fmt.pt,
1333 last_update,
1334 indent,
1335 good_number(packets, stat.rx.pkt),
1336 good_number(bytes, stat.rx.bytes),
1337 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
1338 indent,
1339 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001340 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001342 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001343 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001344 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345 indent, indent,
1346 stat.rx.loss_period.min / 1000.0,
1347 stat.rx.loss_period.avg / 1000.0,
1348 stat.rx.loss_period.max / 1000.0,
1349 stat.rx.loss_period.last / 1000.0,
1350 indent,
1351 stat.rx.jitter.min / 1000.0,
1352 stat.rx.jitter.avg / 1000.0,
1353 stat.rx.jitter.max / 1000.0,
1354 stat.rx.jitter.last / 1000.0,
1355 ""
1356 );
1357
1358 if (len < 1 || len > end-p) {
1359 *p = '\0';
1360 return;
1361 }
1362
1363 p += len;
1364 *p++ = '\n';
1365 *p = '\0';
1366
1367 if (stat.tx.update_cnt == 0)
1368 strcpy(last_update, "never");
1369 else {
1370 pj_gettimeofday(&now);
1371 PJ_TIME_VAL_SUB(now, stat.tx.update);
1372 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1373 now.sec / 3600,
1374 (now.sec % 3600) / 60,
1375 now.sec % 60,
1376 now.msec);
1377 }
1378
1379 len = pj_ansi_snprintf(p, end-p,
1380 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
1381 "%s total %spkt %sB (%sB +IP hdr)\n"
1382 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1383 "%s (msec) min avg max last\n"
1384 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1385 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1386 indent,
1387 info.stream_info[i].tx_pt,
1388 info.stream_info[i].param->info.frm_ptime *
1389 info.stream_info[i].param->setting.frm_per_pkt,
1390 last_update,
1391
1392 indent,
1393 good_number(packets, stat.tx.pkt),
1394 good_number(bytes, stat.tx.bytes),
1395 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
1396
1397 indent,
1398 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001399 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001401 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001403 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
1405 indent, indent,
1406 stat.tx.loss_period.min / 1000.0,
1407 stat.tx.loss_period.avg / 1000.0,
1408 stat.tx.loss_period.max / 1000.0,
1409 stat.tx.loss_period.last / 1000.0,
1410 indent,
1411 stat.tx.jitter.min / 1000.0,
1412 stat.tx.jitter.avg / 1000.0,
1413 stat.tx.jitter.max / 1000.0,
1414 stat.tx.jitter.last / 1000.0,
1415 ""
1416 );
1417
1418 if (len < 1 || len > end-p) {
1419 *p = '\0';
1420 return;
1421 }
1422
1423 p += len;
1424 *p++ = '\n';
1425 *p = '\0';
1426
1427 len = pj_ansi_snprintf(p, end-p,
1428 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1429 indent,
1430 stat.rtt.min / 1000.0,
1431 stat.rtt.avg / 1000.0,
1432 stat.rtt.max / 1000.0,
1433 stat.rtt.last / 1000.0
1434 );
1435 if (len < 1 || len > end-p) {
1436 *p = '\0';
1437 return;
1438 }
1439
1440 p += len;
1441 *p++ = '\n';
1442 *p = '\0';
1443 }
1444}
1445
1446
1447/* Print call info */
1448static void print_call(const char *title,
1449 int call_id,
1450 char *buf, pj_size_t size)
1451{
1452 int len;
1453 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1454 pjsip_dialog *dlg = inv->dlg;
1455 char userinfo[128];
1456
1457 /* Dump invite sesion info. */
1458
1459 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1460 if (len < 1)
1461 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1462 else
1463 userinfo[len] = '\0';
1464
1465 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1466 title,
1467 pjsip_inv_state_name(inv->state),
1468 userinfo);
1469 if (len < 1 || len >= (int)size) {
1470 pj_ansi_strcpy(buf, "<--uri too long-->");
1471 len = 18;
1472 } else
1473 buf[len] = '\0';
1474}
1475
1476
1477/*
1478 * Dump call and media statistics to string.
1479 */
1480PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1481 pj_bool_t with_media,
1482 char *buffer,
1483 unsigned maxlen,
1484 const char *indent)
1485{
1486 pjsua_call *call;
1487 pj_time_val duration, res_delay, con_delay;
1488 char tmp[128];
1489 char *p, *end;
1490 int len;
1491
1492 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1493 PJ_EINVAL);
1494
1495 PJSUA_LOCK();
1496
1497 call = &pjsua_var.calls[call_id];
1498
1499 *buffer = '\0';
1500 p = buffer;
1501 end = buffer + maxlen;
1502 len = 0;
1503
1504 if (call->inv == NULL) {
1505 PJSUA_UNLOCK();
1506 return PJ_EINVALIDOP;
1507 }
1508
1509 print_call(indent, call_id, tmp, sizeof(tmp));
1510
1511 len = pj_ansi_strlen(tmp);
1512 pj_ansi_strcpy(buffer, tmp);
1513
1514 p += len;
1515 *p++ = '\r';
1516 *p++ = '\n';
1517
1518 /* Calculate call duration */
1519 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1520 pj_gettimeofday(&duration);
1521 PJ_TIME_VAL_SUB(duration, call->conn_time);
1522 con_delay = call->conn_time;
1523 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1524 } else {
1525 duration.sec = duration.msec = 0;
1526 con_delay.sec = con_delay.msec = 0;
1527 }
1528
1529 /* Calculate first response delay */
1530 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1531 res_delay = call->res_time;
1532 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1533 } else {
1534 res_delay.sec = res_delay.msec = 0;
1535 }
1536
1537 /* Print duration */
1538 len = pj_ansi_snprintf(p, end-p,
1539 "%s Call time: %02dh:%02dm:%02ds, "
1540 "1st res in %d ms, conn in %dms",
1541 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001542 (int)(duration.sec / 3600),
1543 (int)((duration.sec % 3600)/60),
1544 (int)(duration.sec % 60),
1545 (int)PJ_TIME_VAL_MSEC(res_delay),
1546 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001547
1548 if (len > 0 && len < end-p) {
1549 p += len;
1550 *p++ = '\n';
1551 *p = '\0';
1552 }
1553
1554 /* Dump session statistics */
1555 if (with_media && call->session)
1556 dump_media_session(indent, p, end-p, call->session);
1557
1558 PJSUA_UNLOCK();
1559
1560 return PJ_SUCCESS;
1561}
1562
1563
1564/*
1565 * Destroy the call's media
1566 */
1567static pj_status_t call_destroy_media(int call_id)
1568{
1569 pjsua_call *call = &pjsua_var.calls[call_id];
1570
1571 if (call->conf_slot != PJSUA_INVALID_ID) {
1572 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1573 call->conf_slot = PJSUA_INVALID_ID;
1574 }
1575
1576 if (call->session) {
1577 /* Destroy session (this will also close RTP/RTCP sockets). */
1578 pjmedia_session_destroy(call->session);
1579 call->session = NULL;
1580
1581 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1582 call_id));
1583
1584 }
1585
1586 call->media_st = PJSUA_CALL_MEDIA_NONE;
1587
1588 return PJ_SUCCESS;
1589}
1590
1591
Benny Prijono84126ab2006-02-09 09:30:09 +00001592/*
1593 * This callback receives notification from invite session when the
1594 * session state has changed.
1595 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001596static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1597 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001598{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001599 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001600
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601 PJSUA_LOCK();
1602
1603 call = inv->dlg->mod_data[pjsua_var.mod.id];
1604
1605 if (!call) {
1606 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001607 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001608 }
1609
Benny Prijonoe21e7842006-04-09 16:46:05 +00001610
1611 /* Get call times */
1612 switch (inv->state) {
1613 case PJSIP_INV_STATE_EARLY:
1614 case PJSIP_INV_STATE_CONNECTING:
1615 if (call->res_time.sec == 0)
1616 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001617 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 pj_strncpy(&call->last_text,
1619 &e->body.tsx_state.tsx->status_text,
1620 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001621 break;
1622 case PJSIP_INV_STATE_CONFIRMED:
1623 pj_gettimeofday(&call->conn_time);
1624 break;
1625 case PJSIP_INV_STATE_DISCONNECTED:
1626 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001627 if (call->res_time.sec == 0)
1628 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001629 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1630 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 pj_strncpy(&call->last_text,
1632 &e->body.tsx_state.tsx->status_text,
1633 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001634 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001635 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001636 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001637 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001638 pj_strncpy(&call->last_text,
1639 &e->body.tsx_state.tsx->status_text,
1640 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001641 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001642 }
1643
Benny Prijono26ff9062006-02-21 23:47:00 +00001644 /* If this is an outgoing INVITE that was created because of
1645 * REFER/transfer, send NOTIFY to transferer.
1646 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001647 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001648 int st_code = -1;
1649 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1650
1651
Benny Prijonoa91a0032006-02-26 21:23:45 +00001652 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001653 case PJSIP_INV_STATE_NULL:
1654 case PJSIP_INV_STATE_CALLING:
1655 /* Do nothing */
1656 break;
1657
1658 case PJSIP_INV_STATE_EARLY:
1659 case PJSIP_INV_STATE_CONNECTING:
1660 st_code = e->body.tsx_state.tsx->status_code;
1661 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1662 break;
1663
1664 case PJSIP_INV_STATE_CONFIRMED:
1665 /* When state is confirmed, send the final 200/OK and terminate
1666 * subscription.
1667 */
1668 st_code = e->body.tsx_state.tsx->status_code;
1669 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1670 break;
1671
1672 case PJSIP_INV_STATE_DISCONNECTED:
1673 st_code = e->body.tsx_state.tsx->status_code;
1674 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1675 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001676
Benny Prijono8b1889b2006-06-06 18:40:40 +00001677 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001678 /* Nothing to do. Just to keep gcc from complaining about
1679 * unused enums.
1680 */
1681 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001682 }
1683
1684 if (st_code != -1) {
1685 pjsip_tx_data *tdata;
1686 pj_status_t status;
1687
Benny Prijonoa91a0032006-02-26 21:23:45 +00001688 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001689 ev_state, st_code,
1690 NULL, &tdata);
1691 if (status != PJ_SUCCESS) {
1692 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1693 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001694 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001695 if (status != PJ_SUCCESS) {
1696 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1697 }
1698 }
1699 }
1700 }
1701
Benny Prijono84126ab2006-02-09 09:30:09 +00001702
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703 if (pjsua_var.ua_cfg.cb.on_call_state)
1704 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001705
1706 /* call->inv may be NULL now */
1707
Benny Prijono84126ab2006-02-09 09:30:09 +00001708 /* Destroy media session when invite session is disconnected. */
1709 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001710
Benny Prijonoa91a0032006-02-26 21:23:45 +00001711 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001712
Benny Prijono275fd682006-03-22 11:59:11 +00001713 if (call)
1714 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001715
Benny Prijono105217f2006-03-06 16:25:59 +00001716 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001717 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001718 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001719 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001720
1721 PJSUA_UNLOCK();
1722}
1723
1724/*
1725 * This callback is called by invite session framework when UAC session
1726 * has forked.
1727 */
1728static void pjsua_call_on_forked( pjsip_inv_session *inv,
1729 pjsip_event *e)
1730{
1731 PJ_UNUSED_ARG(inv);
1732 PJ_UNUSED_ARG(e);
1733
1734 PJ_TODO(HANDLE_FORKED_DIALOG);
1735}
1736
1737
1738/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00001739 * Disconnect call upon error.
1740 */
1741static void call_disconnect( pjsip_inv_session *inv,
1742 int code )
1743{
1744 pjsip_tx_data *tdata;
1745 pj_status_t status;
1746
1747 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
1748 if (status == PJ_SUCCESS)
1749 pjsip_inv_send_msg(inv, tdata);
1750}
1751
1752/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 * Callback to be called when SDP offer/answer negotiation has just completed
1754 * in the session. This function will start/update media if negotiation
1755 * has succeeded.
1756 */
1757static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1758 pj_status_t status)
1759{
1760 int prev_media_st = 0;
1761 pjsua_call *call;
1762 pjmedia_session_info sess_info;
1763 const pjmedia_sdp_session *local_sdp;
1764 const pjmedia_sdp_session *remote_sdp;
1765 pjmedia_port *media_port;
1766 pj_str_t port_name;
1767 char tmp[PJSIP_MAX_URL_SIZE];
1768
1769 PJSUA_LOCK();
1770
1771 call = inv->dlg->mod_data[pjsua_var.mod.id];
1772
1773 if (status != PJ_SUCCESS) {
1774
1775 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1776
1777 /* Disconnect call if we're not in the middle of initializing an
1778 * UAS dialog and if this is not a re-INVITE
1779 */
1780 if (inv->state != PJSIP_INV_STATE_NULL &&
1781 inv->state != PJSIP_INV_STATE_CONFIRMED)
1782 {
1783 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1784 }
1785
1786 PJSUA_UNLOCK();
1787 return;
1788 }
1789
1790 /* Destroy existing media session, if any. */
1791
1792 if (call) {
1793 prev_media_st = call->media_st;
1794 call_destroy_media(call->index);
1795 }
1796
1797 /* Get local and remote SDP */
1798
1799 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1800 if (status != PJ_SUCCESS) {
1801 pjsua_perror(THIS_FILE,
1802 "Unable to retrieve currently active local SDP",
1803 status);
1804 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1805 PJSUA_UNLOCK();
1806 return;
1807 }
1808
1809
1810 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1811 if (status != PJ_SUCCESS) {
1812 pjsua_perror(THIS_FILE,
1813 "Unable to retrieve currently active remote SDP",
1814 status);
1815 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1816 PJSUA_UNLOCK();
1817 return;
1818 }
1819
1820 /* Create media session info based on SDP parameters.
1821 * We only support one stream per session at the moment
1822 */
1823 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1824 pjsua_var.med_endpt,
1825 1,&sess_info,
1826 local_sdp, remote_sdp);
1827 if (status != PJ_SUCCESS) {
1828 pjsua_perror(THIS_FILE, "Unable to create media session",
1829 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001830 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001831 PJSUA_UNLOCK();
1832 return;
1833 }
1834
1835 /* Check if media is put on-hold */
1836 if (sess_info.stream_cnt == 0 ||
1837 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1838 {
1839
1840 /* Determine who puts the call on-hold */
1841 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1842 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1843 /* It was local who offer hold */
1844 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1845 } else {
1846 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1847 }
1848 }
1849
1850 call->media_dir = PJMEDIA_DIR_NONE;
1851
1852 } else {
1853
1854 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00001855 if (pjsua_var.media_cfg.ptime != 0) {
1856 sess_info.stream_info[0].param->setting.frm_per_pkt =
1857 pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime;
1858 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
1859 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1860 }
1861
1862 /* Disable VAD, if this option is specified. */
1863 if (pjsua_var.media_cfg.no_vad) {
1864 sess_info.stream_info[0].param->setting.vad = 0;
1865 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001866
1867
1868 /* Optionally, application may modify other stream settings here
1869 * (such as jitter buffer parameters, codec ptime, etc.)
1870 */
1871
1872 /* Create session based on session info. */
1873 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1874 &call->med_tp,
1875 call, &call->session );
1876 if (status != PJ_SUCCESS) {
1877 pjsua_perror(THIS_FILE, "Unable to create media session",
1878 status);
1879 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1880 PJSUA_UNLOCK();
1881 return;
1882 }
1883
1884
1885 /* Get the port interface of the first stream in the session.
1886 * We need the port interface to add to the conference bridge.
1887 */
1888 pjmedia_session_get_port(call->session, 0, &media_port);
1889
1890
1891 /*
1892 * Add the call to conference bridge.
1893 */
1894 port_name.ptr = tmp;
1895 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1896 call->inv->dlg->remote.info->uri,
1897 tmp, sizeof(tmp));
1898 if (port_name.slen < 1) {
1899 port_name = pj_str("call");
1900 }
1901 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1902 media_port,
1903 &port_name,
1904 (unsigned*)&call->conf_slot);
1905 if (status != PJ_SUCCESS) {
1906 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1907 status);
1908 call_destroy_media(call->index);
1909 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1910 PJSUA_UNLOCK();
1911 return;
1912 }
1913
1914 /* Call's media state is active */
1915 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1916 call->media_dir = sess_info.stream_info[0].dir;
1917 }
1918
1919 /* Print info. */
1920 {
1921 char info[80];
1922 int info_len = 0;
1923 unsigned i;
1924
1925 for (i=0; i<sess_info.stream_cnt; ++i) {
1926 int len;
1927 const char *dir;
1928 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1929
1930 switch (strm_info->dir) {
1931 case PJMEDIA_DIR_NONE:
1932 dir = "inactive";
1933 break;
1934 case PJMEDIA_DIR_ENCODING:
1935 dir = "sendonly";
1936 break;
1937 case PJMEDIA_DIR_DECODING:
1938 dir = "recvonly";
1939 break;
1940 case PJMEDIA_DIR_ENCODING_DECODING:
1941 dir = "sendrecv";
1942 break;
1943 default:
1944 dir = "unknown";
1945 break;
1946 }
1947 len = pj_ansi_sprintf( info+info_len,
1948 ", stream #%d: %.*s (%s)", i,
1949 (int)strm_info->fmt.encoding_name.slen,
1950 strm_info->fmt.encoding_name.ptr,
1951 dir);
1952 if (len > 0)
1953 info_len += len;
1954 }
1955 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1956 }
1957
1958 /* Call application callback, if any */
1959 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1960 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1961
1962
1963 PJSUA_UNLOCK();
1964}
1965
1966
1967/*
1968 * Create inactive SDP for call hold.
1969 */
1970static pj_status_t create_inactive_sdp(pjsua_call *call,
1971 pjmedia_sdp_session **p_answer)
1972{
1973 pj_status_t status;
1974 pjmedia_sdp_conn *conn;
1975 pjmedia_sdp_attr *attr;
1976 pjmedia_sdp_session *sdp;
1977
1978 /* Create new offer */
1979 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1980 &call->skinfo, &sdp);
1981 if (status != PJ_SUCCESS) {
1982 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1983 return status;
1984 }
1985
1986 /* Get SDP media connection line */
1987 conn = sdp->media[0]->conn;
1988 if (!conn)
1989 conn = sdp->conn;
1990
1991 /* Modify address */
1992 conn->addr = pj_str("0.0.0.0");
1993
1994 /* Remove existing directions attributes */
1995 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
1996 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
1997 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
1998 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
1999
2000 /* Add inactive attribute */
2001 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2002 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2003
2004 *p_answer = sdp;
2005
2006 return status;
2007}
2008
2009
2010/*
2011 * Called when session received new offer.
2012 */
2013static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2014 const pjmedia_sdp_session *offer)
2015{
2016 const char *remote_state;
2017 pjsua_call *call;
2018 pjmedia_sdp_conn *conn;
2019 pjmedia_sdp_session *answer;
2020 pj_bool_t is_remote_active;
2021 pj_status_t status;
2022
2023 PJSUA_LOCK();
2024
2025 call = inv->dlg->mod_data[pjsua_var.mod.id];
2026
2027 /*
2028 * See if remote is offering active media (i.e. not on-hold)
2029 */
2030 is_remote_active = PJ_TRUE;
2031
2032 conn = offer->media[0]->conn;
2033 if (!conn)
2034 conn = offer->conn;
2035
2036 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2037 pj_strcmp2(&conn->addr, "0")==0)
2038 {
2039 is_remote_active = PJ_FALSE;
2040
2041 }
2042 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2043 {
2044 is_remote_active = PJ_FALSE;
2045 }
2046
2047 remote_state = (is_remote_active ? "active" : "inactive");
2048
2049 /* Supply candidate answer */
2050 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2051 PJ_LOG(4,(THIS_FILE,
2052 "Call %d: RX new media offer, creating inactive SDP "
2053 "(media in offer is %s)", call->index, remote_state));
2054 status = create_inactive_sdp( call, &answer );
2055 } else {
2056 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2057 call->index));
2058 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2059 call->inv->pool, 1,
2060 &call->skinfo, &answer);
2061 }
2062
2063 if (status != PJ_SUCCESS) {
2064 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2065 PJSUA_UNLOCK();
2066 return;
2067 }
2068
2069 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2070 if (status != PJ_SUCCESS) {
2071 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2072 PJSUA_UNLOCK();
2073 return;
2074 }
2075
2076 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002077}
2078
2079
2080/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002081 * Callback called by event framework when the xfer subscription state
2082 * has changed.
2083 */
2084static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2085{
2086
2087 PJ_UNUSED_ARG(event);
2088
2089 /*
2090 * We're only interested when subscription is terminated, to
2091 * clear the xfer_sub member of the inv_data.
2092 */
2093 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002094 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002095
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002096 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002097 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002098 return;
2099
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002100 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002101 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002102
2103 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2104 }
2105}
2106
2107
2108/*
2109 * Follow transfer (REFER) request.
2110 */
2111static void on_call_transfered( pjsip_inv_session *inv,
2112 pjsip_rx_data *rdata )
2113{
2114 pj_status_t status;
2115 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002116 pjsua_call *existing_call;
2117 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002118 const pj_str_t str_refer_to = { "Refer-To", 8};
2119 pjsip_generic_string_hdr *refer_to;
2120 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002121 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +00002122 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002123 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002124 pjsip_evsub *sub;
2125
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002126 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002127
Benny Prijono26ff9062006-02-21 23:47:00 +00002128 /* Find the Refer-To header */
2129 refer_to = (pjsip_generic_string_hdr*)
2130 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2131
2132 if (refer_to == NULL) {
2133 /* Invalid Request.
2134 * No Refer-To header!
2135 */
2136 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002137 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002138 return;
2139 }
2140
Benny Prijono9fc735d2006-05-28 14:58:12 +00002141 /* Notify callback */
2142 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002143 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2144 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2145 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002146
2147 if (code < 200)
2148 code = 200;
2149 if (code >= 300) {
2150 /* Application rejects call transfer request */
2151 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2152 return;
2153 }
2154
Benny Prijono26ff9062006-02-21 23:47:00 +00002155 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2156 (int)inv->dlg->remote.info_str.slen,
2157 inv->dlg->remote.info_str.ptr,
2158 (int)refer_to->hvalue.slen,
2159 refer_to->hvalue.ptr));
2160
2161 /* Init callback */
Benny Prijonoac623b32006-07-03 15:19:31 +00002162 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono26ff9062006-02-21 23:47:00 +00002163 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2164
2165 /* Create transferee event subscription */
2166 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2167 if (status != PJ_SUCCESS) {
2168 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +00002169 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002170 return;
2171 }
2172
2173 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002174 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002175
2176 /* Create initial NOTIFY request */
2177 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2178 100, NULL, &tdata);
2179 if (status != PJ_SUCCESS) {
2180 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
2181 return;
2182 }
2183
2184 /* Send initial NOTIFY request */
2185 status = pjsip_xfer_send_request( sub, tdata);
2186 if (status != PJ_SUCCESS) {
2187 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2188 return;
2189 }
2190
2191 /* We're cheating here.
2192 * We need to get a null terminated string from a pj_str_t.
2193 * So grab the pointer from the hvalue and NULL terminate it, knowing
2194 * that the NULL position will be occupied by a newline.
2195 */
2196 uri = refer_to->hvalue.ptr;
2197 uri[refer_to->hvalue.slen] = '\0';
2198
2199 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002200 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002201 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2202 existing_call->user_data, NULL,
2203 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002204 if (status != PJ_SUCCESS) {
2205
2206 /* Notify xferer about the error */
2207 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2208 500, NULL, &tdata);
2209 if (status != PJ_SUCCESS) {
2210 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2211 status);
2212 return;
2213 }
2214 status = pjsip_xfer_send_request(sub, tdata);
2215 if (status != PJ_SUCCESS) {
2216 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2217 status);
2218 return;
2219 }
2220 return;
2221 }
2222
2223 /* Put the server subscription in inv_data.
2224 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2225 * reported back to the server subscription.
2226 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002227 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002228
2229 /* Put the invite_data in the subscription. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002230 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2231 &pjsua_var.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +00002232}
2233
2234
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002235
Benny Prijono26ff9062006-02-21 23:47:00 +00002236/*
2237 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002238 * session. We use this to trap:
2239 * - incoming REFER request.
2240 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002241 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002242static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2243 pjsip_transaction *tsx,
2244 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002245{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002246 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2247
2248 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002249
Benny Prijono26ff9062006-02-21 23:47:00 +00002250 if (tsx->role==PJSIP_ROLE_UAS &&
2251 tsx->state==PJSIP_TSX_STATE_TRYING &&
2252 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2253 {
2254 /*
2255 * Incoming REFER request.
2256 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002257 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002258
Benny Prijono26ff9062006-02-21 23:47:00 +00002259 }
Benny Prijonob0808372006-03-02 21:18:58 +00002260 else if (tsx->role==PJSIP_ROLE_UAS &&
2261 tsx->state==PJSIP_TSX_STATE_TRYING &&
2262 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2263 {
2264 /*
2265 * Incoming MESSAGE request!
2266 */
2267 pjsip_rx_data *rdata;
2268 pjsip_msg *msg;
2269 pjsip_accept_hdr *accept_hdr;
2270 pj_status_t status;
2271
2272 rdata = e->body.tsx_state.src.rdata;
2273 msg = rdata->msg_info.msg;
2274
2275 /* Request MUST have message body, with Content-Type equal to
2276 * "text/plain".
2277 */
2278 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2279
2280 pjsip_hdr hdr_list;
2281
2282 pj_list_init(&hdr_list);
2283 pj_list_push_back(&hdr_list, accept_hdr);
2284
2285 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2286 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002287 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002288 return;
2289 }
2290
2291 /* Respond with 200 first, so that remote doesn't retransmit in case
2292 * the UI takes too long to process the message.
2293 */
2294 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2295
2296 /* Process MESSAGE request */
2297 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2298 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002299
Benny Prijonob0808372006-03-02 21:18:58 +00002300 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002301 else if (tsx->role == PJSIP_ROLE_UAC &&
2302 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002303 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002304 /* Handle outgoing pager status */
2305 if (tsx->status_code >= 200) {
2306 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002307
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002308 im_data = tsx->mod_data[pjsua_var.mod.id];
2309 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002310
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002311 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2312 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2313 &im_data->to,
2314 &im_data->body,
2315 im_data->user_data,
2316 tsx->status_code,
2317 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002318 }
Benny Prijonofccab712006-02-22 22:23:22 +00002319 }
Benny Prijono834aee32006-02-19 01:38:06 +00002320 }
Benny Prijono834aee32006-02-19 01:38:06 +00002321
Benny Prijono26ff9062006-02-21 23:47:00 +00002322
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002323 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002324}