blob: bb2ccd18dd6355ff1b952fb53f56dcd23b9f427a [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
Benny Prijonod524e822006-09-22 12:48:18 +000070/*
71 * Callback called by event framework when the xfer subscription state
72 * has changed.
73 */
74static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000075
76/*
77 * Reset call descriptor.
78 */
79static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000080{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000081 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000082
Benny Prijonoeebe9af2006-06-13 22:57:13 +000083 call->index = id;
84 call->inv = NULL;
85 call->user_data = NULL;
86 call->session = NULL;
87 call->xfer_sub = NULL;
88 call->last_code = 0;
89 call->conf_slot = PJSUA_INVALID_ID;
90 call->last_text.ptr = call->last_text_buf_;
91 call->last_text.slen = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000092}
93
94
Benny Prijono275fd682006-03-22 11:59:11 +000095/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000096 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +000097 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000098pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +000099{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000100 pjsip_inv_callback inv_cb;
101 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000102 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000104
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000105 /* Init calls array. */
106 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
107 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000108
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109 /* Copy config */
110 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000111
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000112 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000113 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000114 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
115 inv_cb.on_new_session = &pjsua_call_on_forked;
116 inv_cb.on_media_update = &pjsua_call_on_media_update;
117 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
118 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000119
Benny Prijono275fd682006-03-22 11:59:11 +0000120
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000121 /* Initialize invite session module: */
122 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
123 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
124
Benny Prijonoc8141a82006-08-20 09:12:19 +0000125 /* Add "norefersub" in Supported header */
126 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
127 NULL, 1, &str_norefersub);
128
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 return status;
130}
131
132
133/*
134 * Start call subsystem.
135 */
136pj_status_t pjsua_call_subsys_start(void)
137{
138 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000139 return PJ_SUCCESS;
140}
141
142
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000144 * Get maximum number of calls configured in pjsua.
145 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000146PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000147{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000149}
150
151
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152/*
153 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000154 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000155PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000156{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000158}
159
160
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161/*
162 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000163 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000164PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
165 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000166{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000167 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000170
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000171 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000172
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000173 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
174 if (!pjsua_var.calls[i].inv)
175 continue;
176 ids[c] = i;
177 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000178 }
179
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000180 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183
184 return PJ_SUCCESS;
185}
186
187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188/*
189 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
192 const pj_str_t *dest_uri,
193 unsigned options,
194 void *user_data,
195 const pjsua_msg_data *msg_data,
196 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000197{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000198 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000199 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000200 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201 pjsua_acc *acc;
202 pjsua_call *call;
203 unsigned call_id;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000204 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000205 pjsip_tx_data *tdata;
206 pj_status_t status;
207
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 /* Check that account is valid */
210 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211 PJ_EINVAL);
212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 /* Options must be zero for now */
214 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
215
216 PJSUA_LOCK();
217
218 acc = &pjsua_var.acc[acc_id];
219 if (!acc->valid) {
220 pjsua_perror(THIS_FILE, "Unable to make call because account "
221 "is not valid", PJ_EINVALIDOP);
222 PJSUA_UNLOCK();
223 return PJ_EINVALIDOP;
224 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000225
Benny Prijonoa91a0032006-02-26 21:23:45 +0000226 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
228 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000229 break;
230 }
231
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000232 if (call_id == pjsua_var.ua_cfg.max_calls) {
233 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
234 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000235 return PJ_ETOOMANY;
236 }
237
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 call = &pjsua_var.calls[call_id];
239
Benny Prijono093d3022006-09-24 00:07:11 +0000240 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
241 (int)dest_uri->slen, dest_uri->ptr));
242
Benny Prijonoe21e7842006-04-09 16:46:05 +0000243 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000245
Benny Prijonoe21e7842006-04-09 16:46:05 +0000246 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000247 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000248
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000249 /* Create suitable Contact header */
250 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
251 acc_id, dest_uri);
252 if (status != PJ_SUCCESS) {
253 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
254 PJSUA_UNLOCK();
255 return status;
256 }
257
Benny Prijonoe21e7842006-04-09 16:46:05 +0000258 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000259 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000260 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000262 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000263 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000264 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000265 return status;
266 }
267
268 /* Get media capability from media endpoint: */
269
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000270 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
271 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000272 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000273 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000274 goto on_error;
275 }
276
277 /* Create the INVITE session: */
278
279 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
280 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000281 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000282 goto on_error;
283 }
284
285
286 /* Create and associate our data in the session. */
287
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000288 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000289
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000290 dlg->mod_data[pjsua_var.mod.id] = call;
291 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000292
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000293 /* Attach user data */
294 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000295
296 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000297 if (!pj_list_empty(&acc->route_set))
298 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000299
300
301 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000303 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000304 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000305 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000306
307
308 /* Create initial INVITE: */
309
310 status = pjsip_inv_invite(inv, &tdata);
311 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000312 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
313 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 goto on_error;
315 }
316
317
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000318 /* Add additional headers etc */
319
320 pjsua_process_msg_data( tdata, msg_data);
321
Benny Prijono093d3022006-09-24 00:07:11 +0000322 /* Must increment call counter now */
323 ++pjsua_var.call_cnt;
324
Benny Prijono84126ab2006-02-09 09:30:09 +0000325 /* Send initial INVITE: */
326
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000327 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000328 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000329 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
330 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000331
332 /* Upon failure to send first request, both dialog and invite
333 * session would have been cleared.
334 */
335 inv = NULL;
336 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000337 goto on_error;
338 }
339
Benny Prijono84126ab2006-02-09 09:30:09 +0000340 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000341
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000342 if (p_call_id)
343 *p_call_id = call_id;
344
345 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000346
347 return PJ_SUCCESS;
348
349
350on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000351 if (inv != NULL) {
352 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000353 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000354 pjsip_dlg_terminate(dlg);
355 }
356
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000357 if (call_id != -1) {
358 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000359 }
360
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000362 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000363}
364
365
366/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000367 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000369 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000370pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000371{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000372 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000373 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
374 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
375 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000376 pjsip_tx_data *response = NULL;
377 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000378 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000379 int acc_id;
380 pjsua_call *call;
381 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000382 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000383 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000384
Benny Prijono26ff9062006-02-21 23:47:00 +0000385 /* Don't want to handle anything but INVITE */
386 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
387 return PJ_FALSE;
388
389 /* Don't want to handle anything that's already associated with
390 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000391 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000392 if (dlg || tsx)
393 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000394
Benny Prijono148c9dd2006-09-19 13:37:53 +0000395 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000396
Benny Prijono26ff9062006-02-21 23:47:00 +0000397 /* Verify that we can handle the request. */
398 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 pjsua_var.endpt, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000400 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000401
Benny Prijono26ff9062006-02-21 23:47:00 +0000402 /*
403 * No we can't handle the incoming INVITE request.
404 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000405
Benny Prijono26ff9062006-02-21 23:47:00 +0000406 if (response) {
407 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000408
Benny Prijono26ff9062006-02-21 23:47:00 +0000409 pjsip_get_response_addr(response->pool, rdata, &res_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000410 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
Benny Prijono26ff9062006-02-21 23:47:00 +0000411 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000412
413 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000414
Benny Prijono26ff9062006-02-21 23:47:00 +0000415 /* Respond with 500 (Internal Server Error) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000417 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000418 }
419
Benny Prijono148c9dd2006-09-19 13:37:53 +0000420 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000421 return PJ_TRUE;
422 }
423
424
425 /*
426 * Yes we can handle the incoming INVITE request.
427 */
428
429 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000430 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
431 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000432 break;
433 }
434
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
436 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000437 PJSIP_SC_BUSY_HERE, NULL,
438 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000439 PJ_LOG(2,(THIS_FILE,
440 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000441 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000442 return PJ_TRUE;
443 }
444
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 /* Clear call descriptor */
446 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000447
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000448 call = &pjsua_var.calls[call_id];
449
450 /* Mark call start time. */
451 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000452
Benny Prijono26ff9062006-02-21 23:47:00 +0000453 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000454 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
455 rdata->tp_info.pool, 1,
456 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000457 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000458 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000459 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000460 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000461 return PJ_TRUE;
462 }
463
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000464 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000465 * Get which account is most likely to be associated with this incoming
466 * call. We need the account to find which contact URI to put for
467 * the call.
468 */
Benny Prijono093d3022006-09-24 00:07:11 +0000469 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000470
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000471 /* Get suitable Contact header */
472 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
473 acc_id, rdata);
474 if (status != PJ_SUCCESS) {
475 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
476 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
477 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000478 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000479 return PJ_TRUE;
480 }
481
Benny Prijono26ff9062006-02-21 23:47:00 +0000482 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000483 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000484 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000485 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000487 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000488 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000489 return PJ_TRUE;
490 }
491
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000492 /* Set credentials */
493 if (pjsua_var.acc[acc_id].cred_cnt) {
494 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
495 pjsua_var.acc[acc_id].cred_cnt,
496 pjsua_var.acc[acc_id].cred);
497 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000498
499 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000500 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
501 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000502 pjsip_hdr hdr_list;
503 pjsip_warning_hdr *w;
504
505 w = pjsip_warning_hdr_create_from_status(dlg->pool,
506 pjsip_endpt_name(pjsua_var.endpt),
507 status);
508 pj_list_init(&hdr_list);
509 pj_list_push_back(&hdr_list, w);
510
511 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
512
513 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000514 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000515 */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000516 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000517 return PJ_TRUE;
518 }
519
520
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 /* Create and attach pjsua_var data to the dialog: */
522 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000523
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000524 dlg->mod_data[pjsua_var.mod.id] = call;
525 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000526
527
Benny Prijono64f851e2006-02-23 13:49:28 +0000528 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000529 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000530 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000531 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000532 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000533 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
534 status);
535
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
537 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000538 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000539 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000540
541 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000542 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000543 if (status != PJ_SUCCESS)
544 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000545 }
546
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000548
Benny Prijono105217f2006-03-06 16:25:59 +0000549
Benny Prijono8b1889b2006-06-06 18:40:40 +0000550 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 if (pjsua_var.ua_cfg.cb.on_incoming_call)
552 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000553
Benny Prijono26ff9062006-02-21 23:47:00 +0000554 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000555 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000556 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000557}
558
559
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560
561/*
562 * Check if the specified call has active INVITE session and the INVITE
563 * session has not been disconnected.
564 */
565PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
566{
567 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
568 PJ_EINVAL);
569 return pjsua_var.calls[call_id].inv != NULL &&
570 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
571}
572
573
574/*
575 * Check if call has an active media session.
576 */
577PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
578{
579 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
580 PJ_EINVAL);
581 return pjsua_var.calls[call_id].session != NULL;
582}
583
584
Benny Prijono148c9dd2006-09-19 13:37:53 +0000585/* Acquire lock to the specified call_id */
586static pj_status_t acquire_call(const char *title,
587 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000588 pjsua_call **p_call,
589 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000590{
591 enum { MAX_RETRY=50 };
592 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000593 pjsua_call *call = NULL;
594 pj_bool_t has_pjsua_lock = PJ_FALSE;
595 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000596
597 for (retry=0; retry<MAX_RETRY; ++retry) {
598
599 has_pjsua_lock = PJ_FALSE;
600
601 status = PJSUA_TRY_LOCK();
602 if (status != PJ_SUCCESS) {
603 pj_thread_sleep(retry/10);
604 continue;
605 }
606
607 has_pjsua_lock = PJ_TRUE;
608 call = &pjsua_var.calls[call_id];
609
610 if (call->inv == NULL) {
611 PJSUA_UNLOCK();
612 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
613 return PJSIP_ESESSIONTERMINATED;
614 }
615
616 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
617 if (status != PJ_SUCCESS) {
618 PJSUA_UNLOCK();
619 pj_thread_sleep(retry/10);
620 continue;
621 }
622
623 PJSUA_UNLOCK();
624
625 break;
626 }
627
628 if (status != PJ_SUCCESS) {
629 if (has_pjsua_lock == PJ_FALSE)
630 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
631 "(possibly system has deadlocked) in %s",
632 title));
633 else
634 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
635 "(possibly system has deadlocked) in %s",
636 title));
637 return PJ_ETIMEDOUT;
638 }
639
640 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000641 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000642
643 return PJ_SUCCESS;
644}
645
646
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000647/*
648 * Get the conference port identification associated with the call.
649 */
650PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
651{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000652 pjsua_call *call;
653 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000654 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000655 pj_status_t status;
656
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
658 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000659
Benny Prijonodc752ca2006-09-22 16:55:42 +0000660 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000661 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000662 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000663
664 port_id = call->conf_slot;
665
Benny Prijonodc752ca2006-09-22 16:55:42 +0000666 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000667
668 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000669}
670
671
Benny Prijono148c9dd2006-09-19 13:37:53 +0000672
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000673/*
674 * Obtain detail information about the specified call.
675 */
676PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
677 pjsua_call_info *info)
678{
679 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000680 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000681 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682
683 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
684 PJ_EINVAL);
685
Benny Prijonoac623b32006-07-03 15:19:31 +0000686 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687
Benny Prijonodc752ca2006-09-22 16:55:42 +0000688 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000689 if (status != PJ_SUCCESS) {
690 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000691 }
692
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000693 /* id and role */
694 info->id = call_id;
695 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000696 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000697
698 /* local info */
699 info->local_info.ptr = info->buf_.local_info;
700 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
701 sizeof(info->buf_.local_info));
702
703 /* local contact */
704 info->local_contact.ptr = info->buf_.local_contact;
705 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
706 call->inv->dlg->local.contact->uri,
707 info->local_contact.ptr,
708 sizeof(info->buf_.local_contact));
709
710 /* remote info */
711 info->remote_info.ptr = info->buf_.remote_info;
712 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
713 sizeof(info->buf_.remote_info));
714
715 /* remote contact */
716 if (call->inv->dlg->remote.contact) {
717 int len;
718 info->remote_contact.ptr = info->buf_.remote_contact;
719 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
720 call->inv->dlg->remote.contact->uri,
721 info->remote_contact.ptr,
722 sizeof(info->buf_.remote_contact));
723 if (len < 0) len = 0;
724 info->remote_contact.slen = len;
725 } else {
726 info->remote_contact.slen = 0;
727 }
728
729 /* call id */
730 info->call_id.ptr = info->buf_.call_id;
731 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
732 sizeof(info->buf_.call_id));
733
734 /* state, state_text */
735 info->state = call->inv->state;
736 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
737
738 /* If call is disconnected, set the last_status from the cause code */
739 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
740 /* last_status, last_status_text */
741 info->last_status = call->inv->cause;
742
743 info->last_status_text.ptr = info->buf_.last_status_text;
744 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
745 sizeof(info->buf_.last_status_text));
746 } else {
747 /* last_status, last_status_text */
748 info->last_status = call->last_code;
749
750 info->last_status_text.ptr = info->buf_.last_status_text;
751 pj_strncpy(&info->last_status_text, &call->last_text,
752 sizeof(info->buf_.last_status_text));
753 }
754
755 /* media status and dir */
756 info->media_status = call->media_st;
757 info->media_dir = call->media_dir;
758
759
760 /* conference slot number */
761 info->conf_slot = call->conf_slot;
762
763 /* calculate duration */
764 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
765
766 info->total_duration = call->dis_time;
767 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
768
769 if (call->conn_time.sec) {
770 info->connect_duration = call->dis_time;
771 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
772 }
773
774 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
775
776 pj_gettimeofday(&info->total_duration);
777 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
778
779 pj_gettimeofday(&info->connect_duration);
780 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
781
782 } else {
783 pj_gettimeofday(&info->total_duration);
784 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
785 }
786
Benny Prijonodc752ca2006-09-22 16:55:42 +0000787 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000788
789 return PJ_SUCCESS;
790}
791
792
793/*
794 * Attach application specific data to the call.
795 */
796PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
797 void *user_data)
798{
799 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
800 PJ_EINVAL);
801 pjsua_var.calls[call_id].user_data = user_data;
802
803 return PJ_SUCCESS;
804}
805
806
807/*
808 * Get user data attached to the call.
809 */
810PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
811{
812 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
813 NULL);
814 return pjsua_var.calls[call_id].user_data;
815}
816
817
818/*
819 * Send response to incoming INVITE request.
820 */
821PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
822 unsigned code,
823 const pj_str_t *reason,
824 const pjsua_msg_data *msg_data)
825{
826 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000827 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000828 pjsip_tx_data *tdata;
829 pj_status_t status;
830
831 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
832 PJ_EINVAL);
833
Benny Prijonodc752ca2006-09-22 16:55:42 +0000834 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000835 if (status != PJ_SUCCESS)
836 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000837
Benny Prijono2e507c22006-06-23 15:04:11 +0000838 if (call->res_time.sec == 0)
839 pj_gettimeofday(&call->res_time);
840
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000841 /* Create response message */
842 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
843 if (status != PJ_SUCCESS) {
844 pjsua_perror(THIS_FILE, "Error creating response",
845 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000846 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000847 return status;
848 }
849
850 /* Add additional headers etc */
851 pjsua_process_msg_data( tdata, msg_data);
852
853 /* Send the message */
854 status = pjsip_inv_send_msg(call->inv, tdata);
855 if (status != PJ_SUCCESS)
856 pjsua_perror(THIS_FILE, "Error sending response",
857 status);
858
Benny Prijonodc752ca2006-09-22 16:55:42 +0000859 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860
861 return status;
862}
863
864
865/*
866 * Hangup call by using method that is appropriate according to the
867 * call state.
868 */
869PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
870 unsigned code,
871 const pj_str_t *reason,
872 const pjsua_msg_data *msg_data)
873{
874 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000875 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000876 pj_status_t status;
877 pjsip_tx_data *tdata;
878
879
Benny Prijono148c9dd2006-09-19 13:37:53 +0000880 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
881 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
882 call_id));
883 }
884
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000885 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
886 PJ_EINVAL);
887
Benny Prijonodc752ca2006-09-22 16:55:42 +0000888 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000889 if (status != PJ_SUCCESS)
890 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000891
892 if (code==0) {
893 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
894 code = PJSIP_SC_OK;
895 else if (call->inv->role == PJSIP_ROLE_UAS)
896 code = PJSIP_SC_DECLINE;
897 else
898 code = PJSIP_SC_REQUEST_TERMINATED;
899 }
900
901 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
902 if (status != PJ_SUCCESS) {
903 pjsua_perror(THIS_FILE,
904 "Failed to create end session message",
905 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000906 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000907 return status;
908 }
909
910 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
911 * as p_tdata when INVITE transaction has not been answered
912 * with any provisional responses.
913 */
914 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +0000915 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000916 return PJ_SUCCESS;
917 }
918
919 /* Add additional headers etc */
920 pjsua_process_msg_data( tdata, msg_data);
921
922 /* Send the message */
923 status = pjsip_inv_send_msg(call->inv, tdata);
924 if (status != PJ_SUCCESS) {
925 pjsua_perror(THIS_FILE,
926 "Failed to send end session message",
927 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000928 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000929 return status;
930 }
931
Benny Prijonodc752ca2006-09-22 16:55:42 +0000932 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000933
934 return PJ_SUCCESS;
935}
936
937
938/*
939 * Put the specified call on hold.
940 */
941PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
942 const pjsua_msg_data *msg_data)
943{
944 pjmedia_sdp_session *sdp;
945 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000946 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000947 pjsip_tx_data *tdata;
948 pj_status_t status;
949
950 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
951 PJ_EINVAL);
952
Benny Prijonodc752ca2006-09-22 16:55:42 +0000953 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000954 if (status != PJ_SUCCESS)
955 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000956
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000957
958 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
959 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +0000960 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000961 return PJSIP_ESESSIONSTATE;
962 }
963
964 status = create_inactive_sdp(call, &sdp);
965 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +0000966 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000967 return status;
968 }
969
970 /* Create re-INVITE with new offer */
971 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
972 if (status != PJ_SUCCESS) {
973 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000974 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000975 return status;
976 }
977
978 /* Add additional headers etc */
979 pjsua_process_msg_data( tdata, msg_data);
980
981 /* Send the request */
982 status = pjsip_inv_send_msg( call->inv, tdata);
983 if (status != PJ_SUCCESS) {
984 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +0000985 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000986 return status;
987 }
988
Benny Prijonodc752ca2006-09-22 16:55:42 +0000989 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000990
991 return PJ_SUCCESS;
992}
993
994
995/*
996 * Send re-INVITE (to release hold).
997 */
998PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
999 pj_bool_t unhold,
1000 const pjsua_msg_data *msg_data)
1001{
1002 pjmedia_sdp_session *sdp;
1003 pjsip_tx_data *tdata;
1004 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001005 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001006 pj_status_t status;
1007
1008
1009 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1010 PJ_EINVAL);
1011
Benny Prijonodc752ca2006-09-22 16:55:42 +00001012 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001013 if (status != PJ_SUCCESS)
1014 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001015
1016 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1017 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001018 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001019 return PJSIP_ESESSIONSTATE;
1020 }
1021
1022 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001023 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001024 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
1026 1, &call->skinfo, &sdp);
1027 if (status != PJ_SUCCESS) {
1028 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1029 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001030 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001031 return status;
1032 }
1033
1034 /* Create re-INVITE with new offer */
1035 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1036 if (status != PJ_SUCCESS) {
1037 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001038 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001039 return status;
1040 }
1041
1042 /* Add additional headers etc */
1043 pjsua_process_msg_data( tdata, msg_data);
1044
1045 /* Send the request */
1046 status = pjsip_inv_send_msg( call->inv, tdata);
1047 if (status != PJ_SUCCESS) {
1048 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001049 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001050 return status;
1051 }
1052
Benny Prijonodc752ca2006-09-22 16:55:42 +00001053 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001054
1055 return PJ_SUCCESS;
1056}
1057
1058
1059/*
1060 * Initiate call transfer to the specified address.
1061 */
1062PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1063 const pj_str_t *dest,
1064 const pjsua_msg_data *msg_data)
1065{
1066 pjsip_evsub *sub;
1067 pjsip_tx_data *tdata;
1068 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001069 pjsip_dialog *dlg;
Benny Prijonod524e822006-09-22 12:48:18 +00001070 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071 pj_status_t status;
1072
1073
1074 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1075 PJ_EINVAL);
1076
Benny Prijonodc752ca2006-09-22 16:55:42 +00001077 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001078 if (status != PJ_SUCCESS)
1079 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001080
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001081
Benny Prijonod524e822006-09-22 12:48:18 +00001082 /* Create xfer client subscription. */
1083 pj_bzero(&xfer_cb, sizeof(xfer_cb));
1084 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
1085
1086 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001087 if (status != PJ_SUCCESS) {
1088 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001089 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001090 return status;
1091 }
1092
1093 /*
1094 * Create REFER request.
1095 */
1096 status = pjsip_xfer_initiate(sub, dest, &tdata);
1097 if (status != PJ_SUCCESS) {
1098 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001099 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001100 return status;
1101 }
1102
1103 /* Add additional headers etc */
1104 pjsua_process_msg_data( tdata, msg_data);
1105
1106 /* Send. */
1107 status = pjsip_xfer_send_request(sub, tdata);
1108 if (status != PJ_SUCCESS) {
1109 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001110 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001111 return status;
1112 }
1113
1114 /* For simplicity (that's what this program is intended to be!),
1115 * leave the original invite session as it is. More advanced application
1116 * may want to hold the INVITE, or terminate the invite, or whatever.
1117 */
1118
Benny Prijonodc752ca2006-09-22 16:55:42 +00001119 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001120
1121 return PJ_SUCCESS;
1122
1123}
1124
1125
1126/*
1127 * Send DTMF digits to remote using RFC 2833 payload formats.
1128 */
1129PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1130 const pj_str_t *digits)
1131{
1132 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001133 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001134 pj_status_t status;
1135
1136 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1137 PJ_EINVAL);
1138
Benny Prijonodc752ca2006-09-22 16:55:42 +00001139 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001140 if (status != PJ_SUCCESS)
1141 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001142
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001143 if (!call->session) {
1144 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001146 return PJ_EINVALIDOP;
1147 }
1148
1149 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1150
Benny Prijonodc752ca2006-09-22 16:55:42 +00001151 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001152
1153 return status;
1154}
1155
1156
1157/**
1158 * Send instant messaging inside INVITE session.
1159 */
1160PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1161 const pj_str_t *mime_type,
1162 const pj_str_t *content,
1163 const pjsua_msg_data *msg_data,
1164 void *user_data)
1165{
1166 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001167 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001168 const pj_str_t mime_text_plain = pj_str("text/plain");
1169 pjsip_media_type ctype;
1170 pjsua_im_data *im_data;
1171 pjsip_tx_data *tdata;
1172 pj_status_t status;
1173
1174
1175 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1176 PJ_EINVAL);
1177
Benny Prijonodc752ca2006-09-22 16:55:42 +00001178 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001179 if (status != PJ_SUCCESS)
1180 return status;
1181
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001182 /* Set default media type if none is specified */
1183 if (mime_type == NULL) {
1184 mime_type = &mime_text_plain;
1185 }
1186
1187 /* Create request message. */
1188 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1189 -1, &tdata);
1190 if (status != PJ_SUCCESS) {
1191 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1192 goto on_return;
1193 }
1194
1195 /* Add accept header. */
1196 pjsip_msg_add_hdr( tdata->msg,
1197 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1198
1199 /* Parse MIME type */
1200 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1201
1202 /* Create "text/plain" message body. */
1203 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1204 &ctype.subtype, content);
1205 if (tdata->msg->body == NULL) {
1206 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1207 pjsip_tx_data_dec_ref(tdata);
1208 goto on_return;
1209 }
1210
1211 /* Add additional headers etc */
1212 pjsua_process_msg_data( tdata, msg_data);
1213
1214 /* Create IM data and attach to the request. */
1215 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1216 im_data->acc_id = call->acc_id;
1217 im_data->call_id = call_id;
1218 im_data->to = call->inv->dlg->remote.info_str;
1219 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1220 im_data->user_data = user_data;
1221
1222
1223 /* Send the request. */
1224 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1225 pjsua_var.mod.id, im_data);
1226 if (status != PJ_SUCCESS) {
1227 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1228 goto on_return;
1229 }
1230
1231on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001232 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233 return status;
1234}
1235
1236
1237/*
1238 * Send IM typing indication inside INVITE session.
1239 */
1240PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1241 pj_bool_t is_typing,
1242 const pjsua_msg_data*msg_data)
1243{
1244 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001245 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 pjsip_tx_data *tdata;
1247 pj_status_t status;
1248
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001249 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1250 PJ_EINVAL);
1251
Benny Prijonodc752ca2006-09-22 16:55:42 +00001252 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001253 if (status != PJ_SUCCESS)
1254 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001255
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001256 /* Create request message. */
1257 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1258 -1, &tdata);
1259 if (status != PJ_SUCCESS) {
1260 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1261 goto on_return;
1262 }
1263
1264 /* Create "application/im-iscomposing+xml" msg body. */
1265 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1266 NULL, NULL, -1);
1267
1268 /* Add additional headers etc */
1269 pjsua_process_msg_data( tdata, msg_data);
1270
1271 /* Send the request. */
1272 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1273 if (status != PJ_SUCCESS) {
1274 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1275 goto on_return;
1276 }
1277
1278on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001279 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280 return status;
1281}
1282
1283
1284/*
1285 * Terminate all calls.
1286 */
1287PJ_DEF(void) pjsua_call_hangup_all(void)
1288{
1289 unsigned i;
1290
1291 PJSUA_LOCK();
1292
1293 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1294 if (pjsua_var.calls[i].inv)
1295 pjsua_call_hangup(i, 0, NULL, NULL);
1296 }
1297
1298 PJSUA_UNLOCK();
1299}
1300
1301
1302static const char *good_number(char *buf, pj_int32_t val)
1303{
1304 if (val < 1000) {
1305 pj_ansi_sprintf(buf, "%d", val);
1306 } else if (val < 1000000) {
1307 pj_ansi_sprintf(buf, "%d.%dK",
1308 val / 1000,
1309 (val % 1000) / 100);
1310 } else {
1311 pj_ansi_sprintf(buf, "%d.%02dM",
1312 val / 1000000,
1313 (val % 1000000) / 10000);
1314 }
1315
1316 return buf;
1317}
1318
1319
1320/* Dump media session */
1321static void dump_media_session(const char *indent,
1322 char *buf, unsigned maxlen,
1323 pjmedia_session *session)
1324{
1325 unsigned i;
1326 char *p = buf, *end = buf+maxlen;
1327 int len;
1328 pjmedia_session_info info;
1329
1330 pjmedia_session_get_info(session, &info);
1331
1332 for (i=0; i<info.stream_cnt; ++i) {
1333 pjmedia_rtcp_stat stat;
1334 const char *rem_addr;
1335 int rem_port;
1336 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001337 char last_update[64];
Benny Prijono80019eb2006-08-07 13:22:23 +00001338 char packets[32], bytes[32], ipbytes[32], avg_bps[32];
1339 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001340
1341 pjmedia_session_get_stream_stat(session, i, &stat);
1342 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1343 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1344
1345 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1346 dir = "sendonly";
1347 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1348 dir = "recvonly";
1349 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1350 dir = "sendrecv";
1351 else
1352 dir = "inactive";
1353
1354
1355 len = pj_ansi_snprintf(buf, end-p,
1356 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1357 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001358 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 info.stream_info[i].fmt.encoding_name.ptr,
1360 info.stream_info[i].fmt.clock_rate / 1000,
1361 dir,
1362 rem_addr, rem_port);
1363 if (len < 1 || len > end-p) {
1364 *p = '\0';
1365 return;
1366 }
1367
1368 p += len;
1369 *p++ = '\n';
1370 *p = '\0';
1371
1372 if (stat.rx.update_cnt == 0)
1373 strcpy(last_update, "never");
1374 else {
1375 pj_gettimeofday(&now);
1376 PJ_TIME_VAL_SUB(now, stat.rx.update);
1377 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1378 now.sec / 3600,
1379 (now.sec % 3600) / 60,
1380 now.sec % 60,
1381 now.msec);
1382 }
1383
Benny Prijono80019eb2006-08-07 13:22:23 +00001384 pj_gettimeofday(&media_duration);
1385 PJ_TIME_VAL_SUB(media_duration, stat.start);
1386 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1387 media_duration.msec = 1;
1388
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389 len = pj_ansi_snprintf(p, end-p,
1390 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001391 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001392 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1393 "%s (msec) min avg max last\n"
1394 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1395 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1396 indent, info.stream_info[i].fmt.pt,
1397 last_update,
1398 indent,
1399 good_number(packets, stat.rx.pkt),
1400 good_number(bytes, stat.rx.bytes),
1401 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001402 good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001403 indent,
1404 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001405 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001407 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001408 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001409 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410 indent, indent,
1411 stat.rx.loss_period.min / 1000.0,
1412 stat.rx.loss_period.avg / 1000.0,
1413 stat.rx.loss_period.max / 1000.0,
1414 stat.rx.loss_period.last / 1000.0,
1415 indent,
1416 stat.rx.jitter.min / 1000.0,
1417 stat.rx.jitter.avg / 1000.0,
1418 stat.rx.jitter.max / 1000.0,
1419 stat.rx.jitter.last / 1000.0,
1420 ""
1421 );
1422
1423 if (len < 1 || len > end-p) {
1424 *p = '\0';
1425 return;
1426 }
1427
1428 p += len;
1429 *p++ = '\n';
1430 *p = '\0';
1431
1432 if (stat.tx.update_cnt == 0)
1433 strcpy(last_update, "never");
1434 else {
1435 pj_gettimeofday(&now);
1436 PJ_TIME_VAL_SUB(now, stat.tx.update);
1437 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1438 now.sec / 3600,
1439 (now.sec % 3600) / 60,
1440 now.sec % 60,
1441 now.msec);
1442 }
1443
1444 len = pj_ansi_snprintf(p, end-p,
1445 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono80019eb2006-08-07 13:22:23 +00001446 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001447 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1448 "%s (msec) min avg max last\n"
1449 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1450 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1451 indent,
1452 info.stream_info[i].tx_pt,
1453 info.stream_info[i].param->info.frm_ptime *
1454 info.stream_info[i].param->setting.frm_per_pkt,
1455 last_update,
1456
1457 indent,
1458 good_number(packets, stat.tx.pkt),
1459 good_number(bytes, stat.tx.bytes),
1460 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
Benny Prijono80019eb2006-08-07 13:22:23 +00001461 good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001462
1463 indent,
1464 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001465 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001467 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001469 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470
1471 indent, indent,
1472 stat.tx.loss_period.min / 1000.0,
1473 stat.tx.loss_period.avg / 1000.0,
1474 stat.tx.loss_period.max / 1000.0,
1475 stat.tx.loss_period.last / 1000.0,
1476 indent,
1477 stat.tx.jitter.min / 1000.0,
1478 stat.tx.jitter.avg / 1000.0,
1479 stat.tx.jitter.max / 1000.0,
1480 stat.tx.jitter.last / 1000.0,
1481 ""
1482 );
1483
1484 if (len < 1 || len > end-p) {
1485 *p = '\0';
1486 return;
1487 }
1488
1489 p += len;
1490 *p++ = '\n';
1491 *p = '\0';
1492
1493 len = pj_ansi_snprintf(p, end-p,
1494 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1495 indent,
1496 stat.rtt.min / 1000.0,
1497 stat.rtt.avg / 1000.0,
1498 stat.rtt.max / 1000.0,
1499 stat.rtt.last / 1000.0
1500 );
1501 if (len < 1 || len > end-p) {
1502 *p = '\0';
1503 return;
1504 }
1505
1506 p += len;
1507 *p++ = '\n';
1508 *p = '\0';
1509 }
1510}
1511
1512
1513/* Print call info */
1514static void print_call(const char *title,
1515 int call_id,
1516 char *buf, pj_size_t size)
1517{
1518 int len;
1519 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1520 pjsip_dialog *dlg = inv->dlg;
1521 char userinfo[128];
1522
1523 /* Dump invite sesion info. */
1524
1525 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1526 if (len < 1)
1527 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1528 else
1529 userinfo[len] = '\0';
1530
1531 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1532 title,
1533 pjsip_inv_state_name(inv->state),
1534 userinfo);
1535 if (len < 1 || len >= (int)size) {
1536 pj_ansi_strcpy(buf, "<--uri too long-->");
1537 len = 18;
1538 } else
1539 buf[len] = '\0';
1540}
1541
1542
1543/*
1544 * Dump call and media statistics to string.
1545 */
1546PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1547 pj_bool_t with_media,
1548 char *buffer,
1549 unsigned maxlen,
1550 const char *indent)
1551{
1552 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001553 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554 pj_time_val duration, res_delay, con_delay;
1555 char tmp[128];
1556 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001557 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001558 int len;
1559
1560 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1561 PJ_EINVAL);
1562
Benny Prijonodc752ca2006-09-22 16:55:42 +00001563 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001564 if (status != PJ_SUCCESS)
1565 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566
1567 *buffer = '\0';
1568 p = buffer;
1569 end = buffer + maxlen;
1570 len = 0;
1571
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001572 print_call(indent, call_id, tmp, sizeof(tmp));
1573
1574 len = pj_ansi_strlen(tmp);
1575 pj_ansi_strcpy(buffer, tmp);
1576
1577 p += len;
1578 *p++ = '\r';
1579 *p++ = '\n';
1580
1581 /* Calculate call duration */
1582 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1583 pj_gettimeofday(&duration);
1584 PJ_TIME_VAL_SUB(duration, call->conn_time);
1585 con_delay = call->conn_time;
1586 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1587 } else {
1588 duration.sec = duration.msec = 0;
1589 con_delay.sec = con_delay.msec = 0;
1590 }
1591
1592 /* Calculate first response delay */
1593 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1594 res_delay = call->res_time;
1595 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1596 } else {
1597 res_delay.sec = res_delay.msec = 0;
1598 }
1599
1600 /* Print duration */
1601 len = pj_ansi_snprintf(p, end-p,
1602 "%s Call time: %02dh:%02dm:%02ds, "
1603 "1st res in %d ms, conn in %dms",
1604 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00001605 (int)(duration.sec / 3600),
1606 (int)((duration.sec % 3600)/60),
1607 (int)(duration.sec % 60),
1608 (int)PJ_TIME_VAL_MSEC(res_delay),
1609 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610
1611 if (len > 0 && len < end-p) {
1612 p += len;
1613 *p++ = '\n';
1614 *p = '\0';
1615 }
1616
1617 /* Dump session statistics */
1618 if (with_media && call->session)
1619 dump_media_session(indent, p, end-p, call->session);
1620
Benny Prijonodc752ca2006-09-22 16:55:42 +00001621 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001622
1623 return PJ_SUCCESS;
1624}
1625
1626
1627/*
1628 * Destroy the call's media
1629 */
1630static pj_status_t call_destroy_media(int call_id)
1631{
1632 pjsua_call *call = &pjsua_var.calls[call_id];
1633
1634 if (call->conf_slot != PJSUA_INVALID_ID) {
1635 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1636 call->conf_slot = PJSUA_INVALID_ID;
1637 }
1638
1639 if (call->session) {
1640 /* Destroy session (this will also close RTP/RTCP sockets). */
1641 pjmedia_session_destroy(call->session);
1642 call->session = NULL;
1643
1644 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1645 call_id));
1646
1647 }
1648
1649 call->media_st = PJSUA_CALL_MEDIA_NONE;
1650
1651 return PJ_SUCCESS;
1652}
1653
1654
Benny Prijono84126ab2006-02-09 09:30:09 +00001655/*
1656 * This callback receives notification from invite session when the
1657 * session state has changed.
1658 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001659static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1660 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001661{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001663
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001664 PJSUA_LOCK();
1665
1666 call = inv->dlg->mod_data[pjsua_var.mod.id];
1667
1668 if (!call) {
1669 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001670 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001671 }
1672
Benny Prijonoe21e7842006-04-09 16:46:05 +00001673
1674 /* Get call times */
1675 switch (inv->state) {
1676 case PJSIP_INV_STATE_EARLY:
1677 case PJSIP_INV_STATE_CONNECTING:
1678 if (call->res_time.sec == 0)
1679 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001680 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681 pj_strncpy(&call->last_text,
1682 &e->body.tsx_state.tsx->status_text,
1683 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001684 break;
1685 case PJSIP_INV_STATE_CONFIRMED:
1686 pj_gettimeofday(&call->conn_time);
1687 break;
1688 case PJSIP_INV_STATE_DISCONNECTED:
1689 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00001690 if (call->res_time.sec == 0)
1691 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001692 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1693 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694 pj_strncpy(&call->last_text,
1695 &e->body.tsx_state.tsx->status_text,
1696 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001697 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001698 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001699 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001700 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701 pj_strncpy(&call->last_text,
1702 &e->body.tsx_state.tsx->status_text,
1703 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001704 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001705 }
1706
Benny Prijono26ff9062006-02-21 23:47:00 +00001707 /* If this is an outgoing INVITE that was created because of
1708 * REFER/transfer, send NOTIFY to transferer.
1709 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001710 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001711 int st_code = -1;
1712 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1713
1714
Benny Prijonoa91a0032006-02-26 21:23:45 +00001715 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001716 case PJSIP_INV_STATE_NULL:
1717 case PJSIP_INV_STATE_CALLING:
1718 /* Do nothing */
1719 break;
1720
1721 case PJSIP_INV_STATE_EARLY:
1722 case PJSIP_INV_STATE_CONNECTING:
1723 st_code = e->body.tsx_state.tsx->status_code;
1724 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1725 break;
1726
1727 case PJSIP_INV_STATE_CONFIRMED:
1728 /* When state is confirmed, send the final 200/OK and terminate
1729 * subscription.
1730 */
1731 st_code = e->body.tsx_state.tsx->status_code;
1732 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1733 break;
1734
1735 case PJSIP_INV_STATE_DISCONNECTED:
1736 st_code = e->body.tsx_state.tsx->status_code;
1737 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1738 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001739
Benny Prijono8b1889b2006-06-06 18:40:40 +00001740 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001741 /* Nothing to do. Just to keep gcc from complaining about
1742 * unused enums.
1743 */
1744 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001745 }
1746
1747 if (st_code != -1) {
1748 pjsip_tx_data *tdata;
1749 pj_status_t status;
1750
Benny Prijonoa91a0032006-02-26 21:23:45 +00001751 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001752 ev_state, st_code,
1753 NULL, &tdata);
1754 if (status != PJ_SUCCESS) {
1755 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1756 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001757 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001758 if (status != PJ_SUCCESS) {
1759 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1760 }
1761 }
1762 }
1763 }
1764
Benny Prijono84126ab2006-02-09 09:30:09 +00001765
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001766 if (pjsua_var.ua_cfg.cb.on_call_state)
1767 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001768
1769 /* call->inv may be NULL now */
1770
Benny Prijono84126ab2006-02-09 09:30:09 +00001771 /* Destroy media session when invite session is disconnected. */
1772 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001773
Benny Prijonoa91a0032006-02-26 21:23:45 +00001774 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001775
Benny Prijono275fd682006-03-22 11:59:11 +00001776 if (call)
1777 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001778
Benny Prijono105217f2006-03-06 16:25:59 +00001779 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001780 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001782 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001783
1784 PJSUA_UNLOCK();
1785}
1786
1787/*
1788 * This callback is called by invite session framework when UAC session
1789 * has forked.
1790 */
1791static void pjsua_call_on_forked( pjsip_inv_session *inv,
1792 pjsip_event *e)
1793{
1794 PJ_UNUSED_ARG(inv);
1795 PJ_UNUSED_ARG(e);
1796
1797 PJ_TODO(HANDLE_FORKED_DIALOG);
1798}
1799
1800
1801/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00001802 * Disconnect call upon error.
1803 */
1804static void call_disconnect( pjsip_inv_session *inv,
1805 int code )
1806{
1807 pjsip_tx_data *tdata;
1808 pj_status_t status;
1809
1810 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
1811 if (status == PJ_SUCCESS)
1812 pjsip_inv_send_msg(inv, tdata);
1813}
1814
1815/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001816 * Callback to be called when SDP offer/answer negotiation has just completed
1817 * in the session. This function will start/update media if negotiation
1818 * has succeeded.
1819 */
1820static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1821 pj_status_t status)
1822{
1823 int prev_media_st = 0;
1824 pjsua_call *call;
1825 pjmedia_session_info sess_info;
1826 const pjmedia_sdp_session *local_sdp;
1827 const pjmedia_sdp_session *remote_sdp;
1828 pjmedia_port *media_port;
1829 pj_str_t port_name;
1830 char tmp[PJSIP_MAX_URL_SIZE];
1831
1832 PJSUA_LOCK();
1833
1834 call = inv->dlg->mod_data[pjsua_var.mod.id];
1835
1836 if (status != PJ_SUCCESS) {
1837
1838 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1839
1840 /* Disconnect call if we're not in the middle of initializing an
1841 * UAS dialog and if this is not a re-INVITE
1842 */
1843 if (inv->state != PJSIP_INV_STATE_NULL &&
1844 inv->state != PJSIP_INV_STATE_CONFIRMED)
1845 {
1846 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1847 }
1848
1849 PJSUA_UNLOCK();
1850 return;
1851 }
1852
1853 /* Destroy existing media session, if any. */
1854
1855 if (call) {
1856 prev_media_st = call->media_st;
1857 call_destroy_media(call->index);
1858 }
1859
1860 /* Get local and remote SDP */
1861
1862 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1863 if (status != PJ_SUCCESS) {
1864 pjsua_perror(THIS_FILE,
1865 "Unable to retrieve currently active local SDP",
1866 status);
1867 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1868 PJSUA_UNLOCK();
1869 return;
1870 }
1871
1872
1873 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1874 if (status != PJ_SUCCESS) {
1875 pjsua_perror(THIS_FILE,
1876 "Unable to retrieve currently active remote SDP",
1877 status);
1878 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1879 PJSUA_UNLOCK();
1880 return;
1881 }
1882
1883 /* Create media session info based on SDP parameters.
1884 * We only support one stream per session at the moment
1885 */
1886 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1887 pjsua_var.med_endpt,
1888 1,&sess_info,
1889 local_sdp, remote_sdp);
1890 if (status != PJ_SUCCESS) {
1891 pjsua_perror(THIS_FILE, "Unable to create media session",
1892 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00001893 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001894 PJSUA_UNLOCK();
1895 return;
1896 }
1897
1898 /* Check if media is put on-hold */
1899 if (sess_info.stream_cnt == 0 ||
1900 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1901 {
1902
1903 /* Determine who puts the call on-hold */
1904 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1905 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1906 /* It was local who offer hold */
1907 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1908 } else {
1909 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1910 }
1911 }
1912
1913 call->media_dir = PJMEDIA_DIR_NONE;
1914
1915 } else {
1916
1917 /* Override ptime, if this option is specified. */
Benny Prijono0a12f002006-07-26 17:05:39 +00001918 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono00cae612006-07-31 15:19:36 +00001919 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
1920 (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono0a12f002006-07-26 17:05:39 +00001921 if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
1922 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1923 }
1924
1925 /* Disable VAD, if this option is specified. */
1926 if (pjsua_var.media_cfg.no_vad) {
1927 sess_info.stream_info[0].param->setting.vad = 0;
1928 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001929
1930
1931 /* Optionally, application may modify other stream settings here
1932 * (such as jitter buffer parameters, codec ptime, etc.)
1933 */
1934
1935 /* Create session based on session info. */
1936 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1937 &call->med_tp,
1938 call, &call->session );
1939 if (status != PJ_SUCCESS) {
1940 pjsua_perror(THIS_FILE, "Unable to create media session",
1941 status);
1942 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1943 PJSUA_UNLOCK();
1944 return;
1945 }
1946
1947
1948 /* Get the port interface of the first stream in the session.
1949 * We need the port interface to add to the conference bridge.
1950 */
1951 pjmedia_session_get_port(call->session, 0, &media_port);
1952
1953
1954 /*
1955 * Add the call to conference bridge.
1956 */
1957 port_name.ptr = tmp;
1958 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1959 call->inv->dlg->remote.info->uri,
1960 tmp, sizeof(tmp));
1961 if (port_name.slen < 1) {
1962 port_name = pj_str("call");
1963 }
1964 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1965 media_port,
1966 &port_name,
1967 (unsigned*)&call->conf_slot);
1968 if (status != PJ_SUCCESS) {
1969 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1970 status);
1971 call_destroy_media(call->index);
1972 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1973 PJSUA_UNLOCK();
1974 return;
1975 }
1976
1977 /* Call's media state is active */
1978 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1979 call->media_dir = sess_info.stream_info[0].dir;
1980 }
1981
1982 /* Print info. */
1983 {
1984 char info[80];
1985 int info_len = 0;
1986 unsigned i;
1987
1988 for (i=0; i<sess_info.stream_cnt; ++i) {
1989 int len;
1990 const char *dir;
1991 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1992
1993 switch (strm_info->dir) {
1994 case PJMEDIA_DIR_NONE:
1995 dir = "inactive";
1996 break;
1997 case PJMEDIA_DIR_ENCODING:
1998 dir = "sendonly";
1999 break;
2000 case PJMEDIA_DIR_DECODING:
2001 dir = "recvonly";
2002 break;
2003 case PJMEDIA_DIR_ENCODING_DECODING:
2004 dir = "sendrecv";
2005 break;
2006 default:
2007 dir = "unknown";
2008 break;
2009 }
2010 len = pj_ansi_sprintf( info+info_len,
2011 ", stream #%d: %.*s (%s)", i,
2012 (int)strm_info->fmt.encoding_name.slen,
2013 strm_info->fmt.encoding_name.ptr,
2014 dir);
2015 if (len > 0)
2016 info_len += len;
2017 }
2018 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
2019 }
2020
2021 /* Call application callback, if any */
2022 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2023 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2024
2025
2026 PJSUA_UNLOCK();
2027}
2028
2029
2030/*
2031 * Create inactive SDP for call hold.
2032 */
2033static pj_status_t create_inactive_sdp(pjsua_call *call,
2034 pjmedia_sdp_session **p_answer)
2035{
2036 pj_status_t status;
2037 pjmedia_sdp_conn *conn;
2038 pjmedia_sdp_attr *attr;
2039 pjmedia_sdp_session *sdp;
2040
2041 /* Create new offer */
2042 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
2043 &call->skinfo, &sdp);
2044 if (status != PJ_SUCCESS) {
2045 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2046 return status;
2047 }
2048
2049 /* Get SDP media connection line */
2050 conn = sdp->media[0]->conn;
2051 if (!conn)
2052 conn = sdp->conn;
2053
2054 /* Modify address */
2055 conn->addr = pj_str("0.0.0.0");
2056
2057 /* Remove existing directions attributes */
2058 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2059 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2060 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2061 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2062
2063 /* Add inactive attribute */
2064 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2065 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2066
2067 *p_answer = sdp;
2068
2069 return status;
2070}
2071
2072
2073/*
2074 * Called when session received new offer.
2075 */
2076static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2077 const pjmedia_sdp_session *offer)
2078{
2079 const char *remote_state;
2080 pjsua_call *call;
2081 pjmedia_sdp_conn *conn;
2082 pjmedia_sdp_session *answer;
2083 pj_bool_t is_remote_active;
2084 pj_status_t status;
2085
2086 PJSUA_LOCK();
2087
2088 call = inv->dlg->mod_data[pjsua_var.mod.id];
2089
2090 /*
2091 * See if remote is offering active media (i.e. not on-hold)
2092 */
2093 is_remote_active = PJ_TRUE;
2094
2095 conn = offer->media[0]->conn;
2096 if (!conn)
2097 conn = offer->conn;
2098
2099 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2100 pj_strcmp2(&conn->addr, "0")==0)
2101 {
2102 is_remote_active = PJ_FALSE;
2103
2104 }
2105 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
2106 {
2107 is_remote_active = PJ_FALSE;
2108 }
2109
2110 remote_state = (is_remote_active ? "active" : "inactive");
2111
2112 /* Supply candidate answer */
2113 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2114 PJ_LOG(4,(THIS_FILE,
2115 "Call %d: RX new media offer, creating inactive SDP "
2116 "(media in offer is %s)", call->index, remote_state));
2117 status = create_inactive_sdp( call, &answer );
2118 } else {
2119 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2120 call->index));
2121 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
2122 call->inv->pool, 1,
2123 &call->skinfo, &answer);
2124 }
2125
2126 if (status != PJ_SUCCESS) {
2127 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2128 PJSUA_UNLOCK();
2129 return;
2130 }
2131
2132 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2133 if (status != PJ_SUCCESS) {
2134 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2135 PJSUA_UNLOCK();
2136 return;
2137 }
2138
2139 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002140}
2141
2142
2143/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002144 * Callback called by event framework when the xfer subscription state
2145 * has changed.
2146 */
2147static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2148{
2149
2150 PJ_UNUSED_ARG(event);
2151
2152 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002153 * When subscription is terminated, clear the xfer_sub member of
2154 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002155 */
2156 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002157 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002158
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002159 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002160 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002161 return;
2162
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002164 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002165
Benny Prijonod524e822006-09-22 12:48:18 +00002166 PJ_LOG(4,(THIS_FILE, "Xfer subscription terminated"));
2167
2168 }
2169 /*
2170 * When subscription is accepted (got 200/OK to REFER), check if
2171 * subscription suppressed.
2172 */
2173 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2174
2175 pjsip_rx_data *rdata;
2176 pjsip_generic_string_hdr *refer_sub;
2177 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2178
2179 /* Must be receipt of response message */
2180 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2181 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2182 rdata = event->body.tsx_state.src.rdata;
2183
2184 /* Find Refer-Sub header */
2185 refer_sub = (pjsip_generic_string_hdr*)
2186 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2187 &REFER_SUB, NULL);
2188
2189 /* Check if subscription is suppressed */
2190 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2191 /* Yes, subscription is suppressed.
2192 * Terminate our subscription now.
2193 */
2194 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2195 "event subcription..."));
2196 pjsip_evsub_terminate(sub, PJ_TRUE);
2197 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002198 }
2199}
2200
2201
2202/*
2203 * Follow transfer (REFER) request.
2204 */
2205static void on_call_transfered( pjsip_inv_session *inv,
2206 pjsip_rx_data *rdata )
2207{
2208 pj_status_t status;
2209 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002210 pjsua_call *existing_call;
2211 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002212 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002213 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002214 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002215 pjsip_generic_string_hdr *refer_sub;
2216 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002217 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002218 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002219 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002220 pjsip_evsub *sub;
2221
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002222 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002223
Benny Prijono26ff9062006-02-21 23:47:00 +00002224 /* Find the Refer-To header */
2225 refer_to = (pjsip_generic_string_hdr*)
2226 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2227
2228 if (refer_to == NULL) {
2229 /* Invalid Request.
2230 * No Refer-To header!
2231 */
2232 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002233 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002234 return;
2235 }
2236
Benny Prijonoc8141a82006-08-20 09:12:19 +00002237 /* Find optional Refer-Sub header */
2238 refer_sub = (pjsip_generic_string_hdr*)
2239 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2240
2241 if (refer_sub) {
2242 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2243 no_refer_sub = PJ_TRUE;
2244 }
2245
2246
Benny Prijono9fc735d2006-05-28 14:58:12 +00002247 /* Notify callback */
2248 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002249 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2250 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2251 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002252
2253 if (code < 200)
2254 code = 200;
2255 if (code >= 300) {
2256 /* Application rejects call transfer request */
2257 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2258 return;
2259 }
2260
Benny Prijono26ff9062006-02-21 23:47:00 +00002261 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2262 (int)inv->dlg->remote.info_str.slen,
2263 inv->dlg->remote.info_str.ptr,
2264 (int)refer_to->hvalue.slen,
2265 refer_to->hvalue.ptr));
2266
Benny Prijonoc8141a82006-08-20 09:12:19 +00002267 if (no_refer_sub) {
2268 /*
2269 * Always answer with 200.
2270 */
2271 pjsip_tx_data *tdata;
2272 const pj_str_t str_false = { "false", 5};
2273 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002274
Benny Prijonoc8141a82006-08-20 09:12:19 +00002275 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2276 if (status != PJ_SUCCESS) {
2277 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2278 status);
2279 return;
2280 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002281
Benny Prijonoc8141a82006-08-20 09:12:19 +00002282 /* Add Refer-Sub header */
2283 hdr = (pjsip_hdr*)
2284 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2285 &str_false);
2286 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002287
Benny Prijono26ff9062006-02-21 23:47:00 +00002288
Benny Prijonoc8141a82006-08-20 09:12:19 +00002289 /* Send answer */
2290 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2291 tdata);
2292 if (status != PJ_SUCCESS) {
2293 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2294 status);
2295 return;
2296 }
2297
2298 /* Don't have subscription */
2299 sub = NULL;
2300
2301 } else {
2302 struct pjsip_evsub_user xfer_cb;
2303 pjsip_hdr hdr_list;
2304
2305 /* Init callback */
2306 pj_bzero(&xfer_cb, sizeof(xfer_cb));
2307 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2308
2309 /* Init additional header list to be sent with REFER response */
2310 pj_list_init(&hdr_list);
2311
2312 /* Create transferee event subscription */
2313 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2314 if (status != PJ_SUCCESS) {
2315 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2316 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2317 return;
2318 }
2319
2320 /* If there's Refer-Sub header and the value is "true", send back
2321 * Refer-Sub in the response with value "true" too.
2322 */
2323 if (refer_sub) {
2324 const pj_str_t str_true = { "true", 4 };
2325 pjsip_hdr *hdr;
2326
2327 hdr = (pjsip_hdr*)
2328 pjsip_generic_string_hdr_create(inv->dlg->pool,
2329 &str_refer_sub,
2330 &str_true);
2331 pj_list_push_back(&hdr_list, hdr);
2332
2333 }
2334
2335 /* Accept the REFER request, send 200 (OK). */
2336 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2337
2338 /* Create initial NOTIFY request */
2339 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2340 100, NULL, &tdata);
2341 if (status != PJ_SUCCESS) {
2342 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2343 status);
2344 return;
2345 }
2346
2347 /* Send initial NOTIFY request */
2348 status = pjsip_xfer_send_request( sub, tdata);
2349 if (status != PJ_SUCCESS) {
2350 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2351 return;
2352 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002353 }
2354
2355 /* We're cheating here.
2356 * We need to get a null terminated string from a pj_str_t.
2357 * So grab the pointer from the hvalue and NULL terminate it, knowing
2358 * that the NULL position will be occupied by a newline.
2359 */
2360 uri = refer_to->hvalue.ptr;
2361 uri[refer_to->hvalue.slen] = '\0';
2362
2363 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002364 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002365 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2366 existing_call->user_data, NULL,
2367 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002368 if (status != PJ_SUCCESS) {
2369
Benny Prijonoc8141a82006-08-20 09:12:19 +00002370 /* Notify xferer about the error (if we have subscription) */
2371 if (sub) {
2372 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2373 500, NULL, &tdata);
2374 if (status != PJ_SUCCESS) {
2375 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2376 status);
2377 return;
2378 }
2379 status = pjsip_xfer_send_request(sub, tdata);
2380 if (status != PJ_SUCCESS) {
2381 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2382 status);
2383 return;
2384 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002385 }
2386 return;
2387 }
2388
Benny Prijonoc8141a82006-08-20 09:12:19 +00002389 if (sub) {
2390 /* Put the server subscription in inv_data.
2391 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2392 * reported back to the server subscription.
2393 */
2394 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002395
Benny Prijonoc8141a82006-08-20 09:12:19 +00002396 /* Put the invite_data in the subscription. */
2397 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2398 &pjsua_var.calls[new_call]);
2399 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002400}
2401
2402
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002403
Benny Prijono26ff9062006-02-21 23:47:00 +00002404/*
2405 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002406 * session. We use this to trap:
2407 * - incoming REFER request.
2408 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002409 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002410static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2411 pjsip_transaction *tsx,
2412 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002413{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002414 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2415
2416 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002417
Benny Prijono26ff9062006-02-21 23:47:00 +00002418 if (tsx->role==PJSIP_ROLE_UAS &&
2419 tsx->state==PJSIP_TSX_STATE_TRYING &&
2420 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2421 {
2422 /*
2423 * Incoming REFER request.
2424 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002425 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002426
Benny Prijono26ff9062006-02-21 23:47:00 +00002427 }
Benny Prijonob0808372006-03-02 21:18:58 +00002428 else if (tsx->role==PJSIP_ROLE_UAS &&
2429 tsx->state==PJSIP_TSX_STATE_TRYING &&
2430 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2431 {
2432 /*
2433 * Incoming MESSAGE request!
2434 */
2435 pjsip_rx_data *rdata;
2436 pjsip_msg *msg;
2437 pjsip_accept_hdr *accept_hdr;
2438 pj_status_t status;
2439
2440 rdata = e->body.tsx_state.src.rdata;
2441 msg = rdata->msg_info.msg;
2442
2443 /* Request MUST have message body, with Content-Type equal to
2444 * "text/plain".
2445 */
2446 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2447
2448 pjsip_hdr hdr_list;
2449
2450 pj_list_init(&hdr_list);
2451 pj_list_push_back(&hdr_list, accept_hdr);
2452
2453 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2454 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002455 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002456 return;
2457 }
2458
2459 /* Respond with 200 first, so that remote doesn't retransmit in case
2460 * the UI takes too long to process the message.
2461 */
2462 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2463
2464 /* Process MESSAGE request */
2465 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2466 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002467
Benny Prijonob0808372006-03-02 21:18:58 +00002468 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002469 else if (tsx->role == PJSIP_ROLE_UAC &&
2470 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002471 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002472 /* Handle outgoing pager status */
2473 if (tsx->status_code >= 200) {
2474 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002475
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002476 im_data = tsx->mod_data[pjsua_var.mod.id];
2477 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002478
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002479 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2480 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2481 &im_data->to,
2482 &im_data->body,
2483 im_data->user_data,
2484 tsx->status_code,
2485 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002486 }
Benny Prijonofccab712006-02-22 22:23:22 +00002487 }
Benny Prijono834aee32006-02-19 01:38:06 +00002488 }
Benny Prijono834aee32006-02-19 01:38:06 +00002489
Benny Prijono26ff9062006-02-21 23:47:00 +00002490
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002491 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002492}