blob: 5306d253cfade5a4145577404e692349eafc45c5 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00004 *
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/*
Benny Prijono77998ce2007-06-20 10:03:46 +000053 * Called to generate new offer.
54 */
55static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
56 pjmedia_sdp_session **offer);
57
58/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059 * This callback is called when transaction state has changed in INVITE
60 * session. We use this to trap:
61 * - incoming REFER request.
62 * - incoming MESSAGE request.
63 */
64static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
65 pjsip_transaction *tsx,
66 pjsip_event *e);
67
68
Benny Prijonoeebe9af2006-06-13 22:57:13 +000069
70/* Create inactive SDP for call hold. */
71static pj_status_t create_inactive_sdp(pjsua_call *call,
72 pjmedia_sdp_session **p_answer);
73
Benny Prijono7129cc72007-11-05 05:54:25 +000074/* Update SDP version in the offer */
75static void update_sdp_version(pjsua_call *call,
76 pjmedia_sdp_session *sdp)
77{
78 const pjmedia_sdp_session *old_sdp = NULL;
79 pj_status_t status;
80
81 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &old_sdp);
82 if (status != PJ_SUCCESS || old_sdp == NULL)
83 return;
84
85 sdp->origin.version = old_sdp->origin.version + 1;
86}
87
88
Benny Prijonod524e822006-09-22 12:48:18 +000089/*
90 * Callback called by event framework when the xfer subscription state
91 * has changed.
92 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000093static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
94static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
95
96/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000097 * Reset call descriptor.
98 */
99static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000100{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 call->index = id;
104 call->inv = NULL;
105 call->user_data = NULL;
106 call->session = NULL;
Benny Prijono8147f402007-11-21 14:50:07 +0000107 call->ssrc = pj_rand();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000109 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 call->conf_slot = PJSUA_INVALID_ID;
111 call->last_text.ptr = call->last_text_buf_;
112 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000113 call->conn_time.sec = 0;
114 call->conn_time.msec = 0;
115 call->res_time.sec = 0;
116 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000117 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Benny Prijono105217f2006-03-06 16:25:59 +0000118}
119
120
Benny Prijono275fd682006-03-22 11:59:11 +0000121/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000123 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000125{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 pjsip_inv_callback inv_cb;
127 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000128 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000130
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 /* Init calls array. */
132 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
133 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000134
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 /* Copy config */
136 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000137
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000139 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
141 inv_cb.on_new_session = &pjsua_call_on_forked;
142 inv_cb.on_media_update = &pjsua_call_on_media_update;
143 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000144 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijono275fd682006-03-22 11:59:11 +0000147
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000148 /* Initialize invite session module: */
149 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
150 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
151
Benny Prijonoc8141a82006-08-20 09:12:19 +0000152 /* Add "norefersub" in Supported header */
153 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
154 NULL, 1, &str_norefersub);
155
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156 return status;
157}
158
159
160/*
161 * Start call subsystem.
162 */
163pj_status_t pjsua_call_subsys_start(void)
164{
165 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000166 return PJ_SUCCESS;
167}
168
169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000171 * Get maximum number of calls configured in pjsua.
172 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000173PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000174{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000175 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000176}
177
178
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000179/*
180 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000181 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000182PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185}
186
187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188/*
189 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000190 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
192 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
201 if (!pjsua_var.calls[i].inv)
202 continue;
203 ids[c] = i;
204 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205 }
206
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
211 return PJ_SUCCESS;
212}
213
214
Benny Prijono1f7767b2007-10-03 18:28:49 +0000215#define LATE_SDP 0
216
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217/*
218 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000220PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
221 const pj_str_t *dest_uri,
222 unsigned options,
223 void *user_data,
224 const pjsua_msg_data *msg_data,
225 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000226{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000227 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000228 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000229 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230 pjsua_acc *acc;
231 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000232 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000233 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000234 pjsip_tx_data *tdata;
235 pj_status_t status;
236
Benny Prijono9fc735d2006-05-28 14:58:12 +0000237
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000239 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000240 PJ_EINVAL);
241
Benny Prijono320fa4d2006-12-07 10:09:16 +0000242 /* Check arguments */
243 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
244
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000245 PJSUA_LOCK();
246
247 acc = &pjsua_var.acc[acc_id];
248 if (!acc->valid) {
249 pjsua_perror(THIS_FILE, "Unable to make call because account "
250 "is not valid", PJ_EINVALIDOP);
251 PJSUA_UNLOCK();
252 return PJ_EINVALIDOP;
253 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000254
Benny Prijonoa91a0032006-02-26 21:23:45 +0000255 /* Find free call slot. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000256 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000257 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000258 break;
259 }
260
Benny Prijonoa1e69682007-05-11 15:14:34 +0000261 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000262 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
263 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000264 return PJ_ETOOMANY;
265 }
266
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 call = &pjsua_var.calls[call_id];
268
Benny Prijono320fa4d2006-12-07 10:09:16 +0000269 /* Verify that destination URI is valid before calling
270 * pjsua_acc_create_uac_contact, or otherwise there
271 * a misleading "Invalid Contact URI" error will be printed
272 * when pjsua_acc_create_uac_contact() fails.
273 */
274 if (1) {
275 pj_pool_t *pool;
276 pjsip_uri *uri;
277 pj_str_t dup;
278
279 pool = pjsua_pool_create("tmp-uri", 4000, 4000);
280 if (!pool) {
281 pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
282 PJSUA_UNLOCK();
283 return PJ_ENOMEM;
284 }
285
286 pj_strdup_with_null(pool, &dup, dest_uri);
287 uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0);
288 pj_pool_release(pool);
289
290 if (uri == NULL) {
291 pjsua_perror(THIS_FILE, "Unable to make call",
292 PJSIP_EINVALIDREQURI);
293 PJSUA_UNLOCK();
294 return PJSIP_EINVALIDREQURI;
295 }
296 }
297
Benny Prijono093d3022006-09-24 00:07:11 +0000298 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
299 (int)dest_uri->slen, dest_uri->ptr));
300
Benny Prijonoe21e7842006-04-09 16:46:05 +0000301 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000303
Benny Prijonoe21e7842006-04-09 16:46:05 +0000304 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000305 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000306
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000307 /* Create suitable Contact header */
308 status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
309 acc_id, dest_uri);
310 if (status != PJ_SUCCESS) {
311 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
312 PJSUA_UNLOCK();
313 return status;
314 }
315
Benny Prijonoe21e7842006-04-09 16:46:05 +0000316 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000317 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000318 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000319 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000320 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000321 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000322 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000323 return status;
324 }
325
Benny Prijonoc97608e2007-03-23 16:34:20 +0000326 /* Init media channel */
327 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
328 if (status != PJ_SUCCESS) {
329 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
330 goto on_error;
331 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
Benny Prijonoc97608e2007-03-23 16:34:20 +0000333 /* Create SDP offer */
Benny Prijono1f7767b2007-10-03 18:28:49 +0000334#if LATE_SDP
335 offer = NULL;
336#else
Benny Prijonoc97608e2007-03-23 16:34:20 +0000337 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000338 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000339 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000340 goto on_error;
341 }
Benny Prijono1f7767b2007-10-03 18:28:49 +0000342#endif
Benny Prijono84126ab2006-02-09 09:30:09 +0000343
344 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000345 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000346 if (acc->cfg.require_100rel)
347 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000348
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000349 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000350 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000351 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000352 goto on_error;
353 }
354
355
356 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000357 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000358 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000359
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000360 dlg->mod_data[pjsua_var.mod.id] = call;
361 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000362
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000363 /* Attach user data */
364 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000365
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000366 /* If account is locked to specific transport, then lock dialog
367 * to this transport too.
368 */
369 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
370 pjsip_tpselector tp_sel;
371
372 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
373 pjsip_dlg_set_transport(dlg, &tp_sel);
374 }
375
Benny Prijono84126ab2006-02-09 09:30:09 +0000376 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000377 if (!pj_list_empty(&acc->route_set))
378 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000379
380
381 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000382 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000383 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000384 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000385 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000386
Benny Prijono48ab2b72007-11-08 09:24:30 +0000387 /* Set authentication preference */
388 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000389
390 /* Create initial INVITE: */
391
392 status = pjsip_inv_invite(inv, &tdata);
393 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000394 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
395 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000396 goto on_error;
397 }
398
399
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000400 /* Add additional headers etc */
401
402 pjsua_process_msg_data( tdata, msg_data);
403
Benny Prijono093d3022006-09-24 00:07:11 +0000404 /* Must increment call counter now */
405 ++pjsua_var.call_cnt;
406
Benny Prijono84126ab2006-02-09 09:30:09 +0000407 /* Send initial INVITE: */
408
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000409 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000410 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000411 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
412 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000413
414 /* Upon failure to send first request, both dialog and invite
415 * session would have been cleared.
416 */
417 inv = NULL;
418 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000419 goto on_error;
420 }
421
Benny Prijono84126ab2006-02-09 09:30:09 +0000422 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000423
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 if (p_call_id)
425 *p_call_id = call_id;
426
427 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000428
429 return PJ_SUCCESS;
430
431
432on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000433 if (inv != NULL) {
434 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000435 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000436 pjsip_dlg_terminate(dlg);
437 }
438
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439 if (call_id != -1) {
440 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000441 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000442 }
443
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000444 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000445 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000446}
447
448
Benny Prijono91a6a172007-10-31 08:59:29 +0000449/* Get the NAT type information in remote's SDP */
450static void update_remote_nat_type(pjsua_call *call,
451 const pjmedia_sdp_session *sdp)
452{
453 const pjmedia_sdp_attr *xnat;
454
455 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
456 if (xnat) {
457 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
458 } else {
459 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
460 }
461
462 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
463 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
464}
465
466
Benny Prijonodc39fe82006-05-26 12:17:46 +0000467/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000468 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000469 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000470 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000471pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000472{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000473 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000474 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000475 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000476 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
477 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000478 pjsip_tx_data *response = NULL;
479 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000480 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481 int acc_id;
482 pjsua_call *call;
483 int call_id = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000484 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000485 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000486
Benny Prijono26ff9062006-02-21 23:47:00 +0000487 /* Don't want to handle anything but INVITE */
488 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
489 return PJ_FALSE;
490
491 /* Don't want to handle anything that's already associated with
492 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000493 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000494 if (dlg || tsx)
495 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000496
Benny Prijono148c9dd2006-09-19 13:37:53 +0000497 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000498
Benny Prijono26ff9062006-02-21 23:47:00 +0000499 /* Find free call slot. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) {
501 if (pjsua_var.calls[call_id].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000502 break;
503 }
504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 if (call_id == (int)pjsua_var.ua_cfg.max_calls) {
506 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000507 PJSIP_SC_BUSY_HERE, NULL,
508 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000509 PJ_LOG(2,(THIS_FILE,
510 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000511 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000512 return PJ_TRUE;
513 }
514
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000515 /* Clear call descriptor */
516 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000517
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 call = &pjsua_var.calls[call_id];
519
520 /* Mark call start time. */
521 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000522
Benny Prijono053f5222006-11-11 16:16:04 +0000523 /* Check INVITE request for Replaces header. If Replaces header is
524 * present, the function will make sure that we can handle the request.
525 */
526 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
527 &response);
528 if (status != PJ_SUCCESS) {
529 /*
530 * Something wrong with the Replaces header.
531 */
532 if (response) {
533 pjsip_response_addr res_addr;
534
535 pjsip_get_response_addr(response->pool, rdata, &res_addr);
536 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
537 NULL, NULL);
538
539 } else {
540
541 /* Respond with 500 (Internal Server Error) */
542 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
543 NULL, NULL);
544 }
545
546 PJSUA_UNLOCK();
547 return PJ_TRUE;
548 }
549
550 /* If this INVITE request contains Replaces header, notify application
551 * about the request so that application can do subsequent checking
552 * if it wants to.
553 */
554 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
555 pjsua_call *replaced_call;
556 int st_code = 200;
557 pj_str_t st_text = { "OK", 2 };
558
559 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000560 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000561
562 /* Notify application */
563 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
564 rdata, &st_code, &st_text);
565
566 /* Must specify final response */
567 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
568
569 /* Check if application rejects this request. */
570 if (st_code >= 300) {
571
572 if (st_text.slen == 2)
573 st_text = *pjsip_get_status_text(st_code);
574
575 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
576 st_code, &st_text, NULL, NULL, NULL);
577 PJSUA_UNLOCK();
578 return PJ_TRUE;
579 }
580 }
581
582
Benny Prijonoc97608e2007-03-23 16:34:20 +0000583 /* Init media channel */
584 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000585 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000587 NULL, NULL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000588 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000589 return PJ_TRUE;
590 }
591
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000592
Benny Prijonoc97608e2007-03-23 16:34:20 +0000593 /* Get media capability from media endpoint: */
594 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
595 if (status != PJ_SUCCESS) {
596 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
597 NULL, NULL);
598 pjsua_media_channel_deinit(call->index);
599 PJSUA_UNLOCK();
600 return PJ_TRUE;
601 }
602
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000603 /*
604 * Get which account is most likely to be associated with this incoming
605 * call. We need the account to find which contact URI to put for
606 * the call.
607 */
608 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000609
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000610 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000611 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000612 if (pjsua_var.acc[acc_id].cfg.require_100rel)
613 options |= PJSIP_INV_REQUIRE_100REL;
614
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000615 status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
616 pjsua_var.endpt, &response);
617 if (status != PJ_SUCCESS) {
618
619 /*
620 * No we can't handle the incoming INVITE request.
621 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000622 if (response) {
623 pjsip_response_addr res_addr;
624
625 pjsip_get_response_addr(response->pool, rdata, &res_addr);
626 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
627 NULL, NULL);
628
629 } else {
630
631 /* Respond with 500 (Internal Server Error) */
632 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
633 NULL, NULL);
634 }
635
Benny Prijonoc97608e2007-03-23 16:34:20 +0000636 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000637 PJSUA_UNLOCK();
638 return PJ_TRUE;
639 }
640
641
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000642 /* Get suitable Contact header */
643 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
644 acc_id, rdata);
645 if (status != PJ_SUCCESS) {
646 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
647 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
648 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000649 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000650 PJSUA_UNLOCK();
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000651 return PJ_TRUE;
652 }
653
Benny Prijono26ff9062006-02-21 23:47:00 +0000654 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000655 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000656 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000657 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000658 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000659 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000660 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000661 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000662 return PJ_TRUE;
663 }
664
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000665 /* Set credentials */
666 if (pjsua_var.acc[acc_id].cred_cnt) {
667 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
668 pjsua_var.acc[acc_id].cred_cnt,
669 pjsua_var.acc[acc_id].cred);
670 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000671
Benny Prijono48ab2b72007-11-08 09:24:30 +0000672 /* Set preference */
673 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
674 &pjsua_var.acc[acc_id].cfg.auth_pref);
675
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000677 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000678 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000679 pjsip_hdr hdr_list;
680 pjsip_warning_hdr *w;
681
682 w = pjsip_warning_hdr_create_from_status(dlg->pool,
683 pjsip_endpt_name(pjsua_var.endpt),
684 status);
685 pj_list_init(&hdr_list);
686 pj_list_push_back(&hdr_list, w);
687
688 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
689
690 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000691 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000692 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000693 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000694 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000695 return PJ_TRUE;
696 }
697
Benny Prijonoea9fd392007-11-06 03:41:40 +0000698 /* Update NAT type of remote endpoint, only when there is SDP in
699 * incoming INVITE!
700 */
701 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
702 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
703 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000704 const pjmedia_sdp_session *remote_sdp;
705
706 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
707 update_remote_nat_type(call, remote_sdp);
708 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000709
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000710 /* Create and attach pjsua_var data to the dialog: */
711 call->inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000712
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000713 dlg->mod_data[pjsua_var.mod.id] = call;
714 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000715
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000716 /* If account is locked to specific transport, then lock dialog
717 * to this transport too.
718 */
719 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
720 pjsip_tpselector tp_sel;
721
722 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
723 pjsip_dlg_set_transport(dlg, &tp_sel);
724 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000725
Benny Prijono64f851e2006-02-23 13:49:28 +0000726 /* Must answer with some response to initial INVITE.
Benny Prijono64f851e2006-02-23 13:49:28 +0000727 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000728 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000729 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000730 if (status != PJ_SUCCESS) {
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000731 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
732 status);
733
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000734 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
735 pjsip_inv_terminate(inv, 500, PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000736 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000737 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000738 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000739
740 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000741 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000742 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000743 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000744 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000745 }
746
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000747 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000748
Benny Prijono105217f2006-03-06 16:25:59 +0000749
Benny Prijono053f5222006-11-11 16:16:04 +0000750 /* Check if this request should replace existing call */
751 if (replaced_dlg) {
752 pjsip_inv_session *replaced_inv;
753 struct pjsua_call *replaced_call;
754 pjsip_tx_data *tdata;
755
756 /* Get the invite session in the dialog */
757 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
758
759 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000760 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000761
762 /* Notify application */
763 if (pjsua_var.ua_cfg.cb.on_call_replaced)
764 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
765 call_id);
766
767 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
768 call_id));
769
770 /* Answer the new call with 200 response */
771 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
772 if (status == PJ_SUCCESS)
773 status = pjsip_inv_send_msg(inv, tdata);
774
775 if (status != PJ_SUCCESS)
776 pjsua_perror(THIS_FILE, "Error answering session", status);
777
Benny Prijonofed7f4c2007-03-27 11:01:45 +0000778 /* Note that inv may be invalid if 200/OK has caused error in
779 * starting the media.
780 */
Benny Prijono053f5222006-11-11 16:16:04 +0000781
782 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
783 replaced_call->index));
784
785 /* Disconnect replaced invite session */
786 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
787 &tdata);
788 if (status == PJ_SUCCESS && tdata)
789 status = pjsip_inv_send_msg(replaced_inv, tdata);
790
791 if (status != PJ_SUCCESS)
792 pjsua_perror(THIS_FILE, "Error terminating session", status);
793
794
795 } else {
796
Benny Prijonob5388cf2007-01-04 22:45:08 +0000797 /* Notify application if on_incoming_call() is overriden,
798 * otherwise hangup the call with 480
799 */
800 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +0000801 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +0000802 } else {
803 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
804 NULL, NULL);
805 }
Benny Prijono053f5222006-11-11 16:16:04 +0000806 }
807
Benny Prijono8b1889b2006-06-06 18:40:40 +0000808
Benny Prijono26ff9062006-02-21 23:47:00 +0000809 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +0000810 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000811 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000812}
813
814
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000815
816/*
817 * Check if the specified call has active INVITE session and the INVITE
818 * session has not been disconnected.
819 */
820PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
821{
822 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
823 PJ_EINVAL);
824 return pjsua_var.calls[call_id].inv != NULL &&
825 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
826}
827
828
829/*
830 * Check if call has an active media session.
831 */
832PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
833{
834 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
835 PJ_EINVAL);
836 return pjsua_var.calls[call_id].session != NULL;
837}
838
839
Benny Prijono148c9dd2006-09-19 13:37:53 +0000840/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +0000841pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +0000842 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +0000843 pjsua_call **p_call,
844 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +0000845{
846 enum { MAX_RETRY=50 };
847 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +0000848 pjsua_call *call = NULL;
849 pj_bool_t has_pjsua_lock = PJ_FALSE;
850 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000851
852 for (retry=0; retry<MAX_RETRY; ++retry) {
853
854 has_pjsua_lock = PJ_FALSE;
855
856 status = PJSUA_TRY_LOCK();
857 if (status != PJ_SUCCESS) {
858 pj_thread_sleep(retry/10);
859 continue;
860 }
861
862 has_pjsua_lock = PJ_TRUE;
863 call = &pjsua_var.calls[call_id];
864
865 if (call->inv == NULL) {
866 PJSUA_UNLOCK();
867 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
868 return PJSIP_ESESSIONTERMINATED;
869 }
870
871 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
872 if (status != PJ_SUCCESS) {
873 PJSUA_UNLOCK();
874 pj_thread_sleep(retry/10);
875 continue;
876 }
877
878 PJSUA_UNLOCK();
879
880 break;
881 }
882
883 if (status != PJ_SUCCESS) {
884 if (has_pjsua_lock == PJ_FALSE)
885 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
886 "(possibly system has deadlocked) in %s",
887 title));
888 else
889 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
890 "(possibly system has deadlocked) in %s",
891 title));
892 return PJ_ETIMEDOUT;
893 }
894
895 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000896 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000897
898 return PJ_SUCCESS;
899}
900
901
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000902/*
903 * Get the conference port identification associated with the call.
904 */
905PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
906{
Benny Prijono148c9dd2006-09-19 13:37:53 +0000907 pjsua_call *call;
908 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000909 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000910 pj_status_t status;
911
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000912 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
913 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000914
Benny Prijonodc752ca2006-09-22 16:55:42 +0000915 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000916 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +0000917 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000918
919 port_id = call->conf_slot;
920
Benny Prijonodc752ca2006-09-22 16:55:42 +0000921 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000922
923 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000924}
925
926
Benny Prijono148c9dd2006-09-19 13:37:53 +0000927
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000928/*
929 * Obtain detail information about the specified call.
930 */
931PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
932 pjsua_call_info *info)
933{
934 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +0000935 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +0000936 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000937
938 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
939 PJ_EINVAL);
940
Benny Prijonoac623b32006-07-03 15:19:31 +0000941 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000942
Benny Prijonodc752ca2006-09-22 16:55:42 +0000943 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000944 if (status != PJ_SUCCESS) {
945 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000946 }
947
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000948 /* id and role */
949 info->id = call_id;
950 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +0000951 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000952
953 /* local info */
954 info->local_info.ptr = info->buf_.local_info;
955 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
956 sizeof(info->buf_.local_info));
957
958 /* local contact */
959 info->local_contact.ptr = info->buf_.local_contact;
960 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
961 call->inv->dlg->local.contact->uri,
962 info->local_contact.ptr,
963 sizeof(info->buf_.local_contact));
964
965 /* remote info */
966 info->remote_info.ptr = info->buf_.remote_info;
967 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
968 sizeof(info->buf_.remote_info));
969
970 /* remote contact */
971 if (call->inv->dlg->remote.contact) {
972 int len;
973 info->remote_contact.ptr = info->buf_.remote_contact;
974 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
975 call->inv->dlg->remote.contact->uri,
976 info->remote_contact.ptr,
977 sizeof(info->buf_.remote_contact));
978 if (len < 0) len = 0;
979 info->remote_contact.slen = len;
980 } else {
981 info->remote_contact.slen = 0;
982 }
983
984 /* call id */
985 info->call_id.ptr = info->buf_.call_id;
986 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
987 sizeof(info->buf_.call_id));
988
989 /* state, state_text */
990 info->state = call->inv->state;
991 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
992
993 /* If call is disconnected, set the last_status from the cause code */
994 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
995 /* last_status, last_status_text */
996 info->last_status = call->inv->cause;
997
998 info->last_status_text.ptr = info->buf_.last_status_text;
999 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1000 sizeof(info->buf_.last_status_text));
1001 } else {
1002 /* last_status, last_status_text */
1003 info->last_status = call->last_code;
1004
1005 info->last_status_text.ptr = info->buf_.last_status_text;
1006 pj_strncpy(&info->last_status_text, &call->last_text,
1007 sizeof(info->buf_.last_status_text));
1008 }
1009
1010 /* media status and dir */
1011 info->media_status = call->media_st;
1012 info->media_dir = call->media_dir;
1013
1014
1015 /* conference slot number */
1016 info->conf_slot = call->conf_slot;
1017
1018 /* calculate duration */
1019 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1020
1021 info->total_duration = call->dis_time;
1022 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1023
1024 if (call->conn_time.sec) {
1025 info->connect_duration = call->dis_time;
1026 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1027 }
1028
1029 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1030
1031 pj_gettimeofday(&info->total_duration);
1032 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1033
1034 pj_gettimeofday(&info->connect_duration);
1035 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1036
1037 } else {
1038 pj_gettimeofday(&info->total_duration);
1039 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1040 }
1041
Benny Prijonodc752ca2006-09-22 16:55:42 +00001042 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001043
1044 return PJ_SUCCESS;
1045}
1046
1047
1048/*
1049 * Attach application specific data to the call.
1050 */
1051PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1052 void *user_data)
1053{
1054 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1055 PJ_EINVAL);
1056 pjsua_var.calls[call_id].user_data = user_data;
1057
1058 return PJ_SUCCESS;
1059}
1060
1061
1062/*
1063 * Get user data attached to the call.
1064 */
1065PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1066{
1067 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1068 NULL);
1069 return pjsua_var.calls[call_id].user_data;
1070}
1071
1072
1073/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001074 * Get remote's NAT type.
1075 */
1076PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1077 pj_stun_nat_type *p_type)
1078{
1079 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1080 PJ_EINVAL);
1081 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1082
1083 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1084 return PJ_SUCCESS;
1085}
1086
1087
1088/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001089 * Send response to incoming INVITE request.
1090 */
1091PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1092 unsigned code,
1093 const pj_str_t *reason,
1094 const pjsua_msg_data *msg_data)
1095{
1096 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001097 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001098 pjsip_tx_data *tdata;
1099 pj_status_t status;
1100
1101 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1102 PJ_EINVAL);
1103
Benny Prijonodc752ca2006-09-22 16:55:42 +00001104 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001105 if (status != PJ_SUCCESS)
1106 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001107
Benny Prijono2e507c22006-06-23 15:04:11 +00001108 if (call->res_time.sec == 0)
1109 pj_gettimeofday(&call->res_time);
1110
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001111 if (reason && reason->slen == 0)
1112 reason = NULL;
1113
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001114 /* Create response message */
1115 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1116 if (status != PJ_SUCCESS) {
1117 pjsua_perror(THIS_FILE, "Error creating response",
1118 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001119 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001120 return status;
1121 }
1122
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001123 /* Call might have been disconnected if application is answering with
1124 * 200/OK and the media failed to start.
1125 */
1126 if (call->inv == NULL) {
1127 pjsip_dlg_dec_lock(dlg);
1128 return PJSIP_ESESSIONTERMINATED;
1129 }
1130
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001131 /* Add additional headers etc */
1132 pjsua_process_msg_data( tdata, msg_data);
1133
1134 /* Send the message */
1135 status = pjsip_inv_send_msg(call->inv, tdata);
1136 if (status != PJ_SUCCESS)
1137 pjsua_perror(THIS_FILE, "Error sending response",
1138 status);
1139
Benny Prijonodc752ca2006-09-22 16:55:42 +00001140 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001141
1142 return status;
1143}
1144
1145
1146/*
1147 * Hangup call by using method that is appropriate according to the
1148 * call state.
1149 */
1150PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1151 unsigned code,
1152 const pj_str_t *reason,
1153 const pjsua_msg_data *msg_data)
1154{
1155 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001156 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001157 pj_status_t status;
1158 pjsip_tx_data *tdata;
1159
1160
Benny Prijono148c9dd2006-09-19 13:37:53 +00001161 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1162 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1163 call_id));
1164 }
1165
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001166 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1167 PJ_EINVAL);
1168
Benny Prijonodc752ca2006-09-22 16:55:42 +00001169 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001170 if (status != PJ_SUCCESS)
1171 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001172
1173 if (code==0) {
1174 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1175 code = PJSIP_SC_OK;
1176 else if (call->inv->role == PJSIP_ROLE_UAS)
1177 code = PJSIP_SC_DECLINE;
1178 else
1179 code = PJSIP_SC_REQUEST_TERMINATED;
1180 }
1181
1182 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1183 if (status != PJ_SUCCESS) {
1184 pjsua_perror(THIS_FILE,
1185 "Failed to create end session message",
1186 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001187 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001188 return status;
1189 }
1190
1191 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1192 * as p_tdata when INVITE transaction has not been answered
1193 * with any provisional responses.
1194 */
1195 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001196 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001197 return PJ_SUCCESS;
1198 }
1199
1200 /* Add additional headers etc */
1201 pjsua_process_msg_data( tdata, msg_data);
1202
1203 /* Send the message */
1204 status = pjsip_inv_send_msg(call->inv, tdata);
1205 if (status != PJ_SUCCESS) {
1206 pjsua_perror(THIS_FILE,
1207 "Failed to send end session message",
1208 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001209 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001210 return status;
1211 }
1212
Benny Prijonodc752ca2006-09-22 16:55:42 +00001213 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001214
1215 return PJ_SUCCESS;
1216}
1217
1218
1219/*
1220 * Put the specified call on hold.
1221 */
1222PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1223 const pjsua_msg_data *msg_data)
1224{
1225 pjmedia_sdp_session *sdp;
1226 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001227 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001228 pjsip_tx_data *tdata;
1229 pj_status_t status;
1230
1231 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1232 PJ_EINVAL);
1233
Benny Prijonodc752ca2006-09-22 16:55:42 +00001234 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001235 if (status != PJ_SUCCESS)
1236 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001238
1239 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1240 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001241 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 return PJSIP_ESESSIONSTATE;
1243 }
1244
1245 status = create_inactive_sdp(call, &sdp);
1246 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001247 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 return status;
1249 }
1250
Benny Prijono7129cc72007-11-05 05:54:25 +00001251 update_sdp_version(call, sdp);
1252
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 /* Create re-INVITE with new offer */
1254 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1255 if (status != PJ_SUCCESS) {
1256 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001257 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258 return status;
1259 }
1260
1261 /* Add additional headers etc */
1262 pjsua_process_msg_data( tdata, msg_data);
1263
1264 /* Send the request */
1265 status = pjsip_inv_send_msg( call->inv, tdata);
1266 if (status != PJ_SUCCESS) {
1267 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001268 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 return status;
1270 }
1271
Benny Prijonodc752ca2006-09-22 16:55:42 +00001272 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001273
1274 return PJ_SUCCESS;
1275}
1276
1277
1278/*
1279 * Send re-INVITE (to release hold).
1280 */
1281PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1282 pj_bool_t unhold,
1283 const pjsua_msg_data *msg_data)
1284{
1285 pjmedia_sdp_session *sdp;
1286 pjsip_tx_data *tdata;
1287 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001288 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001289 pj_status_t status;
1290
1291
1292 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1293 PJ_EINVAL);
1294
Benny Prijonodc752ca2006-09-22 16:55:42 +00001295 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001296 if (status != PJ_SUCCESS)
1297 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001298
1299 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1300 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001301 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001302 return PJSIP_ESESSIONSTATE;
1303 }
1304
Benny Prijono667952e2007-04-02 19:27:54 +00001305 /* Init media channel */
1306 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1307 if (status != PJ_SUCCESS) {
1308 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1309 pjsip_dlg_dec_lock(dlg);
1310 return PJSIP_ESESSIONSTATE;
1311 }
1312
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001313 /* Create SDP */
Benny Prijono00cae612006-07-31 15:19:36 +00001314 PJ_UNUSED_ARG(unhold);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001315 PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001316 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001317 if (status != PJ_SUCCESS) {
1318 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1319 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001320 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001321 return status;
1322 }
1323
Benny Prijono7129cc72007-11-05 05:54:25 +00001324 update_sdp_version(call, sdp);
1325
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001326 /* Create re-INVITE with new offer */
1327 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1328 if (status != PJ_SUCCESS) {
1329 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001330 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001331 return status;
1332 }
1333
1334 /* Add additional headers etc */
1335 pjsua_process_msg_data( tdata, msg_data);
1336
1337 /* Send the request */
1338 status = pjsip_inv_send_msg( call->inv, tdata);
1339 if (status != PJ_SUCCESS) {
1340 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001341 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001342 return status;
1343 }
1344
Benny Prijonodc752ca2006-09-22 16:55:42 +00001345 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001346
1347 return PJ_SUCCESS;
1348}
1349
1350
1351/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001352 * Send UPDATE request.
1353 */
1354PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1355 unsigned options,
1356 const pjsua_msg_data *msg_data)
1357{
1358 pjmedia_sdp_session *sdp;
1359 pjsip_tx_data *tdata;
1360 pjsua_call *call;
1361 pjsip_dialog *dlg;
1362 pj_status_t status;
1363
1364 PJ_UNUSED_ARG(options);
1365
1366 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1367 PJ_EINVAL);
1368
1369 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1370 if (status != PJ_SUCCESS)
1371 return status;
1372
1373 /* Init media channel */
1374 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
1375 if (status != PJ_SUCCESS) {
1376 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
1377 pjsip_dlg_dec_lock(dlg);
1378 return PJSIP_ESESSIONSTATE;
1379 }
1380
1381 /* Create SDP */
1382 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
1383 if (status != PJ_SUCCESS) {
1384 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1385 status);
1386 pjsip_dlg_dec_lock(dlg);
1387 return status;
1388 }
1389
1390 /* Create re-INVITE with new offer */
1391 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1392 if (status != PJ_SUCCESS) {
1393 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1394 pjsip_dlg_dec_lock(dlg);
1395 return status;
1396 }
1397
1398 /* Add additional headers etc */
1399 pjsua_process_msg_data( tdata, msg_data);
1400
1401 /* Send the request */
1402 status = pjsip_inv_send_msg( call->inv, tdata);
1403 if (status != PJ_SUCCESS) {
1404 pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
1405 pjsip_dlg_dec_lock(dlg);
1406 return status;
1407 }
1408
1409 pjsip_dlg_dec_lock(dlg);
1410
1411 return PJ_SUCCESS;
1412}
1413
1414
1415/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001416 * Initiate call transfer to the specified address.
1417 */
1418PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1419 const pj_str_t *dest,
1420 const pjsua_msg_data *msg_data)
1421{
1422 pjsip_evsub *sub;
1423 pjsip_tx_data *tdata;
1424 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001425 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001426 pjsip_generic_string_hdr *gs_hdr;
1427 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001428 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001429 pj_status_t status;
1430
1431
1432 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1433 PJ_EINVAL);
1434
Benny Prijonodc752ca2006-09-22 16:55:42 +00001435 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001436 if (status != PJ_SUCCESS)
1437 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439
Benny Prijonod524e822006-09-22 12:48:18 +00001440 /* Create xfer client subscription. */
1441 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001442 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001443
1444 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445 if (status != PJ_SUCCESS) {
1446 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001447 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001448 return status;
1449 }
1450
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001451 /* Associate this call with the client subscription */
1452 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1453
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454 /*
1455 * Create REFER request.
1456 */
1457 status = pjsip_xfer_initiate(sub, dest, &tdata);
1458 if (status != PJ_SUCCESS) {
1459 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001460 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001461 return status;
1462 }
1463
Benny Prijono053f5222006-11-11 16:16:04 +00001464 /* Add Referred-By header */
1465 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1466 &dlg->local.info_str);
1467 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1468
1469
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470 /* Add additional headers etc */
1471 pjsua_process_msg_data( tdata, msg_data);
1472
1473 /* Send. */
1474 status = pjsip_xfer_send_request(sub, tdata);
1475 if (status != PJ_SUCCESS) {
1476 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001477 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 return status;
1479 }
1480
1481 /* For simplicity (that's what this program is intended to be!),
1482 * leave the original invite session as it is. More advanced application
1483 * may want to hold the INVITE, or terminate the invite, or whatever.
1484 */
1485
Benny Prijonodc752ca2006-09-22 16:55:42 +00001486 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487
1488 return PJ_SUCCESS;
1489
1490}
1491
1492
1493/*
Benny Prijono053f5222006-11-11 16:16:04 +00001494 * Initiate attended call transfer to the specified address.
1495 */
1496PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1497 pjsua_call_id dest_call_id,
1498 unsigned options,
1499 const pjsua_msg_data *msg_data)
1500{
1501 pjsua_call *dest_call;
1502 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001503 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001504 pj_str_t str_dest;
1505 int len;
1506 pjsip_uri *uri;
1507 pj_status_t status;
1508
1509
1510 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1511 PJ_EINVAL);
1512 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1513 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1514 PJ_EINVAL);
1515
1516 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1517 &dest_call, &dest_dlg);
1518 if (status != PJ_SUCCESS)
1519 return status;
1520
1521 /*
1522 * Create REFER destination URI with Replaces field.
1523 */
1524
1525 /* Make sure we have sufficient buffer's length */
1526 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1527 dest_dlg->call_id->id.slen +
1528 dest_dlg->remote.info->tag.slen +
1529 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001530 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001531
1532 /* Print URI */
1533 str_dest_buf[0] = '<';
1534 str_dest.slen = 1;
1535
Benny Prijonoa1e69682007-05-11 15:14:34 +00001536 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001537 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1538 str_dest_buf+1, sizeof(str_dest_buf)-1);
1539 if (len < 0)
1540 return PJSIP_EURITOOLONG;
1541
1542 str_dest.slen += len;
1543
1544
1545 /* Build the URI */
1546 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1547 sizeof(str_dest_buf) - str_dest.slen,
1548 "?%s"
1549 "Replaces=%.*s"
1550 "%%3Bto-tag%%3D%.*s"
1551 "%%3Bfrom-tag%%3D%.*s>",
1552 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1553 "" : "Require=replaces&"),
1554 (int)dest_dlg->call_id->id.slen,
1555 dest_dlg->call_id->id.ptr,
1556 (int)dest_dlg->remote.info->tag.slen,
1557 dest_dlg->remote.info->tag.ptr,
1558 (int)dest_dlg->local.info->tag.slen,
1559 dest_dlg->local.info->tag.ptr);
1560
1561 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1562 PJSIP_EURITOOLONG);
1563
1564 str_dest.ptr = str_dest_buf;
1565 str_dest.slen += len;
1566
1567 pjsip_dlg_dec_lock(dest_dlg);
1568
1569 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1570}
1571
1572
1573/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001574 * Send DTMF digits to remote using RFC 2833 payload formats.
1575 */
1576PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1577 const pj_str_t *digits)
1578{
1579 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001580 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001581 pj_status_t status;
1582
1583 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1584 PJ_EINVAL);
1585
Benny Prijonodc752ca2006-09-22 16:55:42 +00001586 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001587 if (status != PJ_SUCCESS)
1588 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590 if (!call->session) {
1591 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001592 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593 return PJ_EINVALIDOP;
1594 }
1595
1596 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1597
Benny Prijonodc752ca2006-09-22 16:55:42 +00001598 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001599
1600 return status;
1601}
1602
1603
1604/**
1605 * Send instant messaging inside INVITE session.
1606 */
1607PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1608 const pj_str_t *mime_type,
1609 const pj_str_t *content,
1610 const pjsua_msg_data *msg_data,
1611 void *user_data)
1612{
1613 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001614 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001615 const pj_str_t mime_text_plain = pj_str("text/plain");
1616 pjsip_media_type ctype;
1617 pjsua_im_data *im_data;
1618 pjsip_tx_data *tdata;
1619 pj_status_t status;
1620
1621
1622 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1623 PJ_EINVAL);
1624
Benny Prijonodc752ca2006-09-22 16:55:42 +00001625 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001626 if (status != PJ_SUCCESS)
1627 return status;
1628
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629 /* Set default media type if none is specified */
1630 if (mime_type == NULL) {
1631 mime_type = &mime_text_plain;
1632 }
1633
1634 /* Create request message. */
1635 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1636 -1, &tdata);
1637 if (status != PJ_SUCCESS) {
1638 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1639 goto on_return;
1640 }
1641
1642 /* Add accept header. */
1643 pjsip_msg_add_hdr( tdata->msg,
1644 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1645
1646 /* Parse MIME type */
1647 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1648
1649 /* Create "text/plain" message body. */
1650 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1651 &ctype.subtype, content);
1652 if (tdata->msg->body == NULL) {
1653 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1654 pjsip_tx_data_dec_ref(tdata);
1655 goto on_return;
1656 }
1657
1658 /* Add additional headers etc */
1659 pjsua_process_msg_data( tdata, msg_data);
1660
1661 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001662 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 im_data->acc_id = call->acc_id;
1664 im_data->call_id = call_id;
1665 im_data->to = call->inv->dlg->remote.info_str;
1666 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1667 im_data->user_data = user_data;
1668
1669
1670 /* Send the request. */
1671 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1672 pjsua_var.mod.id, im_data);
1673 if (status != PJ_SUCCESS) {
1674 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1675 goto on_return;
1676 }
1677
1678on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001679 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 return status;
1681}
1682
1683
1684/*
1685 * Send IM typing indication inside INVITE session.
1686 */
1687PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1688 pj_bool_t is_typing,
1689 const pjsua_msg_data*msg_data)
1690{
1691 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001692 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001693 pjsip_tx_data *tdata;
1694 pj_status_t status;
1695
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001696 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1697 PJ_EINVAL);
1698
Benny Prijonodc752ca2006-09-22 16:55:42 +00001699 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001700 if (status != PJ_SUCCESS)
1701 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001703 /* Create request message. */
1704 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1705 -1, &tdata);
1706 if (status != PJ_SUCCESS) {
1707 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1708 goto on_return;
1709 }
1710
1711 /* Create "application/im-iscomposing+xml" msg body. */
1712 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1713 NULL, NULL, -1);
1714
1715 /* Add additional headers etc */
1716 pjsua_process_msg_data( tdata, msg_data);
1717
1718 /* Send the request. */
1719 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1720 if (status != PJ_SUCCESS) {
1721 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1722 goto on_return;
1723 }
1724
1725on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001726 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727 return status;
1728}
1729
1730
1731/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001732 * Send arbitrary request.
1733 */
1734PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
1735 const pj_str_t *method_str,
1736 const pjsua_msg_data *msg_data)
1737{
1738 pjsua_call *call;
1739 pjsip_dialog *dlg;
1740 pjsip_method method;
1741 pjsip_tx_data *tdata;
1742 pj_status_t status;
1743
1744 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1745 PJ_EINVAL);
1746
1747 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
1748 if (status != PJ_SUCCESS)
1749 return status;
1750
1751 /* Init method */
1752 pjsip_method_init_np(&method, (pj_str_t*)method_str);
1753
1754 /* Create request message. */
1755 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
1756 if (status != PJ_SUCCESS) {
1757 pjsua_perror(THIS_FILE, "Unable to create request", status);
1758 goto on_return;
1759 }
1760
1761 /* Add additional headers etc */
1762 pjsua_process_msg_data( tdata, msg_data);
1763
1764 /* Send the request. */
1765 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
1766 if (status != PJ_SUCCESS) {
1767 pjsua_perror(THIS_FILE, "Unable to send request", status);
1768 goto on_return;
1769 }
1770
1771on_return:
1772 pjsip_dlg_dec_lock(dlg);
1773 return status;
1774}
1775
1776
1777/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001778 * Terminate all calls.
1779 */
1780PJ_DEF(void) pjsua_call_hangup_all(void)
1781{
1782 unsigned i;
1783
1784 PJSUA_LOCK();
1785
1786 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1787 if (pjsua_var.calls[i].inv)
1788 pjsua_call_hangup(i, 0, NULL, NULL);
1789 }
1790
1791 PJSUA_UNLOCK();
1792}
1793
1794
Benny Prijono627cbb42007-09-25 20:48:49 +00001795const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001796{
1797 if (val < 1000) {
1798 pj_ansi_sprintf(buf, "%d", val);
1799 } else if (val < 1000000) {
1800 pj_ansi_sprintf(buf, "%d.%dK",
1801 val / 1000,
1802 (val % 1000) / 100);
1803 } else {
1804 pj_ansi_sprintf(buf, "%d.%02dM",
1805 val / 1000000,
1806 (val % 1000000) / 10000);
1807 }
1808
1809 return buf;
1810}
1811
1812
1813/* Dump media session */
1814static void dump_media_session(const char *indent,
1815 char *buf, unsigned maxlen,
1816 pjmedia_session *session)
1817{
1818 unsigned i;
1819 char *p = buf, *end = buf+maxlen;
1820 int len;
1821 pjmedia_session_info info;
1822
1823 pjmedia_session_get_info(session, &info);
1824
1825 for (i=0; i<info.stream_cnt; ++i) {
1826 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00001827 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001829 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00001830 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00001831 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00001832 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001833
1834 pjmedia_session_get_stream_stat(session, i, &stat);
Benny Prijono5186eae2007-12-03 14:38:25 +00001835 rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
1836 rem_addr_buf, sizeof(rem_addr_buf), 3);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001837
1838 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
1839 dir = "sendonly";
1840 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
1841 dir = "recvonly";
1842 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
1843 dir = "sendrecv";
1844 else
1845 dir = "inactive";
1846
1847
1848 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00001849 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00001851 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001852 info.stream_info[i].fmt.encoding_name.ptr,
1853 info.stream_info[i].fmt.clock_rate / 1000,
1854 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00001855 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001856 if (len < 1 || len > end-p) {
1857 *p = '\0';
1858 return;
1859 }
1860
1861 p += len;
1862 *p++ = '\n';
1863 *p = '\0';
1864
1865 if (stat.rx.update_cnt == 0)
1866 strcpy(last_update, "never");
1867 else {
1868 pj_gettimeofday(&now);
1869 PJ_TIME_VAL_SUB(now, stat.rx.update);
1870 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1871 now.sec / 3600,
1872 (now.sec % 3600) / 60,
1873 now.sec % 60,
1874 now.msec);
1875 }
1876
Benny Prijono80019eb2006-08-07 13:22:23 +00001877 pj_gettimeofday(&media_duration);
1878 PJ_TIME_VAL_SUB(media_duration, stat.start);
1879 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
1880 media_duration.msec = 1;
1881
Benny Prijono1402a4a2008-01-08 23:41:22 +00001882 /* protect against division by zero */
1883 if (stat.rx.pkt == 0)
1884 stat.rx.pkt = 1;
1885 if (stat.tx.pkt == 0)
1886 stat.tx.pkt = 1;
1887
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001888 len = pj_ansi_snprintf(p, end-p,
1889 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001890 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001891 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1892 "%s (msec) min avg max last\n"
1893 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1894 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1895 indent, info.stream_info[i].fmt.pt,
1896 last_update,
1897 indent,
1898 good_number(packets, stat.rx.pkt),
1899 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001900 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00001901 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
1902 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001903 indent,
1904 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001905 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001906 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001907 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001908 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001909 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001910 indent, indent,
1911 stat.rx.loss_period.min / 1000.0,
1912 stat.rx.loss_period.avg / 1000.0,
1913 stat.rx.loss_period.max / 1000.0,
1914 stat.rx.loss_period.last / 1000.0,
1915 indent,
1916 stat.rx.jitter.min / 1000.0,
1917 stat.rx.jitter.avg / 1000.0,
1918 stat.rx.jitter.max / 1000.0,
1919 stat.rx.jitter.last / 1000.0,
1920 ""
1921 );
1922
1923 if (len < 1 || len > end-p) {
1924 *p = '\0';
1925 return;
1926 }
1927
1928 p += len;
1929 *p++ = '\n';
1930 *p = '\0';
1931
1932 if (stat.tx.update_cnt == 0)
1933 strcpy(last_update, "never");
1934 else {
1935 pj_gettimeofday(&now);
1936 PJ_TIME_VAL_SUB(now, stat.tx.update);
1937 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1938 now.sec / 3600,
1939 (now.sec % 3600) / 60,
1940 now.sec % 60,
1941 now.msec);
1942 }
1943
1944 len = pj_ansi_snprintf(p, end-p,
1945 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00001946 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001947 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
1948 "%s (msec) min avg max last\n"
1949 "%s loss period: %7.3f %7.3f %7.3f %7.3f\n"
1950 "%s jitter : %7.3f %7.3f %7.3f %7.3f%s",
1951 indent,
1952 info.stream_info[i].tx_pt,
1953 info.stream_info[i].param->info.frm_ptime *
1954 info.stream_info[i].param->setting.frm_per_pkt,
1955 last_update,
1956
1957 indent,
1958 good_number(packets, stat.tx.pkt),
1959 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00001960 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00001961 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
1962 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001963
1964 indent,
1965 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00001966 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001967 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00001968 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001969 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00001970 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971
1972 indent, indent,
1973 stat.tx.loss_period.min / 1000.0,
1974 stat.tx.loss_period.avg / 1000.0,
1975 stat.tx.loss_period.max / 1000.0,
1976 stat.tx.loss_period.last / 1000.0,
1977 indent,
1978 stat.tx.jitter.min / 1000.0,
1979 stat.tx.jitter.avg / 1000.0,
1980 stat.tx.jitter.max / 1000.0,
1981 stat.tx.jitter.last / 1000.0,
1982 ""
1983 );
1984
1985 if (len < 1 || len > end-p) {
1986 *p = '\0';
1987 return;
1988 }
1989
1990 p += len;
1991 *p++ = '\n';
1992 *p = '\0';
1993
1994 len = pj_ansi_snprintf(p, end-p,
1995 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f",
1996 indent,
1997 stat.rtt.min / 1000.0,
1998 stat.rtt.avg / 1000.0,
1999 stat.rtt.max / 1000.0,
2000 stat.rtt.last / 1000.0
2001 );
2002 if (len < 1 || len > end-p) {
2003 *p = '\0';
2004 return;
2005 }
2006
2007 p += len;
2008 *p++ = '\n';
2009 *p = '\0';
2010 }
2011}
2012
2013
2014/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002015void print_call(const char *title,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002016 int call_id,
2017 char *buf, pj_size_t size)
2018{
2019 int len;
2020 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2021 pjsip_dialog *dlg = inv->dlg;
2022 char userinfo[128];
2023
2024 /* Dump invite sesion info. */
2025
2026 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
2027 if (len < 1)
2028 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2029 else
2030 userinfo[len] = '\0';
2031
2032 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2033 title,
2034 pjsip_inv_state_name(inv->state),
2035 userinfo);
2036 if (len < 1 || len >= (int)size) {
2037 pj_ansi_strcpy(buf, "<--uri too long-->");
2038 len = 18;
2039 } else
2040 buf[len] = '\0';
2041}
2042
2043
2044/*
2045 * Dump call and media statistics to string.
2046 */
2047PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2048 pj_bool_t with_media,
2049 char *buffer,
2050 unsigned maxlen,
2051 const char *indent)
2052{
2053 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002054 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002055 pj_time_val duration, res_delay, con_delay;
2056 char tmp[128];
2057 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002058 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059 int len;
2060
2061 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2062 PJ_EINVAL);
2063
Benny Prijonodc752ca2006-09-22 16:55:42 +00002064 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002065 if (status != PJ_SUCCESS)
2066 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067
2068 *buffer = '\0';
2069 p = buffer;
2070 end = buffer + maxlen;
2071 len = 0;
2072
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002073 print_call(indent, call_id, tmp, sizeof(tmp));
2074
2075 len = pj_ansi_strlen(tmp);
2076 pj_ansi_strcpy(buffer, tmp);
2077
2078 p += len;
2079 *p++ = '\r';
2080 *p++ = '\n';
2081
2082 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002083 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084 pj_gettimeofday(&duration);
2085 PJ_TIME_VAL_SUB(duration, call->conn_time);
2086 con_delay = call->conn_time;
2087 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2088 } else {
2089 duration.sec = duration.msec = 0;
2090 con_delay.sec = con_delay.msec = 0;
2091 }
2092
2093 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002094 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002095 res_delay = call->res_time;
2096 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2097 } else {
2098 res_delay.sec = res_delay.msec = 0;
2099 }
2100
2101 /* Print duration */
2102 len = pj_ansi_snprintf(p, end-p,
2103 "%s Call time: %02dh:%02dm:%02ds, "
2104 "1st res in %d ms, conn in %dms",
2105 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002106 (int)(duration.sec / 3600),
2107 (int)((duration.sec % 3600)/60),
2108 (int)(duration.sec % 60),
2109 (int)PJ_TIME_VAL_MSEC(res_delay),
2110 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002111
2112 if (len > 0 && len < end-p) {
2113 p += len;
2114 *p++ = '\n';
2115 *p = '\0';
2116 }
2117
2118 /* Dump session statistics */
2119 if (with_media && call->session)
2120 dump_media_session(indent, p, end-p, call->session);
2121
Benny Prijonodc752ca2006-09-22 16:55:42 +00002122 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002123
2124 return PJ_SUCCESS;
2125}
2126
2127
2128/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002129 * This callback receives notification from invite session when the
2130 * session state has changed.
2131 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002132static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2133 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002134{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002135 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002136
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002137 PJSUA_LOCK();
2138
Benny Prijonoa1e69682007-05-11 15:14:34 +00002139 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002140
2141 if (!call) {
2142 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002143 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002144 }
2145
Benny Prijonoe21e7842006-04-09 16:46:05 +00002146
2147 /* Get call times */
2148 switch (inv->state) {
2149 case PJSIP_INV_STATE_EARLY:
2150 case PJSIP_INV_STATE_CONNECTING:
2151 if (call->res_time.sec == 0)
2152 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002153 call->last_code = (pjsip_status_code)
2154 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002155 pj_strncpy(&call->last_text,
2156 &e->body.tsx_state.tsx->status_text,
2157 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002158 break;
2159 case PJSIP_INV_STATE_CONFIRMED:
2160 pj_gettimeofday(&call->conn_time);
2161 break;
2162 case PJSIP_INV_STATE_DISCONNECTED:
2163 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002164 if (call->res_time.sec == 0)
2165 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +00002166 if (e->body.tsx_state.tsx->status_code > call->last_code) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002167 call->last_code = (pjsip_status_code)
2168 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 pj_strncpy(&call->last_text,
2170 &e->body.tsx_state.tsx->status_text,
2171 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002172 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002173 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002174 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002175 call->last_code = (pjsip_status_code)
2176 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177 pj_strncpy(&call->last_text,
2178 &e->body.tsx_state.tsx->status_text,
2179 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002180 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002181 }
2182
Benny Prijono26ff9062006-02-21 23:47:00 +00002183 /* If this is an outgoing INVITE that was created because of
2184 * REFER/transfer, send NOTIFY to transferer.
2185 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002186 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002187 int st_code = -1;
2188 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2189
2190
Benny Prijonoa91a0032006-02-26 21:23:45 +00002191 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002192 case PJSIP_INV_STATE_NULL:
2193 case PJSIP_INV_STATE_CALLING:
2194 /* Do nothing */
2195 break;
2196
2197 case PJSIP_INV_STATE_EARLY:
2198 case PJSIP_INV_STATE_CONNECTING:
2199 st_code = e->body.tsx_state.tsx->status_code;
2200 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2201 break;
2202
2203 case PJSIP_INV_STATE_CONFIRMED:
2204 /* When state is confirmed, send the final 200/OK and terminate
2205 * subscription.
2206 */
2207 st_code = e->body.tsx_state.tsx->status_code;
2208 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2209 break;
2210
2211 case PJSIP_INV_STATE_DISCONNECTED:
2212 st_code = e->body.tsx_state.tsx->status_code;
2213 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2214 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002215
Benny Prijono8b1889b2006-06-06 18:40:40 +00002216 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002217 /* Nothing to do. Just to keep gcc from complaining about
2218 * unused enums.
2219 */
2220 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002221 }
2222
2223 if (st_code != -1) {
2224 pjsip_tx_data *tdata;
2225 pj_status_t status;
2226
Benny Prijonoa91a0032006-02-26 21:23:45 +00002227 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002228 ev_state, st_code,
2229 NULL, &tdata);
2230 if (status != PJ_SUCCESS) {
2231 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2232 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002233 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002234 if (status != PJ_SUCCESS) {
2235 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2236 }
2237 }
2238 }
2239 }
2240
Benny Prijono84126ab2006-02-09 09:30:09 +00002241
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242 if (pjsua_var.ua_cfg.cb.on_call_state)
2243 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002244
2245 /* call->inv may be NULL now */
2246
Benny Prijono84126ab2006-02-09 09:30:09 +00002247 /* Destroy media session when invite session is disconnected. */
2248 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002249
Benny Prijonoa91a0032006-02-26 21:23:45 +00002250 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002251
Benny Prijono275fd682006-03-22 11:59:11 +00002252 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00002253 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00002254
Benny Prijono105217f2006-03-06 16:25:59 +00002255 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002256 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002257 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00002258
2259 /* Reset call */
2260 reset_call(call->index);
2261
Benny Prijono84126ab2006-02-09 09:30:09 +00002262 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002263
2264 PJSUA_UNLOCK();
2265}
2266
2267/*
2268 * This callback is called by invite session framework when UAC session
2269 * has forked.
2270 */
2271static void pjsua_call_on_forked( pjsip_inv_session *inv,
2272 pjsip_event *e)
2273{
2274 PJ_UNUSED_ARG(inv);
2275 PJ_UNUSED_ARG(e);
2276
2277 PJ_TODO(HANDLE_FORKED_DIALOG);
2278}
2279
2280
2281/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00002282 * Disconnect call upon error.
2283 */
2284static void call_disconnect( pjsip_inv_session *inv,
2285 int code )
2286{
2287 pjsip_tx_data *tdata;
2288 pj_status_t status;
2289
2290 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
2291 if (status == PJ_SUCCESS)
2292 pjsip_inv_send_msg(inv, tdata);
2293}
2294
2295/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002296 * Callback to be called when SDP offer/answer negotiation has just completed
2297 * in the session. This function will start/update media if negotiation
2298 * has succeeded.
2299 */
2300static void pjsua_call_on_media_update(pjsip_inv_session *inv,
2301 pj_status_t status)
2302{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002303 pjsua_call *call;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00002304 const pjmedia_sdp_session *local_sdp;
2305 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002306
2307 PJSUA_LOCK();
2308
Benny Prijonoa1e69682007-05-11 15:14:34 +00002309 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002310
2311 if (status != PJ_SUCCESS) {
2312
2313 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
2314
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002315 /* Stop/destroy media, if any */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002316 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +00002317
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002318 /* Disconnect call if we're not in the middle of initializing an
2319 * UAS dialog and if this is not a re-INVITE
2320 */
2321 if (inv->state != PJSIP_INV_STATE_NULL &&
2322 inv->state != PJSIP_INV_STATE_CONFIRMED)
2323 {
2324 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2325 }
2326
2327 PJSUA_UNLOCK();
2328 return;
2329 }
2330
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002331
2332 /* Get local and remote SDP */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002333 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
2334 if (status != PJ_SUCCESS) {
2335 pjsua_perror(THIS_FILE,
2336 "Unable to retrieve currently active local SDP",
2337 status);
2338 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2339 PJSUA_UNLOCK();
2340 return;
2341 }
2342
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002343 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
2344 if (status != PJ_SUCCESS) {
2345 pjsua_perror(THIS_FILE,
2346 "Unable to retrieve currently active remote SDP",
2347 status);
2348 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
2349 PJSUA_UNLOCK();
2350 return;
2351 }
2352
Benny Prijono91a6a172007-10-31 08:59:29 +00002353 /* Update remote's NAT type */
2354 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2355 update_remote_nat_type(call, remote_sdp);
2356 }
2357
2358 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00002359 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002360 if (status != PJ_SUCCESS) {
2361 pjsua_perror(THIS_FILE, "Unable to create media session",
2362 status);
Benny Prijonoa38ada02006-07-02 14:22:35 +00002363 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002364 pjsua_media_channel_deinit(call->index);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002365 PJSUA_UNLOCK();
2366 return;
2367 }
2368
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002369
2370 /* Call application callback, if any */
2371 if (pjsua_var.ua_cfg.cb.on_call_media_state)
2372 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
2373
2374
2375 PJSUA_UNLOCK();
2376}
2377
2378
2379/*
2380 * Create inactive SDP for call hold.
2381 */
2382static pj_status_t create_inactive_sdp(pjsua_call *call,
2383 pjmedia_sdp_session **p_answer)
2384{
2385 pj_status_t status;
2386 pjmedia_sdp_conn *conn;
2387 pjmedia_sdp_attr *attr;
Benny Prijono617c5bc2007-04-02 19:51:21 +00002388 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002389 pjmedia_sdp_session *sdp;
2390
Benny Prijono617c5bc2007-04-02 19:51:21 +00002391 /* Get media socket info */
2392 pjmedia_transport_get_info(call->med_tp, &skinfo);
2393
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002394 /* Create new offer */
2395 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +00002396 &skinfo, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002397 if (status != PJ_SUCCESS) {
2398 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2399 return status;
2400 }
2401
2402 /* Get SDP media connection line */
2403 conn = sdp->media[0]->conn;
2404 if (!conn)
2405 conn = sdp->conn;
2406
2407 /* Modify address */
2408 conn->addr = pj_str("0.0.0.0");
2409
2410 /* Remove existing directions attributes */
2411 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
2412 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
2413 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
2414 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
2415
2416 /* Add inactive attribute */
2417 attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
2418 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
2419
2420 *p_answer = sdp;
2421
2422 return status;
2423}
2424
2425
2426/*
2427 * Called when session received new offer.
2428 */
2429static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
2430 const pjmedia_sdp_session *offer)
2431{
2432 const char *remote_state;
2433 pjsua_call *call;
2434 pjmedia_sdp_conn *conn;
2435 pjmedia_sdp_session *answer;
2436 pj_bool_t is_remote_active;
2437 pj_status_t status;
2438
2439 PJSUA_LOCK();
2440
Benny Prijonoa1e69682007-05-11 15:14:34 +00002441 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002442
2443 /*
2444 * See if remote is offering active media (i.e. not on-hold)
2445 */
2446 is_remote_active = PJ_TRUE;
2447
2448 conn = offer->media[0]->conn;
2449 if (!conn)
2450 conn = offer->conn;
2451
2452 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
2453 pj_strcmp2(&conn->addr, "0")==0)
2454 {
2455 is_remote_active = PJ_FALSE;
2456
2457 }
Benny Prijono8eb07bf2007-11-08 09:39:06 +00002458 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
2459 pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002460 {
2461 is_remote_active = PJ_FALSE;
2462 }
2463
2464 remote_state = (is_remote_active ? "active" : "inactive");
2465
2466 /* Supply candidate answer */
2467 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
2468 PJ_LOG(4,(THIS_FILE,
2469 "Call %d: RX new media offer, creating inactive SDP "
2470 "(media in offer is %s)", call->index, remote_state));
2471 status = create_inactive_sdp( call, &answer );
2472 } else {
Benny Prijono667952e2007-04-02 19:27:54 +00002473
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002474 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
2475 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00002476
2477 /* Init media channel */
Benny Prijonoc9f6ea72007-05-23 07:12:23 +00002478 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
Benny Prijono667952e2007-04-02 19:27:54 +00002479 if (status != PJ_SUCCESS) {
2480 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2481 PJSUA_UNLOCK();
2482 return;
2483 }
2484
Benny Prijonoc97608e2007-03-23 16:34:20 +00002485 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002486 }
2487
2488 if (status != PJ_SUCCESS) {
2489 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2490 PJSUA_UNLOCK();
2491 return;
2492 }
2493
2494 status = pjsip_inv_set_sdp_answer(call->inv, answer);
2495 if (status != PJ_SUCCESS) {
2496 pjsua_perror(THIS_FILE, "Unable to set answer", status);
2497 PJSUA_UNLOCK();
2498 return;
2499 }
2500
2501 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00002502}
2503
2504
2505/*
Benny Prijono77998ce2007-06-20 10:03:46 +00002506 * Called to generate new offer.
2507 */
2508static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
2509 pjmedia_sdp_session **offer)
2510{
2511 pjsua_call *call;
2512 pj_status_t status;
2513
2514 PJSUA_LOCK();
2515
2516 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
2517
2518 /* See if we've put call on hold. */
2519 if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
2520 PJ_LOG(4,(THIS_FILE,
2521 "Call %d: call is on-hold locally, creating inactive SDP ",
2522 call->index));
2523 status = create_inactive_sdp( call, offer );
2524 } else {
2525
2526 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
2527 call->index));
2528
2529 /* Init media channel */
2530 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
2531 if (status != PJ_SUCCESS) {
2532 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
2533 PJSUA_UNLOCK();
2534 return;
2535 }
2536
2537 status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, offer);
2538 }
2539
2540 if (status != PJ_SUCCESS) {
2541 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
2542 PJSUA_UNLOCK();
2543 return;
2544 }
2545
Benny Prijono7129cc72007-11-05 05:54:25 +00002546 update_sdp_version(call, *offer);
Benny Prijono77998ce2007-06-20 10:03:46 +00002547
2548 PJSUA_UNLOCK();
2549}
2550
2551
2552/*
Benny Prijono26ff9062006-02-21 23:47:00 +00002553 * Callback called by event framework when the xfer subscription state
2554 * has changed.
2555 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002556static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
2557{
2558
2559 PJ_UNUSED_ARG(event);
2560
2561 /*
2562 * When subscription is accepted (got 200/OK to REFER), check if
2563 * subscription suppressed.
2564 */
2565 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
2566
2567 pjsip_rx_data *rdata;
2568 pjsip_generic_string_hdr *refer_sub;
2569 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
2570 pjsua_call *call;
2571
Benny Prijonoa1e69682007-05-11 15:14:34 +00002572 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002573
2574 /* Must be receipt of response message */
2575 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
2576 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
2577 rdata = event->body.tsx_state.src.rdata;
2578
2579 /* Find Refer-Sub header */
2580 refer_sub = (pjsip_generic_string_hdr*)
2581 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
2582 &REFER_SUB, NULL);
2583
2584 /* Check if subscription is suppressed */
2585 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
2586 /* Since no subscription is desired, assume that call has been
2587 * transfered successfully.
2588 */
2589 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2590 const pj_str_t ACCEPTED = { "Accepted", 8 };
2591 pj_bool_t cont = PJ_FALSE;
2592 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2593 200,
2594 &ACCEPTED,
2595 PJ_TRUE,
2596 &cont);
2597 }
2598
2599 /* Yes, subscription is suppressed.
2600 * Terminate our subscription now.
2601 */
2602 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
2603 "event subcription..."));
2604 pjsip_evsub_terminate(sub, PJ_TRUE);
2605
2606 } else {
2607 /* Notify application about call transfer progress.
2608 * Initially notify with 100/Accepted status.
2609 */
2610 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2611 const pj_str_t ACCEPTED = { "Accepted", 8 };
2612 pj_bool_t cont = PJ_FALSE;
2613 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2614 100,
2615 &ACCEPTED,
2616 PJ_FALSE,
2617 &cont);
2618 }
2619 }
2620 }
2621 /*
2622 * On incoming NOTIFY, notify application about call transfer progress.
2623 */
2624 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
2625 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
2626 {
2627 pjsua_call *call;
2628 pjsip_msg *msg;
2629 pjsip_msg_body *body;
2630 pjsip_status_line status_line;
2631 pj_bool_t is_last;
2632 pj_bool_t cont;
2633 pj_status_t status;
2634
Benny Prijonoa1e69682007-05-11 15:14:34 +00002635 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002636
2637 /* When subscription is terminated, clear the xfer_sub member of
2638 * the inv_data.
2639 */
2640 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
2641 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2642 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
2643
2644 }
2645
2646 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
2647 /* Application is not interested with call progress status */
2648 return;
2649 }
2650
2651 /* This better be a NOTIFY request */
2652 if (event->type == PJSIP_EVENT_TSX_STATE &&
2653 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
2654 {
2655 pjsip_rx_data *rdata;
2656
2657 rdata = event->body.tsx_state.src.rdata;
2658
2659 /* Check if there's body */
2660 msg = rdata->msg_info.msg;
2661 body = msg->body;
2662 if (!body) {
2663 PJ_LOG(4,(THIS_FILE,
2664 "Warning: received NOTIFY without message body"));
2665 return;
2666 }
2667
2668 /* Check for appropriate content */
2669 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
2670 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
2671 {
2672 PJ_LOG(4,(THIS_FILE,
2673 "Warning: received NOTIFY with non message/sipfrag "
2674 "content"));
2675 return;
2676 }
2677
2678 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002679 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002680 &status_line);
2681 if (status != PJ_SUCCESS) {
2682 PJ_LOG(4,(THIS_FILE,
2683 "Warning: received NOTIFY with invalid "
2684 "message/sipfrag content"));
2685 return;
2686 }
2687
2688 } else {
2689 status_line.code = 500;
2690 status_line.reason = *pjsip_get_status_text(500);
2691 }
2692
2693 /* Notify application */
2694 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
2695 cont = !is_last;
2696 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
2697 status_line.code,
2698 &status_line.reason,
2699 is_last, &cont);
2700
2701 if (!cont) {
2702 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
2703 }
2704 }
2705}
2706
2707
2708/*
2709 * Callback called by event framework when the xfer subscription state
2710 * has changed.
2711 */
2712static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00002713{
2714
2715 PJ_UNUSED_ARG(event);
2716
2717 /*
Benny Prijonod524e822006-09-22 12:48:18 +00002718 * When subscription is terminated, clear the xfer_sub member of
2719 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00002720 */
2721 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002722 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002723
Benny Prijonoa1e69682007-05-11 15:14:34 +00002724 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002725 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00002726 return;
2727
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002728 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002729 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00002730
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002731 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00002732 }
2733}
2734
2735
2736/*
2737 * Follow transfer (REFER) request.
2738 */
2739static void on_call_transfered( pjsip_inv_session *inv,
2740 pjsip_rx_data *rdata )
2741{
2742 pj_status_t status;
2743 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00002744 pjsua_call *existing_call;
2745 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002746 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00002747 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00002748 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00002749 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002750 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00002751 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002752 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002753 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00002754 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002755 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002756 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00002757 pjsip_evsub *sub;
2758
Benny Prijonoa1e69682007-05-11 15:14:34 +00002759 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00002760
Benny Prijono26ff9062006-02-21 23:47:00 +00002761 /* Find the Refer-To header */
2762 refer_to = (pjsip_generic_string_hdr*)
2763 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
2764
2765 if (refer_to == NULL) {
2766 /* Invalid Request.
2767 * No Refer-To header!
2768 */
2769 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00002770 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00002771 return;
2772 }
2773
Benny Prijonoc8141a82006-08-20 09:12:19 +00002774 /* Find optional Refer-Sub header */
2775 refer_sub = (pjsip_generic_string_hdr*)
2776 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
2777
2778 if (refer_sub) {
2779 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
2780 no_refer_sub = PJ_TRUE;
2781 }
2782
Benny Prijono053f5222006-11-11 16:16:04 +00002783 /* Find optional Referred-By header (to be copied onto outgoing INVITE
2784 * request.
2785 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002786 ref_by_hdr = (pjsip_hdr*)
2787 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00002788 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00002789
Benny Prijono9fc735d2006-05-28 14:58:12 +00002790 /* Notify callback */
2791 code = PJSIP_SC_OK;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002792 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
2793 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
2794 &refer_to->hvalue,
2795 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00002796
2797 if (code < 200)
Benny Prijonoba5926a2007-05-02 11:29:37 +00002798 code = PJSIP_SC_OK;
Benny Prijono9fc735d2006-05-28 14:58:12 +00002799 if (code >= 300) {
2800 /* Application rejects call transfer request */
2801 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
2802 return;
2803 }
2804
Benny Prijono26ff9062006-02-21 23:47:00 +00002805 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
2806 (int)inv->dlg->remote.info_str.slen,
2807 inv->dlg->remote.info_str.ptr,
2808 (int)refer_to->hvalue.slen,
2809 refer_to->hvalue.ptr));
2810
Benny Prijonoc8141a82006-08-20 09:12:19 +00002811 if (no_refer_sub) {
2812 /*
2813 * Always answer with 200.
2814 */
2815 pjsip_tx_data *tdata;
2816 const pj_str_t str_false = { "false", 5};
2817 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00002818
Benny Prijonoc8141a82006-08-20 09:12:19 +00002819 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
2820 if (status != PJ_SUCCESS) {
2821 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2822 status);
2823 return;
2824 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002825
Benny Prijonoc8141a82006-08-20 09:12:19 +00002826 /* Add Refer-Sub header */
2827 hdr = (pjsip_hdr*)
2828 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
2829 &str_false);
2830 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00002831
Benny Prijono26ff9062006-02-21 23:47:00 +00002832
Benny Prijonoc8141a82006-08-20 09:12:19 +00002833 /* Send answer */
2834 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
2835 tdata);
2836 if (status != PJ_SUCCESS) {
2837 pjsua_perror(THIS_FILE, "Unable to create 200 response to REFER",
2838 status);
2839 return;
2840 }
2841
2842 /* Don't have subscription */
2843 sub = NULL;
2844
2845 } else {
2846 struct pjsip_evsub_user xfer_cb;
2847 pjsip_hdr hdr_list;
2848
2849 /* Init callback */
2850 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002851 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00002852
2853 /* Init additional header list to be sent with REFER response */
2854 pj_list_init(&hdr_list);
2855
2856 /* Create transferee event subscription */
2857 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
2858 if (status != PJ_SUCCESS) {
2859 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
2860 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
2861 return;
2862 }
2863
2864 /* If there's Refer-Sub header and the value is "true", send back
2865 * Refer-Sub in the response with value "true" too.
2866 */
2867 if (refer_sub) {
2868 const pj_str_t str_true = { "true", 4 };
2869 pjsip_hdr *hdr;
2870
2871 hdr = (pjsip_hdr*)
2872 pjsip_generic_string_hdr_create(inv->dlg->pool,
2873 &str_refer_sub,
2874 &str_true);
2875 pj_list_push_back(&hdr_list, hdr);
2876
2877 }
2878
2879 /* Accept the REFER request, send 200 (OK). */
2880 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
2881
2882 /* Create initial NOTIFY request */
2883 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
2884 100, NULL, &tdata);
2885 if (status != PJ_SUCCESS) {
2886 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2887 status);
2888 return;
2889 }
2890
2891 /* Send initial NOTIFY request */
2892 status = pjsip_xfer_send_request( sub, tdata);
2893 if (status != PJ_SUCCESS) {
2894 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
2895 return;
2896 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002897 }
2898
2899 /* We're cheating here.
2900 * We need to get a null terminated string from a pj_str_t.
2901 * So grab the pointer from the hvalue and NULL terminate it, knowing
2902 * that the NULL position will be occupied by a newline.
2903 */
2904 uri = refer_to->hvalue.ptr;
2905 uri[refer_to->hvalue.slen] = '\0';
2906
Benny Prijono053f5222006-11-11 16:16:04 +00002907 /* Init msg_data */
2908 pjsua_msg_data_init(&msg_data);
2909
2910 /* If Referred-By header is present in the REFER request, copy this
2911 * to the outgoing INVITE request.
2912 */
2913 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002914 pjsip_hdr *dup = (pjsip_hdr*)
2915 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00002916 pj_list_push_back(&msg_data.hdr_list, dup);
2917 }
2918
Benny Prijono26ff9062006-02-21 23:47:00 +00002919 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00002920 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002921 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00002922 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002923 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00002924 if (status != PJ_SUCCESS) {
2925
Benny Prijonoc8141a82006-08-20 09:12:19 +00002926 /* Notify xferer about the error (if we have subscription) */
2927 if (sub) {
2928 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
2929 500, NULL, &tdata);
2930 if (status != PJ_SUCCESS) {
2931 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
2932 status);
2933 return;
2934 }
2935 status = pjsip_xfer_send_request(sub, tdata);
2936 if (status != PJ_SUCCESS) {
2937 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
2938 status);
2939 return;
2940 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002941 }
2942 return;
2943 }
2944
Benny Prijonoc8141a82006-08-20 09:12:19 +00002945 if (sub) {
2946 /* Put the server subscription in inv_data.
2947 * Subsequent state changed in pjsua_inv_on_state_changed() will be
2948 * reported back to the server subscription.
2949 */
2950 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00002951
Benny Prijonoc8141a82006-08-20 09:12:19 +00002952 /* Put the invite_data in the subscription. */
2953 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
2954 &pjsua_var.calls[new_call]);
2955 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002956}
2957
2958
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002959
Benny Prijono26ff9062006-02-21 23:47:00 +00002960/*
2961 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00002962 * session. We use this to trap:
2963 * - incoming REFER request.
2964 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00002965 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002966static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
2967 pjsip_transaction *tsx,
2968 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00002969{
Benny Prijonoa1e69682007-05-11 15:14:34 +00002970 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002971
2972 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00002973
Benny Prijonofeb69f42007-10-05 09:12:26 +00002974 /* Notify application callback first */
2975 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
2976 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
2977 }
2978
Benny Prijono26ff9062006-02-21 23:47:00 +00002979 if (tsx->role==PJSIP_ROLE_UAS &&
2980 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00002981 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00002982 {
2983 /*
2984 * Incoming REFER request.
2985 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002986 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00002987
Benny Prijono26ff9062006-02-21 23:47:00 +00002988 }
Benny Prijonob0808372006-03-02 21:18:58 +00002989 else if (tsx->role==PJSIP_ROLE_UAS &&
2990 tsx->state==PJSIP_TSX_STATE_TRYING &&
2991 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
2992 {
2993 /*
2994 * Incoming MESSAGE request!
2995 */
2996 pjsip_rx_data *rdata;
2997 pjsip_msg *msg;
2998 pjsip_accept_hdr *accept_hdr;
2999 pj_status_t status;
3000
3001 rdata = e->body.tsx_state.src.rdata;
3002 msg = rdata->msg_info.msg;
3003
3004 /* Request MUST have message body, with Content-Type equal to
3005 * "text/plain".
3006 */
3007 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3008
3009 pjsip_hdr hdr_list;
3010
3011 pj_list_init(&hdr_list);
3012 pj_list_push_back(&hdr_list, accept_hdr);
3013
3014 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3015 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003016 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003017 return;
3018 }
3019
3020 /* Respond with 200 first, so that remote doesn't retransmit in case
3021 * the UI takes too long to process the message.
3022 */
3023 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3024
3025 /* Process MESSAGE request */
3026 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3027 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003028
Benny Prijonob0808372006-03-02 21:18:58 +00003029 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003030 else if (tsx->role == PJSIP_ROLE_UAC &&
3031 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003032 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003033 /* Handle outgoing pager status */
3034 if (tsx->status_code >= 200) {
3035 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003036
Benny Prijonoa1e69682007-05-11 15:14:34 +00003037 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003038 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003039
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003040 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3041 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3042 &im_data->to,
3043 &im_data->body,
3044 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003045 (pjsip_status_code)
3046 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003047 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003048 }
Benny Prijonofccab712006-02-22 22:23:22 +00003049 }
Benny Prijono834aee32006-02-19 01:38:06 +00003050 }
Benny Prijono834aee32006-02-19 01:38:06 +00003051
Benny Prijono26ff9062006-02-21 23:47:00 +00003052
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003053 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003054}