blob: 8870b87e849fcbe2b1cedffb361a54fa261a4074 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono268ca612006-02-07 12:34:11 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
Benny Prijono268ca612006-02-07 12:34:11 +000022
Benny Prijono268ca612006-02-07 12:34:11 +000023
Benny Prijono84126ab2006-02-09 09:30:09 +000024#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000025
26
Benny Prijonobb995fd2009-08-12 11:03:23 +000027/* Internal prototypes */
28static void resolve_stun_entry(pjsua_stun_resolve *sess);
29
30
Benny Prijonoeebe9af2006-06-13 22:57:13 +000031/* PJSUA application instance. */
32struct pjsua_data pjsua_var;
Benny Prijono84126ab2006-02-09 09:30:09 +000033
34
Benny Prijono44e88ea2007-10-30 15:42:35 +000035PJ_DEF(struct pjsua_data*) pjsua_get_var(void)
36{
37 return &pjsua_var;
38}
39
40
Benny Prijonoeebe9af2006-06-13 22:57:13 +000041/* Display error */
42PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
43 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000044{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000045 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000046
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonod6e362a2008-07-19 17:53:47 +000048 PJ_LOG(1,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000049}
50
51
Benny Prijonoeebe9af2006-06-13 22:57:13 +000052static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000053{
54 unsigned i;
55
Benny Prijonoc97608e2007-03-23 16:34:20 +000056 pj_bzero(&pjsua_var, sizeof(pjsua_var));
57
Benny Prijonoeebe9af2006-06-13 22:57:13 +000058 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
59 pjsua_var.acc[i].index = i;
60
61 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
62 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000063
64 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000065 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonobb995fd2009-08-12 11:03:23 +000066 pj_list_init(&pjsua_var.stun_res);
Benny Prijonod64c1502009-08-17 10:42:55 +000067
68 pjsua_config_default(&pjsua_var.ua_cfg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000069}
Benny Prijonodc39fe82006-05-26 12:17:46 +000070
71
Benny Prijono1f61a8f2007-08-16 10:11:44 +000072PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
73{
74 pj_bzero(cfg, sizeof(*cfg));
75
76 cfg->msg_logging = PJ_TRUE;
77 cfg->level = 5;
78 cfg->console_level = 4;
79 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
Benny Prijonod6e362a2008-07-19 17:53:47 +000080 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE |
81 PJ_LOG_HAS_SPACE;
82#if defined(PJ_WIN32) && PJ_WIN32 != 0
83 cfg->decor |= PJ_LOG_HAS_COLOR;
84#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +000085}
86
87PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
88 pjsua_logging_config *dst,
89 const pjsua_logging_config *src)
90{
91 pj_memcpy(dst, src, sizeof(*src));
92 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
93}
94
95PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
96{
97 pj_bzero(cfg, sizeof(*cfg));
98
Benny Prijono6627f3e2009-02-10 11:08:54 +000099 cfg->max_calls = ((PJSUA_MAX_CALLS) < 4) ? (PJSUA_MAX_CALLS) : 4;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000100 cfg->thread_cnt = 1;
Benny Prijono91a6a172007-10-31 08:59:29 +0000101 cfg->nat_type_in_sdp = 1;
Benny Prijonobb995fd2009-08-12 11:03:23 +0000102 cfg->stun_ignore_failure = PJ_TRUE;
Benny Prijono91d06b62008-09-20 12:16:56 +0000103 cfg->force_lr = PJ_TRUE;
Benny Prijonofe1bd342009-11-20 23:33:07 +0000104 cfg->enable_unsolicited_mwi = PJ_TRUE;
Benny Prijonod8179652008-01-23 20:39:07 +0000105#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
106 cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
107 cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING;
108#endif
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000109 cfg->hangup_forked_call = PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000110
Nanang Izzuddin65add622009-08-11 16:26:20 +0000111 pjsip_timer_setting_default(&cfg->timer_setting);
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000112}
113
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000114PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
115 pjsua_config *dst,
116 const pjsua_config *src)
117{
118 unsigned i;
119
120 pj_memcpy(dst, src, sizeof(*src));
121
122 for (i=0; i<src->outbound_proxy_cnt; ++i) {
123 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
124 &src->outbound_proxy[i]);
125 }
126
127 for (i=0; i<src->cred_count; ++i) {
128 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
129 }
130
131 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
132 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
133 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
Benny Prijonobb995fd2009-08-12 11:03:23 +0000134
135 for (i=0; i<src->stun_srv_cnt; ++i) {
136 pj_strdup_with_null(pool, &dst->stun_srv[i], &src->stun_srv[i]);
137 }
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000138}
139
140PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
141{
142 pj_bzero(msg_data, sizeof(*msg_data));
143 pj_list_init(&msg_data->hdr_list);
144}
145
146PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
147{
148 pj_bzero(cfg, sizeof(*cfg));
149 pjsip_tls_setting_default(&cfg->tls_setting);
150}
151
152PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
153 pjsua_transport_config *dst,
154 const pjsua_transport_config *src)
155{
156 PJ_UNUSED_ARG(pool);
157 pj_memcpy(dst, src, sizeof(*src));
158}
159
160PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
161{
162 pj_bzero(cfg, sizeof(*cfg));
163
164 cfg->reg_timeout = PJSUA_REG_INTERVAL;
Benny Prijono384dab42009-10-14 01:58:04 +0000165 cfg->unreg_timeout = PJSUA_UNREG_TIMEOUT;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000166 pjsip_publishc_opt_default(&cfg->publish_opt);
Benny Prijono534a9ba2009-10-13 14:01:59 +0000167 cfg->unpublish_max_wait_time_msec = PJSUA_UNPUBLISH_MAX_WAIT_TIME_MSEC;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000168 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijonoe8554ef2008-03-22 09:33:52 +0000169 cfg->allow_contact_rewrite = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000170 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000171 cfg->require_timer = pjsua_var.ua_cfg.require_timer;
Nanang Izzuddin65add622009-08-11 16:26:20 +0000172 cfg->timer_setting = pjsua_var.ua_cfg.timer_setting;
Benny Prijonobddef2c2007-10-31 13:28:08 +0000173 cfg->ka_interval = 15;
174 cfg->ka_data = pj_str("\r\n");
Benny Prijonod8179652008-01-23 20:39:07 +0000175#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
176 cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
177 cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
178#endif
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000179}
180
181PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
182{
183 pj_bzero(cfg, sizeof(*cfg));
184}
185
186PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
187{
188 pj_bzero(cfg, sizeof(*cfg));
189
190 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000191 cfg->snd_clock_rate = 0;
Benny Prijono7d60d052008-03-29 12:24:20 +0000192 cfg->channel_count = 1;
Benny Prijono37c710b2008-01-10 12:09:26 +0000193 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME;
194 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000195 cfg->has_ioqueue = PJ_TRUE;
196 cfg->thread_cnt = 1;
197 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
198 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
199 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
Benny Prijono10454dc2009-02-21 14:21:59 +0000200 cfg->snd_rec_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
201 cfg->snd_play_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000202 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Benny Prijonof798e502009-03-09 13:08:16 +0000203 cfg->snd_auto_close_time = 1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000204
Benny Prijono329d6382009-05-29 13:04:03 +0000205 cfg->ice_max_host_cands = -1;
206 pj_ice_sess_options_default(&cfg->ice_opt);
207
Benny Prijonof76e1392008-06-06 14:51:48 +0000208 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000209}
210
Benny Prijonodc39fe82006-05-26 12:17:46 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212/*****************************************************************************
213 * This is a very simple PJSIP module, whose sole purpose is to display
214 * incoming and outgoing messages to log. This module will have priority
215 * higher than transport layer, which means:
216 *
217 * - incoming messages will come to this module first before reaching
218 * transaction layer.
219 *
220 * - outgoing messages will come to this module last, after the message
221 * has been 'printed' to contiguous buffer by transport layer and
222 * appropriate transport instance has been decided for this message.
223 *
224 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000225
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226/* Notification on incoming messages */
227static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
228{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000229 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
230 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 "--end msg--",
232 rdata->msg_info.len,
233 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000234 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235 rdata->pkt_info.src_name,
236 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000237 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 rdata->msg_info.msg_buf));
239
240 /* Always return false, otherwise messages will not get processed! */
241 return PJ_FALSE;
242}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000243
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244/* Notification on outgoing messages */
245static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
246{
247
248 /* Important note:
249 * tp_info field is only valid after outgoing messages has passed
250 * transport layer. So don't try to access tp_info when the module
251 * has lower priority than transport layer.
252 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000253
Benny Prijonob4a17c92006-07-10 14:40:21 +0000254 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
255 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000256 "--end msg--",
257 (tdata->buf.cur - tdata->buf.start),
258 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000259 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000260 tdata->tp_info.dst_name,
261 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000262 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000263 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000264
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000265 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000266 return PJ_SUCCESS;
267}
268
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000269/* The module instance. */
270static pjsip_module pjsua_msg_logger =
271{
272 NULL, NULL, /* prev, next. */
273 { "mod-pjsua-log", 13 }, /* Name. */
274 -1, /* Id */
275 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
276 NULL, /* load() */
277 NULL, /* start() */
278 NULL, /* stop() */
279 NULL, /* unload() */
280 &logging_on_rx_msg, /* on_rx_request() */
281 &logging_on_rx_msg, /* on_rx_response() */
282 &logging_on_tx_msg, /* on_tx_request. */
283 &logging_on_tx_msg, /* on_tx_response() */
284 NULL, /* on_tsx_state() */
285
286};
287
288
289/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000290 * Another simple module to handle incoming OPTIONS request
291 */
292
293/* Notification on incoming request */
294static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
295{
296 pjsip_tx_data *tdata;
297 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000298 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000299 pjmedia_sdp_session *sdp;
300 const pjsip_hdr *cap_hdr;
301 pj_status_t status;
302
303 /* Only want to handle OPTIONS requests */
304 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000305 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000306 {
307 return PJ_FALSE;
308 }
309
Benny Prijono384dab42009-10-14 01:58:04 +0000310 /* Don't want to handle if shutdown is in progress */
311 if (pjsua_var.thread_quit_flag) {
312 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
313 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
314 NULL, NULL);
315 return PJ_TRUE;
316 }
317
Benny Prijono56315612006-07-18 14:39:40 +0000318 /* Create basic response. */
319 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
320 &tdata);
321 if (status != PJ_SUCCESS) {
322 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
323 return PJ_TRUE;
324 }
325
326 /* Add Allow header */
327 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
328 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000329 pjsip_msg_add_hdr(tdata->msg,
330 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000331 }
332
333 /* Add Accept header */
334 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
335 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000336 pjsip_msg_add_hdr(tdata->msg,
337 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000338 }
339
340 /* Add Supported header */
341 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
342 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000343 pjsip_msg_add_hdr(tdata->msg,
344 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000345 }
346
347 /* Add Allow-Events header from the evsub module */
348 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
349 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000350 pjsip_msg_add_hdr(tdata->msg,
351 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000352 }
353
354 /* Add User-Agent header */
355 if (pjsua_var.ua_cfg.user_agent.slen) {
356 const pj_str_t USER_AGENT = { "User-Agent", 10};
357 pjsip_hdr *h;
358
359 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
360 &USER_AGENT,
361 &pjsua_var.ua_cfg.user_agent);
362 pjsip_msg_add_hdr(tdata->msg, h);
363 }
364
Benny Prijonoa248b952009-08-12 22:28:47 +0000365 /* Get media socket info, make sure transport is ready */
366 if (pjsua_var.calls[0].med_tp) {
367 pjmedia_transport_info_init(&tpinfo);
368 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000369
Benny Prijonoa248b952009-08-12 22:28:47 +0000370 /* Add SDP body, using call0's RTP address */
371 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
372 &tpinfo.sock_info, &sdp);
373 if (status == PJ_SUCCESS) {
374 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
375 }
Benny Prijono56315612006-07-18 14:39:40 +0000376 }
377
378 /* Send response statelessly */
379 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
380 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
381 if (status != PJ_SUCCESS)
382 pjsip_tx_data_dec_ref(tdata);
383
384 return PJ_TRUE;
385}
386
387
388/* The module instance. */
389static pjsip_module pjsua_options_handler =
390{
391 NULL, NULL, /* prev, next. */
392 { "mod-pjsua-options", 17 }, /* Name. */
393 -1, /* Id */
394 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
395 NULL, /* load() */
396 NULL, /* start() */
397 NULL, /* stop() */
398 NULL, /* unload() */
399 &options_on_rx_request, /* on_rx_request() */
400 NULL, /* on_rx_response() */
401 NULL, /* on_tx_request. */
402 NULL, /* on_tx_response() */
403 NULL, /* on_tsx_state() */
404
405};
406
407
408/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000409 * These two functions are the main callbacks registered to PJSIP stack
410 * to receive SIP request and response messages that are outside any
411 * dialogs and any transactions.
412 */
Benny Prijono268ca612006-02-07 12:34:11 +0000413
414/*
415 * Handler for receiving incoming requests.
416 *
417 * This handler serves multiple purposes:
418 * - it receives requests outside dialogs.
419 * - it receives requests inside dialogs, when the requests are
420 * unhandled by other dialog usages. Example of these
421 * requests are: MESSAGE.
422 */
423static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
424{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 pj_bool_t processed = PJ_FALSE;
426
427 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000428
Benny Prijono84126ab2006-02-09 09:30:09 +0000429 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000430
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000432 }
433
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 PJSUA_UNLOCK();
435
436 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000437}
438
439
440/*
441 * Handler for receiving incoming responses.
442 *
443 * This handler serves multiple purposes:
444 * - it receives strayed responses (i.e. outside any dialog and
445 * outside any transactions).
446 * - it receives responses coming to a transaction, when pjsua
447 * module is set as transaction user for the transaction.
448 * - it receives responses inside a dialog, when these responses
449 * are unhandled by other dialog usages.
450 */
451static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
452{
453 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000454 return PJ_FALSE;
455}
456
457
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000458/*****************************************************************************
459 * Logging.
460 */
461
462/* Log callback */
463static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000464{
Benny Prijono572d4852006-11-23 21:50:02 +0000465 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466
467 if (pjsua_var.log_file) {
468 pj_ssize_t size = len;
469 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000470 /* This will slow things down considerably! Don't do it!
471 pj_file_flush(pjsua_var.log_file);
472 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000473 }
474
Benny Prijono572d4852006-11-23 21:50:02 +0000475 if (level <= (int)pjsua_var.log_cfg.console_level) {
476 if (pjsua_var.log_cfg.cb)
477 (*pjsua_var.log_cfg.cb)(level, buffer, len);
478 else
479 pj_log_write(level, buffer, len);
480 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481}
482
483
484/*
485 * Application can call this function at any time (after pjsua_create(), of
486 * course) to change logging settings.
487 */
488PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
489{
490 pj_status_t status;
491
492 /* Save config. */
493 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
494
495 /* Redirect log function to ours */
496 pj_log_set_log_func( &log_writer );
497
Benny Prijono9cb09a22007-08-30 09:35:10 +0000498 /* Set decor */
499 pj_log_set_decor(pjsua_var.log_cfg.decor);
500
Benny Prijono4190cf92008-01-18 13:25:05 +0000501 /* Set log level */
502 pj_log_set_level(pjsua_var.log_cfg.level);
503
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 /* Close existing file, if any */
505 if (pjsua_var.log_file) {
506 pj_file_close(pjsua_var.log_file);
507 pjsua_var.log_file = NULL;
508 }
509
510 /* If output log file is desired, create the file: */
511 if (pjsua_var.log_cfg.log_filename.slen) {
Benny Prijonodbe3f4b2009-05-07 16:56:04 +0000512 unsigned flags = PJ_O_WRONLY;
513 flags |= pjsua_var.log_cfg.log_file_flags;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000514 status = pj_file_open(pjsua_var.pool,
515 pjsua_var.log_cfg.log_filename.ptr,
Benny Prijonodbe3f4b2009-05-07 16:56:04 +0000516 flags,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000517 &pjsua_var.log_file);
518
519 if (status != PJ_SUCCESS) {
520 pjsua_perror(THIS_FILE, "Error creating log file", status);
521 return status;
522 }
523 }
524
525 /* Unregister msg logging if it's previously registered */
526 if (pjsua_msg_logger.id >= 0) {
527 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
528 pjsua_msg_logger.id = -1;
529 }
530
531 /* Enable SIP message logging */
532 if (pjsua_var.log_cfg.msg_logging)
533 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
534
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 return PJ_SUCCESS;
536}
537
538
539/*****************************************************************************
540 * PJSUA Base API.
541 */
542
543/* Worker thread function. */
544static int worker_thread(void *arg)
545{
546 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000547
Benny Prijono268ca612006-02-07 12:34:11 +0000548 PJ_UNUSED_ARG(arg);
549
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000550 while (!pjsua_var.thread_quit_flag) {
551 int count;
552
553 count = pjsua_handle_events(TIMEOUT);
554 if (count < 0)
555 pj_thread_sleep(TIMEOUT);
556 }
Benny Prijono268ca612006-02-07 12:34:11 +0000557
558 return 0;
559}
560
Benny Prijonodc39fe82006-05-26 12:17:46 +0000561
Benny Prijono8389c312008-02-21 21:36:34 +0000562/* Init random seed */
563static void init_random_seed(void)
564{
565 pj_sockaddr addr;
566 const pj_str_t *hostname;
567 pj_uint32_t pid;
568 pj_time_val t;
569 unsigned seed=0;
570
571 /* Add hostname */
572 hostname = pj_gethostname();
573 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
574
575 /* Add primary IP address */
576 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
577 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
578
579 /* Get timeofday */
580 pj_gettimeofday(&t);
581 seed = pj_hash_calc(seed, &t, sizeof(t));
582
583 /* Add PID */
584 pid = pj_getpid();
585 seed = pj_hash_calc(seed, &pid, sizeof(pid));
586
587 /* Init random seed */
588 pj_srand(seed);
589}
590
Benny Prijono268ca612006-02-07 12:34:11 +0000591/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000592 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000593 */
594PJ_DEF(pj_status_t) pjsua_create(void)
595{
596 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000597
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000598 /* Init pjsua data */
599 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000600
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 /* Set default logging settings */
602 pjsua_logging_config_default(&pjsua_var.log_cfg);
603
604 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000605 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000606 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
607
Benny Prijono8389c312008-02-21 21:36:34 +0000608 /* Init random seed */
609 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000610
Benny Prijonofccab712006-02-22 22:23:22 +0000611 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000612 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000613 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000614
Benny Prijonoec921342007-03-24 13:00:30 +0000615 /* Init PJNATH */
616 status = pjnath_init();
617 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000618
Benny Prijono094d3ad2006-11-21 08:41:00 +0000619 /* Set default sound device ID */
Benny Prijono96e74f32009-02-22 12:00:12 +0000620 pjsua_var.cap_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
621 pjsua_var.play_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
Benny Prijono094d3ad2006-11-21 08:41:00 +0000622
Benny Prijono268ca612006-02-07 12:34:11 +0000623 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000624 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000625
626 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000627 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000628
629 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000630
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 /* Create mutex */
632 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
633 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000634 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000635 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000636 return status;
637 }
638
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000639 /* Must create SIP endpoint to initialize SIP parser. The parser
640 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000641 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000642 status = pjsip_endpt_create(&pjsua_var.cp.factory,
643 pj_gethostname()->ptr,
644 &pjsua_var.endpt);
645 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000646
647
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000648 return PJ_SUCCESS;
649}
650
651
652/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000653 * Initialize pjsua with the specified settings. All the settings are
654 * optional, and the default values will be used when the config is not
655 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000656 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
658 const pjsua_logging_config *log_cfg,
659 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000660{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000661 pjsua_config default_cfg;
662 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000663 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000664 pjsip_ua_init_param ua_init_param;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000665 pj_status_t status;
666
667
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000668 /* Create default configurations when the config is not supplied */
669
670 if (ua_cfg == NULL) {
671 pjsua_config_default(&default_cfg);
672 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000673 }
674
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000675 if (media_cfg == NULL) {
676 pjsua_media_config_default(&default_media_cfg);
677 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000678 }
679
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680 /* Initialize logging first so that info/errors can be captured */
681 if (log_cfg) {
682 status = pjsua_reconfigure_logging(log_cfg);
Benny Prijono329d6382009-05-29 13:04:03 +0000683 if (status != PJ_SUCCESS)
684 return status;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000685 }
686
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000687 /* If nameserver is configured, create DNS resolver instance and
688 * set it to be used by SIP resolver.
689 */
690 if (ua_cfg->nameserver_count) {
691#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000692 unsigned i;
693
694 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000695 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
696 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000697 if (status != PJ_SUCCESS) {
698 pjsua_perror(THIS_FILE, "Error creating resolver", status);
699 return status;
700 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000701
702 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000703 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
704 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000705 ua_cfg->nameserver, NULL);
706 if (status != PJ_SUCCESS) {
707 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
708 return status;
709 }
710
711 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000712 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000713 if (status != PJ_SUCCESS) {
714 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
715 return status;
716 }
717
718 /* Print nameservers */
719 for (i=0; i<ua_cfg->nameserver_count; ++i) {
720 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
721 (int)ua_cfg->nameserver[i].slen,
722 ua_cfg->nameserver[i].ptr));
723 }
724#else
725 PJ_LOG(2,(THIS_FILE,
726 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
727#endif
728 }
729
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000730 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000731
732 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000733 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
734 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000735
Benny Prijonodc39fe82006-05-26 12:17:46 +0000736
737 /* Initialize UA layer module: */
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000738 pj_bzero(&ua_init_param, sizeof(ua_init_param));
739 if (ua_cfg->hangup_forked_call) {
740 ua_init_param.on_dlg_forked = &on_dlg_forked;
741 }
742 status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000743 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000744
Benny Prijonodc39fe82006-05-26 12:17:46 +0000745
Benny Prijono053f5222006-11-11 16:16:04 +0000746 /* Initialize Replaces support. */
747 status = pjsip_replaces_init_module( pjsua_var.endpt );
748 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
749
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000750 /* Initialize 100rel support */
751 status = pjsip_100rel_init_module(pjsua_var.endpt);
752 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000753
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000754 /* Initialize session timer support */
755 status = pjsip_timer_init_module(pjsua_var.endpt);
756 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
757
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000758 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000759 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000760 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000761 {
762 NULL, NULL, /* prev, next. */
763 { "mod-pjsua", 9 }, /* Name. */
764 -1, /* Id */
765 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
766 NULL, /* load() */
767 NULL, /* start() */
768 NULL, /* stop() */
769 NULL, /* unload() */
770 &mod_pjsua_on_rx_request, /* on_rx_request() */
771 &mod_pjsua_on_rx_response, /* on_rx_response() */
772 NULL, /* on_tx_request. */
773 NULL, /* on_tx_response() */
774 NULL, /* on_tsx_state() */
775 };
776
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000778
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000779 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
780 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000781 }
782
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000783
Benny Prijonodc39fe82006-05-26 12:17:46 +0000784
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785 /* Initialize PJSUA call subsystem: */
786 status = pjsua_call_subsys_init(ua_cfg);
787 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000788 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000789
Benny Prijonobb995fd2009-08-12 11:03:23 +0000790 /* Convert deprecated STUN settings */
791 if (pjsua_var.ua_cfg.stun_srv_cnt==0) {
792 if (pjsua_var.ua_cfg.stun_domain.slen) {
793 pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
794 pjsua_var.ua_cfg.stun_domain;
795 }
796 if (pjsua_var.ua_cfg.stun_host.slen) {
797 pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
798 pjsua_var.ua_cfg.stun_host;
799 }
800 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000801
Benny Prijonoc97608e2007-03-23 16:34:20 +0000802 /* Start resolving STUN server */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000803 status = resolve_stun_server(PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000804 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
805 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
806 return status;
807 }
808
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000809 /* Initialize PJSUA media subsystem */
810 status = pjsua_media_subsys_init(media_cfg);
811 if (status != PJ_SUCCESS)
812 goto on_error;
813
Benny Prijonodc39fe82006-05-26 12:17:46 +0000814
815 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000816 status = pjsip_evsub_init_module(pjsua_var.endpt);
817 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000818
Benny Prijonodc39fe82006-05-26 12:17:46 +0000819
820 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000821 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
822 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000823
Benny Prijono4dd961b2009-10-26 11:21:37 +0000824 /* Initialize MWI support */
825 status = pjsip_mwi_init_module(pjsua_var.endpt, pjsip_evsub_instance());
826
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000827 /* Init PUBLISH module */
828 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000829
830 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000831 status = pjsip_xfer_init_module( pjsua_var.endpt );
832 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000833
834 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000835 status = pjsua_pres_init();
836 if (status != PJ_SUCCESS)
837 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000838
839 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000840 status = pjsua_im_init();
841 if (status != PJ_SUCCESS)
842 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000843
Benny Prijonoc8141a82006-08-20 09:12:19 +0000844 /* Register OPTIONS handler */
845 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
846
847 /* Add OPTIONS in Allow header */
848 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
849 NULL, 1, &STR_OPTIONS);
850
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000851 /* Start worker thread if needed. */
852 if (pjsua_var.ua_cfg.thread_cnt) {
853 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000854
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000855 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
856 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000857
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000858 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
859 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
860 NULL, 0, 0, &pjsua_var.thread[i]);
861 if (status != PJ_SUCCESS)
862 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000863 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000864 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
865 pjsua_var.ua_cfg.thread_cnt));
866 } else {
867 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000868 }
869
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000870 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000871
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000872 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000873 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000874
Benny Prijono268ca612006-02-07 12:34:11 +0000875 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000876
877on_error:
878 pjsua_destroy();
879 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000880}
881
882
Benny Prijono834aee32006-02-19 01:38:06 +0000883/* Sleep with polling */
884static void busy_sleep(unsigned msec)
885{
886 pj_time_val timeout, now;
887
888 pj_gettimeofday(&timeout);
889 timeout.msec += msec;
890 pj_time_val_normalize(&timeout);
891
892 do {
Nanang Izzuddin90b83202009-03-02 15:48:45 +0000893 int i;
894 i = msec / 10;
895 while (pjsua_handle_events(10) > 0 && i > 0)
896 --i;
Benny Prijono834aee32006-02-19 01:38:06 +0000897 pj_gettimeofday(&now);
898 } while (PJ_TIME_VAL_LT(now, timeout));
899}
900
Benny Prijonobb995fd2009-08-12 11:03:23 +0000901/* Internal function to destroy STUN resolution session
902 * (pj_stun_resolve).
Benny Prijonoebbf6892007-03-24 17:37:25 +0000903 */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000904static void destroy_stun_resolve(pjsua_stun_resolve *sess)
Benny Prijonoebbf6892007-03-24 17:37:25 +0000905{
Benny Prijonobb995fd2009-08-12 11:03:23 +0000906 PJSUA_LOCK();
907 pj_list_erase(sess);
908 PJSUA_UNLOCK();
Benny Prijonoebbf6892007-03-24 17:37:25 +0000909
Benny Prijonobb995fd2009-08-12 11:03:23 +0000910 pj_assert(sess->stun_sock==NULL);
911 pj_pool_release(sess->pool);
912}
Benny Prijonoebbf6892007-03-24 17:37:25 +0000913
Benny Prijonobb995fd2009-08-12 11:03:23 +0000914/* This is the internal function to be called when STUN resolution
915 * session (pj_stun_resolve) has completed.
916 */
917static void stun_resolve_complete(pjsua_stun_resolve *sess)
918{
919 pj_stun_resolve_result result;
920
921 pj_bzero(&result, sizeof(result));
922 result.token = sess->token;
923 result.status = sess->status;
924 result.name = sess->srv[sess->idx];
925 pj_memcpy(&result.addr, &sess->addr, sizeof(result.addr));
926
927 if (result.status == PJ_SUCCESS) {
928 char addr[PJ_INET6_ADDRSTRLEN+10];
929 pj_sockaddr_print(&result.addr, addr, sizeof(addr), 3);
930 PJ_LOG(4,(THIS_FILE,
931 "STUN resolution success, using %.*s, address is %s",
932 (int)sess->srv[sess->idx].slen,
933 sess->srv[sess->idx].ptr,
934 addr));
935 } else {
936 char errmsg[PJ_ERR_MSG_SIZE];
937 pj_strerror(result.status, errmsg, sizeof(errmsg));
938 PJ_LOG(1,(THIS_FILE, "STUN resolution failed: %s", errmsg));
939 }
940
941 sess->cb(&result);
942
943 if (!sess->blocking) {
944 destroy_stun_resolve(sess);
945 }
946}
947
948/* This is the callback called by the STUN socket (pj_stun_sock)
949 * to report it's state. We use this as part of testing the
950 * STUN server.
951 */
952static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
953 pj_stun_sock_op op,
954 pj_status_t status)
955{
956 pjsua_stun_resolve *sess;
957
Nanang Izzuddinae1c6152009-08-17 16:30:04 +0000958 sess = (pjsua_stun_resolve*) pj_stun_sock_get_user_data(stun_sock);
Benny Prijonobb995fd2009-08-12 11:03:23 +0000959 pj_assert(stun_sock == sess->stun_sock);
960
Benny Prijonoebbf6892007-03-24 17:37:25 +0000961 if (status != PJ_SUCCESS) {
Benny Prijonobb995fd2009-08-12 11:03:23 +0000962 char errmsg[PJ_ERR_MSG_SIZE];
963 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000964
Benny Prijonobb995fd2009-08-12 11:03:23 +0000965 PJ_LOG(4,(THIS_FILE, "STUN resolution for %.*s failed: %s",
966 (int)sess->srv[sess->idx].slen,
967 sess->srv[sess->idx].ptr, errmsg));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000968
Benny Prijonobb995fd2009-08-12 11:03:23 +0000969 sess->status = status;
970
971 pj_stun_sock_destroy(stun_sock);
972 sess->stun_sock = NULL;
973
974 ++sess->idx;
975 resolve_stun_entry(sess);
976
977 return PJ_FALSE;
978
979 } else if (op == PJ_STUN_SOCK_BINDING_OP) {
980 pj_stun_sock_info ssi;
981
982 pj_stun_sock_get_info(stun_sock, &ssi);
983 pj_memcpy(&sess->addr, &ssi.srv_addr, sizeof(sess->addr));
984
985 sess->status = PJ_SUCCESS;
986 pj_stun_sock_destroy(stun_sock);
987 sess->stun_sock = NULL;
988
989 stun_resolve_complete(sess);
990
991 return PJ_FALSE;
992
993 } else
994 return PJ_TRUE;
995
996}
997
998/* This is an internal function to resolve and test current
999 * server entry in pj_stun_resolve session. It is called by
1000 * pjsua_resolve_stun_servers() and test_stun_on_status() above
1001 */
1002static void resolve_stun_entry(pjsua_stun_resolve *sess)
1003{
1004 /* Loop while we have entry to try */
1005 for (; sess->idx < sess->count; ++sess->idx) {
1006 const int af = pj_AF_INET();
1007 pj_str_t hostpart;
1008 pj_uint16_t port;
1009 pj_stun_sock_cb stun_sock_cb;
1010
1011 pj_assert(sess->idx < sess->count);
1012
1013 /* Parse the server entry into host:port */
1014 sess->status = pj_sockaddr_parse2(af, 0, &sess->srv[sess->idx],
1015 &hostpart, &port, NULL);
1016 if (sess->status != PJ_SUCCESS) {
1017 PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %.*s",
1018 (int)sess->srv[sess->idx].slen,
1019 sess->srv[sess->idx].ptr));
1020 continue;
Benny Prijonoebbf6892007-03-24 17:37:25 +00001021 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001022
1023 /* Use default port if not specified */
1024 if (port == 0)
1025 port = PJ_STUN_PORT;
1026
1027 pj_assert(sess->stun_sock == NULL);
1028
1029 PJ_LOG(4,(THIS_FILE, "Trying STUN server %.*s (%d of %d)..",
1030 (int)sess->srv[sess->idx].slen,
1031 sess->srv[sess->idx].ptr,
1032 sess->idx+1, sess->count));
1033
1034 /* Use STUN_sock to test this entry */
1035 pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
1036 stun_sock_cb.on_status = &test_stun_on_status;
1037 sess->status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
1038 pj_AF_INET(), &stun_sock_cb,
1039 NULL, sess, &sess->stun_sock);
1040 if (sess->status != PJ_SUCCESS) {
1041 char errmsg[PJ_ERR_MSG_SIZE];
1042 pj_strerror(sess->status, errmsg, sizeof(errmsg));
1043 PJ_LOG(4,(THIS_FILE,
1044 "Error creating STUN socket for %.*s: %s",
1045 (int)sess->srv[sess->idx].slen,
1046 sess->srv[sess->idx].ptr, errmsg));
1047
1048 continue;
1049 }
1050
1051 sess->status = pj_stun_sock_start(sess->stun_sock, &hostpart,
1052 port, pjsua_var.resolver);
1053 if (sess->status != PJ_SUCCESS) {
1054 char errmsg[PJ_ERR_MSG_SIZE];
1055 pj_strerror(sess->status, errmsg, sizeof(errmsg));
1056 PJ_LOG(4,(THIS_FILE,
1057 "Error starting STUN socket for %.*s: %s",
1058 (int)sess->srv[sess->idx].slen,
1059 sess->srv[sess->idx].ptr, errmsg));
1060
1061 pj_stun_sock_destroy(sess->stun_sock);
1062 sess->stun_sock = NULL;
1063 continue;
1064 }
1065
1066 /* Done for now, testing will resume/complete asynchronously in
1067 * stun_sock_cb()
1068 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001069 return;
1070 }
1071
Benny Prijonobb995fd2009-08-12 11:03:23 +00001072 if (sess->idx >= sess->count) {
1073 /* No more entries to try */
1074 PJ_ASSERT_ON_FAIL(sess->status != PJ_SUCCESS,
1075 sess->status = PJ_EUNKNOWN);
1076 stun_resolve_complete(sess);
1077 }
1078}
Benny Prijonoebbf6892007-03-24 17:37:25 +00001079
Benny Prijonoebbf6892007-03-24 17:37:25 +00001080
Benny Prijonobb995fd2009-08-12 11:03:23 +00001081/*
1082 * Resolve STUN server.
1083 */
1084PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count,
1085 pj_str_t srv[],
1086 pj_bool_t wait,
1087 void *token,
1088 pj_stun_resolve_cb cb)
1089{
1090 pj_pool_t *pool;
1091 pjsua_stun_resolve *sess;
1092 pj_status_t status;
1093 unsigned i;
1094
1095 PJ_ASSERT_RETURN(count && srv && cb, PJ_EINVAL);
1096
1097 pool = pjsua_pool_create("stunres", 256, 256);
1098 if (!pool)
1099 return PJ_ENOMEM;
1100
1101 sess = PJ_POOL_ZALLOC_T(pool, pjsua_stun_resolve);
1102 sess->pool = pool;
1103 sess->token = token;
1104 sess->cb = cb;
1105 sess->count = count;
1106 sess->blocking = wait;
1107 sess->status = PJ_EPENDING;
1108 sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
1109 for (i=0; i<count; ++i) {
1110 pj_strdup(pool, &sess->srv[i], &srv[i]);
Benny Prijonoebbf6892007-03-24 17:37:25 +00001111 }
1112
Benny Prijonobb995fd2009-08-12 11:03:23 +00001113 PJSUA_LOCK();
1114 pj_list_push_back(&pjsua_var.stun_res, sess);
1115 PJSUA_UNLOCK();
1116
1117 resolve_stun_entry(sess);
1118
1119 if (!wait)
1120 return PJ_SUCCESS;
1121
1122 while (sess->status == PJ_EPENDING) {
1123 pjsua_handle_events(50);
1124 }
1125
1126 status = sess->status;
1127 destroy_stun_resolve(sess);
1128
1129 return status;
1130}
1131
1132/*
1133 * Cancel pending STUN resolution.
1134 */
1135PJ_DEF(pj_status_t) pjsua_cancel_stun_resolution( void *token,
1136 pj_bool_t notify_cb)
1137{
1138 pjsua_stun_resolve *sess;
1139 unsigned cancelled_count = 0;
1140
1141 PJSUA_LOCK();
1142 sess = pjsua_var.stun_res.next;
1143 while (sess != &pjsua_var.stun_res) {
1144 pjsua_stun_resolve *next = sess->next;
1145
1146 if (sess->token == token) {
1147 if (notify_cb) {
1148 pj_stun_resolve_result result;
1149
1150 pj_bzero(&result, sizeof(result));
1151 result.token = token;
1152 result.status = PJ_ECANCELLED;
1153
1154 sess->cb(&result);
1155 }
1156
1157 destroy_stun_resolve(sess);
1158 ++cancelled_count;
1159 }
1160
1161 sess = next;
1162 }
1163 PJSUA_UNLOCK();
1164
1165 return cancelled_count ? PJ_SUCCESS : PJ_ENOTFOUND;
1166}
1167
1168static void internal_stun_resolve_cb(const pj_stun_resolve_result *result)
1169{
1170 pjsua_var.stun_status = result->status;
1171 if (result->status == PJ_SUCCESS) {
1172 pj_memcpy(&pjsua_var.stun_srv, &result->addr, sizeof(result->addr));
1173 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001174}
1175
Benny Prijono268ca612006-02-07 12:34:11 +00001176/*
Benny Prijonoc97608e2007-03-23 16:34:20 +00001177 * Resolve STUN server.
1178 */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001179pj_status_t resolve_stun_server(pj_bool_t wait)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001180{
1181 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
Benny Prijonobb995fd2009-08-12 11:03:23 +00001182 pj_status_t status;
1183
Benny Prijonoc97608e2007-03-23 16:34:20 +00001184 /* Initialize STUN configuration */
1185 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
1186 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
1187 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
1188
1189 /* Start STUN server resolution */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001190 if (pjsua_var.ua_cfg.stun_srv_cnt) {
1191 pjsua_var.stun_status = PJ_EPENDING;
1192 status = pjsua_resolve_stun_servers(pjsua_var.ua_cfg.stun_srv_cnt,
1193 pjsua_var.ua_cfg.stun_srv,
1194 wait, NULL,
1195 &internal_stun_resolve_cb);
1196 if (wait || status != PJ_SUCCESS) {
Benny Prijonoda9785b2007-04-02 20:43:06 +00001197 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +00001198 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001199 } else {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001200 pjsua_var.stun_status = PJ_SUCCESS;
1201 }
1202
Benny Prijonoc97608e2007-03-23 16:34:20 +00001203 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1204 /* STUN server resolution has been started, wait for the
1205 * result.
1206 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001207 if (wait) {
1208 while (pjsua_var.stun_status == PJ_EPENDING)
1209 pjsua_handle_events(10);
1210 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001211 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001212
1213 if (pjsua_var.stun_status != PJ_EPENDING &&
1214 pjsua_var.stun_status != PJ_SUCCESS &&
1215 pjsua_var.ua_cfg.stun_ignore_failure)
1216 {
1217 PJ_LOG(2,(THIS_FILE,
1218 "Ignoring STUN resolution failure (by setting)"));
1219 pjsua_var.stun_status = PJ_SUCCESS;
1220 }
1221
1222 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001223}
1224
1225/*
Benny Prijono268ca612006-02-07 12:34:11 +00001226 * Destroy pjsua.
1227 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001228PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001229{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001230 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001231
1232 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001234
1235 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1237 if (pjsua_var.thread[i]) {
1238 pj_thread_join(pjsua_var.thread[i]);
1239 pj_thread_destroy(pjsua_var.thread[i]);
1240 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001241 }
Benny Prijono268ca612006-02-07 12:34:11 +00001242 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001243
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 if (pjsua_var.endpt) {
Benny Prijono534a9ba2009-10-13 14:01:59 +00001245 unsigned max_wait;
1246
Benny Prijono384dab42009-10-14 01:58:04 +00001247 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1248
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001249 /* Terminate all calls. */
1250 pjsua_call_hangup_all();
1251
Benny Prijono7f6ee022008-07-31 08:32:46 +00001252 /* Set all accounts to offline */
1253 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1254 if (!pjsua_var.acc[i].valid)
1255 continue;
1256 pjsua_var.acc[i].online_status = PJ_FALSE;
1257 pj_bzero(&pjsua_var.acc[i].rpid, sizeof(pjrpid_element));
1258 }
1259
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001260 /* Terminate all presence subscriptions. */
1261 pjsua_pres_shutdown();
1262
Benny Prijono384dab42009-10-14 01:58:04 +00001263 /* Destroy media (to shutdown media transports etc) */
1264 pjsua_media_subsys_destroy();
1265
Benny Prijono534a9ba2009-10-13 14:01:59 +00001266 /* Wait for sometime until all publish client sessions are done
1267 * (ticket #364)
1268 */
1269 /* First stage, get the maximum wait time */
1270 max_wait = 100;
1271 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1272 if (!pjsua_var.acc[i].valid)
1273 continue;
1274 if (pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec > max_wait)
1275 max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec;
1276 }
1277
1278 /* Second stage, wait for unpublications to complete */
1279 for (i=0; i<(int)(max_wait/50); ++i) {
1280 unsigned j;
1281 for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
1282 if (!pjsua_var.acc[j].valid)
1283 continue;
1284
1285 if (pjsua_var.acc[j].publish_sess)
1286 break;
1287 }
1288 if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
1289 busy_sleep(50);
1290 else
1291 break;
1292 }
1293
1294 /* Third stage, forcefully destroy unfinished unpublications */
1295 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1296 if (pjsua_var.acc[i].publish_sess) {
1297 pjsip_publishc_destroy(pjsua_var.acc[i].publish_sess);
1298 pjsua_var.acc[i].publish_sess = NULL;
1299 }
1300 }
1301
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001302 /* Unregister all accounts */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001303 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1304 if (!pjsua_var.acc[i].valid)
1305 continue;
1306
1307 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001308 pjsua_acc_set_registration(i, PJ_FALSE);
1309 }
1310 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001311
1312 /* Terminate any pending STUN resolution */
1313 if (!pj_list_empty(&pjsua_var.stun_res)) {
1314 pjsua_stun_resolve *sess = pjsua_var.stun_res.next;
1315 while (sess != &pjsua_var.stun_res) {
1316 pjsua_stun_resolve *next = sess->next;
1317 destroy_stun_resolve(sess);
1318 sess = next;
1319 }
1320 }
1321
Benny Prijono384dab42009-10-14 01:58:04 +00001322 /* Wait until all unregistrations are done (ticket #364) */
1323 /* First stage, get the maximum wait time */
1324 max_wait = 100;
1325 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1326 if (!pjsua_var.acc[i].valid)
1327 continue;
1328 if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait)
1329 max_wait = pjsua_var.acc[i].cfg.unreg_timeout;
1330 }
1331
1332 /* Second stage, wait for unregistrations to complete */
1333 for (i=0; i<(int)(max_wait/50); ++i) {
1334 unsigned j;
1335 for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
1336 if (!pjsua_var.acc[j].valid)
1337 continue;
1338
1339 if (pjsua_var.acc[j].regc)
1340 break;
1341 }
1342 if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
1343 busy_sleep(50);
1344 else
1345 break;
1346 }
1347 /* Note variable 'i' is used below */
1348
Benny Prijonoff3b1462008-07-14 09:32:14 +00001349 /* Wait for some time to allow unregistration and ICE/TURN
1350 * transports shutdown to complete:
Benny Prijono384dab42009-10-14 01:58:04 +00001351 */
1352 if (i < 20)
1353 busy_sleep(1000 - i*50);
Benny Prijonoff3b1462008-07-14 09:32:14 +00001354
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001355 PJ_LOG(4,(THIS_FILE, "Destroying..."));
1356
Benny Prijonod7e26582008-07-18 23:51:49 +00001357 /* Must destroy endpoint first before destroying pools in
1358 * buddies or accounts, since shutting down transaction layer
1359 * may emit events which trigger some buddy or account callbacks
1360 * to be called.
1361 */
1362 pjsip_endpt_destroy(pjsua_var.endpt);
1363 pjsua_var.endpt = NULL;
1364
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001365 /* Destroy pool in the buddy object */
1366 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1367 if (pjsua_var.buddy[i].pool) {
1368 pj_pool_release(pjsua_var.buddy[i].pool);
1369 pjsua_var.buddy[i].pool = NULL;
1370 }
1371 }
1372
1373 /* Destroy accounts */
1374 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1375 if (pjsua_var.acc[i].pool) {
1376 pj_pool_release(pjsua_var.acc[i].pool);
1377 pjsua_var.acc[i].pool = NULL;
1378 }
1379 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001380 }
Benny Prijono268ca612006-02-07 12:34:11 +00001381
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001382 /* Destroy mutex */
1383 if (pjsua_var.mutex) {
1384 pj_mutex_destroy(pjsua_var.mutex);
1385 pjsua_var.mutex = NULL;
1386 }
1387
1388 /* Destroy pool and pool factory. */
1389 if (pjsua_var.pool) {
1390 pj_pool_release(pjsua_var.pool);
1391 pjsua_var.pool = NULL;
1392 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001393
1394 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1395
1396 /* End logging */
1397 if (pjsua_var.log_file) {
1398 pj_file_close(pjsua_var.log_file);
1399 pjsua_var.log_file = NULL;
1400 }
1401
1402 /* Shutdown PJLIB */
1403 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404 }
Benny Prijono268ca612006-02-07 12:34:11 +00001405
Benny Prijonof762ee72006-12-01 11:14:37 +00001406 /* Clear pjsua_var */
1407 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1408
Benny Prijono268ca612006-02-07 12:34:11 +00001409 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410 return PJ_SUCCESS;
1411}
1412
1413
1414/**
1415 * Application is recommended to call this function after all initialization
1416 * is done, so that the library can do additional checking set up
1417 * additional
1418 *
1419 * @return PJ_SUCCESS on success, or the appropriate error code.
1420 */
1421PJ_DEF(pj_status_t) pjsua_start(void)
1422{
1423 pj_status_t status;
1424
1425 status = pjsua_call_subsys_start();
1426 if (status != PJ_SUCCESS)
1427 return status;
1428
1429 status = pjsua_media_subsys_start();
1430 if (status != PJ_SUCCESS)
1431 return status;
1432
1433 status = pjsua_pres_start();
1434 if (status != PJ_SUCCESS)
1435 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001436
1437 return PJ_SUCCESS;
1438}
1439
Benny Prijono9fc735d2006-05-28 14:58:12 +00001440
1441/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 * Poll pjsua for events, and if necessary block the caller thread for
1443 * the specified maximum interval (in miliseconds).
1444 */
1445PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1446{
Benny Prijono897f9f82007-05-03 19:56:21 +00001447#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001448
1449 return pj_symbianos_poll(-1, msec_timeout);
1450
Benny Prijono897f9f82007-05-03 19:56:21 +00001451#else
1452
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453 unsigned count = 0;
1454 pj_time_val tv;
1455 pj_status_t status;
1456
1457 tv.sec = 0;
1458 tv.msec = msec_timeout;
1459 pj_time_val_normalize(&tv);
1460
1461 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1462
1463 if (status != PJ_SUCCESS)
1464 return -status;
1465
1466 return count;
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001467
Benny Prijono897f9f82007-05-03 19:56:21 +00001468#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469}
1470
1471
1472/*
1473 * Create memory pool.
1474 */
1475PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1476 pj_size_t increment)
1477{
1478 /* Pool factory is thread safe, no need to lock */
1479 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1480 NULL);
1481}
1482
1483
1484/*
1485 * Internal function to get SIP endpoint instance of pjsua, which is
1486 * needed for example to register module, create transports, etc.
1487 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001488 */
1489PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1490{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001491 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001492}
1493
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001494/*
1495 * Internal function to get media endpoint instance.
1496 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001497 */
1498PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1499{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001501}
1502
Benny Prijono97b87172006-08-24 14:25:14 +00001503/*
1504 * Internal function to get PJSUA pool factory.
1505 */
1506PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1507{
1508 return &pjsua_var.cp.factory;
1509}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001510
1511/*****************************************************************************
1512 * PJSUA SIP Transport API.
1513 */
1514
1515/*
Benny Prijono23674a32007-12-01 08:59:25 +00001516 * Tools to get address string.
1517 */
1518static const char *addr_string(const pj_sockaddr_t *addr)
1519{
1520 static char str[128];
1521 str[0] = '\0';
1522 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1523 pj_sockaddr_get_addr(addr),
1524 str, sizeof(str));
1525 return str;
1526}
1527
1528/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001529 * Create and initialize SIP socket (and possibly resolve public
1530 * address via STUN, depending on config).
1531 */
Benny Prijono23674a32007-12-01 08:59:25 +00001532static pj_status_t create_sip_udp_sock(int af,
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001533 const pjsua_transport_config *cfg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001534 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001535 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536{
Benny Prijono23674a32007-12-01 08:59:25 +00001537 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001538 unsigned port = cfg->port;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001539 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001540 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001541 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001542 pj_status_t status;
1543
Benny Prijonoc97608e2007-03-23 16:34:20 +00001544 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001545 status = resolve_stun_server(PJ_TRUE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001546 if (status != PJ_SUCCESS) {
1547 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1548 return status;
1549 }
1550
Benny Prijono23674a32007-12-01 08:59:25 +00001551 /* Initialize bound address */
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001552 if (cfg->bound_addr.slen) {
1553 status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr,
Benny Prijono23674a32007-12-01 08:59:25 +00001554 (pj_uint16_t)port);
1555 if (status != PJ_SUCCESS) {
1556 pjsua_perror(THIS_FILE,
1557 "Unable to resolve transport bound address",
1558 status);
1559 return status;
1560 }
1561 } else {
1562 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1563 }
1564
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001565 /* Create socket */
Benny Prijono23674a32007-12-01 08:59:25 +00001566 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001567 if (status != PJ_SUCCESS) {
1568 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001569 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001570 }
1571
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001572 /* Apply QoS, if specified */
1573 status = pj_sock_apply_qos2(sock, cfg->qos_type,
1574 &cfg->qos_params,
1575 2, THIS_FILE, "SIP UDP socket");
1576
1577 /* Bind socket */
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001578 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001579 if (status != PJ_SUCCESS) {
1580 pjsua_perror(THIS_FILE, "bind() error", status);
1581 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001582 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001583 }
1584
Benny Prijonoe347cb02007-02-14 14:36:13 +00001585 /* If port is zero, get the bound port */
1586 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001587 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001588 int namelen = sizeof(bound_addr);
1589 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1590 if (status != PJ_SUCCESS) {
1591 pjsua_perror(THIS_FILE, "getsockname() error", status);
1592 pj_sock_close(sock);
1593 return status;
1594 }
1595
Benny Prijono23674a32007-12-01 08:59:25 +00001596 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001597 }
1598
Benny Prijonoc97608e2007-03-23 16:34:20 +00001599 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001600 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1601 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001603 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 }
1605
1606 /* Get the published address, either by STUN or by resolving
1607 * the name of local host.
1608 */
Benny Prijono23674a32007-12-01 08:59:25 +00001609 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001610 /*
1611 * Public address is already specified, no need to resolve the
1612 * address, only set the port.
1613 */
Benny Prijono23674a32007-12-01 08:59:25 +00001614 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1615 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001616
1617 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001618 /*
1619 * STUN is specified, resolve the address with STUN.
1620 */
Benny Prijono23674a32007-12-01 08:59:25 +00001621 if (af != pj_AF_INET()) {
1622 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1623 pj_sock_close(sock);
1624 return PJ_EAFNOTSUP;
1625 }
1626
Benny Prijono14c2b862007-02-21 00:40:05 +00001627 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001628 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1629 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001630 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001632 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001634 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635 }
1636
1637 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001638 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639
Benny Prijono42d08d22007-12-20 11:23:07 +00001640 if (pj_sockaddr_has_addr(&bind_addr)) {
1641 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1642 } else {
1643 status = pj_gethostip(af, p_pub_addr);
1644 if (status != PJ_SUCCESS) {
1645 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1646 pj_sock_close(sock);
1647 return status;
1648 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649 }
1650
Benny Prijono23674a32007-12-01 08:59:25 +00001651 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1652 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 }
1654
1655 *p_sock = sock;
1656
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001657 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001658 addr_string(p_pub_addr),
1659 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001660
Benny Prijonod8410532006-06-15 11:04:33 +00001661 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662}
1663
1664
1665/*
1666 * Create SIP transport.
1667 */
1668PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1669 const pjsua_transport_config *cfg,
1670 pjsua_transport_id *p_id)
1671{
1672 pjsip_transport *tp;
1673 unsigned id;
1674 pj_status_t status;
1675
1676 PJSUA_LOCK();
1677
1678 /* Find empty transport slot */
1679 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001680 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681 break;
1682 }
1683
1684 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1685 status = PJ_ETOOMANY;
1686 pjsua_perror(THIS_FILE, "Error creating transport", status);
1687 goto on_return;
1688 }
1689
1690 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001691 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001692 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001693 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001694 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001695 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001696 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001697 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001698 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001699 pjsip_host_port addr_name;
1700
1701 /* Supply default config if it's not specified */
1702 if (cfg == NULL) {
1703 pjsua_transport_config_default(&config);
1704 cfg = &config;
1705 }
1706
Benny Prijono0a5cad82006-09-26 13:21:02 +00001707 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001708 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1709 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001710 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001711 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1712 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001713 if (status != PJ_SUCCESS) {
1714 pjsua_perror(THIS_FILE,
1715 "Unable to resolve transport public address",
1716 status);
1717 goto on_return;
1718 }
1719 }
1720
1721 /* Create the socket and possibly resolve the address with STUN
1722 * (only when public address is not specified).
1723 */
Benny Prijono23674a32007-12-01 08:59:25 +00001724 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001725 cfg, &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001726 if (status != PJ_SUCCESS)
1727 goto on_return;
1728
Benny Prijono23674a32007-12-01 08:59:25 +00001729 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1730 addr_name.host = pj_str(hostbuf);
1731 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001732
1733 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001734 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1735 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 if (status != PJ_SUCCESS) {
1737 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1738 status);
1739 pj_sock_close(sock);
1740 goto on_return;
1741 }
1742
Benny Prijonoe93e2872006-06-28 16:46:49 +00001743
1744 /* Save the transport */
1745 pjsua_var.tpdata[id].type = type;
1746 pjsua_var.tpdata[id].local_name = tp->local_name;
1747 pjsua_var.tpdata[id].data.tp = tp;
1748
Benny Prijono3569c0d2007-04-06 10:29:20 +00001749#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1750
Benny Prijonob2477142007-12-05 04:09:59 +00001751 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001752 /*
1753 * Create TCP transport.
1754 */
1755 pjsua_transport_config config;
1756 pjsip_tpfactory *tcp;
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001757 pjsip_tcp_transport_cfg tcp_cfg;
1758
1759 pjsip_tcp_transport_cfg_default(&tcp_cfg, pj_AF_INET());
Benny Prijonoe93e2872006-06-28 16:46:49 +00001760
1761 /* Supply default config if it's not specified */
1762 if (cfg == NULL) {
1763 pjsua_transport_config_default(&config);
1764 cfg = &config;
1765 }
1766
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001767 /* Configure bind address */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001768 if (cfg->port)
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001769 pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001770
Benny Prijono0a5cad82006-09-26 13:21:02 +00001771 if (cfg->bound_addr.slen) {
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001772 status = pj_sockaddr_set_str_addr(tcp_cfg.af,
1773 &tcp_cfg.bind_addr,
1774 &cfg->bound_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001775 if (status != PJ_SUCCESS) {
1776 pjsua_perror(THIS_FILE,
1777 "Unable to resolve transport bound address",
1778 status);
1779 goto on_return;
1780 }
1781 }
1782
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001783 /* Set published name */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001784 if (cfg->public_addr.slen)
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001785 tcp_cfg.addr_name.host = cfg->public_addr;
1786
1787 /* Copy the QoS settings */
1788 tcp_cfg.qos_type = cfg->qos_type;
1789 pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params,
1790 sizeof(cfg->qos_params));
Benny Prijonoe93e2872006-06-28 16:46:49 +00001791
1792 /* Create the TCP transport */
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001793 status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001794
1795 if (status != PJ_SUCCESS) {
1796 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1797 status);
1798 goto on_return;
1799 }
1800
1801 /* Save the transport */
1802 pjsua_var.tpdata[id].type = type;
1803 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1804 pjsua_var.tpdata[id].data.factory = tcp;
1805
Benny Prijono3569c0d2007-04-06 10:29:20 +00001806#endif /* PJ_HAS_TCP */
1807
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001808#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1809 } else if (type == PJSIP_TRANSPORT_TLS) {
1810 /*
1811 * Create TLS transport.
1812 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001813 pjsua_transport_config config;
1814 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001815 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001816 pj_sockaddr_in local_addr;
1817
1818 /* Supply default config if it's not specified */
1819 if (cfg == NULL) {
1820 pjsua_transport_config_default(&config);
1821 config.port = 5061;
1822 cfg = &config;
1823 }
1824
1825 /* Init local address */
1826 pj_sockaddr_in_init(&local_addr, 0, 0);
1827
1828 if (cfg->port)
1829 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1830
1831 if (cfg->bound_addr.slen) {
1832 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1833 if (status != PJ_SUCCESS) {
1834 pjsua_perror(THIS_FILE,
1835 "Unable to resolve transport bound address",
1836 status);
1837 goto on_return;
1838 }
1839 }
1840
1841 /* Init published name */
1842 pj_bzero(&a_name, sizeof(pjsip_host_port));
1843 if (cfg->public_addr.slen)
1844 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001845
1846 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001847 &cfg->tls_setting,
1848 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001849 if (status != PJ_SUCCESS) {
1850 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1851 status);
1852 goto on_return;
1853 }
1854
1855 /* Save the transport */
1856 pjsua_var.tpdata[id].type = type;
1857 pjsua_var.tpdata[id].local_name = tls->addr_name;
1858 pjsua_var.tpdata[id].data.factory = tls;
1859#endif
1860
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001861 } else {
1862 status = PJSIP_EUNSUPTRANSPORT;
1863 pjsua_perror(THIS_FILE, "Error creating transport", status);
1864 goto on_return;
1865 }
1866
Nanang Izzuddin2fb937e2010-02-24 05:43:34 +00001867 /* Set transport state callback */
1868 if (pjsua_var.ua_cfg.cb.on_transport_state) {
1869 pjsip_tpmgr_set_status_cb(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
1870 &pjsua_var.ua_cfg.cb.on_transport_state);
1871 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872
1873 /* Return the ID */
1874 if (p_id) *p_id = id;
1875
1876 status = PJ_SUCCESS;
1877
1878on_return:
1879
1880 PJSUA_UNLOCK();
1881
Benny Prijonod8410532006-06-15 11:04:33 +00001882 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001883}
1884
1885
1886/*
1887 * Register transport that has been created by application.
1888 */
1889PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1890 pjsua_transport_id *p_id)
1891{
1892 unsigned id;
1893
1894 PJSUA_LOCK();
1895
1896 /* Find empty transport slot */
1897 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001898 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001899 break;
1900 }
1901
1902 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1903 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1904 PJSUA_UNLOCK();
1905 return PJ_ETOOMANY;
1906 }
1907
1908 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001909 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001910 pjsua_var.tpdata[id].local_name = tp->local_name;
1911 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001912
1913 /* Return the ID */
1914 if (p_id) *p_id = id;
1915
1916 PJSUA_UNLOCK();
1917
1918 return PJ_SUCCESS;
1919}
1920
1921
1922/*
1923 * Enumerate all transports currently created in the system.
1924 */
1925PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1926 unsigned *p_count )
1927{
1928 unsigned i, count;
1929
1930 PJSUA_LOCK();
1931
1932 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1933 ++i)
1934 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001935 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001936 continue;
1937
1938 id[count++] = i;
1939 }
1940
1941 *p_count = count;
1942
1943 PJSUA_UNLOCK();
1944
1945 return PJ_SUCCESS;
1946}
1947
1948
1949/*
1950 * Get information about transports.
1951 */
1952PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1953 pjsua_transport_info *info)
1954{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001955 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001956 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001957
Benny Prijonoac623b32006-07-03 15:19:31 +00001958 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001959
1960 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001961 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1962 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001963
1964 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001965 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001966
1967 PJSUA_LOCK();
1968
Benny Prijonoe93e2872006-06-28 16:46:49 +00001969 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1970
1971 pjsip_transport *tp = t->data.tp;
1972
1973 if (tp == NULL) {
1974 PJSUA_UNLOCK();
1975 return PJ_EINVALIDOP;
1976 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001977
Benny Prijonoe93e2872006-06-28 16:46:49 +00001978 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001979 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001980 info->type_name = pj_str(tp->type_name);
1981 info->info = pj_str(tp->info);
1982 info->flag = tp->flag;
1983 info->addr_len = tp->addr_len;
1984 info->local_addr = tp->local_addr;
1985 info->local_name = tp->local_name;
1986 info->usage_count = pj_atomic_get(tp->ref_cnt);
1987
Benny Prijono0a5cad82006-09-26 13:21:02 +00001988 status = PJ_SUCCESS;
1989
Benny Prijonoe93e2872006-06-28 16:46:49 +00001990 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1991
1992 pjsip_tpfactory *factory = t->data.factory;
1993
1994 if (factory == NULL) {
1995 PJSUA_UNLOCK();
1996 return PJ_EINVALIDOP;
1997 }
1998
1999 info->id = id;
2000 info->type = t->type;
2001 info->type_name = pj_str("TCP");
2002 info->info = pj_str("TCP transport");
2003 info->flag = factory->flag;
2004 info->addr_len = sizeof(factory->local_addr);
2005 info->local_addr = factory->local_addr;
2006 info->local_name = factory->addr_name;
2007 info->usage_count = 0;
2008
Benny Prijono0a5cad82006-09-26 13:21:02 +00002009 status = PJ_SUCCESS;
2010
2011 } else {
2012 pj_assert(!"Unsupported transport");
2013 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00002014 }
2015
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002016
2017 PJSUA_UNLOCK();
2018
Benny Prijono0a5cad82006-09-26 13:21:02 +00002019 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002020}
2021
2022
2023/*
2024 * Disable a transport or re-enable it.
2025 */
2026PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
2027 pj_bool_t enabled)
2028{
2029 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002030 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
2031 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002032
2033 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00002034 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002035
2036
2037 /* To be done!! */
2038 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002039 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002040
2041 return PJ_EINVALIDOP;
2042}
2043
2044
2045/*
2046 * Close the transport.
2047 */
2048PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
2049 pj_bool_t force )
2050{
Benny Prijono5ff61872007-02-01 03:37:11 +00002051 pj_status_t status;
2052
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002053 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002054 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
2055 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002056
2057 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00002058 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059
Benny Prijono0a5cad82006-09-26 13:21:02 +00002060 /* Note: destroy() may not work if there are objects still referencing
2061 * the transport.
2062 */
2063 if (force) {
2064 switch (pjsua_var.tpdata[id].type) {
2065 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00002066 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
2067 if (status != PJ_SUCCESS)
2068 return status;
2069 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
2070 if (status != PJ_SUCCESS)
2071 return status;
2072 break;
2073
2074 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00002075 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00002076 /* This will close the TCP listener, but existing TCP/TLS
2077 * connections (if any) will still linger
2078 */
2079 status = (*pjsua_var.tpdata[id].data.factory->destroy)
2080 (pjsua_var.tpdata[id].data.factory);
2081 if (status != PJ_SUCCESS)
2082 return status;
2083
Benny Prijono0a5cad82006-09-26 13:21:02 +00002084 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00002085
Benny Prijono1ebd6142006-10-19 15:48:02 +00002086 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00002087 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00002088 }
2089
2090 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00002091 /* If force is not specified, transports will be closed at their
2092 * convenient time. However this will leak PJSUA-API transport
2093 * descriptors as PJSUA-API wouldn't know when exactly the
2094 * transport is closed thus it can't cleanup PJSUA transport
2095 * descriptor.
2096 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00002097 switch (pjsua_var.tpdata[id].type) {
2098 case PJSIP_TRANSPORT_UDP:
2099 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00002100 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00002101 case PJSIP_TRANSPORT_TCP:
2102 return (*pjsua_var.tpdata[id].data.factory->destroy)
2103 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00002104 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00002105 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00002106 }
2107 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108
Benny Prijono5ff61872007-02-01 03:37:11 +00002109 /* Cleanup pjsua data when force is applied */
2110 if (force) {
2111 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
2112 pjsua_var.tpdata[id].data.ptr = NULL;
2113 }
2114
2115 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002116}
2117
2118
2119/*
2120 * Add additional headers etc in msg_data specified by application
2121 * when sending requests.
2122 */
2123void pjsua_process_msg_data(pjsip_tx_data *tdata,
2124 const pjsua_msg_data *msg_data)
2125{
2126 pj_bool_t allow_body;
2127 const pjsip_hdr *hdr;
2128
Benny Prijono56315612006-07-18 14:39:40 +00002129 /* Always add User-Agent */
2130 if (pjsua_var.ua_cfg.user_agent.slen &&
2131 tdata->msg->type == PJSIP_REQUEST_MSG)
2132 {
2133 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
2134 pjsip_hdr *h;
2135 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
2136 &STR_USER_AGENT,
2137 &pjsua_var.ua_cfg.user_agent);
2138 pjsip_msg_add_hdr(tdata->msg, h);
2139 }
2140
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141 if (!msg_data)
2142 return;
2143
2144 hdr = msg_data->hdr_list.next;
2145 while (hdr && hdr != &msg_data->hdr_list) {
2146 pjsip_hdr *new_hdr;
2147
Benny Prijonoa1e69682007-05-11 15:14:34 +00002148 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002149 pjsip_msg_add_hdr(tdata->msg, new_hdr);
2150
2151 hdr = hdr->next;
2152 }
2153
2154 allow_body = (tdata->msg->body == NULL);
2155
2156 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
2157 pjsip_media_type ctype;
2158 pjsip_msg_body *body;
2159
2160 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
2161 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
2162 &msg_data->msg_body);
2163 tdata->msg->body = body;
2164 }
2165}
2166
2167
2168/*
2169 * Add route_set to outgoing requests
2170 */
2171void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
2172 const pjsip_route_hdr *route_set )
2173{
2174 const pjsip_route_hdr *r;
2175
2176 r = route_set->next;
2177 while (r != route_set) {
2178 pjsip_route_hdr *new_r;
2179
Benny Prijonoa1e69682007-05-11 15:14:34 +00002180 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002181 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
2182
2183 r = r->next;
2184 }
2185}
2186
2187
2188/*
2189 * Simple version of MIME type parsing (it doesn't support parameters)
2190 */
2191void pjsua_parse_media_type( pj_pool_t *pool,
2192 const pj_str_t *mime,
2193 pjsip_media_type *media_type)
2194{
2195 pj_str_t tmp;
2196 char *pos;
2197
Benny Prijonoac623b32006-07-03 15:19:31 +00002198 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199
2200 pj_strdup_with_null(pool, &tmp, mime);
2201
2202 pos = pj_strchr(&tmp, '/');
2203 if (pos) {
2204 media_type->type.ptr = tmp.ptr;
2205 media_type->type.slen = (pos-tmp.ptr);
2206 media_type->subtype.ptr = pos+1;
2207 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
2208 } else {
2209 media_type->type = tmp;
2210 }
2211}
2212
2213
2214/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002215 * Internal function to init transport selector from transport id.
2216 */
2217void pjsua_init_tpselector(pjsua_transport_id tp_id,
2218 pjsip_tpselector *sel)
2219{
2220 pjsua_transport_data *tpdata;
2221 unsigned flag;
2222
2223 pj_bzero(sel, sizeof(*sel));
2224 if (tp_id == PJSUA_INVALID_ID)
2225 return;
2226
Benny Prijonoa1e69682007-05-11 15:14:34 +00002227 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002228 tpdata = &pjsua_var.tpdata[tp_id];
2229
2230 flag = pjsip_transport_get_flag_from_type(tpdata->type);
2231
2232 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
2233 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
2234 sel->u.transport = tpdata->data.tp;
2235 } else {
2236 sel->type = PJSIP_TPSELECTOR_LISTENER;
2237 sel->u.listener = tpdata->data.factory;
2238 }
2239}
2240
2241
Benny Prijono6ba8c542007-10-16 01:34:14 +00002242/* Callback upon NAT detection completion */
2243static void nat_detect_cb(void *user_data,
2244 const pj_stun_nat_detect_result *res)
2245{
2246 PJ_UNUSED_ARG(user_data);
2247
2248 pjsua_var.nat_in_progress = PJ_FALSE;
2249 pjsua_var.nat_status = res->status;
2250 pjsua_var.nat_type = res->nat_type;
2251
2252 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
2253 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
2254 }
2255}
2256
2257
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002258/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002259 * Detect NAT type.
2260 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00002261PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002262{
2263 pj_status_t status;
2264
Benny Prijono6ba8c542007-10-16 01:34:14 +00002265 if (pjsua_var.nat_in_progress)
2266 return PJ_SUCCESS;
2267
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002268 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +00002269 status = resolve_stun_server(PJ_TRUE);
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002270 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002271 pjsua_var.nat_status = status;
2272 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002273 return status;
2274 }
2275
2276 /* Make sure we have STUN */
2277 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002278 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
2279 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002280 }
2281
Benny Prijono6ba8c542007-10-16 01:34:14 +00002282 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2283 &pjsua_var.stun_cfg,
2284 NULL, &nat_detect_cb);
2285
2286 if (status != PJ_SUCCESS) {
2287 pjsua_var.nat_status = status;
2288 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2289 return status;
2290 }
2291
2292 pjsua_var.nat_in_progress = PJ_TRUE;
2293
2294 return PJ_SUCCESS;
2295}
2296
2297
2298/*
2299 * Get NAT type.
2300 */
2301PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2302{
2303 *type = pjsua_var.nat_type;
2304 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002305}
2306
2307
2308/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002309 * Verify that valid SIP url is given.
2310 */
2311PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2312{
2313 pjsip_uri *p;
2314 pj_pool_t *pool;
2315 char *url;
2316 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2317
2318 if (!len) return -1;
2319
2320 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2321 if (!pool) return -1;
2322
Benny Prijonoa1e69682007-05-11 15:14:34 +00002323 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324 pj_ansi_strcpy(url, c_url);
2325
2326 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002327 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2328 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2329 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002330 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002331 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002332
2333 pj_pool_release(pool);
2334 return p ? 0 : -1;
2335}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002336
Benny Prijono73bb7232009-10-20 13:56:26 +00002337/*
2338 * Schedule a timer entry.
2339 */
2340PJ_DEF(pj_status_t) pjsua_schedule_timer( pj_timer_entry *entry,
2341 const pj_time_val *delay)
2342{
2343 return pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, delay);
2344}
2345
2346/*
2347 * Cancel the previously scheduled timer.
2348 *
2349 */
2350PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry)
2351{
2352 pjsip_endpt_cancel_timer(pjsua_var.endpt, entry);
2353}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002354
Benny Prijono91d06b62008-09-20 12:16:56 +00002355/**
2356 * Normalize route URI (check for ";lr" and append one if it doesn't
2357 * exist and pjsua_config.force_lr is set.
2358 */
2359pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri)
2360{
2361 pj_str_t tmp_uri;
2362 pj_pool_t *tmp_pool;
2363 pjsip_uri *uri_obj;
2364 pjsip_sip_uri *sip_uri;
2365
2366 tmp_pool = pjsua_pool_create("tmplr%p", 512, 512);
2367 if (!tmp_pool)
2368 return PJ_ENOMEM;
2369
2370 pj_strdup_with_null(tmp_pool, &tmp_uri, uri);
2371
2372 uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0);
2373 if (!uri_obj) {
2374 PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s",
2375 (int)uri->slen, uri->ptr));
2376 pj_pool_release(tmp_pool);
2377 return PJSIP_EINVALIDURI;
2378 }
2379
2380 if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) &&
2381 !PJSIP_URI_SCHEME_IS_SIP(uri_obj))
2382 {
2383 PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s",
2384 (int)uri->slen, uri->ptr));
2385 pj_pool_release(tmp_pool);
2386 return PJSIP_EINVALIDSCHEME;
2387 }
2388
2389 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri_obj);
2390
2391 /* Done if force_lr is disabled or if lr parameter is present */
2392 if (!pjsua_var.ua_cfg.force_lr || sip_uri->lr_param) {
2393 pj_pool_release(tmp_pool);
2394 return PJ_SUCCESS;
2395 }
2396
2397 /* Set lr param */
2398 sip_uri->lr_param = 1;
2399
2400 /* Print the URI */
2401 tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE);
2402 tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj,
2403 tmp_uri.ptr, PJSIP_MAX_URL_SIZE);
2404 if (tmp_uri.slen < 1) {
2405 PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s",
2406 (int)uri->slen, uri->ptr));
2407 pj_pool_release(tmp_pool);
2408 return PJSIP_EURITOOLONG;
2409 }
2410
2411 /* Clone the URI */
2412 pj_strdup_with_null(pool, uri, &tmp_uri);
2413
Benny Prijonodc20c592009-10-15 06:55:30 +00002414 pj_pool_release(tmp_pool);
Benny Prijono91d06b62008-09-20 12:16:56 +00002415 return PJ_SUCCESS;
2416}
2417
Benny Prijonoda9785b2007-04-02 20:43:06 +00002418/*
2419 * This is a utility function to dump the stack states to log, using
2420 * verbosity level 3.
2421 */
2422PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2423{
2424 unsigned old_decor;
2425 unsigned i;
Benny Prijonoda9785b2007-04-02 20:43:06 +00002426
2427 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2428
2429 old_decor = pj_log_get_decor();
2430 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2431
2432 if (detail)
2433 pj_dump_config();
2434
2435 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2436
2437 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2438
2439 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2440 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2441 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002442 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002443 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002444
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002445 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002446 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002447
Benny Prijono734fc2d2008-03-17 16:05:35 +00002448 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002449 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002450
Benny Prijono5186eae2007-12-03 14:38:25 +00002451 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002452 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002453 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002454 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002455 }
2456
2457 pjsip_tsx_layer_dump(detail);
2458 pjsip_ua_dump(detail);
2459
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002460// Dumping complete call states may require a 'large' buffer
2461// (about 3KB per call session, including RTCP XR).
2462#if 0
Benny Prijonoda9785b2007-04-02 20:43:06 +00002463 /* Dump all invite sessions: */
2464 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2465
2466 if (pjsua_call_get_count() == 0) {
2467
2468 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2469
2470 } else {
2471 unsigned i;
2472
2473 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2474 if (pjsua_call_is_active(i)) {
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002475 /* Tricky logging, since call states log string tends to be
2476 * longer than PJ_LOG_MAX_SIZE.
2477 */
2478 char buf[1024 * 3];
2479 unsigned call_dump_len;
2480 unsigned part_len;
2481 unsigned part_idx;
2482 unsigned log_decor;
2483
Benny Prijonoda9785b2007-04-02 20:43:06 +00002484 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002485 call_dump_len = strlen(buf);
2486
2487 log_decor = pj_log_get_decor();
2488 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE |
2489 PJ_LOG_HAS_CR));
2490 PJ_LOG(3,(THIS_FILE, "\n"));
2491 pj_log_set_decor(0);
2492
2493 part_idx = 0;
2494 part_len = PJ_LOG_MAX_SIZE-80;
2495 while (part_idx < call_dump_len) {
2496 char p_orig, *p;
2497
2498 p = &buf[part_idx];
2499 if (part_idx + part_len > call_dump_len)
2500 part_len = call_dump_len - part_idx;
2501 p_orig = p[part_len];
2502 p[part_len] = '\0';
2503 PJ_LOG(3,(THIS_FILE, "%s", p));
2504 p[part_len] = p_orig;
2505 part_idx += part_len;
2506 }
2507 pj_log_set_decor(log_decor);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002508 }
2509 }
2510 }
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002511#endif
Benny Prijonoda9785b2007-04-02 20:43:06 +00002512
2513 /* Dump presence status */
2514 pjsua_pres_dump(detail);
2515
2516 pj_log_set_decor(old_decor);
2517 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2518}
2519