blob: 466d981878c27c2108b586216383348e420a6e4c [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. */
107 pj_memset(&inv_cb, 0, sizeof(inv_cb));
108 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 Prijono84126ab2006-02-09 09:30:09 +0000194 pjsip_tx_data *tdata;
195 pj_status_t status;
196
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 /* Check that account is valid */
199 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200 PJ_EINVAL);
201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 /* Options must be zero for now */
203 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
204
205 PJSUA_LOCK();
206
207 acc = &pjsua_var.acc[acc_id];
208 if (!acc->valid) {
209 pjsua_perror(THIS_FILE, "Unable to make call because account "
210 "is not valid", PJ_EINVALIDOP);
211 PJSUA_UNLOCK();
212 return PJ_EINVALIDOP;
213 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000214
Benny Prijonoa91a0032006-02-26 21:23:45 +0000215 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
217 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000218 break;
219 }
220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 if (call_id == pjsua_var.ua_cfg.max_calls) {
222 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
223 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000224 return PJ_ETOOMANY;
225 }
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 call = &pjsua_var.calls[call_id];
228
Benny Prijonoe21e7842006-04-09 16:46:05 +0000229 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000231
Benny Prijonoe21e7842006-04-09 16:46:05 +0000232 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000234
235 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000236 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 &acc->cfg.id, &acc->cfg.contact,
238 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000239 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000240 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000241 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000242 return status;
243 }
244
245 /* Get media capability from media endpoint: */
246
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000247 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
248 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000249 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000250 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000251 goto on_error;
252 }
253
254 /* Create the INVITE session: */
255
256 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
257 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000258 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000259 goto on_error;
260 }
261
262
263 /* Create and associate our data in the session. */
264
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000265 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000266
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 dlg->mod_data[pjsua_var.mod.id] = call;
268 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000269
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000270 /* Attach user data */
271 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000272
273 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000274 if (!pj_list_empty(&acc->route_set))
275 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000276
277
278 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000279 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000280 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000282 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000283
284
285 /* Create initial INVITE: */
286
287 status = pjsip_inv_invite(inv, &tdata);
288 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000289 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
290 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000291 goto on_error;
292 }
293
294
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000295 /* Add additional headers etc */
296
297 pjsua_process_msg_data( tdata, msg_data);
298
Benny Prijono84126ab2006-02-09 09:30:09 +0000299 /* Send initial INVITE: */
300
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000301 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000302 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000303 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
304 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000305
306 /* Upon failure to send first request, both dialog and invite
307 * session would have been cleared.
308 */
309 inv = NULL;
310 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000311 goto on_error;
312 }
313
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000315
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000316 ++pjsua_var.call_cnt;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000317
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000318 if (p_call_id)
319 *p_call_id = call_id;
320
321 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000322
323 return PJ_SUCCESS;
324
325
326on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000327 if (inv != NULL) {
328 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000329 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000330 pjsip_dlg_terminate(dlg);
331 }
332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333 if (call_id != -1) {
334 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000335 }
336
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000338 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000339}
340
341
342/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000343 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000346pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000347{
348 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
349 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
350 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000351 pjsip_tx_data *response = NULL;
352 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000353 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 int acc_id;
355 pjsua_call *call;
356 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000357 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000358 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000359
Benny Prijono26ff9062006-02-21 23:47:00 +0000360 /* Don't want to handle anything but INVITE */
361 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
362 return PJ_FALSE;
363
364 /* Don't want to handle anything that's already associated with
365 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000366 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000367 if (dlg || tsx)
368 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000369
Benny Prijono84126ab2006-02-09 09:30:09 +0000370
Benny Prijono26ff9062006-02-21 23:47:00 +0000371 /* Verify that we can handle the request. */
372 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000373 pjsua_var.endpt, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000374 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000375
Benny Prijono26ff9062006-02-21 23:47:00 +0000376 /*
377 * No we can't handle the incoming INVITE request.
378 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000379
Benny Prijono26ff9062006-02-21 23:47:00 +0000380 if (response) {
381 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000382
Benny Prijono26ff9062006-02-21 23:47:00 +0000383 pjsip_get_response_addr(response->pool, rdata, &res_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
Benny Prijono26ff9062006-02-21 23:47:00 +0000385 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000386
387 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000388
Benny Prijono26ff9062006-02-21 23:47:00 +0000389 /* Respond with 500 (Internal Server Error) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000390 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000391 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000392 }
393
Benny Prijono26ff9062006-02-21 23:47:00 +0000394 return PJ_TRUE;
395 }
396
397
398 /*
399 * Yes we can handle the incoming INVITE request.
400 */
401
402 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
404 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000405 break;
406 }
407
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000408 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
409 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000410 PJSIP_SC_BUSY_HERE, NULL,
411 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000412 PJ_LOG(2,(THIS_FILE,
413 "Unable to accept incoming call (too many calls)"));
Benny Prijono84126ab2006-02-09 09:30:09 +0000414 return PJ_TRUE;
415 }
416
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 /* Clear call descriptor */
418 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000419
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420 call = &pjsua_var.calls[call_id];
421
422 /* Mark call start time. */
423 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000424
Benny Prijono26ff9062006-02-21 23:47:00 +0000425 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
427 rdata->tp_info.pool, 1,
428 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000429 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000430 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000431 NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000432 return PJ_TRUE;
433 }
434
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000436 * Get which account is most likely to be associated with this incoming
437 * call. We need the account to find which contact URI to put for
438 * the call.
439 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000441
Benny Prijono26ff9062006-02-21 23:47:00 +0000442 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000443 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000444 &pjsua_var.acc[acc_id].cfg.contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000445 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000446 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000448 NULL, NULL);
449
Benny Prijono26ff9062006-02-21 23:47:00 +0000450 return PJ_TRUE;
451 }
452
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000453 /* Set credentials */
454 if (pjsua_var.acc[acc_id].cred_cnt) {
455 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
456 pjsua_var.acc[acc_id].cred_cnt,
457 pjsua_var.acc[acc_id].cred);
458 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000459
460 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000461 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
462 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000463 pjsip_hdr hdr_list;
464 pjsip_warning_hdr *w;
465
466 w = pjsip_warning_hdr_create_from_status(dlg->pool,
467 pjsip_endpt_name(pjsua_var.endpt),
468 status);
469 pj_list_init(&hdr_list);
470 pj_list_push_back(&hdr_list, w);
471
472 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
473
474 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000475 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000476 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000477 return PJ_TRUE;
478 }
479
480
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481 /* Create and attach pjsua_var data to the dialog: */
482 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000483
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484 dlg->mod_data[pjsua_var.mod.id] = call;
485 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000486
487
Benny Prijono64f851e2006-02-23 13:49:28 +0000488 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000489 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000490 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000492 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000493 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
494 status);
495
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
497 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000498 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000499
500 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000501 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000502 if (status != PJ_SUCCESS)
503 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000504 }
505
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000506 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000507
Benny Prijono105217f2006-03-06 16:25:59 +0000508
Benny Prijono8b1889b2006-06-06 18:40:40 +0000509 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 if (pjsua_var.ua_cfg.cb.on_incoming_call)
511 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000512
Benny Prijono26ff9062006-02-21 23:47:00 +0000513 /* This INVITE request has been handled. */
514 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000515}
516
517
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518
519/*
520 * Check if the specified call has active INVITE session and the INVITE
521 * session has not been disconnected.
522 */
523PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
524{
525 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
526 PJ_EINVAL);
527 return pjsua_var.calls[call_id].inv != NULL &&
528 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
529}
530
531
532/*
533 * Check if call has an active media session.
534 */
535PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
536{
537 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
538 PJ_EINVAL);
539 return pjsua_var.calls[call_id].session != NULL;
540}
541
542
543/*
544 * Get the conference port identification associated with the call.
545 */
546PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
547{
548 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
549 PJ_EINVAL);
550 return pjsua_var.calls[call_id].conf_slot;
551}
552
553
554/*
555 * Obtain detail information about the specified call.
556 */
557PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
558 pjsua_call_info *info)
559{
560 pjsua_call *call;
561
562 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
563 PJ_EINVAL);
564
565 pj_memset(info, 0, sizeof(*info));
566
567 PJSUA_LOCK();
568
569 call = &pjsua_var.calls[call_id];
570
571 if (call->inv == NULL) {
572 PJSUA_UNLOCK();
573 return PJ_SUCCESS;
574 }
575
576 pjsip_dlg_inc_lock(call->inv->dlg);
577
578
579 /* id and role */
580 info->id = call_id;
581 info->role = call->inv->role;
582
583 /* local info */
584 info->local_info.ptr = info->buf_.local_info;
585 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
586 sizeof(info->buf_.local_info));
587
588 /* local contact */
589 info->local_contact.ptr = info->buf_.local_contact;
590 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
591 call->inv->dlg->local.contact->uri,
592 info->local_contact.ptr,
593 sizeof(info->buf_.local_contact));
594
595 /* remote info */
596 info->remote_info.ptr = info->buf_.remote_info;
597 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
598 sizeof(info->buf_.remote_info));
599
600 /* remote contact */
601 if (call->inv->dlg->remote.contact) {
602 int len;
603 info->remote_contact.ptr = info->buf_.remote_contact;
604 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
605 call->inv->dlg->remote.contact->uri,
606 info->remote_contact.ptr,
607 sizeof(info->buf_.remote_contact));
608 if (len < 0) len = 0;
609 info->remote_contact.slen = len;
610 } else {
611 info->remote_contact.slen = 0;
612 }
613
614 /* call id */
615 info->call_id.ptr = info->buf_.call_id;
616 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
617 sizeof(info->buf_.call_id));
618
619 /* state, state_text */
620 info->state = call->inv->state;
621 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
622
623 /* If call is disconnected, set the last_status from the cause code */
624 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
625 /* last_status, last_status_text */
626 info->last_status = call->inv->cause;
627
628 info->last_status_text.ptr = info->buf_.last_status_text;
629 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
630 sizeof(info->buf_.last_status_text));
631 } else {
632 /* last_status, last_status_text */
633 info->last_status = call->last_code;
634
635 info->last_status_text.ptr = info->buf_.last_status_text;
636 pj_strncpy(&info->last_status_text, &call->last_text,
637 sizeof(info->buf_.last_status_text));
638 }
639
640 /* media status and dir */
641 info->media_status = call->media_st;
642 info->media_dir = call->media_dir;
643
644
645 /* conference slot number */
646 info->conf_slot = call->conf_slot;
647
648 /* calculate duration */
649 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
650
651 info->total_duration = call->dis_time;
652 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
653
654 if (call->conn_time.sec) {
655 info->connect_duration = call->dis_time;
656 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
657 }
658
659 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
660
661 pj_gettimeofday(&info->total_duration);
662 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
663
664 pj_gettimeofday(&info->connect_duration);
665 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
666
667 } else {
668 pj_gettimeofday(&info->total_duration);
669 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
670 }
671
672 pjsip_dlg_dec_lock(call->inv->dlg);
673 PJSUA_UNLOCK();
674
675 return PJ_SUCCESS;
676}
677
678
679/*
680 * Attach application specific data to the call.
681 */
682PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
683 void *user_data)
684{
685 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
686 PJ_EINVAL);
687 pjsua_var.calls[call_id].user_data = user_data;
688
689 return PJ_SUCCESS;
690}
691
692
693/*
694 * Get user data attached to the call.
695 */
696PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
697{
698 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
699 NULL);
700 return pjsua_var.calls[call_id].user_data;
701}
702
703
704/*
705 * Send response to incoming INVITE request.
706 */
707PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
708 unsigned code,
709 const pj_str_t *reason,
710 const pjsua_msg_data *msg_data)
711{
712 pjsua_call *call;
713 pjsip_tx_data *tdata;
714 pj_status_t status;
715
716 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
717 PJ_EINVAL);
718
719 PJSUA_LOCK();
720
721 call = &pjsua_var.calls[call_id];
722
723 if (call->inv == NULL) {
724 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id));
725 PJSUA_UNLOCK();
726 return PJSIP_ESESSIONTERMINATED;
727 }
728
Benny Prijono2e507c22006-06-23 15:04:11 +0000729 if (call->res_time.sec == 0)
730 pj_gettimeofday(&call->res_time);
731
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000732 /* Create response message */
733 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
734 if (status != PJ_SUCCESS) {
735 pjsua_perror(THIS_FILE, "Error creating response",
736 status);
737 PJSUA_UNLOCK();
738 return status;
739 }
740
741 /* Add additional headers etc */
742 pjsua_process_msg_data( tdata, msg_data);
743
744 /* Send the message */
745 status = pjsip_inv_send_msg(call->inv, tdata);
746 if (status != PJ_SUCCESS)
747 pjsua_perror(THIS_FILE, "Error sending response",
748 status);
749
750 PJSUA_UNLOCK();
751
752 return status;
753}
754
755
756/*
757 * Hangup call by using method that is appropriate according to the
758 * call state.
759 */
760PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
761 unsigned code,
762 const pj_str_t *reason,
763 const pjsua_msg_data *msg_data)
764{
765 pjsua_call *call;
766 pj_status_t status;
767 pjsip_tx_data *tdata;
768
769
770 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
771 PJ_EINVAL);
772
773 PJSUA_LOCK();
774
775 call = &pjsua_var.calls[call_id];
776
777 if (!call->inv) {
778 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
779 PJSUA_UNLOCK();
780 return PJ_EINVAL;
781 }
782
783 if (code==0) {
784 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
785 code = PJSIP_SC_OK;
786 else if (call->inv->role == PJSIP_ROLE_UAS)
787 code = PJSIP_SC_DECLINE;
788 else
789 code = PJSIP_SC_REQUEST_TERMINATED;
790 }
791
792 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
793 if (status != PJ_SUCCESS) {
794 pjsua_perror(THIS_FILE,
795 "Failed to create end session message",
796 status);
797 PJSUA_UNLOCK();
798 return status;
799 }
800
801 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
802 * as p_tdata when INVITE transaction has not been answered
803 * with any provisional responses.
804 */
805 if (tdata == NULL) {
806 PJSUA_UNLOCK();
807 return PJ_SUCCESS;
808 }
809
810 /* Add additional headers etc */
811 pjsua_process_msg_data( tdata, msg_data);
812
813 /* Send the message */
814 status = pjsip_inv_send_msg(call->inv, tdata);
815 if (status != PJ_SUCCESS) {
816 pjsua_perror(THIS_FILE,
817 "Failed to send end session message",
818 status);
819 PJSUA_UNLOCK();
820 return status;
821 }
822
823 PJSUA_UNLOCK();
824
825 return PJ_SUCCESS;
826}
827
828
829/*
830 * Put the specified call on hold.
831 */
832PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
833 const pjsua_msg_data *msg_data)
834{
835 pjmedia_sdp_session *sdp;
836 pjsua_call *call;
837 pjsip_tx_data *tdata;
838 pj_status_t status;
839
840 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
841 PJ_EINVAL);
842
843 PJSUA_LOCK();
844
845 call = &pjsua_var.calls[call_id];
846
847 if (!call->inv) {
848 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
849 PJSUA_UNLOCK();
850 return PJSIP_ESESSIONTERMINATED;
851 }
852
853 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
854 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
855 PJSUA_UNLOCK();
856 return PJSIP_ESESSIONSTATE;
857 }
858
859 status = create_inactive_sdp(call, &sdp);
860 if (status != PJ_SUCCESS) {
861 PJSUA_UNLOCK();
862 return status;
863 }
864
865 /* Create re-INVITE with new offer */
866 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
867 if (status != PJ_SUCCESS) {
868 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
869 PJSUA_UNLOCK();
870 return status;
871 }
872
873 /* Add additional headers etc */
874 pjsua_process_msg_data( tdata, msg_data);
875
876 /* Send the request */
877 status = pjsip_inv_send_msg( call->inv, tdata);
878 if (status != PJ_SUCCESS) {
879 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
880 PJSUA_UNLOCK();
881 return status;
882 }
883
884 PJSUA_UNLOCK();
885
886 return PJ_SUCCESS;
887}
888
889
890/*
891 * Send re-INVITE (to release hold).
892 */
893PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
894 pj_bool_t unhold,
895 const pjsua_msg_data *msg_data)
896{
897 pjmedia_sdp_session *sdp;
898 pjsip_tx_data *tdata;
899 pjsua_call *call;
900 pj_status_t status;
901
902
903 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
904 PJ_EINVAL);
905
906 PJSUA_LOCK();
907
908 call = &pjsua_var.calls[call_id];
909
910 if (!call->inv) {
911 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
912 PJSUA_UNLOCK();
913 return PJSIP_ESESSIONTERMINATED;
914 }
915
916
917 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
918 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
919 PJSUA_UNLOCK();
920 return PJSIP_ESESSIONSTATE;
921 }
922
923 /* Create SDP */
924 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
925 1, &call->skinfo, &sdp);
926 if (status != PJ_SUCCESS) {
927 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
928 status);
929 PJSUA_UNLOCK();
930 return status;
931 }
932
933 /* Create re-INVITE with new offer */
934 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
935 if (status != PJ_SUCCESS) {
936 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
937 PJSUA_UNLOCK();
938 return status;
939 }
940
941 /* Add additional headers etc */
942 pjsua_process_msg_data( tdata, msg_data);
943
944 /* Send the request */
945 status = pjsip_inv_send_msg( call->inv, tdata);
946 if (status != PJ_SUCCESS) {
947 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
948 PJSUA_UNLOCK();
949 return status;
950 }
951
952 PJSUA_UNLOCK();
953
954 return PJ_SUCCESS;
955}
956
957
958/*
959 * Initiate call transfer to the specified address.
960 */
961PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
962 const pj_str_t *dest,
963 const pjsua_msg_data *msg_data)
964{
965 pjsip_evsub *sub;
966 pjsip_tx_data *tdata;
967 pjsua_call *call;
968 pj_status_t status;
969
970
971 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
972 PJ_EINVAL);
973
974 PJSUA_LOCK();
975
976 call = &pjsua_var.calls[call_id];
977
978 if (!call->inv) {
979 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
980 PJSUA_UNLOCK();
981 return PJSIP_ESESSIONTERMINATED;
982 }
983
984 /* Create xfer client subscription.
985 * We're not interested in knowing the transfer result, so we
986 * put NULL as the callback.
987 */
988 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
989 if (status != PJ_SUCCESS) {
990 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
991 PJSUA_UNLOCK();
992 return status;
993 }
994
995 /*
996 * Create REFER request.
997 */
998 status = pjsip_xfer_initiate(sub, dest, &tdata);
999 if (status != PJ_SUCCESS) {
1000 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1001 PJSUA_UNLOCK();
1002 return status;
1003 }
1004
1005 /* Add additional headers etc */
1006 pjsua_process_msg_data( tdata, msg_data);
1007
1008 /* Send. */
1009 status = pjsip_xfer_send_request(sub, tdata);
1010 if (status != PJ_SUCCESS) {
1011 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1012 PJSUA_UNLOCK();
1013 return status;
1014 }
1015
1016 /* For simplicity (that's what this program is intended to be!),
1017 * leave the original invite session as it is. More advanced application
1018 * may want to hold the INVITE, or terminate the invite, or whatever.
1019 */
1020
1021 PJSUA_UNLOCK();
1022
1023 return PJ_SUCCESS;
1024
1025}
1026
1027
1028/*
1029 * Send DTMF digits to remote using RFC 2833 payload formats.
1030 */
1031PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1032 const pj_str_t *digits)
1033{
1034 pjsua_call *call;
1035 pj_status_t status;
1036
1037 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1038 PJ_EINVAL);
1039
1040 PJSUA_LOCK();
1041
1042 call = &pjsua_var.calls[call_id];
1043
1044 if (!call->session) {
1045 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1046 PJSUA_UNLOCK();
1047 return PJ_EINVALIDOP;
1048 }
1049
1050 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1051
1052 PJSUA_UNLOCK();
1053
1054 return status;
1055}
1056
1057
1058/**
1059 * Send instant messaging inside INVITE session.
1060 */
1061PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1062 const pj_str_t *mime_type,
1063 const pj_str_t *content,
1064 const pjsua_msg_data *msg_data,
1065 void *user_data)
1066{
1067 pjsua_call *call;
1068 const pj_str_t mime_text_plain = pj_str("text/plain");
1069 pjsip_media_type ctype;
1070 pjsua_im_data *im_data;
1071 pjsip_tx_data *tdata;
1072 pj_status_t status;
1073
1074
1075 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1076 PJ_EINVAL);
1077
1078 PJSUA_LOCK();
1079
1080 call = &pjsua_var.calls[call_id];
1081
1082 if (!call->inv) {
1083 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1084 PJSUA_UNLOCK();
1085 return PJSIP_ESESSIONTERMINATED;
1086 }
1087
1088 /* Lock dialog. */
1089 pjsip_dlg_inc_lock(call->inv->dlg);
1090
1091 /* Set default media type if none is specified */
1092 if (mime_type == NULL) {
1093 mime_type = &mime_text_plain;
1094 }
1095
1096 /* Create request message. */
1097 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1098 -1, &tdata);
1099 if (status != PJ_SUCCESS) {
1100 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1101 goto on_return;
1102 }
1103
1104 /* Add accept header. */
1105 pjsip_msg_add_hdr( tdata->msg,
1106 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1107
1108 /* Parse MIME type */
1109 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1110
1111 /* Create "text/plain" message body. */
1112 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1113 &ctype.subtype, content);
1114 if (tdata->msg->body == NULL) {
1115 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1116 pjsip_tx_data_dec_ref(tdata);
1117 goto on_return;
1118 }
1119
1120 /* Add additional headers etc */
1121 pjsua_process_msg_data( tdata, msg_data);
1122
1123 /* Create IM data and attach to the request. */
1124 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1125 im_data->acc_id = call->acc_id;
1126 im_data->call_id = call_id;
1127 im_data->to = call->inv->dlg->remote.info_str;
1128 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1129 im_data->user_data = user_data;
1130
1131
1132 /* Send the request. */
1133 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1134 pjsua_var.mod.id, im_data);
1135 if (status != PJ_SUCCESS) {
1136 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1137 goto on_return;
1138 }
1139
1140on_return:
1141 pjsip_dlg_dec_lock(call->inv->dlg);
1142 PJSUA_UNLOCK();
1143 return status;
1144}
1145
1146
1147/*
1148 * Send IM typing indication inside INVITE session.
1149 */
1150PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1151 pj_bool_t is_typing,
1152 const pjsua_msg_data*msg_data)
1153{
1154 pjsua_call *call;
1155 pjsip_tx_data *tdata;
1156 pj_status_t status;
1157
1158
1159 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1160 PJ_EINVAL);
1161
1162 PJSUA_LOCK();
1163
1164 call = &pjsua_var.calls[call_id];
1165
1166 if (!call->inv) {
1167 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1168 PJSUA_UNLOCK();
1169 return PJSIP_ESESSIONTERMINATED;
1170 }
1171
1172 /* Lock dialog. */
1173 pjsip_dlg_inc_lock(call->inv->dlg);
1174
1175 /* Create request message. */
1176 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1177 -1, &tdata);
1178 if (status != PJ_SUCCESS) {
1179 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1180 goto on_return;
1181 }
1182
1183 /* Create "application/im-iscomposing+xml" msg body. */
1184 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1185 NULL, NULL, -1);
1186
1187 /* Add additional headers etc */
1188 pjsua_process_msg_data( tdata, msg_data);
1189
1190 /* Send the request. */
1191 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1192 if (status != PJ_SUCCESS) {
1193 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1194 goto on_return;
1195 }
1196
1197on_return:
1198 pjsip_dlg_dec_lock(call->inv->dlg);
1199 PJSUA_UNLOCK();
1200 return status;
1201}
1202
1203
1204/*
1205 * Terminate all calls.
1206 */
1207PJ_DEF(void) pjsua_call_hangup_all(void)
1208{
1209 unsigned i;
1210
1211 PJSUA_LOCK();
1212
1213 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1214 if (pjsua_var.calls[i].inv)
1215 pjsua_call_hangup(i, 0, NULL, NULL);
1216 }
1217
1218 PJSUA_UNLOCK();
1219}
1220
1221
1222static const char *good_number(char *buf, pj_int32_t val)
1223{
1224 if (val < 1000) {
1225 pj_ansi_sprintf(buf, "%d", val);
1226 } else if (val < 1000000) {
1227 pj_ansi_sprintf(buf, "%d.%dK",
1228 val / 1000,
1229 (val % 1000) / 100);
1230 } else {
1231 pj_ansi_sprintf(buf, "%d.%02dM",
1232 val / 1000000,
1233 (val % 1000000) / 10000);
1234 }
1235
1236 return buf;
1237}
1238
1239
1240/* Dump media session */
1241static void dump_media_session(const char *indent,
1242 char *buf, unsigned maxlen,
1243 pjmedia_session *session)
1244{
1245 unsigned i;
1246 char *p = buf, *end = buf+maxlen;
1247 int len;
1248 pjmedia_session_info info;
1249
1250 pjmedia_session_get_info(session, &info);
1251
1252 for (i=0; i<info.stream_cnt; ++i) {
1253 pjmedia_rtcp_stat stat;
1254 const char *rem_addr;
1255 int rem_port;
1256 const char *dir;
1257 char last_update[40];
1258 char packets[16], bytes[16], ipbytes[16];
1259 pj_time_val now;
1260
1261 pjmedia_session_get_stream_stat(session, i, &stat);
1262 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1263 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1264
1265 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1266 dir = "sendonly";
1267 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1268 dir = "recvonly";
1269 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1270 dir = "sendrecv";
1271 else
1272 dir = "inactive";
1273
1274
1275 len = pj_ansi_snprintf(buf, end-p,
1276 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1277 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001278 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001279 info.stream_info[i].fmt.encoding_name.ptr,
1280 info.stream_info[i].fmt.clock_rate / 1000,
1281 dir,
1282 rem_addr, rem_port);
1283 if (len < 1 || len > end-p) {
1284 *p = '\0';
1285 return;
1286 }
1287
1288 p += len;
1289 *p++ = '\n';
1290 *p = '\0';
1291
1292 if (stat.rx.update_cnt == 0)
1293 strcpy(last_update, "never");
1294 else {
1295 pj_gettimeofday(&now);
1296 PJ_TIME_VAL_SUB(now, stat.rx.update);
1297 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1298 now.sec / 3600,
1299 (now.sec % 3600) / 60,
1300 now.sec % 60,
1301 now.msec);
1302 }
1303
1304 len = pj_ansi_snprintf(p, end-p,
1305 "%s RX pt=%d, stat last update: %s\n"
1306 "%s total %spkt %sB (%sB +IP hdr)\n"
1307 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1308 "%s (msec) min avg max last\n"
1309 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1310 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1311 indent, info.stream_info[i].fmt.pt,
1312 last_update,
1313 indent,
1314 good_number(packets, stat.rx.pkt),
1315 good_number(bytes, stat.rx.bytes),
1316 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
1317 indent,
1318 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001319 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001321 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001323 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001324 indent, indent,
1325 stat.rx.loss_period.min / 1000.0,
1326 stat.rx.loss_period.avg / 1000.0,
1327 stat.rx.loss_period.max / 1000.0,
1328 stat.rx.loss_period.last / 1000.0,
1329 indent,
1330 stat.rx.jitter.min / 1000.0,
1331 stat.rx.jitter.avg / 1000.0,
1332 stat.rx.jitter.max / 1000.0,
1333 stat.rx.jitter.last / 1000.0,
1334 ""
1335 );
1336
1337 if (len < 1 || len > end-p) {
1338 *p = '\0';
1339 return;
1340 }
1341
1342 p += len;
1343 *p++ = '\n';
1344 *p = '\0';
1345
1346 if (stat.tx.update_cnt == 0)
1347 strcpy(last_update, "never");
1348 else {
1349 pj_gettimeofday(&now);
1350 PJ_TIME_VAL_SUB(now, stat.tx.update);
1351 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1352 now.sec / 3600,
1353 (now.sec % 3600) / 60,
1354 now.sec % 60,
1355 now.msec);
1356 }
1357
1358 len = pj_ansi_snprintf(p, end-p,
1359 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
1360 "%s total %spkt %sB (%sB +IP hdr)\n"
1361 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1362 "%s (msec) min avg max last\n"
1363 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1364 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1365 indent,
1366 info.stream_info[i].tx_pt,
1367 info.stream_info[i].param->info.frm_ptime *
1368 info.stream_info[i].param->setting.frm_per_pkt,
1369 last_update,
1370
1371 indent,
1372 good_number(packets, stat.tx.pkt),
1373 good_number(bytes, stat.tx.bytes),
1374 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
1375
1376 indent,
1377 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001378 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001379 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001380 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001381 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001382 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383
1384 indent, indent,
1385 stat.tx.loss_period.min / 1000.0,
1386 stat.tx.loss_period.avg / 1000.0,
1387 stat.tx.loss_period.max / 1000.0,
1388 stat.tx.loss_period.last / 1000.0,
1389 indent,
1390 stat.tx.jitter.min / 1000.0,
1391 stat.tx.jitter.avg / 1000.0,
1392 stat.tx.jitter.max / 1000.0,
1393 stat.tx.jitter.last / 1000.0,
1394 ""
1395 );
1396
1397 if (len < 1 || len > end-p) {
1398 *p = '\0';
1399 return;
1400 }
1401
1402 p += len;
1403 *p++ = '\n';
1404 *p = '\0';
1405
1406 len = pj_ansi_snprintf(p, end-p,
1407 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1408 indent,
1409 stat.rtt.min / 1000.0,
1410 stat.rtt.avg / 1000.0,
1411 stat.rtt.max / 1000.0,
1412 stat.rtt.last / 1000.0
1413 );
1414 if (len < 1 || len > end-p) {
1415 *p = '\0';
1416 return;
1417 }
1418
1419 p += len;
1420 *p++ = '\n';
1421 *p = '\0';
1422 }
1423}
1424
1425
1426/* Print call info */
1427static void print_call(const char *title,
1428 int call_id,
1429 char *buf, pj_size_t size)
1430{
1431 int len;
1432 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1433 pjsip_dialog *dlg = inv->dlg;
1434 char userinfo[128];
1435
1436 /* Dump invite sesion info. */
1437
1438 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1439 if (len < 1)
1440 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1441 else
1442 userinfo[len] = '\0';
1443
1444 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1445 title,
1446 pjsip_inv_state_name(inv->state),
1447 userinfo);
1448 if (len < 1 || len >= (int)size) {
1449 pj_ansi_strcpy(buf, "<--uri too long-->");
1450 len = 18;
1451 } else
1452 buf[len] = '\0';
1453}
1454
1455
1456/*
1457 * Dump call and media statistics to string.
1458 */
1459PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1460 pj_bool_t with_media,
1461 char *buffer,
1462 unsigned maxlen,
1463 const char *indent)
1464{
1465 pjsua_call *call;
1466 pj_time_val duration, res_delay, con_delay;
1467 char tmp[128];
1468 char *p, *end;
1469 int len;
1470
1471 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1472 PJ_EINVAL);
1473
1474 PJSUA_LOCK();
1475
1476 call = &pjsua_var.calls[call_id];
1477
1478 *buffer = '\0';
1479 p = buffer;
1480 end = buffer + maxlen;
1481 len = 0;
1482
1483 if (call->inv == NULL) {
1484 PJSUA_UNLOCK();
1485 return PJ_EINVALIDOP;
1486 }
1487
1488 print_call(indent, call_id, tmp, sizeof(tmp));
1489
1490 len = pj_ansi_strlen(tmp);
1491 pj_ansi_strcpy(buffer, tmp);
1492
1493 p += len;
1494 *p++ = '\r';
1495 *p++ = '\n';
1496
1497 /* Calculate call duration */
1498 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1499 pj_gettimeofday(&duration);
1500 PJ_TIME_VAL_SUB(duration, call->conn_time);
1501 con_delay = call->conn_time;
1502 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1503 } else {
1504 duration.sec = duration.msec = 0;
1505 con_delay.sec = con_delay.msec = 0;
1506 }
1507
1508 /* Calculate first response delay */
1509 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1510 res_delay = call->res_time;
1511 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1512 } else {
1513 res_delay.sec = res_delay.msec = 0;
1514 }
1515
1516 /* Print duration */
1517 len = pj_ansi_snprintf(p, end-p,
1518 "%s Call time: %02dh:%02dm:%02ds, "
1519 "1st res in %d ms, conn in %dms",
1520 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001521 (int)(duration.sec / 3600),
1522 (int)((duration.sec % 3600)/60),
1523 (int)(duration.sec % 60),
1524 (int)PJ_TIME_VAL_MSEC(res_delay),
1525 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001526
1527 if (len > 0 && len < end-p) {
1528 p += len;
1529 *p++ = '\n';
1530 *p = '\0';
1531 }
1532
1533 /* Dump session statistics */
1534 if (with_media && call->session)
1535 dump_media_session(indent, p, end-p, call->session);
1536
1537 PJSUA_UNLOCK();
1538
1539 return PJ_SUCCESS;
1540}
1541
1542
1543/*
1544 * Destroy the call's media
1545 */
1546static pj_status_t call_destroy_media(int call_id)
1547{
1548 pjsua_call *call = &pjsua_var.calls[call_id];
1549
1550 if (call->conf_slot != PJSUA_INVALID_ID) {
1551 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1552 call->conf_slot = PJSUA_INVALID_ID;
1553 }
1554
1555 if (call->session) {
1556 /* Destroy session (this will also close RTP/RTCP sockets). */
1557 pjmedia_session_destroy(call->session);
1558 call->session = NULL;
1559
1560 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1561 call_id));
1562
1563 }
1564
1565 call->media_st = PJSUA_CALL_MEDIA_NONE;
1566
1567 return PJ_SUCCESS;
1568}
1569
1570
Benny Prijono84126ab2006-02-09 09:30:09 +00001571/*
1572 * This callback receives notification from invite session when the
1573 * session state has changed.
1574 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001575static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1576 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001577{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001579
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001580 PJSUA_LOCK();
1581
1582 call = inv->dlg->mod_data[pjsua_var.mod.id];
1583
1584 if (!call) {
1585 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001586 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001587 }
1588
Benny Prijonoe21e7842006-04-09 16:46:05 +00001589
1590 /* Get call times */
1591 switch (inv->state) {
1592 case PJSIP_INV_STATE_EARLY:
1593 case PJSIP_INV_STATE_CONNECTING:
1594 if (call->res_time.sec == 0)
1595 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001596 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001597 pj_strncpy(&call->last_text,
1598 &e->body.tsx_state.tsx->status_text,
1599 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001600 break;
1601 case PJSIP_INV_STATE_CONFIRMED:
1602 pj_gettimeofday(&call->conn_time);
1603 break;
1604 case PJSIP_INV_STATE_DISCONNECTED:
1605 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001606 if (call->res_time.sec == 0)
1607 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001608 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1609 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610 pj_strncpy(&call->last_text,
1611 &e->body.tsx_state.tsx->status_text,
1612 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001613 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001614 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001615 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001616 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 pj_strncpy(&call->last_text,
1618 &e->body.tsx_state.tsx->status_text,
1619 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001620 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001621 }
1622
Benny Prijono26ff9062006-02-21 23:47:00 +00001623 /* If this is an outgoing INVITE that was created because of
1624 * REFER/transfer, send NOTIFY to transferer.
1625 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001626 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001627 int st_code = -1;
1628 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1629
1630
Benny Prijonoa91a0032006-02-26 21:23:45 +00001631 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001632 case PJSIP_INV_STATE_NULL:
1633 case PJSIP_INV_STATE_CALLING:
1634 /* Do nothing */
1635 break;
1636
1637 case PJSIP_INV_STATE_EARLY:
1638 case PJSIP_INV_STATE_CONNECTING:
1639 st_code = e->body.tsx_state.tsx->status_code;
1640 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1641 break;
1642
1643 case PJSIP_INV_STATE_CONFIRMED:
1644 /* When state is confirmed, send the final 200/OK and terminate
1645 * subscription.
1646 */
1647 st_code = e->body.tsx_state.tsx->status_code;
1648 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1649 break;
1650
1651 case PJSIP_INV_STATE_DISCONNECTED:
1652 st_code = e->body.tsx_state.tsx->status_code;
1653 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1654 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001655
Benny Prijono8b1889b2006-06-06 18:40:40 +00001656 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001657 /* Nothing to do. Just to keep gcc from complaining about
1658 * unused enums.
1659 */
1660 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001661 }
1662
1663 if (st_code != -1) {
1664 pjsip_tx_data *tdata;
1665 pj_status_t status;
1666
Benny Prijonoa91a0032006-02-26 21:23:45 +00001667 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001668 ev_state, st_code,
1669 NULL, &tdata);
1670 if (status != PJ_SUCCESS) {
1671 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1672 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001673 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001674 if (status != PJ_SUCCESS) {
1675 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1676 }
1677 }
1678 }
1679 }
1680
Benny Prijono84126ab2006-02-09 09:30:09 +00001681
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001682 if (pjsua_var.ua_cfg.cb.on_call_state)
1683 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001684
1685 /* call->inv may be NULL now */
1686
Benny Prijono84126ab2006-02-09 09:30:09 +00001687 /* Destroy media session when invite session is disconnected. */
1688 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001689
Benny Prijonoa91a0032006-02-26 21:23:45 +00001690 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001691
Benny Prijono275fd682006-03-22 11:59:11 +00001692 if (call)
1693 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001694
Benny Prijono105217f2006-03-06 16:25:59 +00001695 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001696 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001697 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001698 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001699
1700 PJSUA_UNLOCK();
1701}
1702
1703/*
1704 * This callback is called by invite session framework when UAC session
1705 * has forked.
1706 */
1707static void pjsua_call_on_forked( pjsip_inv_session *inv,
1708 pjsip_event *e)
1709{
1710 PJ_UNUSED_ARG(inv);
1711 PJ_UNUSED_ARG(e);
1712
1713 PJ_TODO(HANDLE_FORKED_DIALOG);
1714}
1715
1716
1717/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00001718 * Disconnect call upon error.
1719 */
1720static void call_disconnect( pjsip_inv_session *inv,
1721 int code )
1722{
1723 pjsip_tx_data *tdata;
1724 pj_status_t status;
1725
1726 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
1727 if (status == PJ_SUCCESS)
1728 pjsip_inv_send_msg(inv, tdata);
1729}
1730
1731/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001732 * Callback to be called when SDP offer/answer negotiation has just completed
1733 * in the session. This function will start/update media if negotiation
1734 * has succeeded.
1735 */
1736static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1737 pj_status_t status)
1738{
1739 int prev_media_st = 0;
1740 pjsua_call *call;
1741 pjmedia_session_info sess_info;
1742 const pjmedia_sdp_session *local_sdp;
1743 const pjmedia_sdp_session *remote_sdp;
1744 pjmedia_port *media_port;
1745 pj_str_t port_name;
1746 char tmp[PJSIP_MAX_URL_SIZE];
1747
1748 PJSUA_LOCK();
1749
1750 call = inv->dlg->mod_data[pjsua_var.mod.id];
1751
1752 if (status != PJ_SUCCESS) {
1753
1754 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1755
1756 /* Disconnect call if we're not in the middle of initializing an
1757 * UAS dialog and if this is not a re-INVITE
1758 */
1759 if (inv->state != PJSIP_INV_STATE_NULL &&
1760 inv->state != PJSIP_INV_STATE_CONFIRMED)
1761 {
1762 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1763 }
1764
1765 PJSUA_UNLOCK();
1766 return;
1767 }
1768
1769 /* Destroy existing media session, if any. */
1770
1771 if (call) {
1772 prev_media_st = call->media_st;
1773 call_destroy_media(call->index);
1774 }
1775
1776 /* Get local and remote SDP */
1777
1778 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1779 if (status != PJ_SUCCESS) {
1780 pjsua_perror(THIS_FILE,
1781 "Unable to retrieve currently active local SDP",
1782 status);
1783 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1784 PJSUA_UNLOCK();
1785 return;
1786 }
1787
1788
1789 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1790 if (status != PJ_SUCCESS) {
1791 pjsua_perror(THIS_FILE,
1792 "Unable to retrieve currently active remote SDP",
1793 status);
1794 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1795 PJSUA_UNLOCK();
1796 return;
1797 }
1798
1799 /* Create media session info based on SDP parameters.
1800 * We only support one stream per session at the moment
1801 */
1802 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1803 pjsua_var.med_endpt,
1804 1,&sess_info,
1805 local_sdp, remote_sdp);
1806 if (status != PJ_SUCCESS) {
1807 pjsua_perror(THIS_FILE, "Unable to create media session",
1808 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001809 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001810 PJSUA_UNLOCK();
1811 return;
1812 }
1813
1814 /* Check if media is put on-hold */
1815 if (sess_info.stream_cnt == 0 ||
1816 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1817 {
1818
1819 /* Determine who puts the call on-hold */
1820 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1821 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1822 /* It was local who offer hold */
1823 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1824 } else {
1825 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1826 }
1827 }
1828
1829 call->media_dir = PJMEDIA_DIR_NONE;
1830
1831 } else {
1832
1833 /* Override ptime, if this option is specified. */
1834 PJ_TODO(set_codec_ptime_in_call);
1835
1836
1837 /* Optionally, application may modify other stream settings here
1838 * (such as jitter buffer parameters, codec ptime, etc.)
1839 */
1840
1841 /* Create session based on session info. */
1842 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1843 &call->med_tp,
1844 call, &call->session );
1845 if (status != PJ_SUCCESS) {
1846 pjsua_perror(THIS_FILE, "Unable to create media session",
1847 status);
1848 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1849 PJSUA_UNLOCK();
1850 return;
1851 }
1852
1853
1854 /* Get the port interface of the first stream in the session.
1855 * We need the port interface to add to the conference bridge.
1856 */
1857 pjmedia_session_get_port(call->session, 0, &media_port);
1858
1859
1860 /*
1861 * Add the call to conference bridge.
1862 */
1863 port_name.ptr = tmp;
1864 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1865 call->inv->dlg->remote.info->uri,
1866 tmp, sizeof(tmp));
1867 if (port_name.slen < 1) {
1868 port_name = pj_str("call");
1869 }
1870 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1871 media_port,
1872 &port_name,
1873 (unsigned*)&call->conf_slot);
1874 if (status != PJ_SUCCESS) {
1875 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1876 status);
1877 call_destroy_media(call->index);
1878 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1879 PJSUA_UNLOCK();
1880 return;
1881 }
1882
1883 /* Call's media state is active */
1884 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1885 call->media_dir = sess_info.stream_info[0].dir;
1886 }
1887
1888 /* Print info. */
1889 {
1890 char info[80];
1891 int info_len = 0;
1892 unsigned i;
1893
1894 for (i=0; i<sess_info.stream_cnt; ++i) {
1895 int len;
1896 const char *dir;
1897 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1898
1899 switch (strm_info->dir) {
1900 case PJMEDIA_DIR_NONE:
1901 dir = "inactive";
1902 break;
1903 case PJMEDIA_DIR_ENCODING:
1904 dir = "sendonly";
1905 break;
1906 case PJMEDIA_DIR_DECODING:
1907 dir = "recvonly";
1908 break;
1909 case PJMEDIA_DIR_ENCODING_DECODING:
1910 dir = "sendrecv";
1911 break;
1912 default:
1913 dir = "unknown";
1914 break;
1915 }
1916 len = pj_ansi_sprintf( info+info_len,
1917 ", stream #%d: %.*s (%s)", i,
1918 (int)strm_info->fmt.encoding_name.slen,
1919 strm_info->fmt.encoding_name.ptr,
1920 dir);
1921 if (len > 0)
1922 info_len += len;
1923 }
1924 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1925 }
1926
1927 /* Call application callback, if any */
1928 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1929 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1930
1931
1932 PJSUA_UNLOCK();
1933}
1934
1935
1936/*
1937 * Create inactive SDP for call hold.
1938 */
1939static pj_status_t create_inactive_sdp(pjsua_call *call,
1940 pjmedia_sdp_session **p_answer)
1941{
1942 pj_status_t status;
1943 pjmedia_sdp_conn *conn;
1944 pjmedia_sdp_attr *attr;
1945 pjmedia_sdp_session *sdp;
1946
1947 /* Create new offer */
1948 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1949 &call->skinfo, &sdp);
1950 if (status != PJ_SUCCESS) {
1951 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1952 return status;
1953 }
1954
1955 /* Get SDP media connection line */
1956 conn = sdp->media[0]->conn;
1957 if (!conn)
1958 conn = sdp->conn;
1959
1960 /* Modify address */
1961 conn->addr = pj_str("0.0.0.0");
1962
1963 /* Remove existing directions attributes */
1964 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
1965 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
1966 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
1967 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
1968
1969 /* Add inactive attribute */
1970 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
1971 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
1972
1973 *p_answer = sdp;
1974
1975 return status;
1976}
1977
1978
1979/*
1980 * Called when session received new offer.
1981 */
1982static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
1983 const pjmedia_sdp_session *offer)
1984{
1985 const char *remote_state;
1986 pjsua_call *call;
1987 pjmedia_sdp_conn *conn;
1988 pjmedia_sdp_session *answer;
1989 pj_bool_t is_remote_active;
1990 pj_status_t status;
1991
1992 PJSUA_LOCK();
1993
1994 call = inv->dlg->mod_data[pjsua_var.mod.id];
1995
1996 /*
1997 * See if remote is offering active media (i.e. not on-hold)
1998 */
1999 is_remote_active = PJ_TRUE;
2000
2001 conn = offer->media[0]->conn;
2002 if (!conn)
2003 conn = offer->conn;
2004
2005 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2006 pj_strcmp2(&conn->addr, "0")==0)
2007 {
2008 is_remote_active = PJ_FALSE;
2009
2010 }
2011 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2012 {
2013 is_remote_active = PJ_FALSE;
2014 }
2015
2016 remote_state = (is_remote_active ? "active" : "inactive");
2017
2018 /* Supply candidate answer */
2019 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2020 PJ_LOG(4,(THIS_FILE,
2021 "Call %d: RX new media offer, creating inactive SDP "
2022 "(media in offer is %s)", call->index, remote_state));
2023 status = create_inactive_sdp( call, &answer );
2024 } else {
2025 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2026 call->index));
2027 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2028 call->inv->pool, 1,
2029 &call->skinfo, &answer);
2030 }
2031
2032 if (status != PJ_SUCCESS) {
2033 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2034 PJSUA_UNLOCK();
2035 return;
2036 }
2037
2038 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2039 if (status != PJ_SUCCESS) {
2040 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2041 PJSUA_UNLOCK();
2042 return;
2043 }
2044
2045 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002046}
2047
2048
2049/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002050 * Callback called by event framework when the xfer subscription state
2051 * has changed.
2052 */
2053static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2054{
2055
2056 PJ_UNUSED_ARG(event);
2057
2058 /*
2059 * We're only interested when subscription is terminated, to
2060 * clear the xfer_sub member of the inv_data.
2061 */
2062 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002063 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002064
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002066 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002067 return;
2068
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002069 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002070 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002071
2072 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2073 }
2074}
2075
2076
2077/*
2078 * Follow transfer (REFER) request.
2079 */
2080static void on_call_transfered( pjsip_inv_session *inv,
2081 pjsip_rx_data *rdata )
2082{
2083 pj_status_t status;
2084 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002085 pjsua_call *existing_call;
2086 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002087 const pj_str_t str_refer_to = { "Refer-To", 8};
2088 pjsip_generic_string_hdr *refer_to;
2089 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002090 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +00002091 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002092 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002093 pjsip_evsub *sub;
2094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002095 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002096
Benny Prijono26ff9062006-02-21 23:47:00 +00002097 /* Find the Refer-To header */
2098 refer_to = (pjsip_generic_string_hdr*)
2099 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2100
2101 if (refer_to == NULL) {
2102 /* Invalid Request.
2103 * No Refer-To header!
2104 */
2105 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002106 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002107 return;
2108 }
2109
Benny Prijono9fc735d2006-05-28 14:58:12 +00002110 /* Notify callback */
2111 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002112 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2113 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2114 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002115
2116 if (code < 200)
2117 code = 200;
2118 if (code >= 300) {
2119 /* Application rejects call transfer request */
2120 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2121 return;
2122 }
2123
Benny Prijono26ff9062006-02-21 23:47:00 +00002124 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2125 (int)inv->dlg->remote.info_str.slen,
2126 inv->dlg->remote.info_str.ptr,
2127 (int)refer_to->hvalue.slen,
2128 refer_to->hvalue.ptr));
2129
2130 /* Init callback */
2131 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
2132 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2133
2134 /* Create transferee event subscription */
2135 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2136 if (status != PJ_SUCCESS) {
2137 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +00002138 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002139 return;
2140 }
2141
2142 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002143 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002144
2145 /* Create initial NOTIFY request */
2146 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2147 100, NULL, &tdata);
2148 if (status != PJ_SUCCESS) {
2149 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
2150 return;
2151 }
2152
2153 /* Send initial NOTIFY request */
2154 status = pjsip_xfer_send_request( sub, tdata);
2155 if (status != PJ_SUCCESS) {
2156 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2157 return;
2158 }
2159
2160 /* We're cheating here.
2161 * We need to get a null terminated string from a pj_str_t.
2162 * So grab the pointer from the hvalue and NULL terminate it, knowing
2163 * that the NULL position will be occupied by a newline.
2164 */
2165 uri = refer_to->hvalue.ptr;
2166 uri[refer_to->hvalue.slen] = '\0';
2167
2168 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002169 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002170 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2171 existing_call->user_data, NULL,
2172 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002173 if (status != PJ_SUCCESS) {
2174
2175 /* Notify xferer about the error */
2176 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2177 500, NULL, &tdata);
2178 if (status != PJ_SUCCESS) {
2179 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2180 status);
2181 return;
2182 }
2183 status = pjsip_xfer_send_request(sub, tdata);
2184 if (status != PJ_SUCCESS) {
2185 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2186 status);
2187 return;
2188 }
2189 return;
2190 }
2191
2192 /* Put the server subscription in inv_data.
2193 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2194 * reported back to the server subscription.
2195 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002196 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002197
2198 /* Put the invite_data in the subscription. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2200 &pjsua_var.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +00002201}
2202
2203
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002204
Benny Prijono26ff9062006-02-21 23:47:00 +00002205/*
2206 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002207 * session. We use this to trap:
2208 * - incoming REFER request.
2209 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002210 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002211static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2212 pjsip_transaction *tsx,
2213 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002214{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002215 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2216
2217 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002218
Benny Prijono26ff9062006-02-21 23:47:00 +00002219 if (tsx->role==PJSIP_ROLE_UAS &&
2220 tsx->state==PJSIP_TSX_STATE_TRYING &&
2221 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2222 {
2223 /*
2224 * Incoming REFER request.
2225 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002226 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002227
Benny Prijono26ff9062006-02-21 23:47:00 +00002228 }
Benny Prijonob0808372006-03-02 21:18:58 +00002229 else if (tsx->role==PJSIP_ROLE_UAS &&
2230 tsx->state==PJSIP_TSX_STATE_TRYING &&
2231 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2232 {
2233 /*
2234 * Incoming MESSAGE request!
2235 */
2236 pjsip_rx_data *rdata;
2237 pjsip_msg *msg;
2238 pjsip_accept_hdr *accept_hdr;
2239 pj_status_t status;
2240
2241 rdata = e->body.tsx_state.src.rdata;
2242 msg = rdata->msg_info.msg;
2243
2244 /* Request MUST have message body, with Content-Type equal to
2245 * "text/plain".
2246 */
2247 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2248
2249 pjsip_hdr hdr_list;
2250
2251 pj_list_init(&hdr_list);
2252 pj_list_push_back(&hdr_list, accept_hdr);
2253
2254 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2255 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002256 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002257 return;
2258 }
2259
2260 /* Respond with 200 first, so that remote doesn't retransmit in case
2261 * the UI takes too long to process the message.
2262 */
2263 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2264
2265 /* Process MESSAGE request */
2266 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2267 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002268
Benny Prijonob0808372006-03-02 21:18:58 +00002269 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002270 else if (tsx->role == PJSIP_ROLE_UAC &&
2271 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002272 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002273 /* Handle outgoing pager status */
2274 if (tsx->status_code >= 200) {
2275 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002276
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002277 im_data = tsx->mod_data[pjsua_var.mod.id];
2278 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002280 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2281 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2282 &im_data->to,
2283 &im_data->body,
2284 im_data->user_data,
2285 tsx->status_code,
2286 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002287 }
Benny Prijonofccab712006-02-22 22:23:22 +00002288 }
Benny Prijono834aee32006-02-19 01:38:06 +00002289 }
Benny Prijono834aee32006-02-19 01:38:06 +00002290
Benny Prijono26ff9062006-02-21 23:47:00 +00002291
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002292 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002293}