blob: 92c1f92ed9af988293ba707a346e5af6600f1214 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_call.c"
24
25
26/* This callback receives notification from invite session when the
27 * session state has changed.
28 */
29static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
30 pjsip_event *e);
31
32/* This callback is called by invite session framework when UAC session
33 * has forked.
34 */
35static void pjsua_call_on_forked( pjsip_inv_session *inv,
36 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000037
38/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000039 * Callback to be called when SDP offer/answer negotiation has just completed
40 * in the session. This function will start/update media if negotiation
41 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000042 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043static void pjsua_call_on_media_update(pjsip_inv_session *inv,
44 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000045
46/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000048 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000049static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
50 const pjmedia_sdp_session *offer);
51
52/*
53 * This callback is called when transaction state has changed in INVITE
54 * session. We use this to trap:
55 * - incoming REFER request.
56 * - incoming MESSAGE request.
57 */
58static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
59 pjsip_transaction *tsx,
60 pjsip_event *e);
61
62
63/* Destroy the call's media */
64static pj_status_t call_destroy_media(int call_id);
65
66/* Create inactive SDP for call hold. */
67static pj_status_t create_inactive_sdp(pjsua_call *call,
68 pjmedia_sdp_session **p_answer);
69
70
71/*
72 * Reset call descriptor.
73 */
74static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000075{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000077
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078 call->index = id;
79 call->inv = NULL;
80 call->user_data = NULL;
81 call->session = NULL;
82 call->xfer_sub = NULL;
83 call->last_code = 0;
84 call->conf_slot = PJSUA_INVALID_ID;
85 call->last_text.ptr = call->last_text_buf_;
86 call->last_text.slen = 0;
Benny Prijono105217f2006-03-06 16:25:59 +000087}
88
89
Benny Prijono275fd682006-03-22 11:59:11 +000090/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000091 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +000092 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +000094{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 pjsip_inv_callback inv_cb;
96 unsigned i;
97 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +000098
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 /* Init calls array. */
100 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
101 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 /* Copy config */
104 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000105
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 /* Initialize invite session callback. */
107 pj_memset(&inv_cb, 0, sizeof(inv_cb));
108 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
109 inv_cb.on_new_session = &pjsua_call_on_forked;
110 inv_cb.on_media_update = &pjsua_call_on_media_update;
111 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
112 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000113
Benny Prijono275fd682006-03-22 11:59:11 +0000114
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000115 /* Initialize invite session module: */
116 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
117 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
118
119 return status;
120}
121
122
123/*
124 * Start call subsystem.
125 */
126pj_status_t pjsua_call_subsys_start(void)
127{
128 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000129 return PJ_SUCCESS;
130}
131
132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000134 * Get maximum number of calls configured in pjsua.
135 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000136PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000137{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000139}
140
141
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000142/*
143 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000144 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000145PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000146{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000148}
149
150
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151/*
152 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000153 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
155 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000156{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000158
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000160
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000162
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000163 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
164 if (!pjsua_var.calls[i].inv)
165 continue;
166 ids[c] = i;
167 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000168 }
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000173
174 return PJ_SUCCESS;
175}
176
177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178/*
179 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
182 const pj_str_t *dest_uri,
183 unsigned options,
184 void *user_data,
185 const pjsua_msg_data *msg_data,
186 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000187{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000188 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000189 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000190 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191 pjsua_acc *acc;
192 pjsua_call *call;
193 unsigned call_id;
Benny Prijono84126ab2006-02-09 09:30:09 +0000194 pjsip_tx_data *tdata;
195 pj_status_t status;
196
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 /* Check that account is valid */
199 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200 PJ_EINVAL);
201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 /* Options must be zero for now */
203 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
204
205 PJSUA_LOCK();
206
207 acc = &pjsua_var.acc[acc_id];
208 if (!acc->valid) {
209 pjsua_perror(THIS_FILE, "Unable to make call because account "
210 "is not valid", PJ_EINVALIDOP);
211 PJSUA_UNLOCK();
212 return PJ_EINVALIDOP;
213 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000214
Benny Prijonoa91a0032006-02-26 21:23:45 +0000215 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) {
217 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000218 break;
219 }
220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 if (call_id == pjsua_var.ua_cfg.max_calls) {
222 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
223 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000224 return PJ_ETOOMANY;
225 }
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 call = &pjsua_var.calls[call_id];
228
Benny Prijonoe21e7842006-04-09 16:46:05 +0000229 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000231
Benny Prijonoe21e7842006-04-09 16:46:05 +0000232 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000234
235 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000236 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 &acc->cfg.id, &acc->cfg.contact,
238 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000239 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000240 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000241 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000242 return status;
243 }
244
245 /* Get media capability from media endpoint: */
246
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000247 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1,
248 &call->skinfo, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000249 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000250 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000251 goto on_error;
252 }
253
254 /* Create the INVITE session: */
255
256 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
257 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000258 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000259 goto on_error;
260 }
261
262
263 /* Create and associate our data in the session. */
264
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000265 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000266
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 dlg->mod_data[pjsua_var.mod.id] = call;
268 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000269
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000270 /* Attach user data */
271 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000272
273 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000274 if (!pj_list_empty(&acc->route_set))
275 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000276
277
278 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000279 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000280 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000282 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000283
284
285 /* Create initial INVITE: */
286
287 status = pjsip_inv_invite(inv, &tdata);
288 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000289 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
290 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000291 goto on_error;
292 }
293
294
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000295 /* Add additional headers etc */
296
297 pjsua_process_msg_data( tdata, msg_data);
298
Benny Prijono84126ab2006-02-09 09:30:09 +0000299 /* Send initial INVITE: */
300
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000301 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000302 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000303 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
304 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000305
306 /* Upon failure to send first request, both dialog and invite
307 * session would have been cleared.
308 */
309 inv = NULL;
310 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000311 goto on_error;
312 }
313
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000315
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000316 ++pjsua_var.call_cnt;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000317
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000318 if (p_call_id)
319 *p_call_id = call_id;
320
321 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000322
323 return PJ_SUCCESS;
324
325
326on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000327 if (inv != NULL) {
328 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000329 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000330 pjsip_dlg_terminate(dlg);
331 }
332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333 if (call_id != -1) {
334 reset_call(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000335 }
336
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000338 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000339}
340
341
342/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000343 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000346pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000347{
348 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
349 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
350 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000351 pjsip_tx_data *response = NULL;
352 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000353 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 int acc_id;
355 pjsua_call *call;
356 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000357 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000358 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000359
Benny Prijono26ff9062006-02-21 23:47:00 +0000360 /* Don't want to handle anything but INVITE */
361 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
362 return PJ_FALSE;
363
364 /* Don't want to handle anything that's already associated with
365 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000366 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000367 if (dlg || tsx)
368 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000369
Benny Prijono84126ab2006-02-09 09:30:09 +0000370
Benny Prijono26ff9062006-02-21 23:47:00 +0000371 /* Verify that we can handle the request. */
372 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000373 pjsua_var.endpt, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000374 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000375
Benny Prijono26ff9062006-02-21 23:47:00 +0000376 /*
377 * No we can't handle the incoming INVITE request.
378 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000379
Benny Prijono26ff9062006-02-21 23:47:00 +0000380 if (response) {
381 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000382
Benny Prijono26ff9062006-02-21 23:47:00 +0000383 pjsip_get_response_addr(response->pool, rdata, &res_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
Benny Prijono26ff9062006-02-21 23:47:00 +0000385 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000386
387 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000388
Benny Prijono26ff9062006-02-21 23:47:00 +0000389 /* Respond with 500 (Internal Server Error) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000390 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000391 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000392 }
393
Benny Prijono26ff9062006-02-21 23:47:00 +0000394 return PJ_TRUE;
395 }
396
397
398 /*
399 * Yes we can handle the incoming INVITE request.
400 */
401
402 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
404 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000405 break;
406 }
407
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000408 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
409 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000410 PJSIP_SC_BUSY_HERE, NULL,
411 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000412 return PJ_TRUE;
413 }
414
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000415 /* Clear call descriptor */
416 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000417
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418 call = &pjsua_var.calls[call_id];
419
420 /* Mark call start time. */
421 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000422
Benny Prijono26ff9062006-02-21 23:47:00 +0000423 /* Get media capability from media endpoint: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
425 rdata->tp_info.pool, 1,
426 &call->skinfo, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000427 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000429 NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000430 return PJ_TRUE;
431 }
432
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 /*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000434 * Get which account is most likely to be associated with this incoming
435 * call. We need the account to find which contact URI to put for
436 * the call.
437 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000438 acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000439
Benny Prijono26ff9062006-02-21 23:47:00 +0000440 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000441 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 &pjsua_var.acc[acc_id].cfg.contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000443 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000444 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000446 NULL, NULL);
447
Benny Prijono26ff9062006-02-21 23:47:00 +0000448 return PJ_TRUE;
449 }
450
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 /* Set credentials */
452 if (pjsua_var.acc[acc_id].cred_cnt) {
453 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
454 pjsua_var.acc[acc_id].cred_cnt,
455 pjsua_var.acc[acc_id].cred);
456 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000457
458 /* Create invite session: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000459 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
460 if (status != PJ_SUCCESS) {
Benny Prijonob0808372006-03-02 21:18:58 +0000461 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000462 pjsip_dlg_terminate(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000463 return PJ_TRUE;
464 }
465
466
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 /* Create and attach pjsua_var data to the dialog: */
468 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000469
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470 dlg->mod_data[pjsua_var.mod.id] = call;
471 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000472
473
Benny Prijono64f851e2006-02-23 13:49:28 +0000474 /* Must answer with some response to initial INVITE.
475 * If auto-answer flag is set, send 200 straight away, otherwise send 100.
476 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000477 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000479 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000480 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
481 status);
482
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
484 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000485 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000486
487 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000488 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000489 if (status != PJ_SUCCESS)
490 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000491 }
492
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000493 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000494
Benny Prijono105217f2006-03-06 16:25:59 +0000495
Benny Prijono8b1889b2006-06-06 18:40:40 +0000496 /* Notify application */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000497 if (pjsua_var.ua_cfg.cb.on_incoming_call)
498 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000499
Benny Prijono26ff9062006-02-21 23:47:00 +0000500 /* This INVITE request has been handled. */
501 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000502}
503
504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505
506/*
507 * Check if the specified call has active INVITE session and the INVITE
508 * session has not been disconnected.
509 */
510PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
511{
512 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
513 PJ_EINVAL);
514 return pjsua_var.calls[call_id].inv != NULL &&
515 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
516}
517
518
519/*
520 * Check if call has an active media session.
521 */
522PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
523{
524 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
525 PJ_EINVAL);
526 return pjsua_var.calls[call_id].session != NULL;
527}
528
529
530/*
531 * Get the conference port identification associated with the call.
532 */
533PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
534{
535 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
536 PJ_EINVAL);
537 return pjsua_var.calls[call_id].conf_slot;
538}
539
540
541/*
542 * Obtain detail information about the specified call.
543 */
544PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
545 pjsua_call_info *info)
546{
547 pjsua_call *call;
548
549 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
550 PJ_EINVAL);
551
552 pj_memset(info, 0, sizeof(*info));
553
554 PJSUA_LOCK();
555
556 call = &pjsua_var.calls[call_id];
557
558 if (call->inv == NULL) {
559 PJSUA_UNLOCK();
560 return PJ_SUCCESS;
561 }
562
563 pjsip_dlg_inc_lock(call->inv->dlg);
564
565
566 /* id and role */
567 info->id = call_id;
568 info->role = call->inv->role;
569
570 /* local info */
571 info->local_info.ptr = info->buf_.local_info;
572 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
573 sizeof(info->buf_.local_info));
574
575 /* local contact */
576 info->local_contact.ptr = info->buf_.local_contact;
577 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
578 call->inv->dlg->local.contact->uri,
579 info->local_contact.ptr,
580 sizeof(info->buf_.local_contact));
581
582 /* remote info */
583 info->remote_info.ptr = info->buf_.remote_info;
584 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
585 sizeof(info->buf_.remote_info));
586
587 /* remote contact */
588 if (call->inv->dlg->remote.contact) {
589 int len;
590 info->remote_contact.ptr = info->buf_.remote_contact;
591 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
592 call->inv->dlg->remote.contact->uri,
593 info->remote_contact.ptr,
594 sizeof(info->buf_.remote_contact));
595 if (len < 0) len = 0;
596 info->remote_contact.slen = len;
597 } else {
598 info->remote_contact.slen = 0;
599 }
600
601 /* call id */
602 info->call_id.ptr = info->buf_.call_id;
603 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
604 sizeof(info->buf_.call_id));
605
606 /* state, state_text */
607 info->state = call->inv->state;
608 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
609
610 /* If call is disconnected, set the last_status from the cause code */
611 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
612 /* last_status, last_status_text */
613 info->last_status = call->inv->cause;
614
615 info->last_status_text.ptr = info->buf_.last_status_text;
616 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
617 sizeof(info->buf_.last_status_text));
618 } else {
619 /* last_status, last_status_text */
620 info->last_status = call->last_code;
621
622 info->last_status_text.ptr = info->buf_.last_status_text;
623 pj_strncpy(&info->last_status_text, &call->last_text,
624 sizeof(info->buf_.last_status_text));
625 }
626
627 /* media status and dir */
628 info->media_status = call->media_st;
629 info->media_dir = call->media_dir;
630
631
632 /* conference slot number */
633 info->conf_slot = call->conf_slot;
634
635 /* calculate duration */
636 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
637
638 info->total_duration = call->dis_time;
639 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
640
641 if (call->conn_time.sec) {
642 info->connect_duration = call->dis_time;
643 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
644 }
645
646 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
647
648 pj_gettimeofday(&info->total_duration);
649 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
650
651 pj_gettimeofday(&info->connect_duration);
652 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
653
654 } else {
655 pj_gettimeofday(&info->total_duration);
656 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
657 }
658
659 pjsip_dlg_dec_lock(call->inv->dlg);
660 PJSUA_UNLOCK();
661
662 return PJ_SUCCESS;
663}
664
665
666/*
667 * Attach application specific data to the call.
668 */
669PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
670 void *user_data)
671{
672 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
673 PJ_EINVAL);
674 pjsua_var.calls[call_id].user_data = user_data;
675
676 return PJ_SUCCESS;
677}
678
679
680/*
681 * Get user data attached to the call.
682 */
683PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
684{
685 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
686 NULL);
687 return pjsua_var.calls[call_id].user_data;
688}
689
690
691/*
692 * Send response to incoming INVITE request.
693 */
694PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
695 unsigned code,
696 const pj_str_t *reason,
697 const pjsua_msg_data *msg_data)
698{
699 pjsua_call *call;
700 pjsip_tx_data *tdata;
701 pj_status_t status;
702
703 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
704 PJ_EINVAL);
705
706 PJSUA_LOCK();
707
708 call = &pjsua_var.calls[call_id];
709
710 if (call->inv == NULL) {
711 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id));
712 PJSUA_UNLOCK();
713 return PJSIP_ESESSIONTERMINATED;
714 }
715
716 /* Create response message */
717 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
718 if (status != PJ_SUCCESS) {
719 pjsua_perror(THIS_FILE, "Error creating response",
720 status);
721 PJSUA_UNLOCK();
722 return status;
723 }
724
725 /* Add additional headers etc */
726 pjsua_process_msg_data( tdata, msg_data);
727
728 /* Send the message */
729 status = pjsip_inv_send_msg(call->inv, tdata);
730 if (status != PJ_SUCCESS)
731 pjsua_perror(THIS_FILE, "Error sending response",
732 status);
733
734 PJSUA_UNLOCK();
735
736 return status;
737}
738
739
740/*
741 * Hangup call by using method that is appropriate according to the
742 * call state.
743 */
744PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
745 unsigned code,
746 const pj_str_t *reason,
747 const pjsua_msg_data *msg_data)
748{
749 pjsua_call *call;
750 pj_status_t status;
751 pjsip_tx_data *tdata;
752
753
754 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
755 PJ_EINVAL);
756
757 PJSUA_LOCK();
758
759 call = &pjsua_var.calls[call_id];
760
761 if (!call->inv) {
762 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
763 PJSUA_UNLOCK();
764 return PJ_EINVAL;
765 }
766
767 if (code==0) {
768 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
769 code = PJSIP_SC_OK;
770 else if (call->inv->role == PJSIP_ROLE_UAS)
771 code = PJSIP_SC_DECLINE;
772 else
773 code = PJSIP_SC_REQUEST_TERMINATED;
774 }
775
776 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
777 if (status != PJ_SUCCESS) {
778 pjsua_perror(THIS_FILE,
779 "Failed to create end session message",
780 status);
781 PJSUA_UNLOCK();
782 return status;
783 }
784
785 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
786 * as p_tdata when INVITE transaction has not been answered
787 * with any provisional responses.
788 */
789 if (tdata == NULL) {
790 PJSUA_UNLOCK();
791 return PJ_SUCCESS;
792 }
793
794 /* Add additional headers etc */
795 pjsua_process_msg_data( tdata, msg_data);
796
797 /* Send the message */
798 status = pjsip_inv_send_msg(call->inv, tdata);
799 if (status != PJ_SUCCESS) {
800 pjsua_perror(THIS_FILE,
801 "Failed to send end session message",
802 status);
803 PJSUA_UNLOCK();
804 return status;
805 }
806
807 PJSUA_UNLOCK();
808
809 return PJ_SUCCESS;
810}
811
812
813/*
814 * Put the specified call on hold.
815 */
816PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
817 const pjsua_msg_data *msg_data)
818{
819 pjmedia_sdp_session *sdp;
820 pjsua_call *call;
821 pjsip_tx_data *tdata;
822 pj_status_t status;
823
824 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
825 PJ_EINVAL);
826
827 PJSUA_LOCK();
828
829 call = &pjsua_var.calls[call_id];
830
831 if (!call->inv) {
832 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
833 PJSUA_UNLOCK();
834 return PJSIP_ESESSIONTERMINATED;
835 }
836
837 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
838 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
839 PJSUA_UNLOCK();
840 return PJSIP_ESESSIONSTATE;
841 }
842
843 status = create_inactive_sdp(call, &sdp);
844 if (status != PJ_SUCCESS) {
845 PJSUA_UNLOCK();
846 return status;
847 }
848
849 /* Create re-INVITE with new offer */
850 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
851 if (status != PJ_SUCCESS) {
852 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
853 PJSUA_UNLOCK();
854 return status;
855 }
856
857 /* Add additional headers etc */
858 pjsua_process_msg_data( tdata, msg_data);
859
860 /* Send the request */
861 status = pjsip_inv_send_msg( call->inv, tdata);
862 if (status != PJ_SUCCESS) {
863 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
864 PJSUA_UNLOCK();
865 return status;
866 }
867
868 PJSUA_UNLOCK();
869
870 return PJ_SUCCESS;
871}
872
873
874/*
875 * Send re-INVITE (to release hold).
876 */
877PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
878 pj_bool_t unhold,
879 const pjsua_msg_data *msg_data)
880{
881 pjmedia_sdp_session *sdp;
882 pjsip_tx_data *tdata;
883 pjsua_call *call;
884 pj_status_t status;
885
886
887 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
888 PJ_EINVAL);
889
890 PJSUA_LOCK();
891
892 call = &pjsua_var.calls[call_id];
893
894 if (!call->inv) {
895 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
896 PJSUA_UNLOCK();
897 return PJSIP_ESESSIONTERMINATED;
898 }
899
900
901 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
902 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
903 PJSUA_UNLOCK();
904 return PJSIP_ESESSIONSTATE;
905 }
906
907 /* Create SDP */
908 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool,
909 1, &call->skinfo, &sdp);
910 if (status != PJ_SUCCESS) {
911 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
912 status);
913 PJSUA_UNLOCK();
914 return status;
915 }
916
917 /* Create re-INVITE with new offer */
918 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
919 if (status != PJ_SUCCESS) {
920 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
921 PJSUA_UNLOCK();
922 return status;
923 }
924
925 /* Add additional headers etc */
926 pjsua_process_msg_data( tdata, msg_data);
927
928 /* Send the request */
929 status = pjsip_inv_send_msg( call->inv, tdata);
930 if (status != PJ_SUCCESS) {
931 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
932 PJSUA_UNLOCK();
933 return status;
934 }
935
936 PJSUA_UNLOCK();
937
938 return PJ_SUCCESS;
939}
940
941
942/*
943 * Initiate call transfer to the specified address.
944 */
945PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
946 const pj_str_t *dest,
947 const pjsua_msg_data *msg_data)
948{
949 pjsip_evsub *sub;
950 pjsip_tx_data *tdata;
951 pjsua_call *call;
952 pj_status_t status;
953
954
955 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
956 PJ_EINVAL);
957
958 PJSUA_LOCK();
959
960 call = &pjsua_var.calls[call_id];
961
962 if (!call->inv) {
963 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
964 PJSUA_UNLOCK();
965 return PJSIP_ESESSIONTERMINATED;
966 }
967
968 /* Create xfer client subscription.
969 * We're not interested in knowing the transfer result, so we
970 * put NULL as the callback.
971 */
972 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
973 if (status != PJ_SUCCESS) {
974 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
975 PJSUA_UNLOCK();
976 return status;
977 }
978
979 /*
980 * Create REFER request.
981 */
982 status = pjsip_xfer_initiate(sub, dest, &tdata);
983 if (status != PJ_SUCCESS) {
984 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
985 PJSUA_UNLOCK();
986 return status;
987 }
988
989 /* Add additional headers etc */
990 pjsua_process_msg_data( tdata, msg_data);
991
992 /* Send. */
993 status = pjsip_xfer_send_request(sub, tdata);
994 if (status != PJ_SUCCESS) {
995 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
996 PJSUA_UNLOCK();
997 return status;
998 }
999
1000 /* For simplicity (that's what this program is intended to be!),
1001 * leave the original invite session as it is. More advanced application
1002 * may want to hold the INVITE, or terminate the invite, or whatever.
1003 */
1004
1005 PJSUA_UNLOCK();
1006
1007 return PJ_SUCCESS;
1008
1009}
1010
1011
1012/*
1013 * Send DTMF digits to remote using RFC 2833 payload formats.
1014 */
1015PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1016 const pj_str_t *digits)
1017{
1018 pjsua_call *call;
1019 pj_status_t status;
1020
1021 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1022 PJ_EINVAL);
1023
1024 PJSUA_LOCK();
1025
1026 call = &pjsua_var.calls[call_id];
1027
1028 if (!call->session) {
1029 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1030 PJSUA_UNLOCK();
1031 return PJ_EINVALIDOP;
1032 }
1033
1034 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1035
1036 PJSUA_UNLOCK();
1037
1038 return status;
1039}
1040
1041
1042/**
1043 * Send instant messaging inside INVITE session.
1044 */
1045PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1046 const pj_str_t *mime_type,
1047 const pj_str_t *content,
1048 const pjsua_msg_data *msg_data,
1049 void *user_data)
1050{
1051 pjsua_call *call;
1052 const pj_str_t mime_text_plain = pj_str("text/plain");
1053 pjsip_media_type ctype;
1054 pjsua_im_data *im_data;
1055 pjsip_tx_data *tdata;
1056 pj_status_t status;
1057
1058
1059 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1060 PJ_EINVAL);
1061
1062 PJSUA_LOCK();
1063
1064 call = &pjsua_var.calls[call_id];
1065
1066 if (!call->inv) {
1067 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1068 PJSUA_UNLOCK();
1069 return PJSIP_ESESSIONTERMINATED;
1070 }
1071
1072 /* Lock dialog. */
1073 pjsip_dlg_inc_lock(call->inv->dlg);
1074
1075 /* Set default media type if none is specified */
1076 if (mime_type == NULL) {
1077 mime_type = &mime_text_plain;
1078 }
1079
1080 /* Create request message. */
1081 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1082 -1, &tdata);
1083 if (status != PJ_SUCCESS) {
1084 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1085 goto on_return;
1086 }
1087
1088 /* Add accept header. */
1089 pjsip_msg_add_hdr( tdata->msg,
1090 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1091
1092 /* Parse MIME type */
1093 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1094
1095 /* Create "text/plain" message body. */
1096 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1097 &ctype.subtype, content);
1098 if (tdata->msg->body == NULL) {
1099 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1100 pjsip_tx_data_dec_ref(tdata);
1101 goto on_return;
1102 }
1103
1104 /* Add additional headers etc */
1105 pjsua_process_msg_data( tdata, msg_data);
1106
1107 /* Create IM data and attach to the request. */
1108 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
1109 im_data->acc_id = call->acc_id;
1110 im_data->call_id = call_id;
1111 im_data->to = call->inv->dlg->remote.info_str;
1112 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1113 im_data->user_data = user_data;
1114
1115
1116 /* Send the request. */
1117 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1118 pjsua_var.mod.id, im_data);
1119 if (status != PJ_SUCCESS) {
1120 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1121 goto on_return;
1122 }
1123
1124on_return:
1125 pjsip_dlg_dec_lock(call->inv->dlg);
1126 PJSUA_UNLOCK();
1127 return status;
1128}
1129
1130
1131/*
1132 * Send IM typing indication inside INVITE session.
1133 */
1134PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1135 pj_bool_t is_typing,
1136 const pjsua_msg_data*msg_data)
1137{
1138 pjsua_call *call;
1139 pjsip_tx_data *tdata;
1140 pj_status_t status;
1141
1142
1143 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1144 PJ_EINVAL);
1145
1146 PJSUA_LOCK();
1147
1148 call = &pjsua_var.calls[call_id];
1149
1150 if (!call->inv) {
1151 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1152 PJSUA_UNLOCK();
1153 return PJSIP_ESESSIONTERMINATED;
1154 }
1155
1156 /* Lock dialog. */
1157 pjsip_dlg_inc_lock(call->inv->dlg);
1158
1159 /* Create request message. */
1160 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1161 -1, &tdata);
1162 if (status != PJ_SUCCESS) {
1163 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1164 goto on_return;
1165 }
1166
1167 /* Create "application/im-iscomposing+xml" msg body. */
1168 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1169 NULL, NULL, -1);
1170
1171 /* Add additional headers etc */
1172 pjsua_process_msg_data( tdata, msg_data);
1173
1174 /* Send the request. */
1175 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1176 if (status != PJ_SUCCESS) {
1177 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1178 goto on_return;
1179 }
1180
1181on_return:
1182 pjsip_dlg_dec_lock(call->inv->dlg);
1183 PJSUA_UNLOCK();
1184 return status;
1185}
1186
1187
1188/*
1189 * Terminate all calls.
1190 */
1191PJ_DEF(void) pjsua_call_hangup_all(void)
1192{
1193 unsigned i;
1194
1195 PJSUA_LOCK();
1196
1197 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1198 if (pjsua_var.calls[i].inv)
1199 pjsua_call_hangup(i, 0, NULL, NULL);
1200 }
1201
1202 PJSUA_UNLOCK();
1203}
1204
1205
1206static const char *good_number(char *buf, pj_int32_t val)
1207{
1208 if (val < 1000) {
1209 pj_ansi_sprintf(buf, "%d", val);
1210 } else if (val < 1000000) {
1211 pj_ansi_sprintf(buf, "%d.%dK",
1212 val / 1000,
1213 (val % 1000) / 100);
1214 } else {
1215 pj_ansi_sprintf(buf, "%d.%02dM",
1216 val / 1000000,
1217 (val % 1000000) / 10000);
1218 }
1219
1220 return buf;
1221}
1222
1223
1224/* Dump media session */
1225static void dump_media_session(const char *indent,
1226 char *buf, unsigned maxlen,
1227 pjmedia_session *session)
1228{
1229 unsigned i;
1230 char *p = buf, *end = buf+maxlen;
1231 int len;
1232 pjmedia_session_info info;
1233
1234 pjmedia_session_get_info(session, &info);
1235
1236 for (i=0; i<info.stream_cnt; ++i) {
1237 pjmedia_rtcp_stat stat;
1238 const char *rem_addr;
1239 int rem_port;
1240 const char *dir;
1241 char last_update[40];
1242 char packets[16], bytes[16], ipbytes[16];
1243 pj_time_val now;
1244
1245 pjmedia_session_get_stream_stat(session, i, &stat);
1246 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
1247 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
1248
1249 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1250 dir = "sendonly";
1251 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1252 dir = "recvonly";
1253 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1254 dir = "sendrecv";
1255 else
1256 dir = "inactive";
1257
1258
1259 len = pj_ansi_snprintf(buf, end-p,
1260 "%s #%d %.*s @%dKHz, %s, peer=%s:%d",
1261 indent, i,
1262 info.stream_info[i].fmt.encoding_name.slen,
1263 info.stream_info[i].fmt.encoding_name.ptr,
1264 info.stream_info[i].fmt.clock_rate / 1000,
1265 dir,
1266 rem_addr, rem_port);
1267 if (len < 1 || len > end-p) {
1268 *p = '\0';
1269 return;
1270 }
1271
1272 p += len;
1273 *p++ = '\n';
1274 *p = '\0';
1275
1276 if (stat.rx.update_cnt == 0)
1277 strcpy(last_update, "never");
1278 else {
1279 pj_gettimeofday(&now);
1280 PJ_TIME_VAL_SUB(now, stat.rx.update);
1281 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1282 now.sec / 3600,
1283 (now.sec % 3600) / 60,
1284 now.sec % 60,
1285 now.msec);
1286 }
1287
1288 len = pj_ansi_snprintf(p, end-p,
1289 "%s RX pt=%d, stat last update: %s\n"
1290 "%s total %spkt %sB (%sB +IP hdr)\n"
1291 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1292 "%s (msec) min avg max last\n"
1293 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1294 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1295 indent, info.stream_info[i].fmt.pt,
1296 last_update,
1297 indent,
1298 good_number(packets, stat.rx.pkt),
1299 good_number(bytes, stat.rx.bytes),
1300 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
1301 indent,
1302 stat.rx.loss,
1303 stat.rx.loss * 100.0 / stat.rx.pkt,
1304 stat.rx.dup,
1305 stat.rx.dup * 100.0 / stat.rx.pkt,
1306 stat.rx.reorder,
1307 stat.rx.reorder * 100.0 / stat.rx.pkt,
1308 indent, indent,
1309 stat.rx.loss_period.min / 1000.0,
1310 stat.rx.loss_period.avg / 1000.0,
1311 stat.rx.loss_period.max / 1000.0,
1312 stat.rx.loss_period.last / 1000.0,
1313 indent,
1314 stat.rx.jitter.min / 1000.0,
1315 stat.rx.jitter.avg / 1000.0,
1316 stat.rx.jitter.max / 1000.0,
1317 stat.rx.jitter.last / 1000.0,
1318 ""
1319 );
1320
1321 if (len < 1 || len > end-p) {
1322 *p = '\0';
1323 return;
1324 }
1325
1326 p += len;
1327 *p++ = '\n';
1328 *p = '\0';
1329
1330 if (stat.tx.update_cnt == 0)
1331 strcpy(last_update, "never");
1332 else {
1333 pj_gettimeofday(&now);
1334 PJ_TIME_VAL_SUB(now, stat.tx.update);
1335 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1336 now.sec / 3600,
1337 (now.sec % 3600) / 60,
1338 now.sec % 60,
1339 now.msec);
1340 }
1341
1342 len = pj_ansi_snprintf(p, end-p,
1343 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
1344 "%s total %spkt %sB (%sB +IP hdr)\n"
1345 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1346 "%s (msec) min avg max last\n"
1347 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1348 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1349 indent,
1350 info.stream_info[i].tx_pt,
1351 info.stream_info[i].param->info.frm_ptime *
1352 info.stream_info[i].param->setting.frm_per_pkt,
1353 last_update,
1354
1355 indent,
1356 good_number(packets, stat.tx.pkt),
1357 good_number(bytes, stat.tx.bytes),
1358 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
1359
1360 indent,
1361 stat.tx.loss,
1362 stat.tx.loss * 100.0 / stat.tx.pkt,
1363 stat.tx.dup,
1364 stat.tx.dup * 100.0 / stat.tx.pkt,
1365 stat.tx.reorder,
1366 stat.tx.reorder * 100.0 / stat.tx.pkt,
1367
1368 indent, indent,
1369 stat.tx.loss_period.min / 1000.0,
1370 stat.tx.loss_period.avg / 1000.0,
1371 stat.tx.loss_period.max / 1000.0,
1372 stat.tx.loss_period.last / 1000.0,
1373 indent,
1374 stat.tx.jitter.min / 1000.0,
1375 stat.tx.jitter.avg / 1000.0,
1376 stat.tx.jitter.max / 1000.0,
1377 stat.tx.jitter.last / 1000.0,
1378 ""
1379 );
1380
1381 if (len < 1 || len > end-p) {
1382 *p = '\0';
1383 return;
1384 }
1385
1386 p += len;
1387 *p++ = '\n';
1388 *p = '\0';
1389
1390 len = pj_ansi_snprintf(p, end-p,
1391 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1392 indent,
1393 stat.rtt.min / 1000.0,
1394 stat.rtt.avg / 1000.0,
1395 stat.rtt.max / 1000.0,
1396 stat.rtt.last / 1000.0
1397 );
1398 if (len < 1 || len > end-p) {
1399 *p = '\0';
1400 return;
1401 }
1402
1403 p += len;
1404 *p++ = '\n';
1405 *p = '\0';
1406 }
1407}
1408
1409
1410/* Print call info */
1411static void print_call(const char *title,
1412 int call_id,
1413 char *buf, pj_size_t size)
1414{
1415 int len;
1416 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
1417 pjsip_dialog *dlg = inv->dlg;
1418 char userinfo[128];
1419
1420 /* Dump invite sesion info. */
1421
1422 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
1423 if (len < 1)
1424 pj_ansi_strcpy(userinfo, "<--uri too long-->");
1425 else
1426 userinfo[len] = '\0';
1427
1428 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
1429 title,
1430 pjsip_inv_state_name(inv->state),
1431 userinfo);
1432 if (len < 1 || len >= (int)size) {
1433 pj_ansi_strcpy(buf, "<--uri too long-->");
1434 len = 18;
1435 } else
1436 buf[len] = '\0';
1437}
1438
1439
1440/*
1441 * Dump call and media statistics to string.
1442 */
1443PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
1444 pj_bool_t with_media,
1445 char *buffer,
1446 unsigned maxlen,
1447 const char *indent)
1448{
1449 pjsua_call *call;
1450 pj_time_val duration, res_delay, con_delay;
1451 char tmp[128];
1452 char *p, *end;
1453 int len;
1454
1455 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1456 PJ_EINVAL);
1457
1458 PJSUA_LOCK();
1459
1460 call = &pjsua_var.calls[call_id];
1461
1462 *buffer = '\0';
1463 p = buffer;
1464 end = buffer + maxlen;
1465 len = 0;
1466
1467 if (call->inv == NULL) {
1468 PJSUA_UNLOCK();
1469 return PJ_EINVALIDOP;
1470 }
1471
1472 print_call(indent, call_id, tmp, sizeof(tmp));
1473
1474 len = pj_ansi_strlen(tmp);
1475 pj_ansi_strcpy(buffer, tmp);
1476
1477 p += len;
1478 *p++ = '\r';
1479 *p++ = '\n';
1480
1481 /* Calculate call duration */
1482 if (call->inv->state >= PJSIP_INV_STATE_CONFIRMED) {
1483 pj_gettimeofday(&duration);
1484 PJ_TIME_VAL_SUB(duration, call->conn_time);
1485 con_delay = call->conn_time;
1486 PJ_TIME_VAL_SUB(con_delay, call->start_time);
1487 } else {
1488 duration.sec = duration.msec = 0;
1489 con_delay.sec = con_delay.msec = 0;
1490 }
1491
1492 /* Calculate first response delay */
1493 if (call->inv->state >= PJSIP_INV_STATE_EARLY) {
1494 res_delay = call->res_time;
1495 PJ_TIME_VAL_SUB(res_delay, call->start_time);
1496 } else {
1497 res_delay.sec = res_delay.msec = 0;
1498 }
1499
1500 /* Print duration */
1501 len = pj_ansi_snprintf(p, end-p,
1502 "%s Call time: %02dh:%02dm:%02ds, "
1503 "1st res in %d ms, conn in %dms",
1504 indent,
1505 (duration.sec / 3600),
1506 ((duration.sec % 3600)/60),
1507 (duration.sec % 60),
1508 PJ_TIME_VAL_MSEC(res_delay),
1509 PJ_TIME_VAL_MSEC(con_delay));
1510
1511 if (len > 0 && len < end-p) {
1512 p += len;
1513 *p++ = '\n';
1514 *p = '\0';
1515 }
1516
1517 /* Dump session statistics */
1518 if (with_media && call->session)
1519 dump_media_session(indent, p, end-p, call->session);
1520
1521 PJSUA_UNLOCK();
1522
1523 return PJ_SUCCESS;
1524}
1525
1526
1527/*
1528 * Destroy the call's media
1529 */
1530static pj_status_t call_destroy_media(int call_id)
1531{
1532 pjsua_call *call = &pjsua_var.calls[call_id];
1533
1534 if (call->conf_slot != PJSUA_INVALID_ID) {
1535 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
1536 call->conf_slot = PJSUA_INVALID_ID;
1537 }
1538
1539 if (call->session) {
1540 /* Destroy session (this will also close RTP/RTCP sockets). */
1541 pjmedia_session_destroy(call->session);
1542 call->session = NULL;
1543
1544 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
1545 call_id));
1546
1547 }
1548
1549 call->media_st = PJSUA_CALL_MEDIA_NONE;
1550
1551 return PJ_SUCCESS;
1552}
1553
1554
Benny Prijono84126ab2006-02-09 09:30:09 +00001555/*
1556 * This callback receives notification from invite session when the
1557 * session state has changed.
1558 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001559static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
1560 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00001561{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001562 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001563
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564 PJSUA_LOCK();
1565
1566 call = inv->dlg->mod_data[pjsua_var.mod.id];
1567
1568 if (!call) {
1569 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00001570 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571 }
1572
Benny Prijonoe21e7842006-04-09 16:46:05 +00001573
1574 /* Get call times */
1575 switch (inv->state) {
1576 case PJSIP_INV_STATE_EARLY:
1577 case PJSIP_INV_STATE_CONNECTING:
1578 if (call->res_time.sec == 0)
1579 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001580 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001581 pj_strncpy(&call->last_text,
1582 &e->body.tsx_state.tsx->status_text,
1583 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00001584 break;
1585 case PJSIP_INV_STATE_CONFIRMED:
1586 pj_gettimeofday(&call->conn_time);
1587 break;
1588 case PJSIP_INV_STATE_DISCONNECTED:
1589 pj_gettimeofday(&call->dis_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001590 if (e->body.tsx_state.tsx->status_code > call->last_code) {
1591 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001592 pj_strncpy(&call->last_text,
1593 &e->body.tsx_state.tsx->status_text,
1594 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001595 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00001596 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001597 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001598 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001599 pj_strncpy(&call->last_text,
1600 &e->body.tsx_state.tsx->status_text,
1601 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00001602 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00001603 }
1604
Benny Prijono26ff9062006-02-21 23:47:00 +00001605 /* If this is an outgoing INVITE that was created because of
1606 * REFER/transfer, send NOTIFY to transferer.
1607 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00001608 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001609 int st_code = -1;
1610 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1611
1612
Benny Prijonoa91a0032006-02-26 21:23:45 +00001613 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001614 case PJSIP_INV_STATE_NULL:
1615 case PJSIP_INV_STATE_CALLING:
1616 /* Do nothing */
1617 break;
1618
1619 case PJSIP_INV_STATE_EARLY:
1620 case PJSIP_INV_STATE_CONNECTING:
1621 st_code = e->body.tsx_state.tsx->status_code;
1622 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
1623 break;
1624
1625 case PJSIP_INV_STATE_CONFIRMED:
1626 /* When state is confirmed, send the final 200/OK and terminate
1627 * subscription.
1628 */
1629 st_code = e->body.tsx_state.tsx->status_code;
1630 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1631 break;
1632
1633 case PJSIP_INV_STATE_DISCONNECTED:
1634 st_code = e->body.tsx_state.tsx->status_code;
1635 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
1636 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001637
Benny Prijono8b1889b2006-06-06 18:40:40 +00001638 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00001639 /* Nothing to do. Just to keep gcc from complaining about
1640 * unused enums.
1641 */
1642 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00001643 }
1644
1645 if (st_code != -1) {
1646 pjsip_tx_data *tdata;
1647 pj_status_t status;
1648
Benny Prijonoa91a0032006-02-26 21:23:45 +00001649 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00001650 ev_state, st_code,
1651 NULL, &tdata);
1652 if (status != PJ_SUCCESS) {
1653 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
1654 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001655 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001656 if (status != PJ_SUCCESS) {
1657 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
1658 }
1659 }
1660 }
1661 }
1662
Benny Prijono84126ab2006-02-09 09:30:09 +00001663
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001664 if (pjsua_var.ua_cfg.cb.on_call_state)
1665 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001666
1667 /* call->inv may be NULL now */
1668
Benny Prijono84126ab2006-02-09 09:30:09 +00001669 /* Destroy media session when invite session is disconnected. */
1670 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00001671
Benny Prijonoa91a0032006-02-26 21:23:45 +00001672 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00001673
Benny Prijono275fd682006-03-22 11:59:11 +00001674 if (call)
1675 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001676
Benny Prijono105217f2006-03-06 16:25:59 +00001677 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001678 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001679 --pjsua_var.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +00001680 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681
1682 PJSUA_UNLOCK();
1683}
1684
1685/*
1686 * This callback is called by invite session framework when UAC session
1687 * has forked.
1688 */
1689static void pjsua_call_on_forked( pjsip_inv_session *inv,
1690 pjsip_event *e)
1691{
1692 PJ_UNUSED_ARG(inv);
1693 PJ_UNUSED_ARG(e);
1694
1695 PJ_TODO(HANDLE_FORKED_DIALOG);
1696}
1697
1698
1699/*
1700 * Callback to be called when SDP offer/answer negotiation has just completed
1701 * in the session. This function will start/update media if negotiation
1702 * has succeeded.
1703 */
1704static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1705 pj_status_t status)
1706{
1707 int prev_media_st = 0;
1708 pjsua_call *call;
1709 pjmedia_session_info sess_info;
1710 const pjmedia_sdp_session *local_sdp;
1711 const pjmedia_sdp_session *remote_sdp;
1712 pjmedia_port *media_port;
1713 pj_str_t port_name;
1714 char tmp[PJSIP_MAX_URL_SIZE];
1715
1716 PJSUA_LOCK();
1717
1718 call = inv->dlg->mod_data[pjsua_var.mod.id];
1719
1720 if (status != PJ_SUCCESS) {
1721
1722 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
1723
1724 /* Disconnect call if we're not in the middle of initializing an
1725 * UAS dialog and if this is not a re-INVITE
1726 */
1727 if (inv->state != PJSIP_INV_STATE_NULL &&
1728 inv->state != PJSIP_INV_STATE_CONFIRMED)
1729 {
1730 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1731 }
1732
1733 PJSUA_UNLOCK();
1734 return;
1735 }
1736
1737 /* Destroy existing media session, if any. */
1738
1739 if (call) {
1740 prev_media_st = call->media_st;
1741 call_destroy_media(call->index);
1742 }
1743
1744 /* Get local and remote SDP */
1745
1746 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
1747 if (status != PJ_SUCCESS) {
1748 pjsua_perror(THIS_FILE,
1749 "Unable to retrieve currently active local SDP",
1750 status);
1751 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1752 PJSUA_UNLOCK();
1753 return;
1754 }
1755
1756
1757 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
1758 if (status != PJ_SUCCESS) {
1759 pjsua_perror(THIS_FILE,
1760 "Unable to retrieve currently active remote SDP",
1761 status);
1762 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1763 PJSUA_UNLOCK();
1764 return;
1765 }
1766
1767 /* Create media session info based on SDP parameters.
1768 * We only support one stream per session at the moment
1769 */
1770 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
1771 pjsua_var.med_endpt,
1772 1,&sess_info,
1773 local_sdp, remote_sdp);
1774 if (status != PJ_SUCCESS) {
1775 pjsua_perror(THIS_FILE, "Unable to create media session",
1776 status);
1777 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1778 PJSUA_UNLOCK();
1779 return;
1780 }
1781
1782 /* Check if media is put on-hold */
1783 if (sess_info.stream_cnt == 0 ||
1784 sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
1785 {
1786
1787 /* Determine who puts the call on-hold */
1788 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
1789 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
1790 /* It was local who offer hold */
1791 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
1792 } else {
1793 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
1794 }
1795 }
1796
1797 call->media_dir = PJMEDIA_DIR_NONE;
1798
1799 } else {
1800
1801 /* Override ptime, if this option is specified. */
1802 PJ_TODO(set_codec_ptime_in_call);
1803
1804
1805 /* Optionally, application may modify other stream settings here
1806 * (such as jitter buffer parameters, codec ptime, etc.)
1807 */
1808
1809 /* Create session based on session info. */
1810 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1811 &call->med_tp,
1812 call, &call->session );
1813 if (status != PJ_SUCCESS) {
1814 pjsua_perror(THIS_FILE, "Unable to create media session",
1815 status);
1816 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1817 PJSUA_UNLOCK();
1818 return;
1819 }
1820
1821
1822 /* Get the port interface of the first stream in the session.
1823 * We need the port interface to add to the conference bridge.
1824 */
1825 pjmedia_session_get_port(call->session, 0, &media_port);
1826
1827
1828 /*
1829 * Add the call to conference bridge.
1830 */
1831 port_name.ptr = tmp;
1832 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1833 call->inv->dlg->remote.info->uri,
1834 tmp, sizeof(tmp));
1835 if (port_name.slen < 1) {
1836 port_name = pj_str("call");
1837 }
1838 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1839 media_port,
1840 &port_name,
1841 (unsigned*)&call->conf_slot);
1842 if (status != PJ_SUCCESS) {
1843 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1844 status);
1845 call_destroy_media(call->index);
1846 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
1847 PJSUA_UNLOCK();
1848 return;
1849 }
1850
1851 /* Call's media state is active */
1852 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
1853 call->media_dir = sess_info.stream_info[0].dir;
1854 }
1855
1856 /* Print info. */
1857 {
1858 char info[80];
1859 int info_len = 0;
1860 unsigned i;
1861
1862 for (i=0; i<sess_info.stream_cnt; ++i) {
1863 int len;
1864 const char *dir;
1865 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1866
1867 switch (strm_info->dir) {
1868 case PJMEDIA_DIR_NONE:
1869 dir = "inactive";
1870 break;
1871 case PJMEDIA_DIR_ENCODING:
1872 dir = "sendonly";
1873 break;
1874 case PJMEDIA_DIR_DECODING:
1875 dir = "recvonly";
1876 break;
1877 case PJMEDIA_DIR_ENCODING_DECODING:
1878 dir = "sendrecv";
1879 break;
1880 default:
1881 dir = "unknown";
1882 break;
1883 }
1884 len = pj_ansi_sprintf( info+info_len,
1885 ", stream #%d: %.*s (%s)", i,
1886 (int)strm_info->fmt.encoding_name.slen,
1887 strm_info->fmt.encoding_name.ptr,
1888 dir);
1889 if (len > 0)
1890 info_len += len;
1891 }
1892 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1893 }
1894
1895 /* Call application callback, if any */
1896 if (pjsua_var.ua_cfg.cb.on_call_media_state)
1897 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
1898
1899
1900 PJSUA_UNLOCK();
1901}
1902
1903
1904/*
1905 * Create inactive SDP for call hold.
1906 */
1907static pj_status_t create_inactive_sdp(pjsua_call *call,
1908 pjmedia_sdp_session **p_answer)
1909{
1910 pj_status_t status;
1911 pjmedia_sdp_conn *conn;
1912 pjmedia_sdp_attr *attr;
1913 pjmedia_sdp_session *sdp;
1914
1915 /* Create new offer */
1916 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
1917 &call->skinfo, &sdp);
1918 if (status != PJ_SUCCESS) {
1919 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1920 return status;
1921 }
1922
1923 /* Get SDP media connection line */
1924 conn = sdp->media[0]->conn;
1925 if (!conn)
1926 conn = sdp->conn;
1927
1928 /* Modify address */
1929 conn->addr = pj_str("0.0.0.0");
1930
1931 /* Remove existing directions attributes */
1932 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
1933 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
1934 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
1935 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
1936
1937 /* Add inactive attribute */
1938 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
1939 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
1940
1941 *p_answer = sdp;
1942
1943 return status;
1944}
1945
1946
1947/*
1948 * Called when session received new offer.
1949 */
1950static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
1951 const pjmedia_sdp_session *offer)
1952{
1953 const char *remote_state;
1954 pjsua_call *call;
1955 pjmedia_sdp_conn *conn;
1956 pjmedia_sdp_session *answer;
1957 pj_bool_t is_remote_active;
1958 pj_status_t status;
1959
1960 PJSUA_LOCK();
1961
1962 call = inv->dlg->mod_data[pjsua_var.mod.id];
1963
1964 /*
1965 * See if remote is offering active media (i.e. not on-hold)
1966 */
1967 is_remote_active = PJ_TRUE;
1968
1969 conn = offer->media[0]->conn;
1970 if (!conn)
1971 conn = offer->conn;
1972
1973 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
1974 pj_strcmp2(&conn->addr, "0")==0)
1975 {
1976 is_remote_active = PJ_FALSE;
1977
1978 }
1979 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
1980 {
1981 is_remote_active = PJ_FALSE;
1982 }
1983
1984 remote_state = (is_remote_active ? "active" : "inactive");
1985
1986 /* Supply candidate answer */
1987 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
1988 PJ_LOG(4,(THIS_FILE,
1989 "Call %d: RX new media offer, creating inactive SDP "
1990 "(media in offer is %s)", call->index, remote_state));
1991 status = create_inactive_sdp( call, &answer );
1992 } else {
1993 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
1994 call->index));
1995 status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
1996 call->inv->pool, 1,
1997 &call->skinfo, &answer);
1998 }
1999
2000 if (status != PJ_SUCCESS) {
2001 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2002 PJSUA_UNLOCK();
2003 return;
2004 }
2005
2006 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2007 if (status != PJ_SUCCESS) {
2008 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2009 PJSUA_UNLOCK();
2010 return;
2011 }
2012
2013 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002014}
2015
2016
2017/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002018 * Callback called by event framework when the xfer subscription state
2019 * has changed.
2020 */
2021static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2022{
2023
2024 PJ_UNUSED_ARG(event);
2025
2026 /*
2027 * We're only interested when subscription is terminated, to
2028 * clear the xfer_sub member of the inv_data.
2029 */
2030 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002031 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002032
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002033 call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002034 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002035 return;
2036
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002038 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002039
2040 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
2041 }
2042}
2043
2044
2045/*
2046 * Follow transfer (REFER) request.
2047 */
2048static void on_call_transfered( pjsip_inv_session *inv,
2049 pjsip_rx_data *rdata )
2050{
2051 pj_status_t status;
2052 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002053 pjsua_call *existing_call;
2054 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002055 const pj_str_t str_refer_to = { "Refer-To", 8};
2056 pjsip_generic_string_hdr *refer_to;
2057 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002058 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +00002059 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002060 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002061 pjsip_evsub *sub;
2062
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 existing_call = inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002064
Benny Prijono26ff9062006-02-21 23:47:00 +00002065 /* Find the Refer-To header */
2066 refer_to = (pjsip_generic_string_hdr*)
2067 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2068
2069 if (refer_to == NULL) {
2070 /* Invalid Request.
2071 * No Refer-To header!
2072 */
2073 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002074 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002075 return;
2076 }
2077
Benny Prijono9fc735d2006-05-28 14:58:12 +00002078 /* Notify callback */
2079 code = PJSIP_SC_OK;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002080 if (pjsua_var.ua_cfg.cb.on_call_transfered)
2081 (*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
2082 &refer_to->hvalue, &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002083
2084 if (code < 200)
2085 code = 200;
2086 if (code >= 300) {
2087 /* Application rejects call transfer request */
2088 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2089 return;
2090 }
2091
Benny Prijono26ff9062006-02-21 23:47:00 +00002092 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2093 (int)inv->dlg->remote.info_str.slen,
2094 inv->dlg->remote.info_str.ptr,
2095 (int)refer_to->hvalue.slen,
2096 refer_to->hvalue.ptr));
2097
2098 /* Init callback */
2099 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
2100 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
2101
2102 /* Create transferee event subscription */
2103 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2104 if (status != PJ_SUCCESS) {
2105 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +00002106 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002107 return;
2108 }
2109
2110 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002111 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002112
2113 /* Create initial NOTIFY request */
2114 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2115 100, NULL, &tdata);
2116 if (status != PJ_SUCCESS) {
2117 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
2118 return;
2119 }
2120
2121 /* Send initial NOTIFY request */
2122 status = pjsip_xfer_send_request( sub, tdata);
2123 if (status != PJ_SUCCESS) {
2124 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2125 return;
2126 }
2127
2128 /* We're cheating here.
2129 * We need to get a null terminated string from a pj_str_t.
2130 * So grab the pointer from the hvalue and NULL terminate it, knowing
2131 * that the NULL position will be occupied by a newline.
2132 */
2133 uri = refer_to->hvalue.ptr;
2134 uri[refer_to->hvalue.slen] = '\0';
2135
2136 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002137 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002138 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
2139 existing_call->user_data, NULL,
2140 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002141 if (status != PJ_SUCCESS) {
2142
2143 /* Notify xferer about the error */
2144 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2145 500, NULL, &tdata);
2146 if (status != PJ_SUCCESS) {
2147 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2148 status);
2149 return;
2150 }
2151 status = pjsip_xfer_send_request(sub, tdata);
2152 if (status != PJ_SUCCESS) {
2153 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2154 status);
2155 return;
2156 }
2157 return;
2158 }
2159
2160 /* Put the server subscription in inv_data.
2161 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2162 * reported back to the server subscription.
2163 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002164 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002165
2166 /* Put the invite_data in the subscription. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002167 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2168 &pjsua_var.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +00002169}
2170
2171
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002172
Benny Prijono26ff9062006-02-21 23:47:00 +00002173/*
2174 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002175 * session. We use this to trap:
2176 * - incoming REFER request.
2177 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002178 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002179static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2180 pjsip_transaction *tsx,
2181 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002182{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 pjsua_call *call = inv->dlg->mod_data[pjsua_var.mod.id];
2184
2185 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002186
Benny Prijono26ff9062006-02-21 23:47:00 +00002187 if (tsx->role==PJSIP_ROLE_UAS &&
2188 tsx->state==PJSIP_TSX_STATE_TRYING &&
2189 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
2190 {
2191 /*
2192 * Incoming REFER request.
2193 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002194 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002195
Benny Prijono26ff9062006-02-21 23:47:00 +00002196 }
Benny Prijonob0808372006-03-02 21:18:58 +00002197 else if (tsx->role==PJSIP_ROLE_UAS &&
2198 tsx->state==PJSIP_TSX_STATE_TRYING &&
2199 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2200 {
2201 /*
2202 * Incoming MESSAGE request!
2203 */
2204 pjsip_rx_data *rdata;
2205 pjsip_msg *msg;
2206 pjsip_accept_hdr *accept_hdr;
2207 pj_status_t status;
2208
2209 rdata = e->body.tsx_state.src.rdata;
2210 msg = rdata->msg_info.msg;
2211
2212 /* Request MUST have message body, with Content-Type equal to
2213 * "text/plain".
2214 */
2215 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
2216
2217 pjsip_hdr hdr_list;
2218
2219 pj_list_init(&hdr_list);
2220 pj_list_push_back(&hdr_list, accept_hdr);
2221
2222 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
2223 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002224 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00002225 return;
2226 }
2227
2228 /* Respond with 200 first, so that remote doesn't retransmit in case
2229 * the UI takes too long to process the message.
2230 */
2231 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
2232
2233 /* Process MESSAGE request */
2234 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
2235 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002236
Benny Prijonob0808372006-03-02 21:18:58 +00002237 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002238 else if (tsx->role == PJSIP_ROLE_UAC &&
2239 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002240 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002241 /* Handle outgoing pager status */
2242 if (tsx->status_code >= 200) {
2243 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00002244
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002245 im_data = tsx->mod_data[pjsua_var.mod.id];
2246 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00002247
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002248 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
2249 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
2250 &im_data->to,
2251 &im_data->body,
2252 im_data->user_data,
2253 tsx->status_code,
2254 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00002255 }
Benny Prijonofccab712006-02-22 22:23:22 +00002256 }
Benny Prijono834aee32006-02-19 01:38:06 +00002257 }
Benny Prijono834aee32006-02-19 01:38:06 +00002258
Benny Prijono26ff9062006-02-21 23:47:00 +00002259
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002260 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00002261}