blob: 9e41104478876433d0a0e83026cfe26317ee194f [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 Prijono84126ab2006-02-09 09:30:09 +0000412 return PJ_TRUE;
413 }
414
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000415 /* Clear call descriptor */
416 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000417
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418 call = &pjsua_var.calls[call_id];
419
420 /* Mark call start time. */
421 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000422
Benny Prijono26ff9062006-02-21 23:47:00 +0000423 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
425 rdata->tp_info.pool, 1,
426 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000427 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000429 NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000430 return PJ_TRUE;
431 }
432
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000434 * Get which account is most likely to be associated with this incoming
435 * call. We need the account to find which contact URI to put for
436 * the call.
437 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000439
Benny Prijono26ff9062006-02-21 23:47:00 +0000440 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000441 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 &pjsua_var.acc[acc_id].cfg.contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000443 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000444 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000446 NULL, NULL);
447
Benny Prijono26ff9062006-02-21 23:47:00 +0000448 return PJ_TRUE;
449 }
450
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 /* Set credentials */
452 if (pjsua_var.acc[acc_id].cred_cnt) {
453 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
454 pjsua_var.acc[acc_id].cred_cnt,
455 pjsua_var.acc[acc_id].cred);
456 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000457
458 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000459 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
460 if (status != PJ_SUCCESS) {
Benny Prijonob0808372006-03-02 21:18:58 +0000461 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000462 pjsip_dlg_terminate(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000463 return PJ_TRUE;
464 }
465
466
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 /* Create and attach pjsua_var data to the dialog: */
468 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000469
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470 dlg->mod_data[pjsua_var.mod.id] = call;
471 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000472
473
Benny Prijono64f851e2006-02-23 13:49:28 +0000474 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000475 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000476 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000477 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000478 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000479 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
480 status);
481
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000482 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
483 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000484 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000485
486 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000487 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000488 if (status != PJ_SUCCESS)
489 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000490 }
491
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000492 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000493
Benny Prijono105217f2006-03-06 16:25:59 +0000494
Benny Prijono8b1889b2006-06-06 18:40:40 +0000495 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 if (pjsua_var.ua_cfg.cb.on_incoming_call)
497 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000498
Benny Prijono26ff9062006-02-21 23:47:00 +0000499 /* This INVITE request has been handled. */
500 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000501}
502
503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504
505/*
506 * Check if the specified call has active INVITE session and the INVITE
507 * session has not been disconnected.
508 */
509PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
510{
511 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
512 PJ_EINVAL);
513 return pjsua_var.calls[call_id].inv != NULL &&
514 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
515}
516
517
518/*
519 * Check if call has an active media session.
520 */
521PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
522{
523 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
524 PJ_EINVAL);
525 return pjsua_var.calls[call_id].session != NULL;
526}
527
528
529/*
530 * Get the conference port identification associated with the call.
531 */
532PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
533{
534 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
535 PJ_EINVAL);
536 return pjsua_var.calls[call_id].conf_slot;
537}
538
539
540/*
541 * Obtain detail information about the specified call.
542 */
543PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
544 pjsua_call_info *info)
545{
546 pjsua_call *call;
547
548 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
549 PJ_EINVAL);
550
551 pj_memset(info, 0, sizeof(*info));
552
553 PJSUA_LOCK();
554
555 call = &pjsua_var.calls[call_id];
556
557 if (call->inv == NULL) {
558 PJSUA_UNLOCK();
559 return PJ_SUCCESS;
560 }
561
562 pjsip_dlg_inc_lock(call->inv->dlg);
563
564
565 /* id and role */
566 info->id = call_id;
567 info->role = call->inv->role;
568
569 /* local info */
570 info->local_info.ptr = info->buf_.local_info;
571 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
572 sizeof(info->buf_.local_info));
573
574 /* local contact */
575 info->local_contact.ptr = info->buf_.local_contact;
576 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
577 call->inv->dlg->local.contact->uri,
578 info->local_contact.ptr,
579 sizeof(info->buf_.local_contact));
580
581 /* remote info */
582 info->remote_info.ptr = info->buf_.remote_info;
583 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
584 sizeof(info->buf_.remote_info));
585
586 /* remote contact */
587 if (call->inv->dlg->remote.contact) {
588 int len;
589 info->remote_contact.ptr = info->buf_.remote_contact;
590 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
591 call->inv->dlg->remote.contact->uri,
592 info->remote_contact.ptr,
593 sizeof(info->buf_.remote_contact));
594 if (len < 0) len = 0;
595 info->remote_contact.slen = len;
596 } else {
597 info->remote_contact.slen = 0;
598 }
599
600 /* call id */
601 info->call_id.ptr = info->buf_.call_id;
602 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
603 sizeof(info->buf_.call_id));
604
605 /* state, state_text */
606 info->state = call->inv->state;
607 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
608
609 /* If call is disconnected, set the last_status from the cause code */
610 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
611 /* last_status, last_status_text */
612 info->last_status = call->inv->cause;
613
614 info->last_status_text.ptr = info->buf_.last_status_text;
615 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
616 sizeof(info->buf_.last_status_text));
617 } else {
618 /* last_status, last_status_text */
619 info->last_status = call->last_code;
620
621 info->last_status_text.ptr = info->buf_.last_status_text;
622 pj_strncpy(&info->last_status_text, &call->last_text,
623 sizeof(info->buf_.last_status_text));
624 }
625
626 /* media status and dir */
627 info->media_status = call->media_st;
628 info->media_dir = call->media_dir;
629
630
631 /* conference slot number */
632 info->conf_slot = call->conf_slot;
633
634 /* calculate duration */
635 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
636
637 info->total_duration = call->dis_time;
638 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
639
640 if (call->conn_time.sec) {
641 info->connect_duration = call->dis_time;
642 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
643 }
644
645 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
646
647 pj_gettimeofday(&info->total_duration);
648 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
649
650 pj_gettimeofday(&info->connect_duration);
651 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
652
653 } else {
654 pj_gettimeofday(&info->total_duration);
655 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
656 }
657
658 pjsip_dlg_dec_lock(call->inv->dlg);
659 PJSUA_UNLOCK();
660
661 return PJ_SUCCESS;
662}
663
664
665/*
666 * Attach application specific data to the call.
667 */
668PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
669 void *user_data)
670{
671 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
672 PJ_EINVAL);
673 pjsua_var.calls[call_id].user_data = user_data;
674
675 return PJ_SUCCESS;
676}
677
678
679/*
680 * Get user data attached to the call.
681 */
682PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
683{
684 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
685 NULL);
686 return pjsua_var.calls[call_id].user_data;
687}
688
689
690/*
691 * Send response to incoming INVITE request.
692 */
693PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
694 unsigned code,
695 const pj_str_t *reason,
696 const pjsua_msg_data *msg_data)
697{
698 pjsua_call *call;
699 pjsip_tx_data *tdata;
700 pj_status_t status;
701
702 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
703 PJ_EINVAL);
704
705 PJSUA_LOCK();
706
707 call = &pjsua_var.calls[call_id];
708
709 if (call->inv == NULL) {
710 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id));
711 PJSUA_UNLOCK();
712 return PJSIP_ESESSIONTERMINATED;
713 }
714
Benny Prijono2e507c22006-06-23 15:04:11 +0000715 if (call->res_time.sec == 0)
716 pj_gettimeofday(&call->res_time);
717
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000718 /* Create response message */
719 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
720 if (status != PJ_SUCCESS) {
721 pjsua_perror(THIS_FILE, "Error creating response",
722 status);
723 PJSUA_UNLOCK();
724 return status;
725 }
726
727 /* Add additional headers etc */
728 pjsua_process_msg_data( tdata, msg_data);
729
730 /* Send the message */
731 status = pjsip_inv_send_msg(call->inv, tdata);
732 if (status != PJ_SUCCESS)
733 pjsua_perror(THIS_FILE, "Error sending response",
734 status);
735
736 PJSUA_UNLOCK();
737
738 return status;
739}
740
741
742/*
743 * Hangup call by using method that is appropriate according to the
744 * call state.
745 */
746PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
747 unsigned code,
748 const pj_str_t *reason,
749 const pjsua_msg_data *msg_data)
750{
751 pjsua_call *call;
752 pj_status_t status;
753 pjsip_tx_data *tdata;
754
755
756 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
757 PJ_EINVAL);
758
759 PJSUA_LOCK();
760
761 call = &pjsua_var.calls[call_id];
762
763 if (!call->inv) {
764 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
765 PJSUA_UNLOCK();
766 return PJ_EINVAL;
767 }
768
769 if (code==0) {
770 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
771 code = PJSIP_SC_OK;
772 else if (call->inv->role == PJSIP_ROLE_UAS)
773 code = PJSIP_SC_DECLINE;
774 else
775 code = PJSIP_SC_REQUEST_TERMINATED;
776 }
777
778 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
779 if (status != PJ_SUCCESS) {
780 pjsua_perror(THIS_FILE,
781 "Failed to create end session message",
782 status);
783 PJSUA_UNLOCK();
784 return status;
785 }
786
787 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
788 * as p_tdata when INVITE transaction has not been answered
789 * with any provisional responses.
790 */
791 if (tdata == NULL) {
792 PJSUA_UNLOCK();
793 return PJ_SUCCESS;
794 }
795
796 /* Add additional headers etc */
797 pjsua_process_msg_data( tdata, msg_data);
798
799 /* Send the message */
800 status = pjsip_inv_send_msg(call->inv, tdata);
801 if (status != PJ_SUCCESS) {
802 pjsua_perror(THIS_FILE,
803 "Failed to send end session message",
804 status);
805 PJSUA_UNLOCK();
806 return status;
807 }
808
809 PJSUA_UNLOCK();
810
811 return PJ_SUCCESS;
812}
813
814
815/*
816 * Put the specified call on hold.
817 */
818PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
819 const pjsua_msg_data *msg_data)
820{
821 pjmedia_sdp_session *sdp;
822 pjsua_call *call;
823 pjsip_tx_data *tdata;
824 pj_status_t status;
825
826 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
827 PJ_EINVAL);
828
829 PJSUA_LOCK();
830
831 call = &pjsua_var.calls[call_id];
832
833 if (!call->inv) {
834 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
835 PJSUA_UNLOCK();
836 return PJSIP_ESESSIONTERMINATED;
837 }
838
839 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
840 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
841 PJSUA_UNLOCK();
842 return PJSIP_ESESSIONSTATE;
843 }
844
845 status = create_inactive_sdp(call, &sdp);
846 if (status != PJ_SUCCESS) {
847 PJSUA_UNLOCK();
848 return status;
849 }
850
851 /* Create re-INVITE with new offer */
852 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
853 if (status != PJ_SUCCESS) {
854 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
855 PJSUA_UNLOCK();
856 return status;
857 }
858
859 /* Add additional headers etc */
860 pjsua_process_msg_data( tdata, msg_data);
861
862 /* Send the request */
863 status = pjsip_inv_send_msg( call->inv, tdata);
864 if (status != PJ_SUCCESS) {
865 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
866 PJSUA_UNLOCK();
867 return status;
868 }
869
870 PJSUA_UNLOCK();
871
872 return PJ_SUCCESS;
873}
874
875
876/*
877 * Send re-INVITE (to release hold).
878 */
879PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
880 pj_bool_t unhold,
881 const pjsua_msg_data *msg_data)
882{
883 pjmedia_sdp_session *sdp;
884 pjsip_tx_data *tdata;
885 pjsua_call *call;
886 pj_status_t status;
887
888
889 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
890 PJ_EINVAL);
891
892 PJSUA_LOCK();
893
894 call = &pjsua_var.calls[call_id];
895
896 if (!call->inv) {
897 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
898 PJSUA_UNLOCK();
899 return PJSIP_ESESSIONTERMINATED;
900 }
901
902
903 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
904 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
905 PJSUA_UNLOCK();
906 return PJSIP_ESESSIONSTATE;
907 }
908
909 /* Create SDP */
910 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
911 1, &call->skinfo, &sdp);
912 if (status != PJ_SUCCESS) {
913 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
914 status);
915 PJSUA_UNLOCK();
916 return status;
917 }
918
919 /* Create re-INVITE with new offer */
920 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
921 if (status != PJ_SUCCESS) {
922 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
923 PJSUA_UNLOCK();
924 return status;
925 }
926
927 /* Add additional headers etc */
928 pjsua_process_msg_data( tdata, msg_data);
929
930 /* Send the request */
931 status = pjsip_inv_send_msg( call->inv, tdata);
932 if (status != PJ_SUCCESS) {
933 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
934 PJSUA_UNLOCK();
935 return status;
936 }
937
938 PJSUA_UNLOCK();
939
940 return PJ_SUCCESS;
941}
942
943
944/*
945 * Initiate call transfer to the specified address.
946 */
947PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
948 const pj_str_t *dest,
949 const pjsua_msg_data *msg_data)
950{
951 pjsip_evsub *sub;
952 pjsip_tx_data *tdata;
953 pjsua_call *call;
954 pj_status_t status;
955
956
957 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
958 PJ_EINVAL);
959
960 PJSUA_LOCK();
961
962 call = &pjsua_var.calls[call_id];
963
964 if (!call->inv) {
965 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
966 PJSUA_UNLOCK();
967 return PJSIP_ESESSIONTERMINATED;
968 }
969
970 /* Create xfer client subscription.
971 * We're not interested in knowing the transfer result, so we
972 * put NULL as the callback.
973 */
974 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
975 if (status != PJ_SUCCESS) {
976 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
977 PJSUA_UNLOCK();
978 return status;
979 }
980
981 /*
982 * Create REFER request.
983 */
984 status = pjsip_xfer_initiate(sub, dest, &tdata);
985 if (status != PJ_SUCCESS) {
986 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
987 PJSUA_UNLOCK();
988 return status;
989 }
990
991 /* Add additional headers etc */
992 pjsua_process_msg_data( tdata, msg_data);
993
994 /* Send. */
995 status = pjsip_xfer_send_request(sub, tdata);
996 if (status != PJ_SUCCESS) {
997 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
998 PJSUA_UNLOCK();
999 return status;
1000 }
1001
1002 /* For simplicity (that's what this program is intended to be!),
1003 * leave the original invite session as it is. More advanced application
1004 * may want to hold the INVITE, or terminate the invite, or whatever.
1005 */
1006
1007 PJSUA_UNLOCK();
1008
1009 return PJ_SUCCESS;
1010
1011}
1012
1013
1014/*
1015 * Send DTMF digits to remote using RFC 2833 payload formats.
1016 */
1017PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1018 const pj_str_t *digits)
1019{
1020 pjsua_call *call;
1021 pj_status_t status;
1022
1023 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1024 PJ_EINVAL);
1025
1026 PJSUA_LOCK();
1027
1028 call = &pjsua_var.calls[call_id];
1029
1030 if (!call->session) {
1031 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1032 PJSUA_UNLOCK();
1033 return PJ_EINVALIDOP;
1034 }
1035
1036 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1037
1038 PJSUA_UNLOCK();
1039
1040 return status;
1041}
1042
1043
1044/**
1045 * Send instant messaging inside INVITE session.
1046 */
1047PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1048 const pj_str_t *mime_type,
1049 const pj_str_t *content,
1050 const pjsua_msg_data *msg_data,
1051 void *user_data)
1052{
1053 pjsua_call *call;
1054 const pj_str_t mime_text_plain = pj_str("text/plain");
1055 pjsip_media_type ctype;
1056 pjsua_im_data *im_data;
1057 pjsip_tx_data *tdata;
1058 pj_status_t status;
1059
1060
1061 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1062 PJ_EINVAL);
1063
1064 PJSUA_LOCK();
1065
1066 call = &pjsua_var.calls[call_id];
1067
1068 if (!call->inv) {
1069 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1070 PJSUA_UNLOCK();
1071 return PJSIP_ESESSIONTERMINATED;
1072 }
1073
1074 /* Lock dialog. */
1075 pjsip_dlg_inc_lock(call->inv->dlg);
1076
1077 /* Set default media type if none is specified */
1078 if (mime_type == NULL) {
1079 mime_type = &mime_text_plain;
1080 }
1081
1082 /* Create request message. */
1083 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1084 -1, &tdata);
1085 if (status != PJ_SUCCESS) {
1086 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1087 goto on_return;
1088 }
1089
1090 /* Add accept header. */
1091 pjsip_msg_add_hdr( tdata->msg,
1092 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1093
1094 /* Parse MIME type */
1095 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1096
1097 /* Create "text/plain" message body. */
1098 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1099 &ctype.subtype, content);
1100 if (tdata->msg->body == NULL) {
1101 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1102 pjsip_tx_data_dec_ref(tdata);
1103 goto on_return;
1104 }
1105
1106 /* Add additional headers etc */
1107 pjsua_process_msg_data( tdata, msg_data);
1108
1109 /* Create IM data and attach to the request. */
1110 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1111 im_data->acc_id = call->acc_id;
1112 im_data->call_id = call_id;
1113 im_data->to = call->inv->dlg->remote.info_str;
1114 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1115 im_data->user_data = user_data;
1116
1117
1118 /* Send the request. */
1119 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1120 pjsua_var.mod.id, im_data);
1121 if (status != PJ_SUCCESS) {
1122 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1123 goto on_return;
1124 }
1125
1126on_return:
1127 pjsip_dlg_dec_lock(call->inv->dlg);
1128 PJSUA_UNLOCK();
1129 return status;
1130}
1131
1132
1133/*
1134 * Send IM typing indication inside INVITE session.
1135 */
1136PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1137 pj_bool_t is_typing,
1138 const pjsua_msg_data*msg_data)
1139{
1140 pjsua_call *call;
1141 pjsip_tx_data *tdata;
1142 pj_status_t status;
1143
1144
1145 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1146 PJ_EINVAL);
1147
1148 PJSUA_LOCK();
1149
1150 call = &pjsua_var.calls[call_id];
1151
1152 if (!call->inv) {
1153 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1154 PJSUA_UNLOCK();
1155 return PJSIP_ESESSIONTERMINATED;
1156 }
1157
1158 /* Lock dialog. */
1159 pjsip_dlg_inc_lock(call->inv->dlg);
1160
1161 /* Create request message. */
1162 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1163 -1, &tdata);
1164 if (status != PJ_SUCCESS) {
1165 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1166 goto on_return;
1167 }
1168
1169 /* Create "application/im-iscomposing+xml" msg body. */
1170 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1171 NULL, NULL, -1);
1172
1173 /* Add additional headers etc */
1174 pjsua_process_msg_data( tdata, msg_data);
1175
1176 /* Send the request. */
1177 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1178 if (status != PJ_SUCCESS) {
1179 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1180 goto on_return;
1181 }
1182
1183on_return:
1184 pjsip_dlg_dec_lock(call->inv->dlg);
1185 PJSUA_UNLOCK();
1186 return status;
1187}
1188
1189
1190/*
1191 * Terminate all calls.
1192 */
1193PJ_DEF(void) pjsua_call_hangup_all(void)
1194{
1195 unsigned i;
1196
1197 PJSUA_LOCK();
1198
1199 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1200 if (pjsua_var.calls[i].inv)
1201 pjsua_call_hangup(i, 0, NULL, NULL);
1202 }
1203
1204 PJSUA_UNLOCK();
1205}
1206
1207
1208static const char *good_number(char *buf, pj_int32_t val)
1209{
1210 if (val < 1000) {
1211 pj_ansi_sprintf(buf, "%d", val);
1212 } else if (val < 1000000) {
1213 pj_ansi_sprintf(buf, "%d.%dK",
1214 val / 1000,
1215 (val % 1000) / 100);
1216 } else {
1217 pj_ansi_sprintf(buf, "%d.%02dM",
1218 val / 1000000,
1219 (val % 1000000) / 10000);
1220 }
1221
1222 return buf;
1223}
1224
1225
1226/* Dump media session */
1227static void dump_media_session(const char *indent,
1228 char *buf, unsigned maxlen,
1229 pjmedia_session *session)
1230{
1231 unsigned i;
1232 char *p = buf, *end = buf+maxlen;
1233 int len;
1234 pjmedia_session_info info;
1235
1236 pjmedia_session_get_info(session, &info);
1237
1238 for (i=0; i<info.stream_cnt; ++i) {
1239 pjmedia_rtcp_stat stat;
1240 const char *rem_addr;
1241 int rem_port;
1242 const char *dir;
1243 char last_update[40];
1244 char packets[16], bytes[16], ipbytes[16];
1245 pj_time_val now;
1246
1247 pjmedia_session_get_stream_stat(session, i, &stat);
1248 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1249 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1250
1251 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1252 dir = "sendonly";
1253 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1254 dir = "recvonly";
1255 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1256 dir = "sendrecv";
1257 else
1258 dir = "inactive";
1259
1260
1261 len = pj_ansi_snprintf(buf, end-p,
1262 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1263 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001264 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001265 info.stream_info[i].fmt.encoding_name.ptr,
1266 info.stream_info[i].fmt.clock_rate / 1000,
1267 dir,
1268 rem_addr, rem_port);
1269 if (len < 1 || len > end-p) {
1270 *p = '\0';
1271 return;
1272 }
1273
1274 p += len;
1275 *p++ = '\n';
1276 *p = '\0';
1277
1278 if (stat.rx.update_cnt == 0)
1279 strcpy(last_update, "never");
1280 else {
1281 pj_gettimeofday(&now);
1282 PJ_TIME_VAL_SUB(now, stat.rx.update);
1283 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1284 now.sec / 3600,
1285 (now.sec % 3600) / 60,
1286 now.sec % 60,
1287 now.msec);
1288 }
1289
1290 len = pj_ansi_snprintf(p, end-p,
1291 "%s RX pt=%d, stat last update: %s\n"
1292 "%s total %spkt %sB (%sB +IP hdr)\n"
1293 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1294 "%s (msec) min avg max last\n"
1295 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1296 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1297 indent, info.stream_info[i].fmt.pt,
1298 last_update,
1299 indent,
1300 good_number(packets, stat.rx.pkt),
1301 good_number(bytes, stat.rx.bytes),
1302 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
1303 indent,
1304 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001305 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001306 stat.rx.dup,
Benny Prijonof9016512006-06-29 09:41:34 +00001307 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.dup),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001308 stat.rx.reorder,
Benny Prijonof9016512006-06-29 09:41:34 +00001309 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.reorder),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001310 indent, indent,
1311 stat.rx.loss_period.min / 1000.0,
1312 stat.rx.loss_period.avg / 1000.0,
1313 stat.rx.loss_period.max / 1000.0,
1314 stat.rx.loss_period.last / 1000.0,
1315 indent,
1316 stat.rx.jitter.min / 1000.0,
1317 stat.rx.jitter.avg / 1000.0,
1318 stat.rx.jitter.max / 1000.0,
1319 stat.rx.jitter.last / 1000.0,
1320 ""
1321 );
1322
1323 if (len < 1 || len > end-p) {
1324 *p = '\0';
1325 return;
1326 }
1327
1328 p += len;
1329 *p++ = '\n';
1330 *p = '\0';
1331
1332 if (stat.tx.update_cnt == 0)
1333 strcpy(last_update, "never");
1334 else {
1335 pj_gettimeofday(&now);
1336 PJ_TIME_VAL_SUB(now, stat.tx.update);
1337 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1338 now.sec / 3600,
1339 (now.sec % 3600) / 60,
1340 now.sec % 60,
1341 now.msec);
1342 }
1343
1344 len = pj_ansi_snprintf(p, end-p,
1345 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
1346 "%s total %spkt %sB (%sB +IP hdr)\n"
1347 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1348 "%s (msec) min avg max last\n"
1349 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1350 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1351 indent,
1352 info.stream_info[i].tx_pt,
1353 info.stream_info[i].param->info.frm_ptime *
1354 info.stream_info[i].param->setting.frm_per_pkt,
1355 last_update,
1356
1357 indent,
1358 good_number(packets, stat.tx.pkt),
1359 good_number(bytes, stat.tx.bytes),
1360 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
1361
1362 indent,
1363 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001364 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365 stat.tx.dup,
Benny Prijonof9016512006-06-29 09:41:34 +00001366 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.dup),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367 stat.tx.reorder,
Benny Prijonof9016512006-06-29 09:41:34 +00001368 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.reorder),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001369
1370 indent, indent,
1371 stat.tx.loss_period.min / 1000.0,
1372 stat.tx.loss_period.avg / 1000.0,
1373 stat.tx.loss_period.max / 1000.0,
1374 stat.tx.loss_period.last / 1000.0,
1375 indent,
1376 stat.tx.jitter.min / 1000.0,
1377 stat.tx.jitter.avg / 1000.0,
1378 stat.tx.jitter.max / 1000.0,
1379 stat.tx.jitter.last / 1000.0,
1380 ""
1381 );
1382
1383 if (len < 1 || len > end-p) {
1384 *p = '\0';
1385 return;
1386 }
1387
1388 p += len;
1389 *p++ = '\n';
1390 *p = '\0';
1391
1392 len = pj_ansi_snprintf(p, end-p,
1393 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1394 indent,
1395 stat.rtt.min / 1000.0,
1396 stat.rtt.avg / 1000.0,
1397 stat.rtt.max / 1000.0,
1398 stat.rtt.last / 1000.0
1399 );
1400 if (len < 1 || len > end-p) {
1401 *p = '\0';
1402 return;
1403 }
1404
1405 p += len;
1406 *p++ = '\n';
1407 *p = '\0';
1408 }
1409}
1410
1411
1412/* Print call info */
1413static void print_call(const char *title,
1414 int call_id,
1415 char *buf, pj_size_t size)
1416{
1417 int len;
1418 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1419 pjsip_dialog *dlg = inv->dlg;
1420 char userinfo[128];
1421
1422 /* Dump invite sesion info. */
1423
1424 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1425 if (len < 1)
1426 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1427 else
1428 userinfo[len] = '\0';
1429
1430 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1431 title,
1432 pjsip_inv_state_name(inv->state),
1433 userinfo);
1434 if (len < 1 || len >= (int)size) {
1435 pj_ansi_strcpy(buf, "<--uri too long-->");
1436 len = 18;
1437 } else
1438 buf[len] = '\0';
1439}
1440
1441
1442/*
1443 * Dump call and media statistics to string.
1444 */
1445PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1446 pj_bool_t with_media,
1447 char *buffer,
1448 unsigned maxlen,
1449 const char *indent)
1450{
1451 pjsua_call *call;
1452 pj_time_val duration, res_delay, con_delay;
1453 char tmp[128];
1454 char *p, *end;
1455 int len;
1456
1457 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1458 PJ_EINVAL);
1459
1460 PJSUA_LOCK();
1461
1462 call = &pjsua_var.calls[call_id];
1463
1464 *buffer = '\0';
1465 p = buffer;
1466 end = buffer + maxlen;
1467 len = 0;
1468
1469 if (call->inv == NULL) {
1470 PJSUA_UNLOCK();
1471 return PJ_EINVALIDOP;
1472 }
1473
1474 print_call(indent, call_id, tmp, sizeof(tmp));
1475
1476 len = pj_ansi_strlen(tmp);
1477 pj_ansi_strcpy(buffer, tmp);
1478
1479 p += len;
1480 *p++ = '\r';
1481 *p++ = '\n';
1482
1483 /* Calculate call duration */
1484 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1485 pj_gettimeofday(&duration);
1486 PJ_TIME_VAL_SUB(duration, call->conn_time);
1487 con_delay = call->conn_time;
1488 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1489 } else {
1490 duration.sec = duration.msec = 0;
1491 con_delay.sec = con_delay.msec = 0;
1492 }
1493
1494 /* Calculate first response delay */
1495 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1496 res_delay = call->res_time;
1497 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1498 } else {
1499 res_delay.sec = res_delay.msec = 0;
1500 }
1501
1502 /* Print duration */
1503 len = pj_ansi_snprintf(p, end-p,
1504 "%s Call time: %02dh:%02dm:%02ds, "
1505 "1st res in %d ms, conn in %dms",
1506 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001507 (int)(duration.sec / 3600),
1508 (int)((duration.sec % 3600)/60),
1509 (int)(duration.sec % 60),
1510 (int)PJ_TIME_VAL_MSEC(res_delay),
1511 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512
1513 if (len > 0 && len < end-p) {
1514 p += len;
1515 *p++ = '\n';
1516 *p = '\0';
1517 }
1518
1519 /* Dump session statistics */
1520 if (with_media && call->session)
1521 dump_media_session(indent, p, end-p, call->session);
1522
1523 PJSUA_UNLOCK();
1524
1525 return PJ_SUCCESS;
1526}
1527
1528
1529/*
1530 * Destroy the call's media
1531 */
1532static pj_status_t call_destroy_media(int call_id)
1533{
1534 pjsua_call *call = &pjsua_var.calls[call_id];
1535
1536 if (call->conf_slot != PJSUA_INVALID_ID) {
1537 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1538 call->conf_slot = PJSUA_INVALID_ID;
1539 }
1540
1541 if (call->session) {
1542 /* Destroy session (this will also close RTP/RTCP sockets). */
1543 pjmedia_session_destroy(call->session);
1544 call->session = NULL;
1545
1546 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1547 call_id));
1548
1549 }
1550
1551 call->media_st = PJSUA_CALL_MEDIA_NONE;
1552
1553 return PJ_SUCCESS;
1554}
1555
1556
Benny Prijono84126ab2006-02-09 09:30:09 +00001557/*
1558 * This callback receives notification from invite session when the
1559 * session state has changed.
1560 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001561static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1562 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001563{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001565
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566 PJSUA_LOCK();
1567
1568 call = inv->dlg->mod_data[pjsua_var.mod.id];
1569
1570 if (!call) {
1571 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001572 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 }
1574
Benny Prijonoe21e7842006-04-09 16:46:05 +00001575
1576 /* Get call times */
1577 switch (inv->state) {
1578 case PJSIP_INV_STATE_EARLY:
1579 case PJSIP_INV_STATE_CONNECTING:
1580 if (call->res_time.sec == 0)
1581 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001582 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001583 pj_strncpy(&call->last_text,
1584 &e->body.tsx_state.tsx->status_text,
1585 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001586 break;
1587 case PJSIP_INV_STATE_CONFIRMED:
1588 pj_gettimeofday(&call->conn_time);
1589 break;
1590 case PJSIP_INV_STATE_DISCONNECTED:
1591 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001592 if (call->res_time.sec == 0)
1593 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001594 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1595 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596 pj_strncpy(&call->last_text,
1597 &e->body.tsx_state.tsx->status_text,
1598 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001599 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001600 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001601 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001602 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 pj_strncpy(&call->last_text,
1604 &e->body.tsx_state.tsx->status_text,
1605 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001606 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001607 }
1608
Benny Prijono26ff9062006-02-21 23:47:00 +00001609 /* If this is an outgoing INVITE that was created because of
1610 * REFER/transfer, send NOTIFY to transferer.
1611 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001612 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001613 int st_code = -1;
1614 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1615
1616
Benny Prijonoa91a0032006-02-26 21:23:45 +00001617 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001618 case PJSIP_INV_STATE_NULL:
1619 case PJSIP_INV_STATE_CALLING:
1620 /* Do nothing */
1621 break;
1622
1623 case PJSIP_INV_STATE_EARLY:
1624 case PJSIP_INV_STATE_CONNECTING:
1625 st_code = e->body.tsx_state.tsx->status_code;
1626 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1627 break;
1628
1629 case PJSIP_INV_STATE_CONFIRMED:
1630 /* When state is confirmed, send the final 200/OK and terminate
1631 * subscription.
1632 */
1633 st_code = e->body.tsx_state.tsx->status_code;
1634 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1635 break;
1636
1637 case PJSIP_INV_STATE_DISCONNECTED:
1638 st_code = e->body.tsx_state.tsx->status_code;
1639 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1640 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001641
Benny Prijono8b1889b2006-06-06 18:40:40 +00001642 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001643 /* Nothing to do. Just to keep gcc from complaining about
1644 * unused enums.
1645 */
1646 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001647 }
1648
1649 if (st_code != -1) {
1650 pjsip_tx_data *tdata;
1651 pj_status_t status;
1652
Benny Prijonoa91a0032006-02-26 21:23:45 +00001653 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001654 ev_state, st_code,
1655 NULL, &tdata);
1656 if (status != PJ_SUCCESS) {
1657 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1658 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001659 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001660 if (status != PJ_SUCCESS) {
1661 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1662 }
1663 }
1664 }
1665 }
1666
Benny Prijono84126ab2006-02-09 09:30:09 +00001667
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001668 if (pjsua_var.ua_cfg.cb.on_call_state)
1669 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001670
1671 /* call->inv may be NULL now */
1672
Benny Prijono84126ab2006-02-09 09:30:09 +00001673 /* Destroy media session when invite session is disconnected. */
1674 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001675
Benny Prijonoa91a0032006-02-26 21:23:45 +00001676 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001677
Benny Prijono275fd682006-03-22 11:59:11 +00001678 if (call)
1679 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001680
Benny Prijono105217f2006-03-06 16:25:59 +00001681 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001682 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001684 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685
1686 PJSUA_UNLOCK();
1687}
1688
1689/*
1690 * This callback is called by invite session framework when UAC session
1691 * has forked.
1692 */
1693static void pjsua_call_on_forked( pjsip_inv_session *inv,
1694 pjsip_event *e)
1695{
1696 PJ_UNUSED_ARG(inv);
1697 PJ_UNUSED_ARG(e);
1698
1699 PJ_TODO(HANDLE_FORKED_DIALOG);
1700}
1701
1702
1703/*
1704 * Callback to be called when SDP offer/answer negotiation has just completed
1705 * in the session. This function will start/update media if negotiation
1706 * has succeeded.
1707 */
1708static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1709 pj_status_t status)
1710{
1711 int prev_media_st = 0;
1712 pjsua_call *call;
1713 pjmedia_session_info sess_info;
1714 const pjmedia_sdp_session *local_sdp;
1715 const pjmedia_sdp_session *remote_sdp;
1716 pjmedia_port *media_port;
1717 pj_str_t port_name;
1718 char tmp[PJSIP_MAX_URL_SIZE];
1719
1720 PJSUA_LOCK();
1721
1722 call = inv->dlg->mod_data[pjsua_var.mod.id];
1723
1724 if (status != PJ_SUCCESS) {
1725
1726 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1727
1728 /* Disconnect call if we're not in the middle of initializing an
1729 * UAS dialog and if this is not a re-INVITE
1730 */
1731 if (inv->state != PJSIP_INV_STATE_NULL &&
1732 inv->state != PJSIP_INV_STATE_CONFIRMED)
1733 {
1734 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1735 }
1736
1737 PJSUA_UNLOCK();
1738 return;
1739 }
1740
1741 /* Destroy existing media session, if any. */
1742
1743 if (call) {
1744 prev_media_st = call->media_st;
1745 call_destroy_media(call->index);
1746 }
1747
1748 /* Get local and remote SDP */
1749
1750 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1751 if (status != PJ_SUCCESS) {
1752 pjsua_perror(THIS_FILE,
1753 "Unable to retrieve currently active local SDP",
1754 status);
1755 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1756 PJSUA_UNLOCK();
1757 return;
1758 }
1759
1760
1761 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1762 if (status != PJ_SUCCESS) {
1763 pjsua_perror(THIS_FILE,
1764 "Unable to retrieve currently active remote SDP",
1765 status);
1766 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1767 PJSUA_UNLOCK();
1768 return;
1769 }
1770
1771 /* Create media session info based on SDP parameters.
1772 * We only support one stream per session at the moment
1773 */
1774 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1775 pjsua_var.med_endpt,
1776 1,&sess_info,
1777 local_sdp, remote_sdp);
1778 if (status != PJ_SUCCESS) {
1779 pjsua_perror(THIS_FILE, "Unable to create media session",
1780 status);
1781 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1782 PJSUA_UNLOCK();
1783 return;
1784 }
1785
1786 /* Check if media is put on-hold */
1787 if (sess_info.stream_cnt == 0 ||
1788 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1789 {
1790
1791 /* Determine who puts the call on-hold */
1792 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1793 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1794 /* It was local who offer hold */
1795 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1796 } else {
1797 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1798 }
1799 }
1800
1801 call->media_dir = PJMEDIA_DIR_NONE;
1802
1803 } else {
1804
1805 /* Override ptime, if this option is specified. */
1806 PJ_TODO(set_codec_ptime_in_call);
1807
1808
1809 /* Optionally, application may modify other stream settings here
1810 * (such as jitter buffer parameters, codec ptime, etc.)
1811 */
1812
1813 /* Create session based on session info. */
1814 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1815 &call->med_tp,
1816 call, &call->session );
1817 if (status != PJ_SUCCESS) {
1818 pjsua_perror(THIS_FILE, "Unable to create media session",
1819 status);
1820 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1821 PJSUA_UNLOCK();
1822 return;
1823 }
1824
1825
1826 /* Get the port interface of the first stream in the session.
1827 * We need the port interface to add to the conference bridge.
1828 */
1829 pjmedia_session_get_port(call->session, 0, &media_port);
1830
1831
1832 /*
1833 * Add the call to conference bridge.
1834 */
1835 port_name.ptr = tmp;
1836 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1837 call->inv->dlg->remote.info->uri,
1838 tmp, sizeof(tmp));
1839 if (port_name.slen < 1) {
1840 port_name = pj_str("call");
1841 }
1842 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1843 media_port,
1844 &port_name,
1845 (unsigned*)&call->conf_slot);
1846 if (status != PJ_SUCCESS) {
1847 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1848 status);
1849 call_destroy_media(call->index);
1850 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1851 PJSUA_UNLOCK();
1852 return;
1853 }
1854
1855 /* Call's media state is active */
1856 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1857 call->media_dir = sess_info.stream_info[0].dir;
1858 }
1859
1860 /* Print info. */
1861 {
1862 char info[80];
1863 int info_len = 0;
1864 unsigned i;
1865
1866 for (i=0; i<sess_info.stream_cnt; ++i) {
1867 int len;
1868 const char *dir;
1869 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1870
1871 switch (strm_info->dir) {
1872 case PJMEDIA_DIR_NONE:
1873 dir = "inactive";
1874 break;
1875 case PJMEDIA_DIR_ENCODING:
1876 dir = "sendonly";
1877 break;
1878 case PJMEDIA_DIR_DECODING:
1879 dir = "recvonly";
1880 break;
1881 case PJMEDIA_DIR_ENCODING_DECODING:
1882 dir = "sendrecv";
1883 break;
1884 default:
1885 dir = "unknown";
1886 break;
1887 }
1888 len = pj_ansi_sprintf( info+info_len,
1889 ", stream #%d: %.*s (%s)", i,
1890 (int)strm_info->fmt.encoding_name.slen,
1891 strm_info->fmt.encoding_name.ptr,
1892 dir);
1893 if (len > 0)
1894 info_len += len;
1895 }
1896 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1897 }
1898
1899 /* Call application callback, if any */
1900 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1901 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1902
1903
1904 PJSUA_UNLOCK();
1905}
1906
1907
1908/*
1909 * Create inactive SDP for call hold.
1910 */
1911static pj_status_t create_inactive_sdp(pjsua_call *call,
1912 pjmedia_sdp_session **p_answer)
1913{
1914 pj_status_t status;
1915 pjmedia_sdp_conn *conn;
1916 pjmedia_sdp_attr *attr;
1917 pjmedia_sdp_session *sdp;
1918
1919 /* Create new offer */
1920 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1921 &call->skinfo, &sdp);
1922 if (status != PJ_SUCCESS) {
1923 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1924 return status;
1925 }
1926
1927 /* Get SDP media connection line */
1928 conn = sdp->media[0]->conn;
1929 if (!conn)
1930 conn = sdp->conn;
1931
1932 /* Modify address */
1933 conn->addr = pj_str("0.0.0.0");
1934
1935 /* Remove existing directions attributes */
1936 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
1937 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
1938 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
1939 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
1940
1941 /* Add inactive attribute */
1942 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
1943 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
1944
1945 *p_answer = sdp;
1946
1947 return status;
1948}
1949
1950
1951/*
1952 * Called when session received new offer.
1953 */
1954static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
1955 const pjmedia_sdp_session *offer)
1956{
1957 const char *remote_state;
1958 pjsua_call *call;
1959 pjmedia_sdp_conn *conn;
1960 pjmedia_sdp_session *answer;
1961 pj_bool_t is_remote_active;
1962 pj_status_t status;
1963
1964 PJSUA_LOCK();
1965
1966 call = inv->dlg->mod_data[pjsua_var.mod.id];
1967
1968 /*
1969 * See if remote is offering active media (i.e. not on-hold)
1970 */
1971 is_remote_active = PJ_TRUE;
1972
1973 conn = offer->media[0]->conn;
1974 if (!conn)
1975 conn = offer->conn;
1976
1977 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
1978 pj_strcmp2(&conn->addr, "0")==0)
1979 {
1980 is_remote_active = PJ_FALSE;
1981
1982 }
1983 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
1984 {
1985 is_remote_active = PJ_FALSE;
1986 }
1987
1988 remote_state = (is_remote_active ? "active" : "inactive");
1989
1990 /* Supply candidate answer */
1991 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
1992 PJ_LOG(4,(THIS_FILE,
1993 "Call %d: RX new media offer, creating inactive SDP "
1994 "(media in offer is %s)", call->index, remote_state));
1995 status = create_inactive_sdp( call, &answer );
1996 } else {
1997 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
1998 call->index));
1999 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2000 call->inv->pool, 1,
2001 &call->skinfo, &answer);
2002 }
2003
2004 if (status != PJ_SUCCESS) {
2005 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2006 PJSUA_UNLOCK();
2007 return;
2008 }
2009
2010 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2011 if (status != PJ_SUCCESS) {
2012 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2013 PJSUA_UNLOCK();
2014 return;
2015 }
2016
2017 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002018}
2019
2020
2021/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002022 * Callback called by event framework when the xfer subscription state
2023 * has changed.
2024 */
2025static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2026{
2027
2028 PJ_UNUSED_ARG(event);
2029
2030 /*
2031 * We're only interested when subscription is terminated, to
2032 * clear the xfer_sub member of the inv_data.
2033 */
2034 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002035 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002036
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002038 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002039 return;
2040
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002041 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002042 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002043
2044 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2045 }
2046}
2047
2048
2049/*
2050 * Follow transfer (REFER) request.
2051 */
2052static void on_call_transfered( pjsip_inv_session *inv,
2053 pjsip_rx_data *rdata )
2054{
2055 pj_status_t status;
2056 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002057 pjsua_call *existing_call;
2058 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002059 const pj_str_t str_refer_to = { "Refer-To", 8};
2060 pjsip_generic_string_hdr *refer_to;
2061 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002062 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +00002063 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002064 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002065 pjsip_evsub *sub;
2066
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002068
Benny Prijono26ff9062006-02-21 23:47:00 +00002069 /* Find the Refer-To header */
2070 refer_to = (pjsip_generic_string_hdr*)
2071 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2072
2073 if (refer_to == NULL) {
2074 /* Invalid Request.
2075 * No Refer-To header!
2076 */
2077 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002078 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002079 return;
2080 }
2081
Benny Prijono9fc735d2006-05-28 14:58:12 +00002082 /* Notify callback */
2083 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2085 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2086 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002087
2088 if (code < 200)
2089 code = 200;
2090 if (code >= 300) {
2091 /* Application rejects call transfer request */
2092 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2093 return;
2094 }
2095
Benny Prijono26ff9062006-02-21 23:47:00 +00002096 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2097 (int)inv->dlg->remote.info_str.slen,
2098 inv->dlg->remote.info_str.ptr,
2099 (int)refer_to->hvalue.slen,
2100 refer_to->hvalue.ptr));
2101
2102 /* Init callback */
2103 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
2104 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2105
2106 /* Create transferee event subscription */
2107 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2108 if (status != PJ_SUCCESS) {
2109 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +00002110 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002111 return;
2112 }
2113
2114 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002115 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002116
2117 /* Create initial NOTIFY request */
2118 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2119 100, NULL, &tdata);
2120 if (status != PJ_SUCCESS) {
2121 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
2122 return;
2123 }
2124
2125 /* Send initial NOTIFY request */
2126 status = pjsip_xfer_send_request( sub, tdata);
2127 if (status != PJ_SUCCESS) {
2128 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2129 return;
2130 }
2131
2132 /* We're cheating here.
2133 * We need to get a null terminated string from a pj_str_t.
2134 * So grab the pointer from the hvalue and NULL terminate it, knowing
2135 * that the NULL position will be occupied by a newline.
2136 */
2137 uri = refer_to->hvalue.ptr;
2138 uri[refer_to->hvalue.slen] = '\0';
2139
2140 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002141 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002142 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2143 existing_call->user_data, NULL,
2144 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002145 if (status != PJ_SUCCESS) {
2146
2147 /* Notify xferer about the error */
2148 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2149 500, NULL, &tdata);
2150 if (status != PJ_SUCCESS) {
2151 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2152 status);
2153 return;
2154 }
2155 status = pjsip_xfer_send_request(sub, tdata);
2156 if (status != PJ_SUCCESS) {
2157 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2158 status);
2159 return;
2160 }
2161 return;
2162 }
2163
2164 /* Put the server subscription in inv_data.
2165 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2166 * reported back to the server subscription.
2167 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002168 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002169
2170 /* Put the invite_data in the subscription. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2172 &pjsua_var.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +00002173}
2174
2175
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002176
Benny Prijono26ff9062006-02-21 23:47:00 +00002177/*
2178 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002179 * session. We use this to trap:
2180 * - incoming REFER request.
2181 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002182 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002183static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2184 pjsip_transaction *tsx,
2185 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002186{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002187 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2188
2189 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002190
Benny Prijono26ff9062006-02-21 23:47:00 +00002191 if (tsx->role==PJSIP_ROLE_UAS &&
2192 tsx->state==PJSIP_TSX_STATE_TRYING &&
2193 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2194 {
2195 /*
2196 * Incoming REFER request.
2197 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002198 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002199
Benny Prijono26ff9062006-02-21 23:47:00 +00002200 }
Benny Prijonob0808372006-03-02 21:18:58 +00002201 else if (tsx->role==PJSIP_ROLE_UAS &&
2202 tsx->state==PJSIP_TSX_STATE_TRYING &&
2203 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2204 {
2205 /*
2206 * Incoming MESSAGE request!
2207 */
2208 pjsip_rx_data *rdata;
2209 pjsip_msg *msg;
2210 pjsip_accept_hdr *accept_hdr;
2211 pj_status_t status;
2212
2213 rdata = e->body.tsx_state.src.rdata;
2214 msg = rdata->msg_info.msg;
2215
2216 /* Request MUST have message body, with Content-Type equal to
2217 * "text/plain".
2218 */
2219 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2220
2221 pjsip_hdr hdr_list;
2222
2223 pj_list_init(&hdr_list);
2224 pj_list_push_back(&hdr_list, accept_hdr);
2225
2226 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2227 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002229 return;
2230 }
2231
2232 /* Respond with 200 first, so that remote doesn't retransmit in case
2233 * the UI takes too long to process the message.
2234 */
2235 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2236
2237 /* Process MESSAGE request */
2238 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2239 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002240
Benny Prijonob0808372006-03-02 21:18:58 +00002241 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242 else if (tsx->role == PJSIP_ROLE_UAC &&
2243 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002244 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002245 /* Handle outgoing pager status */
2246 if (tsx->status_code >= 200) {
2247 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002248
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002249 im_data = tsx->mod_data[pjsua_var.mod.id];
2250 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002251
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002252 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2253 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2254 &im_data->to,
2255 &im_data->body,
2256 im_data->user_data,
2257 tsx->status_code,
2258 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002259 }
Benny Prijonofccab712006-02-22 22:23:22 +00002260 }
Benny Prijono834aee32006-02-19 01:38:06 +00002261 }
Benny Prijono834aee32006-02-19 01:38:06 +00002262
Benny Prijono26ff9062006-02-21 23:47:00 +00002263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002264 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002265}