blob: f251753633402e5f758ee3fe96713999de6b9089 [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) {
Benny Prijono70972992006-08-05 11:13:58 +0000798 PJ_LOG(3,(THIS_FILE,"Invalid call or call has been disconnected"));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000799 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 Prijono00cae612006-07-31 15:19:36 +0000944 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000945 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000946 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
947 1, &call->skinfo, &sdp);
948 if (status != PJ_SUCCESS) {
949 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
950 status);
951 PJSUA_UNLOCK();
952 return status;
953 }
954
955 /* Create re-INVITE with new offer */
956 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
957 if (status != PJ_SUCCESS) {
958 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
959 PJSUA_UNLOCK();
960 return status;
961 }
962
963 /* Add additional headers etc */
964 pjsua_process_msg_data( tdata, msg_data);
965
966 /* Send the request */
967 status = pjsip_inv_send_msg( call->inv, tdata);
968 if (status != PJ_SUCCESS) {
969 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
970 PJSUA_UNLOCK();
971 return status;
972 }
973
974 PJSUA_UNLOCK();
975
976 return PJ_SUCCESS;
977}
978
979
980/*
981 * Initiate call transfer to the specified address.
982 */
983PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
984 const pj_str_t *dest,
985 const pjsua_msg_data *msg_data)
986{
987 pjsip_evsub *sub;
988 pjsip_tx_data *tdata;
989 pjsua_call *call;
990 pj_status_t status;
991
992
993 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
994 PJ_EINVAL);
995
996 PJSUA_LOCK();
997
998 call = &pjsua_var.calls[call_id];
999
1000 if (!call->inv) {
1001 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1002 PJSUA_UNLOCK();
1003 return PJSIP_ESESSIONTERMINATED;
1004 }
1005
1006 /* Create xfer client subscription.
1007 * We're not interested in knowing the transfer result, so we
1008 * put NULL as the callback.
1009 */
1010 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
1011 if (status != PJ_SUCCESS) {
1012 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
1013 PJSUA_UNLOCK();
1014 return status;
1015 }
1016
1017 /*
1018 * Create REFER request.
1019 */
1020 status = pjsip_xfer_initiate(sub, dest, &tdata);
1021 if (status != PJ_SUCCESS) {
1022 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1023 PJSUA_UNLOCK();
1024 return status;
1025 }
1026
1027 /* Add additional headers etc */
1028 pjsua_process_msg_data( tdata, msg_data);
1029
1030 /* Send. */
1031 status = pjsip_xfer_send_request(sub, tdata);
1032 if (status != PJ_SUCCESS) {
1033 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1034 PJSUA_UNLOCK();
1035 return status;
1036 }
1037
1038 /* For simplicity (that's what this program is intended to be!),
1039 * leave the original invite session as it is. More advanced application
1040 * may want to hold the INVITE, or terminate the invite, or whatever.
1041 */
1042
1043 PJSUA_UNLOCK();
1044
1045 return PJ_SUCCESS;
1046
1047}
1048
1049
1050/*
1051 * Send DTMF digits to remote using RFC 2833 payload formats.
1052 */
1053PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1054 const pj_str_t *digits)
1055{
1056 pjsua_call *call;
1057 pj_status_t status;
1058
1059 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1060 PJ_EINVAL);
1061
1062 PJSUA_LOCK();
1063
1064 call = &pjsua_var.calls[call_id];
1065
1066 if (!call->session) {
1067 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1068 PJSUA_UNLOCK();
1069 return PJ_EINVALIDOP;
1070 }
1071
1072 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1073
1074 PJSUA_UNLOCK();
1075
1076 return status;
1077}
1078
1079
1080/**
1081 * Send instant messaging inside INVITE session.
1082 */
1083PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1084 const pj_str_t *mime_type,
1085 const pj_str_t *content,
1086 const pjsua_msg_data *msg_data,
1087 void *user_data)
1088{
1089 pjsua_call *call;
1090 const pj_str_t mime_text_plain = pj_str("text/plain");
1091 pjsip_media_type ctype;
1092 pjsua_im_data *im_data;
1093 pjsip_tx_data *tdata;
1094 pj_status_t status;
1095
1096
1097 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1098 PJ_EINVAL);
1099
1100 PJSUA_LOCK();
1101
1102 call = &pjsua_var.calls[call_id];
1103
1104 if (!call->inv) {
1105 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1106 PJSUA_UNLOCK();
1107 return PJSIP_ESESSIONTERMINATED;
1108 }
1109
1110 /* Lock dialog. */
1111 pjsip_dlg_inc_lock(call->inv->dlg);
1112
1113 /* Set default media type if none is specified */
1114 if (mime_type == NULL) {
1115 mime_type = &mime_text_plain;
1116 }
1117
1118 /* Create request message. */
1119 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1120 -1, &tdata);
1121 if (status != PJ_SUCCESS) {
1122 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1123 goto on_return;
1124 }
1125
1126 /* Add accept header. */
1127 pjsip_msg_add_hdr( tdata->msg,
1128 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1129
1130 /* Parse MIME type */
1131 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1132
1133 /* Create "text/plain" message body. */
1134 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1135 &ctype.subtype, content);
1136 if (tdata->msg->body == NULL) {
1137 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1138 pjsip_tx_data_dec_ref(tdata);
1139 goto on_return;
1140 }
1141
1142 /* Add additional headers etc */
1143 pjsua_process_msg_data( tdata, msg_data);
1144
1145 /* Create IM data and attach to the request. */
1146 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1147 im_data->acc_id = call->acc_id;
1148 im_data->call_id = call_id;
1149 im_data->to = call->inv->dlg->remote.info_str;
1150 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1151 im_data->user_data = user_data;
1152
1153
1154 /* Send the request. */
1155 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1156 pjsua_var.mod.id, im_data);
1157 if (status != PJ_SUCCESS) {
1158 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1159 goto on_return;
1160 }
1161
1162on_return:
1163 pjsip_dlg_dec_lock(call->inv->dlg);
1164 PJSUA_UNLOCK();
1165 return status;
1166}
1167
1168
1169/*
1170 * Send IM typing indication inside INVITE session.
1171 */
1172PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1173 pj_bool_t is_typing,
1174 const pjsua_msg_data*msg_data)
1175{
1176 pjsua_call *call;
1177 pjsip_tx_data *tdata;
1178 pj_status_t status;
1179
1180
1181 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1182 PJ_EINVAL);
1183
1184 PJSUA_LOCK();
1185
1186 call = &pjsua_var.calls[call_id];
1187
1188 if (!call->inv) {
1189 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1190 PJSUA_UNLOCK();
1191 return PJSIP_ESESSIONTERMINATED;
1192 }
1193
1194 /* Lock dialog. */
1195 pjsip_dlg_inc_lock(call->inv->dlg);
1196
1197 /* Create request message. */
1198 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1199 -1, &tdata);
1200 if (status != PJ_SUCCESS) {
1201 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1202 goto on_return;
1203 }
1204
1205 /* Create "application/im-iscomposing+xml" msg body. */
1206 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1207 NULL, NULL, -1);
1208
1209 /* Add additional headers etc */
1210 pjsua_process_msg_data( tdata, msg_data);
1211
1212 /* Send the request. */
1213 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1214 if (status != PJ_SUCCESS) {
1215 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1216 goto on_return;
1217 }
1218
1219on_return:
1220 pjsip_dlg_dec_lock(call->inv->dlg);
1221 PJSUA_UNLOCK();
1222 return status;
1223}
1224
1225
1226/*
1227 * Terminate all calls.
1228 */
1229PJ_DEF(void) pjsua_call_hangup_all(void)
1230{
1231 unsigned i;
1232
1233 PJSUA_LOCK();
1234
1235 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1236 if (pjsua_var.calls[i].inv)
1237 pjsua_call_hangup(i, 0, NULL, NULL);
1238 }
1239
1240 PJSUA_UNLOCK();
1241}
1242
1243
1244static const char *good_number(char *buf, pj_int32_t val)
1245{
1246 if (val < 1000) {
1247 pj_ansi_sprintf(buf, "%d", val);
1248 } else if (val < 1000000) {
1249 pj_ansi_sprintf(buf, "%d.%dK",
1250 val / 1000,
1251 (val % 1000) / 100);
1252 } else {
1253 pj_ansi_sprintf(buf, "%d.%02dM",
1254 val / 1000000,
1255 (val % 1000000) / 10000);
1256 }
1257
1258 return buf;
1259}
1260
1261
1262/* Dump media session */
1263static void dump_media_session(const char *indent,
1264 char *buf, unsigned maxlen,
1265 pjmedia_session *session)
1266{
1267 unsigned i;
1268 char *p = buf, *end = buf+maxlen;
1269 int len;
1270 pjmedia_session_info info;
1271
1272 pjmedia_session_get_info(session, &info);
1273
1274 for (i=0; i<info.stream_cnt; ++i) {
1275 pjmedia_rtcp_stat stat;
1276 const char *rem_addr;
1277 int rem_port;
1278 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001279 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001280 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1281 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001282
1283 pjmedia_session_get_stream_stat(session, i, &stat);
1284 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1285 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1286
1287 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1288 dir = "sendonly";
1289 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1290 dir = "recvonly";
1291 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1292 dir = "sendrecv";
1293 else
1294 dir = "inactive";
1295
1296
1297 len = pj_ansi_snprintf(buf, end-p,
1298 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1299 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001300 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001301 info.stream_info[i].fmt.encoding_name.ptr,
1302 info.stream_info[i].fmt.clock_rate / 1000,
1303 dir,
1304 rem_addr, rem_port);
1305 if (len < 1 || len > end-p) {
1306 *p = '\0';
1307 return;
1308 }
1309
1310 p += len;
1311 *p++ = '\n';
1312 *p = '\0';
1313
1314 if (stat.rx.update_cnt == 0)
1315 strcpy(last_update, "never");
1316 else {
1317 pj_gettimeofday(&now);
1318 PJ_TIME_VAL_SUB(now, stat.rx.update);
1319 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1320 now.sec / 3600,
1321 (now.sec % 3600) / 60,
1322 now.sec % 60,
1323 now.msec);
1324 }
1325
Benny Prijono80019eb2006-08-07 13:22:23 +00001326 pj_gettimeofday(&media_duration);
1327 PJ_TIME_VAL_SUB(media_duration, stat.start);
1328 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1329 media_duration.msec = 1;
1330
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001331 len = pj_ansi_snprintf(p, end-p,
1332 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001333 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001334 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1335 "%s (msec) min avg max last\n"
1336 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1337 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1338 indent, info.stream_info[i].fmt.pt,
1339 last_update,
1340 indent,
1341 good_number(packets, stat.rx.pkt),
1342 good_number(bytes, stat.rx.bytes),
1343 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001344 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345 indent,
1346 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001347 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001348 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001349 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001350 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001351 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001352 indent, indent,
1353 stat.rx.loss_period.min / 1000.0,
1354 stat.rx.loss_period.avg / 1000.0,
1355 stat.rx.loss_period.max / 1000.0,
1356 stat.rx.loss_period.last / 1000.0,
1357 indent,
1358 stat.rx.jitter.min / 1000.0,
1359 stat.rx.jitter.avg / 1000.0,
1360 stat.rx.jitter.max / 1000.0,
1361 stat.rx.jitter.last / 1000.0,
1362 ""
1363 );
1364
1365 if (len < 1 || len > end-p) {
1366 *p = '\0';
1367 return;
1368 }
1369
1370 p += len;
1371 *p++ = '\n';
1372 *p = '\0';
1373
1374 if (stat.tx.update_cnt == 0)
1375 strcpy(last_update, "never");
1376 else {
1377 pj_gettimeofday(&now);
1378 PJ_TIME_VAL_SUB(now, stat.tx.update);
1379 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1380 now.sec / 3600,
1381 (now.sec % 3600) / 60,
1382 now.sec % 60,
1383 now.msec);
1384 }
1385
1386 len = pj_ansi_snprintf(p, end-p,
1387 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001388 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1390 "%s (msec) min avg max last\n"
1391 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1392 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1393 indent,
1394 info.stream_info[i].tx_pt,
1395 info.stream_info[i].param->info.frm_ptime *
1396 info.stream_info[i].param->setting.frm_per_pkt,
1397 last_update,
1398
1399 indent,
1400 good_number(packets, stat.tx.pkt),
1401 good_number(bytes, stat.tx.bytes),
1402 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001403 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
1405 indent,
1406 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001407 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001408 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001409 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001411 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412
1413 indent, indent,
1414 stat.tx.loss_period.min / 1000.0,
1415 stat.tx.loss_period.avg / 1000.0,
1416 stat.tx.loss_period.max / 1000.0,
1417 stat.tx.loss_period.last / 1000.0,
1418 indent,
1419 stat.tx.jitter.min / 1000.0,
1420 stat.tx.jitter.avg / 1000.0,
1421 stat.tx.jitter.max / 1000.0,
1422 stat.tx.jitter.last / 1000.0,
1423 ""
1424 );
1425
1426 if (len < 1 || len > end-p) {
1427 *p = '\0';
1428 return;
1429 }
1430
1431 p += len;
1432 *p++ = '\n';
1433 *p = '\0';
1434
1435 len = pj_ansi_snprintf(p, end-p,
1436 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1437 indent,
1438 stat.rtt.min / 1000.0,
1439 stat.rtt.avg / 1000.0,
1440 stat.rtt.max / 1000.0,
1441 stat.rtt.last / 1000.0
1442 );
1443 if (len < 1 || len > end-p) {
1444 *p = '\0';
1445 return;
1446 }
1447
1448 p += len;
1449 *p++ = '\n';
1450 *p = '\0';
1451 }
1452}
1453
1454
1455/* Print call info */
1456static void print_call(const char *title,
1457 int call_id,
1458 char *buf, pj_size_t size)
1459{
1460 int len;
1461 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1462 pjsip_dialog *dlg = inv->dlg;
1463 char userinfo[128];
1464
1465 /* Dump invite sesion info. */
1466
1467 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1468 if (len < 1)
1469 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1470 else
1471 userinfo[len] = '\0';
1472
1473 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1474 title,
1475 pjsip_inv_state_name(inv->state),
1476 userinfo);
1477 if (len < 1 || len >= (int)size) {
1478 pj_ansi_strcpy(buf, "<--uri too long-->");
1479 len = 18;
1480 } else
1481 buf[len] = '\0';
1482}
1483
1484
1485/*
1486 * Dump call and media statistics to string.
1487 */
1488PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1489 pj_bool_t with_media,
1490 char *buffer,
1491 unsigned maxlen,
1492 const char *indent)
1493{
1494 pjsua_call *call;
1495 pj_time_val duration, res_delay, con_delay;
1496 char tmp[128];
1497 char *p, *end;
1498 int len;
1499
1500 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1501 PJ_EINVAL);
1502
1503 PJSUA_LOCK();
1504
1505 call = &pjsua_var.calls[call_id];
1506
1507 *buffer = '\0';
1508 p = buffer;
1509 end = buffer + maxlen;
1510 len = 0;
1511
1512 if (call->inv == NULL) {
1513 PJSUA_UNLOCK();
1514 return PJ_EINVALIDOP;
1515 }
1516
1517 print_call(indent, call_id, tmp, sizeof(tmp));
1518
1519 len = pj_ansi_strlen(tmp);
1520 pj_ansi_strcpy(buffer, tmp);
1521
1522 p += len;
1523 *p++ = '\r';
1524 *p++ = '\n';
1525
1526 /* Calculate call duration */
1527 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1528 pj_gettimeofday(&duration);
1529 PJ_TIME_VAL_SUB(duration, call->conn_time);
1530 con_delay = call->conn_time;
1531 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1532 } else {
1533 duration.sec = duration.msec = 0;
1534 con_delay.sec = con_delay.msec = 0;
1535 }
1536
1537 /* Calculate first response delay */
1538 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1539 res_delay = call->res_time;
1540 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1541 } else {
1542 res_delay.sec = res_delay.msec = 0;
1543 }
1544
1545 /* Print duration */
1546 len = pj_ansi_snprintf(p, end-p,
1547 "%s Call time: %02dh:%02dm:%02ds, "
1548 "1st res in %d ms, conn in %dms",
1549 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001550 (int)(duration.sec / 3600),
1551 (int)((duration.sec % 3600)/60),
1552 (int)(duration.sec % 60),
1553 (int)PJ_TIME_VAL_MSEC(res_delay),
1554 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555
1556 if (len > 0 && len < end-p) {
1557 p += len;
1558 *p++ = '\n';
1559 *p = '\0';
1560 }
1561
1562 /* Dump session statistics */
1563 if (with_media && call->session)
1564 dump_media_session(indent, p, end-p, call->session);
1565
1566 PJSUA_UNLOCK();
1567
1568 return PJ_SUCCESS;
1569}
1570
1571
1572/*
1573 * Destroy the call's media
1574 */
1575static pj_status_t call_destroy_media(int call_id)
1576{
1577 pjsua_call *call = &pjsua_var.calls[call_id];
1578
1579 if (call->conf_slot != PJSUA_INVALID_ID) {
1580 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1581 call->conf_slot = PJSUA_INVALID_ID;
1582 }
1583
1584 if (call->session) {
1585 /* Destroy session (this will also close RTP/RTCP sockets). */
1586 pjmedia_session_destroy(call->session);
1587 call->session = NULL;
1588
1589 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1590 call_id));
1591
1592 }
1593
1594 call->media_st = PJSUA_CALL_MEDIA_NONE;
1595
1596 return PJ_SUCCESS;
1597}
1598
1599
Benny Prijono84126ab2006-02-09 09:30:09 +00001600/*
1601 * This callback receives notification from invite session when the
1602 * session state has changed.
1603 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001604static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1605 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001606{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001607 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001608
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609 PJSUA_LOCK();
1610
1611 call = inv->dlg->mod_data[pjsua_var.mod.id];
1612
1613 if (!call) {
1614 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001615 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 }
1617
Benny Prijonoe21e7842006-04-09 16:46:05 +00001618
1619 /* Get call times */
1620 switch (inv->state) {
1621 case PJSIP_INV_STATE_EARLY:
1622 case PJSIP_INV_STATE_CONNECTING:
1623 if (call->res_time.sec == 0)
1624 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001625 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 pj_strncpy(&call->last_text,
1627 &e->body.tsx_state.tsx->status_text,
1628 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001629 break;
1630 case PJSIP_INV_STATE_CONFIRMED:
1631 pj_gettimeofday(&call->conn_time);
1632 break;
1633 case PJSIP_INV_STATE_DISCONNECTED:
1634 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001635 if (call->res_time.sec == 0)
1636 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001637 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1638 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639 pj_strncpy(&call->last_text,
1640 &e->body.tsx_state.tsx->status_text,
1641 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001642 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001643 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001644 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001645 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646 pj_strncpy(&call->last_text,
1647 &e->body.tsx_state.tsx->status_text,
1648 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001649 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001650 }
1651
Benny Prijono26ff9062006-02-21 23:47:00 +00001652 /* If this is an outgoing INVITE that was created because of
1653 * REFER/transfer, send NOTIFY to transferer.
1654 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001655 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001656 int st_code = -1;
1657 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1658
1659
Benny Prijonoa91a0032006-02-26 21:23:45 +00001660 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001661 case PJSIP_INV_STATE_NULL:
1662 case PJSIP_INV_STATE_CALLING:
1663 /* Do nothing */
1664 break;
1665
1666 case PJSIP_INV_STATE_EARLY:
1667 case PJSIP_INV_STATE_CONNECTING:
1668 st_code = e->body.tsx_state.tsx->status_code;
1669 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1670 break;
1671
1672 case PJSIP_INV_STATE_CONFIRMED:
1673 /* When state is confirmed, send the final 200/OK and terminate
1674 * subscription.
1675 */
1676 st_code = e->body.tsx_state.tsx->status_code;
1677 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1678 break;
1679
1680 case PJSIP_INV_STATE_DISCONNECTED:
1681 st_code = e->body.tsx_state.tsx->status_code;
1682 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1683 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001684
Benny Prijono8b1889b2006-06-06 18:40:40 +00001685 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001686 /* Nothing to do. Just to keep gcc from complaining about
1687 * unused enums.
1688 */
1689 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001690 }
1691
1692 if (st_code != -1) {
1693 pjsip_tx_data *tdata;
1694 pj_status_t status;
1695
Benny Prijonoa91a0032006-02-26 21:23:45 +00001696 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001697 ev_state, st_code,
1698 NULL, &tdata);
1699 if (status != PJ_SUCCESS) {
1700 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1701 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001702 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001703 if (status != PJ_SUCCESS) {
1704 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1705 }
1706 }
1707 }
1708 }
1709
Benny Prijono84126ab2006-02-09 09:30:09 +00001710
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001711 if (pjsua_var.ua_cfg.cb.on_call_state)
1712 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001713
1714 /* call->inv may be NULL now */
1715
Benny Prijono84126ab2006-02-09 09:30:09 +00001716 /* Destroy media session when invite session is disconnected. */
1717 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001718
Benny Prijonoa91a0032006-02-26 21:23:45 +00001719 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001720
Benny Prijono275fd682006-03-22 11:59:11 +00001721 if (call)
1722 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001723
Benny Prijono105217f2006-03-06 16:25:59 +00001724 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001725 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001726 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001727 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001728
1729 PJSUA_UNLOCK();
1730}
1731
1732/*
1733 * This callback is called by invite session framework when UAC session
1734 * has forked.
1735 */
1736static void pjsua_call_on_forked( pjsip_inv_session *inv,
1737 pjsip_event *e)
1738{
1739 PJ_UNUSED_ARG(inv);
1740 PJ_UNUSED_ARG(e);
1741
1742 PJ_TODO(HANDLE_FORKED_DIALOG);
1743}
1744
1745
1746/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00001747 * Disconnect call upon error.
1748 */
1749static void call_disconnect( pjsip_inv_session *inv,
1750 int code )
1751{
1752 pjsip_tx_data *tdata;
1753 pj_status_t status;
1754
1755 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
1756 if (status == PJ_SUCCESS)
1757 pjsip_inv_send_msg(inv, tdata);
1758}
1759
1760/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761 * Callback to be called when SDP offer/answer negotiation has just completed
1762 * in the session. This function will start/update media if negotiation
1763 * has succeeded.
1764 */
1765static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1766 pj_status_t status)
1767{
1768 int prev_media_st = 0;
1769 pjsua_call *call;
1770 pjmedia_session_info sess_info;
1771 const pjmedia_sdp_session *local_sdp;
1772 const pjmedia_sdp_session *remote_sdp;
1773 pjmedia_port *media_port;
1774 pj_str_t port_name;
1775 char tmp[PJSIP_MAX_URL_SIZE];
1776
1777 PJSUA_LOCK();
1778
1779 call = inv->dlg->mod_data[pjsua_var.mod.id];
1780
1781 if (status != PJ_SUCCESS) {
1782
1783 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1784
1785 /* Disconnect call if we're not in the middle of initializing an
1786 * UAS dialog and if this is not a re-INVITE
1787 */
1788 if (inv->state != PJSIP_INV_STATE_NULL &&
1789 inv->state != PJSIP_INV_STATE_CONFIRMED)
1790 {
1791 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1792 }
1793
1794 PJSUA_UNLOCK();
1795 return;
1796 }
1797
1798 /* Destroy existing media session, if any. */
1799
1800 if (call) {
1801 prev_media_st = call->media_st;
1802 call_destroy_media(call->index);
1803 }
1804
1805 /* Get local and remote SDP */
1806
1807 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1808 if (status != PJ_SUCCESS) {
1809 pjsua_perror(THIS_FILE,
1810 "Unable to retrieve currently active local SDP",
1811 status);
1812 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1813 PJSUA_UNLOCK();
1814 return;
1815 }
1816
1817
1818 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1819 if (status != PJ_SUCCESS) {
1820 pjsua_perror(THIS_FILE,
1821 "Unable to retrieve currently active remote SDP",
1822 status);
1823 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1824 PJSUA_UNLOCK();
1825 return;
1826 }
1827
1828 /* Create media session info based on SDP parameters.
1829 * We only support one stream per session at the moment
1830 */
1831 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1832 pjsua_var.med_endpt,
1833 1,&sess_info,
1834 local_sdp, remote_sdp);
1835 if (status != PJ_SUCCESS) {
1836 pjsua_perror(THIS_FILE, "Unable to create media session",
1837 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001838 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001839 PJSUA_UNLOCK();
1840 return;
1841 }
1842
1843 /* Check if media is put on-hold */
1844 if (sess_info.stream_cnt == 0 ||
1845 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1846 {
1847
1848 /* Determine who puts the call on-hold */
1849 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1850 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1851 /* It was local who offer hold */
1852 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1853 } else {
1854 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1855 }
1856 }
1857
1858 call->media_dir = PJMEDIA_DIR_NONE;
1859
1860 } else {
1861
1862 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00001863 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00001864 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
1865 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00001866 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
1867 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1868 }
1869
1870 /* Disable VAD, if this option is specified. */
1871 if (pjsua_var.media_cfg.no_vad) {
1872 sess_info.stream_info[0].param->setting.vad = 0;
1873 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001874
1875
1876 /* Optionally, application may modify other stream settings here
1877 * (such as jitter buffer parameters, codec ptime, etc.)
1878 */
1879
1880 /* Create session based on session info. */
1881 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1882 &call->med_tp,
1883 call, &call->session );
1884 if (status != PJ_SUCCESS) {
1885 pjsua_perror(THIS_FILE, "Unable to create media session",
1886 status);
1887 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1888 PJSUA_UNLOCK();
1889 return;
1890 }
1891
1892
1893 /* Get the port interface of the first stream in the session.
1894 * We need the port interface to add to the conference bridge.
1895 */
1896 pjmedia_session_get_port(call->session, 0, &media_port);
1897
1898
1899 /*
1900 * Add the call to conference bridge.
1901 */
1902 port_name.ptr = tmp;
1903 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1904 call->inv->dlg->remote.info->uri,
1905 tmp, sizeof(tmp));
1906 if (port_name.slen < 1) {
1907 port_name = pj_str("call");
1908 }
1909 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1910 media_port,
1911 &port_name,
1912 (unsigned*)&call->conf_slot);
1913 if (status != PJ_SUCCESS) {
1914 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1915 status);
1916 call_destroy_media(call->index);
1917 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1918 PJSUA_UNLOCK();
1919 return;
1920 }
1921
1922 /* Call's media state is active */
1923 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1924 call->media_dir = sess_info.stream_info[0].dir;
1925 }
1926
1927 /* Print info. */
1928 {
1929 char info[80];
1930 int info_len = 0;
1931 unsigned i;
1932
1933 for (i=0; i<sess_info.stream_cnt; ++i) {
1934 int len;
1935 const char *dir;
1936 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1937
1938 switch (strm_info->dir) {
1939 case PJMEDIA_DIR_NONE:
1940 dir = "inactive";
1941 break;
1942 case PJMEDIA_DIR_ENCODING:
1943 dir = "sendonly";
1944 break;
1945 case PJMEDIA_DIR_DECODING:
1946 dir = "recvonly";
1947 break;
1948 case PJMEDIA_DIR_ENCODING_DECODING:
1949 dir = "sendrecv";
1950 break;
1951 default:
1952 dir = "unknown";
1953 break;
1954 }
1955 len = pj_ansi_sprintf( info+info_len,
1956 ", stream #%d: %.*s (%s)", i,
1957 (int)strm_info->fmt.encoding_name.slen,
1958 strm_info->fmt.encoding_name.ptr,
1959 dir);
1960 if (len > 0)
1961 info_len += len;
1962 }
1963 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1964 }
1965
1966 /* Call application callback, if any */
1967 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1968 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1969
1970
1971 PJSUA_UNLOCK();
1972}
1973
1974
1975/*
1976 * Create inactive SDP for call hold.
1977 */
1978static pj_status_t create_inactive_sdp(pjsua_call *call,
1979 pjmedia_sdp_session **p_answer)
1980{
1981 pj_status_t status;
1982 pjmedia_sdp_conn *conn;
1983 pjmedia_sdp_attr *attr;
1984 pjmedia_sdp_session *sdp;
1985
1986 /* Create new offer */
1987 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1988 &call->skinfo, &sdp);
1989 if (status != PJ_SUCCESS) {
1990 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1991 return status;
1992 }
1993
1994 /* Get SDP media connection line */
1995 conn = sdp->media[0]->conn;
1996 if (!conn)
1997 conn = sdp->conn;
1998
1999 /* Modify address */
2000 conn->addr = pj_str("0.0.0.0");
2001
2002 /* Remove existing directions attributes */
2003 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2004 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2005 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2006 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2007
2008 /* Add inactive attribute */
2009 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2010 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2011
2012 *p_answer = sdp;
2013
2014 return status;
2015}
2016
2017
2018/*
2019 * Called when session received new offer.
2020 */
2021static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2022 const pjmedia_sdp_session *offer)
2023{
2024 const char *remote_state;
2025 pjsua_call *call;
2026 pjmedia_sdp_conn *conn;
2027 pjmedia_sdp_session *answer;
2028 pj_bool_t is_remote_active;
2029 pj_status_t status;
2030
2031 PJSUA_LOCK();
2032
2033 call = inv->dlg->mod_data[pjsua_var.mod.id];
2034
2035 /*
2036 * See if remote is offering active media (i.e. not on-hold)
2037 */
2038 is_remote_active = PJ_TRUE;
2039
2040 conn = offer->media[0]->conn;
2041 if (!conn)
2042 conn = offer->conn;
2043
2044 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2045 pj_strcmp2(&conn->addr, "0")==0)
2046 {
2047 is_remote_active = PJ_FALSE;
2048
2049 }
2050 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2051 {
2052 is_remote_active = PJ_FALSE;
2053 }
2054
2055 remote_state = (is_remote_active ? "active" : "inactive");
2056
2057 /* Supply candidate answer */
2058 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2059 PJ_LOG(4,(THIS_FILE,
2060 "Call %d: RX new media offer, creating inactive SDP "
2061 "(media in offer is %s)", call->index, remote_state));
2062 status = create_inactive_sdp( call, &answer );
2063 } else {
2064 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2065 call->index));
2066 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2067 call->inv->pool, 1,
2068 &call->skinfo, &answer);
2069 }
2070
2071 if (status != PJ_SUCCESS) {
2072 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2073 PJSUA_UNLOCK();
2074 return;
2075 }
2076
2077 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2078 if (status != PJ_SUCCESS) {
2079 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2080 PJSUA_UNLOCK();
2081 return;
2082 }
2083
2084 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002085}
2086
2087
2088/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002089 * Callback called by event framework when the xfer subscription state
2090 * has changed.
2091 */
2092static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2093{
2094
2095 PJ_UNUSED_ARG(event);
2096
2097 /*
2098 * We're only interested when subscription is terminated, to
2099 * clear the xfer_sub member of the inv_data.
2100 */
2101 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002102 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002103
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002104 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002105 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002106 return;
2107
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002109 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002110
2111 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2112 }
2113}
2114
2115
2116/*
2117 * Follow transfer (REFER) request.
2118 */
2119static void on_call_transfered( pjsip_inv_session *inv,
2120 pjsip_rx_data *rdata )
2121{
2122 pj_status_t status;
2123 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002124 pjsua_call *existing_call;
2125 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002126 const pj_str_t str_refer_to = { "Refer-To", 8};
2127 pjsip_generic_string_hdr *refer_to;
2128 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002129 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +00002130 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002131 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002132 pjsip_evsub *sub;
2133
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002134 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002135
Benny Prijono26ff9062006-02-21 23:47:00 +00002136 /* Find the Refer-To header */
2137 refer_to = (pjsip_generic_string_hdr*)
2138 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2139
2140 if (refer_to == NULL) {
2141 /* Invalid Request.
2142 * No Refer-To header!
2143 */
2144 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002145 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002146 return;
2147 }
2148
Benny Prijono9fc735d2006-05-28 14:58:12 +00002149 /* Notify callback */
2150 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002151 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2152 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2153 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002154
2155 if (code < 200)
2156 code = 200;
2157 if (code >= 300) {
2158 /* Application rejects call transfer request */
2159 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2160 return;
2161 }
2162
Benny Prijono26ff9062006-02-21 23:47:00 +00002163 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2164 (int)inv->dlg->remote.info_str.slen,
2165 inv->dlg->remote.info_str.ptr,
2166 (int)refer_to->hvalue.slen,
2167 refer_to->hvalue.ptr));
2168
2169 /* Init callback */
Benny Prijonoac623b32006-07-03 15:19:31 +00002170 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono26ff9062006-02-21 23:47:00 +00002171 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2172
2173 /* Create transferee event subscription */
2174 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2175 if (status != PJ_SUCCESS) {
2176 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +00002177 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002178 return;
2179 }
2180
2181 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002182 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002183
2184 /* Create initial NOTIFY request */
2185 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2186 100, NULL, &tdata);
2187 if (status != PJ_SUCCESS) {
2188 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
2189 return;
2190 }
2191
2192 /* Send initial NOTIFY request */
2193 status = pjsip_xfer_send_request( sub, tdata);
2194 if (status != PJ_SUCCESS) {
2195 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2196 return;
2197 }
2198
2199 /* We're cheating here.
2200 * We need to get a null terminated string from a pj_str_t.
2201 * So grab the pointer from the hvalue and NULL terminate it, knowing
2202 * that the NULL position will be occupied by a newline.
2203 */
2204 uri = refer_to->hvalue.ptr;
2205 uri[refer_to->hvalue.slen] = '\0';
2206
2207 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002208 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2210 existing_call->user_data, NULL,
2211 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002212 if (status != PJ_SUCCESS) {
2213
2214 /* Notify xferer about the error */
2215 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2216 500, NULL, &tdata);
2217 if (status != PJ_SUCCESS) {
2218 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2219 status);
2220 return;
2221 }
2222 status = pjsip_xfer_send_request(sub, tdata);
2223 if (status != PJ_SUCCESS) {
2224 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2225 status);
2226 return;
2227 }
2228 return;
2229 }
2230
2231 /* Put the server subscription in inv_data.
2232 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2233 * reported back to the server subscription.
2234 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002235 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002236
2237 /* Put the invite_data in the subscription. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002238 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2239 &pjsua_var.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +00002240}
2241
2242
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002243
Benny Prijono26ff9062006-02-21 23:47:00 +00002244/*
2245 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002246 * session. We use this to trap:
2247 * - incoming REFER request.
2248 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002249 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002250static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2251 pjsip_transaction *tsx,
2252 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002253{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002254 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2255
2256 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002257
Benny Prijono26ff9062006-02-21 23:47:00 +00002258 if (tsx->role==PJSIP_ROLE_UAS &&
2259 tsx->state==PJSIP_TSX_STATE_TRYING &&
2260 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2261 {
2262 /*
2263 * Incoming REFER request.
2264 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002265 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002266
Benny Prijono26ff9062006-02-21 23:47:00 +00002267 }
Benny Prijonob0808372006-03-02 21:18:58 +00002268 else if (tsx->role==PJSIP_ROLE_UAS &&
2269 tsx->state==PJSIP_TSX_STATE_TRYING &&
2270 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2271 {
2272 /*
2273 * Incoming MESSAGE request!
2274 */
2275 pjsip_rx_data *rdata;
2276 pjsip_msg *msg;
2277 pjsip_accept_hdr *accept_hdr;
2278 pj_status_t status;
2279
2280 rdata = e->body.tsx_state.src.rdata;
2281 msg = rdata->msg_info.msg;
2282
2283 /* Request MUST have message body, with Content-Type equal to
2284 * "text/plain".
2285 */
2286 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2287
2288 pjsip_hdr hdr_list;
2289
2290 pj_list_init(&hdr_list);
2291 pj_list_push_back(&hdr_list, accept_hdr);
2292
2293 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2294 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002295 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002296 return;
2297 }
2298
2299 /* Respond with 200 first, so that remote doesn't retransmit in case
2300 * the UI takes too long to process the message.
2301 */
2302 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2303
2304 /* Process MESSAGE request */
2305 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2306 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002307
Benny Prijonob0808372006-03-02 21:18:58 +00002308 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002309 else if (tsx->role == PJSIP_ROLE_UAC &&
2310 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002311 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002312 /* Handle outgoing pager status */
2313 if (tsx->status_code >= 200) {
2314 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002315
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002316 im_data = tsx->mod_data[pjsua_var.mod.id];
2317 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002318
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002319 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2320 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2321 &im_data->to,
2322 &im_data->body,
2323 im_data->user_data,
2324 tsx->status_code,
2325 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002326 }
Benny Prijonofccab712006-02-22 22:23:22 +00002327 }
Benny Prijono834aee32006-02-19 01:38:06 +00002328 }
Benny Prijono834aee32006-02-19 01:38:06 +00002329
Benny Prijono26ff9062006-02-21 23:47:00 +00002330
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002331 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002332}