blob: 5d231cbf9d30b398b74323fcd71e25602d920281 [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;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +0000178 cfg->srtp_optional_dup_offer = pjsua_var.ua_cfg.srtp_optional_dup_offer;
Benny Prijonod8179652008-01-23 20:39:07 +0000179#endif
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +0000180 cfg->reg_retry_interval = PJSUA_REG_RETRY_INTERVAL;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000181}
182
183PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
184{
185 pj_bzero(cfg, sizeof(*cfg));
186}
187
188PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
189{
190 pj_bzero(cfg, sizeof(*cfg));
191
192 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijono50f19b32008-03-11 13:15:43 +0000193 cfg->snd_clock_rate = 0;
Benny Prijono7d60d052008-03-29 12:24:20 +0000194 cfg->channel_count = 1;
Benny Prijono37c710b2008-01-10 12:09:26 +0000195 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME;
196 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000197 cfg->has_ioqueue = PJ_TRUE;
198 cfg->thread_cnt = 1;
199 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
200 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
201 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
Benny Prijono10454dc2009-02-21 14:21:59 +0000202 cfg->snd_rec_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
203 cfg->snd_play_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000204 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
Benny Prijonof798e502009-03-09 13:08:16 +0000205 cfg->snd_auto_close_time = 1;
Benny Prijonof76e1392008-06-06 14:51:48 +0000206
Benny Prijono329d6382009-05-29 13:04:03 +0000207 cfg->ice_max_host_cands = -1;
208 pj_ice_sess_options_default(&cfg->ice_opt);
209
Benny Prijonof76e1392008-06-06 14:51:48 +0000210 cfg->turn_conn_type = PJ_TURN_TP_UDP;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000211}
212
Benny Prijonodc39fe82006-05-26 12:17:46 +0000213
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214/*****************************************************************************
215 * This is a very simple PJSIP module, whose sole purpose is to display
216 * incoming and outgoing messages to log. This module will have priority
217 * higher than transport layer, which means:
218 *
219 * - incoming messages will come to this module first before reaching
220 * transaction layer.
221 *
222 * - outgoing messages will come to this module last, after the message
223 * has been 'printed' to contiguous buffer by transport layer and
224 * appropriate transport instance has been decided for this message.
225 *
226 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000227
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000228/* Notification on incoming messages */
229static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
230{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000231 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
232 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 "--end msg--",
234 rdata->msg_info.len,
235 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000236 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 rdata->pkt_info.src_name,
238 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000239 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000240 rdata->msg_info.msg_buf));
241
242 /* Always return false, otherwise messages will not get processed! */
243 return PJ_FALSE;
244}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000245
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000246/* Notification on outgoing messages */
247static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
248{
249
250 /* Important note:
251 * tp_info field is only valid after outgoing messages has passed
252 * transport layer. So don't try to access tp_info when the module
253 * has lower priority than transport layer.
254 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000255
Benny Prijonob4a17c92006-07-10 14:40:21 +0000256 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
257 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000258 "--end msg--",
259 (tdata->buf.cur - tdata->buf.start),
260 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000261 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000262 tdata->tp_info.dst_name,
263 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000264 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000265 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000266
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000267 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000268 return PJ_SUCCESS;
269}
270
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000271/* The module instance. */
272static pjsip_module pjsua_msg_logger =
273{
274 NULL, NULL, /* prev, next. */
275 { "mod-pjsua-log", 13 }, /* Name. */
276 -1, /* Id */
277 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
278 NULL, /* load() */
279 NULL, /* start() */
280 NULL, /* stop() */
281 NULL, /* unload() */
282 &logging_on_rx_msg, /* on_rx_request() */
283 &logging_on_rx_msg, /* on_rx_response() */
284 &logging_on_tx_msg, /* on_tx_request. */
285 &logging_on_tx_msg, /* on_tx_response() */
286 NULL, /* on_tsx_state() */
287
288};
289
290
291/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000292 * Another simple module to handle incoming OPTIONS request
293 */
294
295/* Notification on incoming request */
296static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
297{
298 pjsip_tx_data *tdata;
299 pjsip_response_addr res_addr;
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000300 pjmedia_transport_info tpinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000301 pjmedia_sdp_session *sdp;
302 const pjsip_hdr *cap_hdr;
303 pj_status_t status;
304
305 /* Only want to handle OPTIONS requests */
306 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000307 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000308 {
309 return PJ_FALSE;
310 }
311
Benny Prijono384dab42009-10-14 01:58:04 +0000312 /* Don't want to handle if shutdown is in progress */
313 if (pjsua_var.thread_quit_flag) {
314 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
315 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
316 NULL, NULL);
317 return PJ_TRUE;
318 }
319
Benny Prijono56315612006-07-18 14:39:40 +0000320 /* Create basic response. */
321 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
322 &tdata);
323 if (status != PJ_SUCCESS) {
324 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
325 return PJ_TRUE;
326 }
327
328 /* Add Allow header */
329 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
330 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000331 pjsip_msg_add_hdr(tdata->msg,
332 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000333 }
334
335 /* Add Accept header */
336 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
337 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000338 pjsip_msg_add_hdr(tdata->msg,
339 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000340 }
341
342 /* Add Supported header */
343 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
344 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000345 pjsip_msg_add_hdr(tdata->msg,
346 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000347 }
348
349 /* Add Allow-Events header from the evsub module */
350 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
351 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000352 pjsip_msg_add_hdr(tdata->msg,
353 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000354 }
355
356 /* Add User-Agent header */
357 if (pjsua_var.ua_cfg.user_agent.slen) {
358 const pj_str_t USER_AGENT = { "User-Agent", 10};
359 pjsip_hdr *h;
360
361 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
362 &USER_AGENT,
363 &pjsua_var.ua_cfg.user_agent);
364 pjsip_msg_add_hdr(tdata->msg, h);
365 }
366
Benny Prijonoa248b952009-08-12 22:28:47 +0000367 /* Get media socket info, make sure transport is ready */
368 if (pjsua_var.calls[0].med_tp) {
369 pjmedia_transport_info_init(&tpinfo);
370 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &tpinfo);
Benny Prijono617c5bc2007-04-02 19:51:21 +0000371
Benny Prijonoa248b952009-08-12 22:28:47 +0000372 /* Add SDP body, using call0's RTP address */
373 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
374 &tpinfo.sock_info, &sdp);
375 if (status == PJ_SUCCESS) {
376 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
377 }
Benny Prijono56315612006-07-18 14:39:40 +0000378 }
379
380 /* Send response statelessly */
381 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
382 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
383 if (status != PJ_SUCCESS)
384 pjsip_tx_data_dec_ref(tdata);
385
386 return PJ_TRUE;
387}
388
389
390/* The module instance. */
391static pjsip_module pjsua_options_handler =
392{
393 NULL, NULL, /* prev, next. */
394 { "mod-pjsua-options", 17 }, /* Name. */
395 -1, /* Id */
396 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
397 NULL, /* load() */
398 NULL, /* start() */
399 NULL, /* stop() */
400 NULL, /* unload() */
401 &options_on_rx_request, /* on_rx_request() */
402 NULL, /* on_rx_response() */
403 NULL, /* on_tx_request. */
404 NULL, /* on_tx_response() */
405 NULL, /* on_tsx_state() */
406
407};
408
409
410/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000411 * These two functions are the main callbacks registered to PJSIP stack
412 * to receive SIP request and response messages that are outside any
413 * dialogs and any transactions.
414 */
Benny Prijono268ca612006-02-07 12:34:11 +0000415
416/*
417 * Handler for receiving incoming requests.
418 *
419 * This handler serves multiple purposes:
420 * - it receives requests outside dialogs.
421 * - it receives requests inside dialogs, when the requests are
422 * unhandled by other dialog usages. Example of these
423 * requests are: MESSAGE.
424 */
425static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
426{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 pj_bool_t processed = PJ_FALSE;
428
429 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000430
Benny Prijono84126ab2006-02-09 09:30:09 +0000431 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000432
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000434 }
435
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 PJSUA_UNLOCK();
437
438 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000439}
440
441
442/*
443 * Handler for receiving incoming responses.
444 *
445 * This handler serves multiple purposes:
446 * - it receives strayed responses (i.e. outside any dialog and
447 * outside any transactions).
448 * - it receives responses coming to a transaction, when pjsua
449 * module is set as transaction user for the transaction.
450 * - it receives responses inside a dialog, when these responses
451 * are unhandled by other dialog usages.
452 */
453static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
454{
455 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000456 return PJ_FALSE;
457}
458
459
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000460/*****************************************************************************
461 * Logging.
462 */
463
464/* Log callback */
465static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000466{
Benny Prijono572d4852006-11-23 21:50:02 +0000467 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468
469 if (pjsua_var.log_file) {
470 pj_ssize_t size = len;
471 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000472 /* This will slow things down considerably! Don't do it!
473 pj_file_flush(pjsua_var.log_file);
474 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000475 }
476
Benny Prijono572d4852006-11-23 21:50:02 +0000477 if (level <= (int)pjsua_var.log_cfg.console_level) {
478 if (pjsua_var.log_cfg.cb)
479 (*pjsua_var.log_cfg.cb)(level, buffer, len);
480 else
481 pj_log_write(level, buffer, len);
482 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000483}
484
485
486/*
487 * Application can call this function at any time (after pjsua_create(), of
488 * course) to change logging settings.
489 */
490PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
491{
492 pj_status_t status;
493
494 /* Save config. */
495 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
496
497 /* Redirect log function to ours */
498 pj_log_set_log_func( &log_writer );
499
Benny Prijono9cb09a22007-08-30 09:35:10 +0000500 /* Set decor */
501 pj_log_set_decor(pjsua_var.log_cfg.decor);
502
Benny Prijono4190cf92008-01-18 13:25:05 +0000503 /* Set log level */
504 pj_log_set_level(pjsua_var.log_cfg.level);
505
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000506 /* Close existing file, if any */
507 if (pjsua_var.log_file) {
508 pj_file_close(pjsua_var.log_file);
509 pjsua_var.log_file = NULL;
510 }
511
512 /* If output log file is desired, create the file: */
513 if (pjsua_var.log_cfg.log_filename.slen) {
Benny Prijonodbe3f4b2009-05-07 16:56:04 +0000514 unsigned flags = PJ_O_WRONLY;
515 flags |= pjsua_var.log_cfg.log_file_flags;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 status = pj_file_open(pjsua_var.pool,
517 pjsua_var.log_cfg.log_filename.ptr,
Benny Prijonodbe3f4b2009-05-07 16:56:04 +0000518 flags,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000519 &pjsua_var.log_file);
520
521 if (status != PJ_SUCCESS) {
522 pjsua_perror(THIS_FILE, "Error creating log file", status);
523 return status;
524 }
525 }
526
527 /* Unregister msg logging if it's previously registered */
528 if (pjsua_msg_logger.id >= 0) {
529 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
530 pjsua_msg_logger.id = -1;
531 }
532
533 /* Enable SIP message logging */
534 if (pjsua_var.log_cfg.msg_logging)
535 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
536
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000537 return PJ_SUCCESS;
538}
539
540
541/*****************************************************************************
542 * PJSUA Base API.
543 */
544
545/* Worker thread function. */
546static int worker_thread(void *arg)
547{
548 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000549
Benny Prijono268ca612006-02-07 12:34:11 +0000550 PJ_UNUSED_ARG(arg);
551
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000552 while (!pjsua_var.thread_quit_flag) {
553 int count;
554
555 count = pjsua_handle_events(TIMEOUT);
556 if (count < 0)
557 pj_thread_sleep(TIMEOUT);
558 }
Benny Prijono268ca612006-02-07 12:34:11 +0000559
560 return 0;
561}
562
Benny Prijonodc39fe82006-05-26 12:17:46 +0000563
Benny Prijono8389c312008-02-21 21:36:34 +0000564/* Init random seed */
565static void init_random_seed(void)
566{
567 pj_sockaddr addr;
568 const pj_str_t *hostname;
569 pj_uint32_t pid;
570 pj_time_val t;
571 unsigned seed=0;
572
573 /* Add hostname */
574 hostname = pj_gethostname();
575 seed = pj_hash_calc(seed, hostname->ptr, (int)hostname->slen);
576
577 /* Add primary IP address */
578 if (pj_gethostip(pj_AF_INET(), &addr)==PJ_SUCCESS)
579 seed = pj_hash_calc(seed, &addr.ipv4.sin_addr, 4);
580
581 /* Get timeofday */
582 pj_gettimeofday(&t);
583 seed = pj_hash_calc(seed, &t, sizeof(t));
584
585 /* Add PID */
586 pid = pj_getpid();
587 seed = pj_hash_calc(seed, &pid, sizeof(pid));
588
589 /* Init random seed */
590 pj_srand(seed);
591}
592
Benny Prijono268ca612006-02-07 12:34:11 +0000593/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000595 */
596PJ_DEF(pj_status_t) pjsua_create(void)
597{
598 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000599
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000600 /* Init pjsua data */
601 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000602
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000603 /* Set default logging settings */
604 pjsua_logging_config_default(&pjsua_var.log_cfg);
605
606 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000607 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000608 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
609
Benny Prijono8389c312008-02-21 21:36:34 +0000610 /* Init random seed */
611 init_random_seed();
Benny Prijono268ca612006-02-07 12:34:11 +0000612
Benny Prijonofccab712006-02-22 22:23:22 +0000613 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000614 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000615 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000616
Benny Prijonoec921342007-03-24 13:00:30 +0000617 /* Init PJNATH */
618 status = pjnath_init();
619 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000620
Benny Prijono094d3ad2006-11-21 08:41:00 +0000621 /* Set default sound device ID */
Benny Prijono96e74f32009-02-22 12:00:12 +0000622 pjsua_var.cap_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
623 pjsua_var.play_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
Benny Prijono094d3ad2006-11-21 08:41:00 +0000624
Benny Prijono268ca612006-02-07 12:34:11 +0000625 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000626 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000627
628 /* Create memory pool for application. */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000629 pjsua_var.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000630
631 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000632
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000633 /* Create mutex */
634 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
635 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000636 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000637 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000638 return status;
639 }
640
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000641 /* Must create SIP endpoint to initialize SIP parser. The parser
642 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000643 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 status = pjsip_endpt_create(&pjsua_var.cp.factory,
645 pj_gethostname()->ptr,
646 &pjsua_var.endpt);
647 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000648
649
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000650 return PJ_SUCCESS;
651}
652
653
654/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000655 * Initialize pjsua with the specified settings. All the settings are
656 * optional, and the default values will be used when the config is not
657 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000658 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000659PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
660 const pjsua_logging_config *log_cfg,
661 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000662{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000663 pjsua_config default_cfg;
664 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000665 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000666 pjsip_ua_init_param ua_init_param;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000667 pj_status_t status;
668
669
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000670 /* Create default configurations when the config is not supplied */
671
672 if (ua_cfg == NULL) {
673 pjsua_config_default(&default_cfg);
674 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000675 }
676
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000677 if (media_cfg == NULL) {
678 pjsua_media_config_default(&default_media_cfg);
679 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000680 }
681
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682 /* Initialize logging first so that info/errors can be captured */
683 if (log_cfg) {
684 status = pjsua_reconfigure_logging(log_cfg);
Benny Prijono329d6382009-05-29 13:04:03 +0000685 if (status != PJ_SUCCESS)
686 return status;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000687 }
688
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000689 /* If nameserver is configured, create DNS resolver instance and
690 * set it to be used by SIP resolver.
691 */
692 if (ua_cfg->nameserver_count) {
693#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000694 unsigned i;
695
696 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000697 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
698 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000699 if (status != PJ_SUCCESS) {
700 pjsua_perror(THIS_FILE, "Error creating resolver", status);
701 return status;
702 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000703
704 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000705 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
706 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000707 ua_cfg->nameserver, NULL);
708 if (status != PJ_SUCCESS) {
709 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
710 return status;
711 }
712
713 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000714 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000715 if (status != PJ_SUCCESS) {
716 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
717 return status;
718 }
719
720 /* Print nameservers */
721 for (i=0; i<ua_cfg->nameserver_count; ++i) {
722 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
723 (int)ua_cfg->nameserver[i].slen,
724 ua_cfg->nameserver[i].ptr));
725 }
726#else
727 PJ_LOG(2,(THIS_FILE,
728 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
729#endif
730 }
731
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000732 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000733
734 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000735 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
736 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000737
Benny Prijonodc39fe82006-05-26 12:17:46 +0000738
739 /* Initialize UA layer module: */
Benny Prijono3c5e28b2008-09-24 10:10:15 +0000740 pj_bzero(&ua_init_param, sizeof(ua_init_param));
741 if (ua_cfg->hangup_forked_call) {
742 ua_init_param.on_dlg_forked = &on_dlg_forked;
743 }
744 status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000745 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000746
Benny Prijonodc39fe82006-05-26 12:17:46 +0000747
Benny Prijono053f5222006-11-11 16:16:04 +0000748 /* Initialize Replaces support. */
749 status = pjsip_replaces_init_module( pjsua_var.endpt );
750 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
751
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000752 /* Initialize 100rel support */
753 status = pjsip_100rel_init_module(pjsua_var.endpt);
754 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000755
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000756 /* Initialize session timer support */
757 status = pjsip_timer_init_module(pjsua_var.endpt);
758 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
759
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000760 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000761 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000762 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000763 {
764 NULL, NULL, /* prev, next. */
765 { "mod-pjsua", 9 }, /* Name. */
766 -1, /* Id */
767 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
768 NULL, /* load() */
769 NULL, /* start() */
770 NULL, /* stop() */
771 NULL, /* unload() */
772 &mod_pjsua_on_rx_request, /* on_rx_request() */
773 &mod_pjsua_on_rx_response, /* on_rx_response() */
774 NULL, /* on_tx_request. */
775 NULL, /* on_tx_response() */
776 NULL, /* on_tsx_state() */
777 };
778
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000779 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000780
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000781 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
782 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000783 }
784
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000785
Benny Prijonodc39fe82006-05-26 12:17:46 +0000786
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000787 /* Initialize PJSUA call subsystem: */
788 status = pjsua_call_subsys_init(ua_cfg);
789 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000790 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000791
Benny Prijonobb995fd2009-08-12 11:03:23 +0000792 /* Convert deprecated STUN settings */
793 if (pjsua_var.ua_cfg.stun_srv_cnt==0) {
794 if (pjsua_var.ua_cfg.stun_domain.slen) {
795 pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
796 pjsua_var.ua_cfg.stun_domain;
797 }
798 if (pjsua_var.ua_cfg.stun_host.slen) {
799 pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
800 pjsua_var.ua_cfg.stun_host;
801 }
802 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803
Benny Prijonoc97608e2007-03-23 16:34:20 +0000804 /* Start resolving STUN server */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000805 status = resolve_stun_server(PJ_FALSE);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000806 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
807 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
808 return status;
809 }
810
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000811 /* Initialize PJSUA media subsystem */
812 status = pjsua_media_subsys_init(media_cfg);
813 if (status != PJ_SUCCESS)
814 goto on_error;
815
Benny Prijonodc39fe82006-05-26 12:17:46 +0000816
817 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000818 status = pjsip_evsub_init_module(pjsua_var.endpt);
819 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000820
Benny Prijonodc39fe82006-05-26 12:17:46 +0000821
822 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000823 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
824 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000825
Benny Prijono4dd961b2009-10-26 11:21:37 +0000826 /* Initialize MWI support */
827 status = pjsip_mwi_init_module(pjsua_var.endpt, pjsip_evsub_instance());
828
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000829 /* Init PUBLISH module */
830 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000831
832 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000833 status = pjsip_xfer_init_module( pjsua_var.endpt );
834 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000835
836 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000837 status = pjsua_pres_init();
838 if (status != PJ_SUCCESS)
839 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000840
841 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000842 status = pjsua_im_init();
843 if (status != PJ_SUCCESS)
844 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000845
Benny Prijonoc8141a82006-08-20 09:12:19 +0000846 /* Register OPTIONS handler */
847 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
848
849 /* Add OPTIONS in Allow header */
850 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
851 NULL, 1, &STR_OPTIONS);
852
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000853 /* Start worker thread if needed. */
854 if (pjsua_var.ua_cfg.thread_cnt) {
855 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000856
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000857 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
858 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000859
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000860 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
861 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
862 NULL, 0, 0, &pjsua_var.thread[i]);
863 if (status != PJ_SUCCESS)
864 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000865 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000866 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
867 pjsua_var.ua_cfg.thread_cnt));
868 } else {
869 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000870 }
871
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000872 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000873
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000874 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000875 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000876
Benny Prijono268ca612006-02-07 12:34:11 +0000877 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000878
879on_error:
880 pjsua_destroy();
881 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000882}
883
884
Benny Prijono834aee32006-02-19 01:38:06 +0000885/* Sleep with polling */
886static void busy_sleep(unsigned msec)
887{
888 pj_time_val timeout, now;
889
890 pj_gettimeofday(&timeout);
891 timeout.msec += msec;
892 pj_time_val_normalize(&timeout);
893
894 do {
Nanang Izzuddin90b83202009-03-02 15:48:45 +0000895 int i;
896 i = msec / 10;
897 while (pjsua_handle_events(10) > 0 && i > 0)
898 --i;
Benny Prijono834aee32006-02-19 01:38:06 +0000899 pj_gettimeofday(&now);
900 } while (PJ_TIME_VAL_LT(now, timeout));
901}
902
Benny Prijonobb995fd2009-08-12 11:03:23 +0000903/* Internal function to destroy STUN resolution session
904 * (pj_stun_resolve).
Benny Prijonoebbf6892007-03-24 17:37:25 +0000905 */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000906static void destroy_stun_resolve(pjsua_stun_resolve *sess)
Benny Prijonoebbf6892007-03-24 17:37:25 +0000907{
Benny Prijonobb995fd2009-08-12 11:03:23 +0000908 PJSUA_LOCK();
909 pj_list_erase(sess);
910 PJSUA_UNLOCK();
Benny Prijonoebbf6892007-03-24 17:37:25 +0000911
Benny Prijonobb995fd2009-08-12 11:03:23 +0000912 pj_assert(sess->stun_sock==NULL);
913 pj_pool_release(sess->pool);
914}
Benny Prijonoebbf6892007-03-24 17:37:25 +0000915
Benny Prijonobb995fd2009-08-12 11:03:23 +0000916/* This is the internal function to be called when STUN resolution
917 * session (pj_stun_resolve) has completed.
918 */
919static void stun_resolve_complete(pjsua_stun_resolve *sess)
920{
921 pj_stun_resolve_result result;
922
923 pj_bzero(&result, sizeof(result));
924 result.token = sess->token;
925 result.status = sess->status;
926 result.name = sess->srv[sess->idx];
927 pj_memcpy(&result.addr, &sess->addr, sizeof(result.addr));
928
929 if (result.status == PJ_SUCCESS) {
930 char addr[PJ_INET6_ADDRSTRLEN+10];
931 pj_sockaddr_print(&result.addr, addr, sizeof(addr), 3);
932 PJ_LOG(4,(THIS_FILE,
933 "STUN resolution success, using %.*s, address is %s",
934 (int)sess->srv[sess->idx].slen,
935 sess->srv[sess->idx].ptr,
936 addr));
937 } else {
938 char errmsg[PJ_ERR_MSG_SIZE];
939 pj_strerror(result.status, errmsg, sizeof(errmsg));
940 PJ_LOG(1,(THIS_FILE, "STUN resolution failed: %s", errmsg));
941 }
942
943 sess->cb(&result);
944
945 if (!sess->blocking) {
946 destroy_stun_resolve(sess);
947 }
948}
949
950/* This is the callback called by the STUN socket (pj_stun_sock)
951 * to report it's state. We use this as part of testing the
952 * STUN server.
953 */
954static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
955 pj_stun_sock_op op,
956 pj_status_t status)
957{
958 pjsua_stun_resolve *sess;
959
Nanang Izzuddinae1c6152009-08-17 16:30:04 +0000960 sess = (pjsua_stun_resolve*) pj_stun_sock_get_user_data(stun_sock);
Benny Prijonobb995fd2009-08-12 11:03:23 +0000961 pj_assert(stun_sock == sess->stun_sock);
962
Benny Prijonoebbf6892007-03-24 17:37:25 +0000963 if (status != PJ_SUCCESS) {
Benny Prijonobb995fd2009-08-12 11:03:23 +0000964 char errmsg[PJ_ERR_MSG_SIZE];
965 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000966
Benny Prijonobb995fd2009-08-12 11:03:23 +0000967 PJ_LOG(4,(THIS_FILE, "STUN resolution for %.*s failed: %s",
968 (int)sess->srv[sess->idx].slen,
969 sess->srv[sess->idx].ptr, errmsg));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000970
Benny Prijonobb995fd2009-08-12 11:03:23 +0000971 sess->status = status;
972
973 pj_stun_sock_destroy(stun_sock);
974 sess->stun_sock = NULL;
975
976 ++sess->idx;
977 resolve_stun_entry(sess);
978
979 return PJ_FALSE;
980
981 } else if (op == PJ_STUN_SOCK_BINDING_OP) {
982 pj_stun_sock_info ssi;
983
984 pj_stun_sock_get_info(stun_sock, &ssi);
985 pj_memcpy(&sess->addr, &ssi.srv_addr, sizeof(sess->addr));
986
987 sess->status = PJ_SUCCESS;
988 pj_stun_sock_destroy(stun_sock);
989 sess->stun_sock = NULL;
990
991 stun_resolve_complete(sess);
992
993 return PJ_FALSE;
994
995 } else
996 return PJ_TRUE;
997
998}
999
1000/* This is an internal function to resolve and test current
1001 * server entry in pj_stun_resolve session. It is called by
1002 * pjsua_resolve_stun_servers() and test_stun_on_status() above
1003 */
1004static void resolve_stun_entry(pjsua_stun_resolve *sess)
1005{
1006 /* Loop while we have entry to try */
1007 for (; sess->idx < sess->count; ++sess->idx) {
1008 const int af = pj_AF_INET();
1009 pj_str_t hostpart;
1010 pj_uint16_t port;
1011 pj_stun_sock_cb stun_sock_cb;
1012
1013 pj_assert(sess->idx < sess->count);
1014
1015 /* Parse the server entry into host:port */
1016 sess->status = pj_sockaddr_parse2(af, 0, &sess->srv[sess->idx],
1017 &hostpart, &port, NULL);
1018 if (sess->status != PJ_SUCCESS) {
1019 PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %.*s",
1020 (int)sess->srv[sess->idx].slen,
1021 sess->srv[sess->idx].ptr));
1022 continue;
Benny Prijonoebbf6892007-03-24 17:37:25 +00001023 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001024
1025 /* Use default port if not specified */
1026 if (port == 0)
1027 port = PJ_STUN_PORT;
1028
1029 pj_assert(sess->stun_sock == NULL);
1030
1031 PJ_LOG(4,(THIS_FILE, "Trying STUN server %.*s (%d of %d)..",
1032 (int)sess->srv[sess->idx].slen,
1033 sess->srv[sess->idx].ptr,
1034 sess->idx+1, sess->count));
1035
1036 /* Use STUN_sock to test this entry */
1037 pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
1038 stun_sock_cb.on_status = &test_stun_on_status;
1039 sess->status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
1040 pj_AF_INET(), &stun_sock_cb,
1041 NULL, sess, &sess->stun_sock);
1042 if (sess->status != PJ_SUCCESS) {
1043 char errmsg[PJ_ERR_MSG_SIZE];
1044 pj_strerror(sess->status, errmsg, sizeof(errmsg));
1045 PJ_LOG(4,(THIS_FILE,
1046 "Error creating STUN socket for %.*s: %s",
1047 (int)sess->srv[sess->idx].slen,
1048 sess->srv[sess->idx].ptr, errmsg));
1049
1050 continue;
1051 }
1052
1053 sess->status = pj_stun_sock_start(sess->stun_sock, &hostpart,
1054 port, pjsua_var.resolver);
1055 if (sess->status != PJ_SUCCESS) {
1056 char errmsg[PJ_ERR_MSG_SIZE];
1057 pj_strerror(sess->status, errmsg, sizeof(errmsg));
1058 PJ_LOG(4,(THIS_FILE,
1059 "Error starting STUN socket for %.*s: %s",
1060 (int)sess->srv[sess->idx].slen,
1061 sess->srv[sess->idx].ptr, errmsg));
1062
1063 pj_stun_sock_destroy(sess->stun_sock);
1064 sess->stun_sock = NULL;
1065 continue;
1066 }
1067
1068 /* Done for now, testing will resume/complete asynchronously in
1069 * stun_sock_cb()
1070 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001071 return;
1072 }
1073
Benny Prijonobb995fd2009-08-12 11:03:23 +00001074 if (sess->idx >= sess->count) {
1075 /* No more entries to try */
1076 PJ_ASSERT_ON_FAIL(sess->status != PJ_SUCCESS,
1077 sess->status = PJ_EUNKNOWN);
1078 stun_resolve_complete(sess);
1079 }
1080}
Benny Prijonoebbf6892007-03-24 17:37:25 +00001081
Benny Prijonoebbf6892007-03-24 17:37:25 +00001082
Benny Prijonobb995fd2009-08-12 11:03:23 +00001083/*
1084 * Resolve STUN server.
1085 */
1086PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count,
1087 pj_str_t srv[],
1088 pj_bool_t wait,
1089 void *token,
1090 pj_stun_resolve_cb cb)
1091{
1092 pj_pool_t *pool;
1093 pjsua_stun_resolve *sess;
1094 pj_status_t status;
1095 unsigned i;
1096
1097 PJ_ASSERT_RETURN(count && srv && cb, PJ_EINVAL);
1098
1099 pool = pjsua_pool_create("stunres", 256, 256);
1100 if (!pool)
1101 return PJ_ENOMEM;
1102
1103 sess = PJ_POOL_ZALLOC_T(pool, pjsua_stun_resolve);
1104 sess->pool = pool;
1105 sess->token = token;
1106 sess->cb = cb;
1107 sess->count = count;
1108 sess->blocking = wait;
1109 sess->status = PJ_EPENDING;
1110 sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
1111 for (i=0; i<count; ++i) {
1112 pj_strdup(pool, &sess->srv[i], &srv[i]);
Benny Prijonoebbf6892007-03-24 17:37:25 +00001113 }
1114
Benny Prijonobb995fd2009-08-12 11:03:23 +00001115 PJSUA_LOCK();
1116 pj_list_push_back(&pjsua_var.stun_res, sess);
1117 PJSUA_UNLOCK();
1118
1119 resolve_stun_entry(sess);
1120
1121 if (!wait)
1122 return PJ_SUCCESS;
1123
1124 while (sess->status == PJ_EPENDING) {
1125 pjsua_handle_events(50);
1126 }
1127
1128 status = sess->status;
1129 destroy_stun_resolve(sess);
1130
1131 return status;
1132}
1133
1134/*
1135 * Cancel pending STUN resolution.
1136 */
1137PJ_DEF(pj_status_t) pjsua_cancel_stun_resolution( void *token,
1138 pj_bool_t notify_cb)
1139{
1140 pjsua_stun_resolve *sess;
1141 unsigned cancelled_count = 0;
1142
1143 PJSUA_LOCK();
1144 sess = pjsua_var.stun_res.next;
1145 while (sess != &pjsua_var.stun_res) {
1146 pjsua_stun_resolve *next = sess->next;
1147
1148 if (sess->token == token) {
1149 if (notify_cb) {
1150 pj_stun_resolve_result result;
1151
1152 pj_bzero(&result, sizeof(result));
1153 result.token = token;
1154 result.status = PJ_ECANCELLED;
1155
1156 sess->cb(&result);
1157 }
1158
1159 destroy_stun_resolve(sess);
1160 ++cancelled_count;
1161 }
1162
1163 sess = next;
1164 }
1165 PJSUA_UNLOCK();
1166
1167 return cancelled_count ? PJ_SUCCESS : PJ_ENOTFOUND;
1168}
1169
1170static void internal_stun_resolve_cb(const pj_stun_resolve_result *result)
1171{
1172 pjsua_var.stun_status = result->status;
1173 if (result->status == PJ_SUCCESS) {
1174 pj_memcpy(&pjsua_var.stun_srv, &result->addr, sizeof(result->addr));
1175 }
Benny Prijonoebbf6892007-03-24 17:37:25 +00001176}
1177
Benny Prijono268ca612006-02-07 12:34:11 +00001178/*
Benny Prijonoc97608e2007-03-23 16:34:20 +00001179 * Resolve STUN server.
1180 */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001181pj_status_t resolve_stun_server(pj_bool_t wait)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001182{
1183 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
Benny Prijonobb995fd2009-08-12 11:03:23 +00001184 pj_status_t status;
1185
Benny Prijonoc97608e2007-03-23 16:34:20 +00001186 /* Initialize STUN configuration */
1187 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
1188 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
1189 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
1190
1191 /* Start STUN server resolution */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001192 if (pjsua_var.ua_cfg.stun_srv_cnt) {
1193 pjsua_var.stun_status = PJ_EPENDING;
1194 status = pjsua_resolve_stun_servers(pjsua_var.ua_cfg.stun_srv_cnt,
1195 pjsua_var.ua_cfg.stun_srv,
1196 wait, NULL,
1197 &internal_stun_resolve_cb);
1198 if (wait || status != PJ_SUCCESS) {
Benny Prijonoda9785b2007-04-02 20:43:06 +00001199 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +00001200 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001201 } else {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001202 pjsua_var.stun_status = PJ_SUCCESS;
1203 }
1204
Benny Prijonoc97608e2007-03-23 16:34:20 +00001205 } else if (pjsua_var.stun_status == PJ_EPENDING) {
1206 /* STUN server resolution has been started, wait for the
1207 * result.
1208 */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001209 if (wait) {
1210 while (pjsua_var.stun_status == PJ_EPENDING)
1211 pjsua_handle_events(10);
1212 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001213 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001214
1215 if (pjsua_var.stun_status != PJ_EPENDING &&
1216 pjsua_var.stun_status != PJ_SUCCESS &&
1217 pjsua_var.ua_cfg.stun_ignore_failure)
1218 {
1219 PJ_LOG(2,(THIS_FILE,
1220 "Ignoring STUN resolution failure (by setting)"));
1221 pjsua_var.stun_status = PJ_SUCCESS;
1222 }
1223
1224 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001225}
1226
1227/*
Benny Prijono268ca612006-02-07 12:34:11 +00001228 * Destroy pjsua.
1229 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001230PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001231{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001232 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001233
1234 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001235 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001236
1237 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001238 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1239 if (pjsua_var.thread[i]) {
1240 pj_thread_join(pjsua_var.thread[i]);
1241 pj_thread_destroy(pjsua_var.thread[i]);
1242 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001243 }
Benny Prijono268ca612006-02-07 12:34:11 +00001244 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001245
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 if (pjsua_var.endpt) {
Benny Prijono534a9ba2009-10-13 14:01:59 +00001247 unsigned max_wait;
1248
Benny Prijono384dab42009-10-14 01:58:04 +00001249 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1250
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001251 /* Terminate all calls. */
1252 pjsua_call_hangup_all();
1253
Benny Prijono7f6ee022008-07-31 08:32:46 +00001254 /* Set all accounts to offline */
1255 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1256 if (!pjsua_var.acc[i].valid)
1257 continue;
1258 pjsua_var.acc[i].online_status = PJ_FALSE;
1259 pj_bzero(&pjsua_var.acc[i].rpid, sizeof(pjrpid_element));
1260 }
1261
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001262 /* Terminate all presence subscriptions. */
1263 pjsua_pres_shutdown();
1264
Benny Prijono384dab42009-10-14 01:58:04 +00001265 /* Destroy media (to shutdown media transports etc) */
1266 pjsua_media_subsys_destroy();
1267
Benny Prijono534a9ba2009-10-13 14:01:59 +00001268 /* Wait for sometime until all publish client sessions are done
1269 * (ticket #364)
1270 */
1271 /* First stage, get the maximum wait time */
1272 max_wait = 100;
1273 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1274 if (!pjsua_var.acc[i].valid)
1275 continue;
1276 if (pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec > max_wait)
1277 max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec;
1278 }
1279
1280 /* Second stage, wait for unpublications to complete */
1281 for (i=0; i<(int)(max_wait/50); ++i) {
1282 unsigned j;
1283 for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
1284 if (!pjsua_var.acc[j].valid)
1285 continue;
1286
1287 if (pjsua_var.acc[j].publish_sess)
1288 break;
1289 }
1290 if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
1291 busy_sleep(50);
1292 else
1293 break;
1294 }
1295
1296 /* Third stage, forcefully destroy unfinished unpublications */
1297 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1298 if (pjsua_var.acc[i].publish_sess) {
1299 pjsip_publishc_destroy(pjsua_var.acc[i].publish_sess);
1300 pjsua_var.acc[i].publish_sess = NULL;
1301 }
1302 }
1303
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001304 /* Unregister all accounts */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001305 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1306 if (!pjsua_var.acc[i].valid)
1307 continue;
1308
1309 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001310 pjsua_acc_set_registration(i, PJ_FALSE);
1311 }
1312 }
Benny Prijonobb995fd2009-08-12 11:03:23 +00001313
1314 /* Terminate any pending STUN resolution */
1315 if (!pj_list_empty(&pjsua_var.stun_res)) {
1316 pjsua_stun_resolve *sess = pjsua_var.stun_res.next;
1317 while (sess != &pjsua_var.stun_res) {
1318 pjsua_stun_resolve *next = sess->next;
1319 destroy_stun_resolve(sess);
1320 sess = next;
1321 }
1322 }
1323
Benny Prijono384dab42009-10-14 01:58:04 +00001324 /* Wait until all unregistrations are done (ticket #364) */
1325 /* First stage, get the maximum wait time */
1326 max_wait = 100;
1327 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1328 if (!pjsua_var.acc[i].valid)
1329 continue;
1330 if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait)
1331 max_wait = pjsua_var.acc[i].cfg.unreg_timeout;
1332 }
1333
1334 /* Second stage, wait for unregistrations to complete */
1335 for (i=0; i<(int)(max_wait/50); ++i) {
1336 unsigned j;
1337 for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
1338 if (!pjsua_var.acc[j].valid)
1339 continue;
1340
1341 if (pjsua_var.acc[j].regc)
1342 break;
1343 }
1344 if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
1345 busy_sleep(50);
1346 else
1347 break;
1348 }
1349 /* Note variable 'i' is used below */
1350
Benny Prijonoff3b1462008-07-14 09:32:14 +00001351 /* Wait for some time to allow unregistration and ICE/TURN
1352 * transports shutdown to complete:
Benny Prijono384dab42009-10-14 01:58:04 +00001353 */
1354 if (i < 20)
1355 busy_sleep(1000 - i*50);
Benny Prijonoff3b1462008-07-14 09:32:14 +00001356
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001357 PJ_LOG(4,(THIS_FILE, "Destroying..."));
1358
Benny Prijonod7e26582008-07-18 23:51:49 +00001359 /* Must destroy endpoint first before destroying pools in
1360 * buddies or accounts, since shutting down transaction layer
1361 * may emit events which trigger some buddy or account callbacks
1362 * to be called.
1363 */
1364 pjsip_endpt_destroy(pjsua_var.endpt);
1365 pjsua_var.endpt = NULL;
1366
Benny Prijono4d1cc7b2008-07-14 09:55:01 +00001367 /* Destroy pool in the buddy object */
1368 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
1369 if (pjsua_var.buddy[i].pool) {
1370 pj_pool_release(pjsua_var.buddy[i].pool);
1371 pjsua_var.buddy[i].pool = NULL;
1372 }
1373 }
1374
1375 /* Destroy accounts */
1376 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1377 if (pjsua_var.acc[i].pool) {
1378 pj_pool_release(pjsua_var.acc[i].pool);
1379 pjsua_var.acc[i].pool = NULL;
1380 }
1381 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001382 }
Benny Prijono268ca612006-02-07 12:34:11 +00001383
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 /* Destroy mutex */
1385 if (pjsua_var.mutex) {
1386 pj_mutex_destroy(pjsua_var.mutex);
1387 pjsua_var.mutex = NULL;
1388 }
1389
1390 /* Destroy pool and pool factory. */
1391 if (pjsua_var.pool) {
1392 pj_pool_release(pjsua_var.pool);
1393 pjsua_var.pool = NULL;
1394 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001395
1396 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1397
1398 /* End logging */
1399 if (pjsua_var.log_file) {
1400 pj_file_close(pjsua_var.log_file);
1401 pjsua_var.log_file = NULL;
1402 }
1403
1404 /* Shutdown PJLIB */
1405 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406 }
Benny Prijono268ca612006-02-07 12:34:11 +00001407
Benny Prijonof762ee72006-12-01 11:14:37 +00001408 /* Clear pjsua_var */
1409 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1410
Benny Prijono268ca612006-02-07 12:34:11 +00001411 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412 return PJ_SUCCESS;
1413}
1414
1415
1416/**
1417 * Application is recommended to call this function after all initialization
1418 * is done, so that the library can do additional checking set up
1419 * additional
1420 *
1421 * @return PJ_SUCCESS on success, or the appropriate error code.
1422 */
1423PJ_DEF(pj_status_t) pjsua_start(void)
1424{
1425 pj_status_t status;
1426
1427 status = pjsua_call_subsys_start();
1428 if (status != PJ_SUCCESS)
1429 return status;
1430
1431 status = pjsua_media_subsys_start();
1432 if (status != PJ_SUCCESS)
1433 return status;
1434
1435 status = pjsua_pres_start();
1436 if (status != PJ_SUCCESS)
1437 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001438
1439 return PJ_SUCCESS;
1440}
1441
Benny Prijono9fc735d2006-05-28 14:58:12 +00001442
1443/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 * Poll pjsua for events, and if necessary block the caller thread for
1445 * the specified maximum interval (in miliseconds).
1446 */
1447PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1448{
Benny Prijono897f9f82007-05-03 19:56:21 +00001449#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001450
1451 return pj_symbianos_poll(-1, msec_timeout);
1452
Benny Prijono897f9f82007-05-03 19:56:21 +00001453#else
1454
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001455 unsigned count = 0;
1456 pj_time_val tv;
1457 pj_status_t status;
1458
1459 tv.sec = 0;
1460 tv.msec = msec_timeout;
1461 pj_time_val_normalize(&tv);
1462
1463 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1464
1465 if (status != PJ_SUCCESS)
1466 return -status;
1467
1468 return count;
Nanang Izzuddin82f7a412008-12-17 11:36:22 +00001469
Benny Prijono897f9f82007-05-03 19:56:21 +00001470#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471}
1472
1473
1474/*
1475 * Create memory pool.
1476 */
1477PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1478 pj_size_t increment)
1479{
1480 /* Pool factory is thread safe, no need to lock */
1481 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1482 NULL);
1483}
1484
1485
1486/*
1487 * Internal function to get SIP endpoint instance of pjsua, which is
1488 * needed for example to register module, create transports, etc.
1489 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001490 */
1491PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1492{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001494}
1495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496/*
1497 * Internal function to get media endpoint instance.
1498 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001499 */
1500PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1501{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001503}
1504
Benny Prijono97b87172006-08-24 14:25:14 +00001505/*
1506 * Internal function to get PJSUA pool factory.
1507 */
1508PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1509{
1510 return &pjsua_var.cp.factory;
1511}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512
1513/*****************************************************************************
1514 * PJSUA SIP Transport API.
1515 */
1516
1517/*
Benny Prijono23674a32007-12-01 08:59:25 +00001518 * Tools to get address string.
1519 */
1520static const char *addr_string(const pj_sockaddr_t *addr)
1521{
1522 static char str[128];
1523 str[0] = '\0';
1524 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1525 pj_sockaddr_get_addr(addr),
1526 str, sizeof(str));
1527 return str;
1528}
1529
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001530void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
1531 pjsip_transport_state state,
1532 const pjsip_transport_state_info *info);
1533
Benny Prijono21407322010-03-10 13:33:25 +00001534/* Callback to receive transport state notifications */
1535static void on_tp_state_callback(pjsip_transport *tp,
1536 pjsip_transport_state state,
1537 const pjsip_transport_state_info *info)
1538{
1539 if (pjsua_var.ua_cfg.cb.on_transport_state) {
1540 (*pjsua_var.ua_cfg.cb.on_transport_state)(tp, state, info);
1541 }
1542 if (pjsua_var.old_tp_cb) {
1543 (*pjsua_var.old_tp_cb)(tp, state, info);
1544 }
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001545 pjsua_acc_on_tp_state_changed(tp, state, info);
Benny Prijono21407322010-03-10 13:33:25 +00001546}
1547
Benny Prijono23674a32007-12-01 08:59:25 +00001548/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001549 * Create and initialize SIP socket (and possibly resolve public
1550 * address via STUN, depending on config).
1551 */
Benny Prijono23674a32007-12-01 08:59:25 +00001552static pj_status_t create_sip_udp_sock(int af,
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001553 const pjsua_transport_config *cfg,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001555 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001556{
Benny Prijono23674a32007-12-01 08:59:25 +00001557 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001558 unsigned port = cfg->port;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001559 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001560 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001561 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001562 pj_status_t status;
1563
Benny Prijonoc97608e2007-03-23 16:34:20 +00001564 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +00001565 status = resolve_stun_server(PJ_TRUE);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001566 if (status != PJ_SUCCESS) {
1567 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1568 return status;
1569 }
1570
Benny Prijono23674a32007-12-01 08:59:25 +00001571 /* Initialize bound address */
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001572 if (cfg->bound_addr.slen) {
1573 status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr,
Benny Prijono23674a32007-12-01 08:59:25 +00001574 (pj_uint16_t)port);
1575 if (status != PJ_SUCCESS) {
1576 pjsua_perror(THIS_FILE,
1577 "Unable to resolve transport bound address",
1578 status);
1579 return status;
1580 }
1581 } else {
1582 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1583 }
1584
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001585 /* Create socket */
Benny Prijono23674a32007-12-01 08:59:25 +00001586 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001587 if (status != PJ_SUCCESS) {
1588 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001589 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590 }
1591
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001592 /* Apply QoS, if specified */
1593 status = pj_sock_apply_qos2(sock, cfg->qos_type,
1594 &cfg->qos_params,
1595 2, THIS_FILE, "SIP UDP socket");
1596
1597 /* Bind socket */
Benny Prijono5f55a1f2007-12-05 05:08:29 +00001598 status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001599 if (status != PJ_SUCCESS) {
1600 pjsua_perror(THIS_FILE, "bind() error", status);
1601 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001602 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 }
1604
Benny Prijonoe347cb02007-02-14 14:36:13 +00001605 /* If port is zero, get the bound port */
1606 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001607 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001608 int namelen = sizeof(bound_addr);
1609 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1610 if (status != PJ_SUCCESS) {
1611 pjsua_perror(THIS_FILE, "getsockname() error", status);
1612 pj_sock_close(sock);
1613 return status;
1614 }
1615
Benny Prijono23674a32007-12-01 08:59:25 +00001616 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001617 }
1618
Benny Prijonoc97608e2007-03-23 16:34:20 +00001619 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001620 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1621 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001622 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001623 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624 }
1625
1626 /* Get the published address, either by STUN or by resolving
1627 * the name of local host.
1628 */
Benny Prijono23674a32007-12-01 08:59:25 +00001629 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001630 /*
1631 * Public address is already specified, no need to resolve the
1632 * address, only set the port.
1633 */
Benny Prijono23674a32007-12-01 08:59:25 +00001634 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1635 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001636
1637 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001638 /*
1639 * STUN is specified, resolve the address with STUN.
1640 */
Benny Prijono23674a32007-12-01 08:59:25 +00001641 if (af != pj_AF_INET()) {
1642 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1643 pj_sock_close(sock);
1644 return PJ_EAFNOTSUP;
1645 }
1646
Benny Prijono14c2b862007-02-21 00:40:05 +00001647 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001648 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1649 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001650 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001651 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001652 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001654 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001655 }
1656
1657 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001658 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659
Benny Prijono42d08d22007-12-20 11:23:07 +00001660 if (pj_sockaddr_has_addr(&bind_addr)) {
1661 pj_sockaddr_copy_addr(p_pub_addr, &bind_addr);
1662 } else {
1663 status = pj_gethostip(af, p_pub_addr);
1664 if (status != PJ_SUCCESS) {
1665 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
1666 pj_sock_close(sock);
1667 return status;
1668 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001669 }
1670
Benny Prijono23674a32007-12-01 08:59:25 +00001671 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1672 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673 }
1674
1675 *p_sock = sock;
1676
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001677 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001678 addr_string(p_pub_addr),
1679 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680
Benny Prijonod8410532006-06-15 11:04:33 +00001681 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001682}
1683
1684
1685/*
1686 * Create SIP transport.
1687 */
1688PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1689 const pjsua_transport_config *cfg,
1690 pjsua_transport_id *p_id)
1691{
1692 pjsip_transport *tp;
1693 unsigned id;
1694 pj_status_t status;
1695
1696 PJSUA_LOCK();
1697
1698 /* Find empty transport slot */
1699 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001700 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701 break;
1702 }
1703
1704 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1705 status = PJ_ETOOMANY;
1706 pjsua_perror(THIS_FILE, "Error creating transport", status);
1707 goto on_return;
1708 }
1709
1710 /* Create the transport */
Benny Prijonob2477142007-12-05 04:09:59 +00001711 if (type==PJSIP_TRANSPORT_UDP || type==PJSIP_TRANSPORT_UDP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001712 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001713 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001714 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001715 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001716 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001717 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001718 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001719 pjsip_host_port addr_name;
1720
1721 /* Supply default config if it's not specified */
1722 if (cfg == NULL) {
1723 pjsua_transport_config_default(&config);
1724 cfg = &config;
1725 }
1726
Benny Prijono0a5cad82006-09-26 13:21:02 +00001727 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001728 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1729 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001730 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001731 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1732 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001733 if (status != PJ_SUCCESS) {
1734 pjsua_perror(THIS_FILE,
1735 "Unable to resolve transport public address",
1736 status);
1737 goto on_return;
1738 }
1739 }
1740
1741 /* Create the socket and possibly resolve the address with STUN
1742 * (only when public address is not specified).
1743 */
Benny Prijono23674a32007-12-01 08:59:25 +00001744 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001745 cfg, &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001746 if (status != PJ_SUCCESS)
1747 goto on_return;
1748
Benny Prijono23674a32007-12-01 08:59:25 +00001749 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1750 addr_name.host = pj_str(hostbuf);
1751 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752
1753 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001754 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1755 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001756 if (status != PJ_SUCCESS) {
1757 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1758 status);
1759 pj_sock_close(sock);
1760 goto on_return;
1761 }
1762
Benny Prijonoe93e2872006-06-28 16:46:49 +00001763
1764 /* Save the transport */
1765 pjsua_var.tpdata[id].type = type;
1766 pjsua_var.tpdata[id].local_name = tp->local_name;
1767 pjsua_var.tpdata[id].data.tp = tp;
1768
Benny Prijono3569c0d2007-04-06 10:29:20 +00001769#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1770
Benny Prijonob2477142007-12-05 04:09:59 +00001771 } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001772 /*
1773 * Create TCP transport.
1774 */
1775 pjsua_transport_config config;
1776 pjsip_tpfactory *tcp;
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001777 pjsip_tcp_transport_cfg tcp_cfg;
1778
1779 pjsip_tcp_transport_cfg_default(&tcp_cfg, pj_AF_INET());
Benny Prijonoe93e2872006-06-28 16:46:49 +00001780
1781 /* Supply default config if it's not specified */
1782 if (cfg == NULL) {
1783 pjsua_transport_config_default(&config);
1784 cfg = &config;
1785 }
1786
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001787 /* Configure bind address */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001788 if (cfg->port)
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001789 pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001790
Benny Prijono0a5cad82006-09-26 13:21:02 +00001791 if (cfg->bound_addr.slen) {
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001792 status = pj_sockaddr_set_str_addr(tcp_cfg.af,
1793 &tcp_cfg.bind_addr,
1794 &cfg->bound_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001795 if (status != PJ_SUCCESS) {
1796 pjsua_perror(THIS_FILE,
1797 "Unable to resolve transport bound address",
1798 status);
1799 goto on_return;
1800 }
1801 }
1802
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001803 /* Set published name */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001804 if (cfg->public_addr.slen)
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001805 tcp_cfg.addr_name.host = cfg->public_addr;
1806
1807 /* Copy the QoS settings */
1808 tcp_cfg.qos_type = cfg->qos_type;
1809 pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params,
1810 sizeof(cfg->qos_params));
Benny Prijonoe93e2872006-06-28 16:46:49 +00001811
1812 /* Create the TCP transport */
Benny Prijono4d79b0f2009-10-25 09:02:07 +00001813 status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001814
1815 if (status != PJ_SUCCESS) {
1816 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1817 status);
1818 goto on_return;
1819 }
1820
1821 /* Save the transport */
1822 pjsua_var.tpdata[id].type = type;
1823 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1824 pjsua_var.tpdata[id].data.factory = tcp;
1825
Benny Prijono3569c0d2007-04-06 10:29:20 +00001826#endif /* PJ_HAS_TCP */
1827
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001828#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1829 } else if (type == PJSIP_TRANSPORT_TLS) {
1830 /*
1831 * Create TLS transport.
1832 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001833 pjsua_transport_config config;
1834 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001835 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001836 pj_sockaddr_in local_addr;
1837
1838 /* Supply default config if it's not specified */
1839 if (cfg == NULL) {
1840 pjsua_transport_config_default(&config);
1841 config.port = 5061;
1842 cfg = &config;
1843 }
1844
1845 /* Init local address */
1846 pj_sockaddr_in_init(&local_addr, 0, 0);
1847
1848 if (cfg->port)
1849 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1850
1851 if (cfg->bound_addr.slen) {
1852 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1853 if (status != PJ_SUCCESS) {
1854 pjsua_perror(THIS_FILE,
1855 "Unable to resolve transport bound address",
1856 status);
1857 goto on_return;
1858 }
1859 }
1860
1861 /* Init published name */
1862 pj_bzero(&a_name, sizeof(pjsip_host_port));
1863 if (cfg->public_addr.slen)
1864 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001865
1866 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001867 &cfg->tls_setting,
1868 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001869 if (status != PJ_SUCCESS) {
1870 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1871 status);
1872 goto on_return;
1873 }
1874
1875 /* Save the transport */
1876 pjsua_var.tpdata[id].type = type;
1877 pjsua_var.tpdata[id].local_name = tls->addr_name;
1878 pjsua_var.tpdata[id].data.factory = tls;
1879#endif
1880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001881 } else {
1882 status = PJSIP_EUNSUPTRANSPORT;
1883 pjsua_perror(THIS_FILE, "Error creating transport", status);
1884 goto on_return;
1885 }
1886
Nanang Izzuddin2fb937e2010-02-24 05:43:34 +00001887 /* Set transport state callback */
1888 if (pjsua_var.ua_cfg.cb.on_transport_state) {
Benny Prijono21407322010-03-10 13:33:25 +00001889 pjsip_tp_state_callback tpcb;
1890 pjsip_tpmgr *tpmgr;
1891
1892 tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
Nanang Izzuddin91cfa962010-04-14 06:57:35 +00001893 tpcb = pjsip_tpmgr_get_state_cb(tpmgr);
Benny Prijono21407322010-03-10 13:33:25 +00001894
1895 if (tpcb != &on_tp_state_callback) {
1896 pjsua_var.old_tp_cb = tpcb;
Nanang Izzuddin91cfa962010-04-14 06:57:35 +00001897 pjsip_tpmgr_set_state_cb(tpmgr, &on_tp_state_callback);
Benny Prijono21407322010-03-10 13:33:25 +00001898 }
Nanang Izzuddin2fb937e2010-02-24 05:43:34 +00001899 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001900
1901 /* Return the ID */
1902 if (p_id) *p_id = id;
1903
1904 status = PJ_SUCCESS;
1905
1906on_return:
1907
1908 PJSUA_UNLOCK();
1909
Benny Prijonod8410532006-06-15 11:04:33 +00001910 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001911}
1912
1913
1914/*
1915 * Register transport that has been created by application.
1916 */
1917PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1918 pjsua_transport_id *p_id)
1919{
1920 unsigned id;
1921
1922 PJSUA_LOCK();
1923
1924 /* Find empty transport slot */
1925 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001926 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001927 break;
1928 }
1929
1930 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1931 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1932 PJSUA_UNLOCK();
1933 return PJ_ETOOMANY;
1934 }
1935
1936 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001937 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001938 pjsua_var.tpdata[id].local_name = tp->local_name;
1939 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940
1941 /* Return the ID */
1942 if (p_id) *p_id = id;
1943
1944 PJSUA_UNLOCK();
1945
1946 return PJ_SUCCESS;
1947}
1948
1949
1950/*
1951 * Enumerate all transports currently created in the system.
1952 */
1953PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1954 unsigned *p_count )
1955{
1956 unsigned i, count;
1957
1958 PJSUA_LOCK();
1959
1960 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1961 ++i)
1962 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001963 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001964 continue;
1965
1966 id[count++] = i;
1967 }
1968
1969 *p_count = count;
1970
1971 PJSUA_UNLOCK();
1972
1973 return PJ_SUCCESS;
1974}
1975
1976
1977/*
1978 * Get information about transports.
1979 */
1980PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1981 pjsua_transport_info *info)
1982{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001983 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001984 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001985
Benny Prijonoac623b32006-07-03 15:19:31 +00001986 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001987
1988 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001989 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1990 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001991
1992 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001993 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001994
1995 PJSUA_LOCK();
1996
Benny Prijonoe93e2872006-06-28 16:46:49 +00001997 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1998
1999 pjsip_transport *tp = t->data.tp;
2000
2001 if (tp == NULL) {
2002 PJSUA_UNLOCK();
2003 return PJ_EINVALIDOP;
2004 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002005
Benny Prijonoe93e2872006-06-28 16:46:49 +00002006 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00002007 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00002008 info->type_name = pj_str(tp->type_name);
2009 info->info = pj_str(tp->info);
2010 info->flag = tp->flag;
2011 info->addr_len = tp->addr_len;
2012 info->local_addr = tp->local_addr;
2013 info->local_name = tp->local_name;
2014 info->usage_count = pj_atomic_get(tp->ref_cnt);
2015
Benny Prijono0a5cad82006-09-26 13:21:02 +00002016 status = PJ_SUCCESS;
2017
Benny Prijonoe93e2872006-06-28 16:46:49 +00002018 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
2019
2020 pjsip_tpfactory *factory = t->data.factory;
2021
2022 if (factory == NULL) {
2023 PJSUA_UNLOCK();
2024 return PJ_EINVALIDOP;
2025 }
2026
2027 info->id = id;
2028 info->type = t->type;
2029 info->type_name = pj_str("TCP");
2030 info->info = pj_str("TCP transport");
2031 info->flag = factory->flag;
2032 info->addr_len = sizeof(factory->local_addr);
2033 info->local_addr = factory->local_addr;
2034 info->local_name = factory->addr_name;
2035 info->usage_count = 0;
2036
Benny Prijono0a5cad82006-09-26 13:21:02 +00002037 status = PJ_SUCCESS;
2038
2039 } else {
2040 pj_assert(!"Unsupported transport");
2041 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00002042 }
2043
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002044
2045 PJSUA_UNLOCK();
2046
Benny Prijono0a5cad82006-09-26 13:21:02 +00002047 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002048}
2049
2050
2051/*
2052 * Disable a transport or re-enable it.
2053 */
2054PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
2055 pj_bool_t enabled)
2056{
2057 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002058 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
2059 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002060
2061 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00002062 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063
2064
2065 /* To be done!! */
2066 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002067 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068
2069 return PJ_EINVALIDOP;
2070}
2071
2072
2073/*
2074 * Close the transport.
2075 */
2076PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
2077 pj_bool_t force )
2078{
Benny Prijono5ff61872007-02-01 03:37:11 +00002079 pj_status_t status;
2080
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002081 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002082 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
2083 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084
2085 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00002086 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002087
Benny Prijono0a5cad82006-09-26 13:21:02 +00002088 /* Note: destroy() may not work if there are objects still referencing
2089 * the transport.
2090 */
2091 if (force) {
2092 switch (pjsua_var.tpdata[id].type) {
2093 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00002094 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
2095 if (status != PJ_SUCCESS)
2096 return status;
2097 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
2098 if (status != PJ_SUCCESS)
2099 return status;
2100 break;
2101
2102 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00002103 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00002104 /* This will close the TCP listener, but existing TCP/TLS
2105 * connections (if any) will still linger
2106 */
2107 status = (*pjsua_var.tpdata[id].data.factory->destroy)
2108 (pjsua_var.tpdata[id].data.factory);
2109 if (status != PJ_SUCCESS)
2110 return status;
2111
Benny Prijono0a5cad82006-09-26 13:21:02 +00002112 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00002113
Benny Prijono1ebd6142006-10-19 15:48:02 +00002114 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00002115 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00002116 }
2117
2118 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00002119 /* If force is not specified, transports will be closed at their
2120 * convenient time. However this will leak PJSUA-API transport
2121 * descriptors as PJSUA-API wouldn't know when exactly the
2122 * transport is closed thus it can't cleanup PJSUA transport
2123 * descriptor.
2124 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00002125 switch (pjsua_var.tpdata[id].type) {
2126 case PJSIP_TRANSPORT_UDP:
2127 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00002128 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00002129 case PJSIP_TRANSPORT_TCP:
2130 return (*pjsua_var.tpdata[id].data.factory->destroy)
2131 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00002132 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00002133 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00002134 }
2135 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002136
Benny Prijono5ff61872007-02-01 03:37:11 +00002137 /* Cleanup pjsua data when force is applied */
2138 if (force) {
2139 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
2140 pjsua_var.tpdata[id].data.ptr = NULL;
2141 }
2142
2143 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002144}
2145
2146
2147/*
2148 * Add additional headers etc in msg_data specified by application
2149 * when sending requests.
2150 */
2151void pjsua_process_msg_data(pjsip_tx_data *tdata,
2152 const pjsua_msg_data *msg_data)
2153{
2154 pj_bool_t allow_body;
2155 const pjsip_hdr *hdr;
2156
Benny Prijono56315612006-07-18 14:39:40 +00002157 /* Always add User-Agent */
2158 if (pjsua_var.ua_cfg.user_agent.slen &&
2159 tdata->msg->type == PJSIP_REQUEST_MSG)
2160 {
2161 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
2162 pjsip_hdr *h;
2163 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
2164 &STR_USER_AGENT,
2165 &pjsua_var.ua_cfg.user_agent);
2166 pjsip_msg_add_hdr(tdata->msg, h);
2167 }
2168
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 if (!msg_data)
2170 return;
2171
2172 hdr = msg_data->hdr_list.next;
2173 while (hdr && hdr != &msg_data->hdr_list) {
2174 pjsip_hdr *new_hdr;
2175
Benny Prijonoa1e69682007-05-11 15:14:34 +00002176 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177 pjsip_msg_add_hdr(tdata->msg, new_hdr);
2178
2179 hdr = hdr->next;
2180 }
2181
2182 allow_body = (tdata->msg->body == NULL);
2183
2184 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
2185 pjsip_media_type ctype;
2186 pjsip_msg_body *body;
2187
2188 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
2189 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
2190 &msg_data->msg_body);
2191 tdata->msg->body = body;
2192 }
2193}
2194
2195
2196/*
2197 * Add route_set to outgoing requests
2198 */
2199void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
2200 const pjsip_route_hdr *route_set )
2201{
2202 const pjsip_route_hdr *r;
2203
2204 r = route_set->next;
2205 while (r != route_set) {
2206 pjsip_route_hdr *new_r;
2207
Benny Prijonoa1e69682007-05-11 15:14:34 +00002208 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
2210
2211 r = r->next;
2212 }
2213}
2214
2215
2216/*
2217 * Simple version of MIME type parsing (it doesn't support parameters)
2218 */
2219void pjsua_parse_media_type( pj_pool_t *pool,
2220 const pj_str_t *mime,
2221 pjsip_media_type *media_type)
2222{
2223 pj_str_t tmp;
2224 char *pos;
2225
Benny Prijonoac623b32006-07-03 15:19:31 +00002226 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002227
2228 pj_strdup_with_null(pool, &tmp, mime);
2229
2230 pos = pj_strchr(&tmp, '/');
2231 if (pos) {
2232 media_type->type.ptr = tmp.ptr;
2233 media_type->type.slen = (pos-tmp.ptr);
2234 media_type->subtype.ptr = pos+1;
2235 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
2236 } else {
2237 media_type->type = tmp;
2238 }
2239}
2240
2241
2242/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002243 * Internal function to init transport selector from transport id.
2244 */
2245void pjsua_init_tpselector(pjsua_transport_id tp_id,
2246 pjsip_tpselector *sel)
2247{
2248 pjsua_transport_data *tpdata;
2249 unsigned flag;
2250
2251 pj_bzero(sel, sizeof(*sel));
2252 if (tp_id == PJSUA_INVALID_ID)
2253 return;
2254
Benny Prijonoa1e69682007-05-11 15:14:34 +00002255 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002256 tpdata = &pjsua_var.tpdata[tp_id];
2257
2258 flag = pjsip_transport_get_flag_from_type(tpdata->type);
2259
2260 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
2261 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
2262 sel->u.transport = tpdata->data.tp;
2263 } else {
2264 sel->type = PJSIP_TPSELECTOR_LISTENER;
2265 sel->u.listener = tpdata->data.factory;
2266 }
2267}
2268
2269
Benny Prijono6ba8c542007-10-16 01:34:14 +00002270/* Callback upon NAT detection completion */
2271static void nat_detect_cb(void *user_data,
2272 const pj_stun_nat_detect_result *res)
2273{
2274 PJ_UNUSED_ARG(user_data);
2275
2276 pjsua_var.nat_in_progress = PJ_FALSE;
2277 pjsua_var.nat_status = res->status;
2278 pjsua_var.nat_type = res->nat_type;
2279
2280 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
2281 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
2282 }
2283}
2284
2285
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002286/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002287 * Detect NAT type.
2288 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00002289PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002290{
2291 pj_status_t status;
2292
Benny Prijono6ba8c542007-10-16 01:34:14 +00002293 if (pjsua_var.nat_in_progress)
2294 return PJ_SUCCESS;
2295
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002296 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +00002297 status = resolve_stun_server(PJ_TRUE);
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002298 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002299 pjsua_var.nat_status = status;
2300 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002301 return status;
2302 }
2303
2304 /* Make sure we have STUN */
2305 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00002306 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
2307 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002308 }
2309
Benny Prijono6ba8c542007-10-16 01:34:14 +00002310 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
2311 &pjsua_var.stun_cfg,
2312 NULL, &nat_detect_cb);
2313
2314 if (status != PJ_SUCCESS) {
2315 pjsua_var.nat_status = status;
2316 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
2317 return status;
2318 }
2319
2320 pjsua_var.nat_in_progress = PJ_TRUE;
2321
2322 return PJ_SUCCESS;
2323}
2324
2325
2326/*
2327 * Get NAT type.
2328 */
2329PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
2330{
2331 *type = pjsua_var.nat_type;
2332 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002333}
2334
2335
2336/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002337 * Verify that valid SIP url is given.
2338 */
2339PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
2340{
2341 pjsip_uri *p;
2342 pj_pool_t *pool;
2343 char *url;
2344 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
2345
2346 if (!len) return -1;
2347
2348 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
2349 if (!pool) return -1;
2350
Benny Prijonoa1e69682007-05-11 15:14:34 +00002351 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002352 pj_ansi_strcpy(url, c_url);
2353
2354 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002355 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
2356 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
2357 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002358 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00002359 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002360
2361 pj_pool_release(pool);
2362 return p ? 0 : -1;
2363}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002364
Benny Prijono73bb7232009-10-20 13:56:26 +00002365/*
2366 * Schedule a timer entry.
2367 */
2368PJ_DEF(pj_status_t) pjsua_schedule_timer( pj_timer_entry *entry,
2369 const pj_time_val *delay)
2370{
2371 return pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, delay);
2372}
2373
2374/*
2375 * Cancel the previously scheduled timer.
2376 *
2377 */
2378PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry)
2379{
2380 pjsip_endpt_cancel_timer(pjsua_var.endpt, entry);
2381}
Benny Prijonoda9785b2007-04-02 20:43:06 +00002382
Benny Prijono91d06b62008-09-20 12:16:56 +00002383/**
2384 * Normalize route URI (check for ";lr" and append one if it doesn't
2385 * exist and pjsua_config.force_lr is set.
2386 */
2387pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri)
2388{
2389 pj_str_t tmp_uri;
2390 pj_pool_t *tmp_pool;
2391 pjsip_uri *uri_obj;
2392 pjsip_sip_uri *sip_uri;
2393
2394 tmp_pool = pjsua_pool_create("tmplr%p", 512, 512);
2395 if (!tmp_pool)
2396 return PJ_ENOMEM;
2397
2398 pj_strdup_with_null(tmp_pool, &tmp_uri, uri);
2399
2400 uri_obj = pjsip_parse_uri(tmp_pool, tmp_uri.ptr, tmp_uri.slen, 0);
2401 if (!uri_obj) {
2402 PJ_LOG(1,(THIS_FILE, "Invalid route URI: %.*s",
2403 (int)uri->slen, uri->ptr));
2404 pj_pool_release(tmp_pool);
2405 return PJSIP_EINVALIDURI;
2406 }
2407
2408 if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) &&
2409 !PJSIP_URI_SCHEME_IS_SIP(uri_obj))
2410 {
2411 PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s",
2412 (int)uri->slen, uri->ptr));
2413 pj_pool_release(tmp_pool);
2414 return PJSIP_EINVALIDSCHEME;
2415 }
2416
2417 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri_obj);
2418
2419 /* Done if force_lr is disabled or if lr parameter is present */
2420 if (!pjsua_var.ua_cfg.force_lr || sip_uri->lr_param) {
2421 pj_pool_release(tmp_pool);
2422 return PJ_SUCCESS;
2423 }
2424
2425 /* Set lr param */
2426 sip_uri->lr_param = 1;
2427
2428 /* Print the URI */
2429 tmp_uri.ptr = (char*) pj_pool_alloc(tmp_pool, PJSIP_MAX_URL_SIZE);
2430 tmp_uri.slen = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, uri_obj,
2431 tmp_uri.ptr, PJSIP_MAX_URL_SIZE);
2432 if (tmp_uri.slen < 1) {
2433 PJ_LOG(1,(THIS_FILE, "Route URI is too long: %.*s",
2434 (int)uri->slen, uri->ptr));
2435 pj_pool_release(tmp_pool);
2436 return PJSIP_EURITOOLONG;
2437 }
2438
2439 /* Clone the URI */
2440 pj_strdup_with_null(pool, uri, &tmp_uri);
2441
Benny Prijonodc20c592009-10-15 06:55:30 +00002442 pj_pool_release(tmp_pool);
Benny Prijono91d06b62008-09-20 12:16:56 +00002443 return PJ_SUCCESS;
2444}
2445
Benny Prijonoda9785b2007-04-02 20:43:06 +00002446/*
2447 * This is a utility function to dump the stack states to log, using
2448 * verbosity level 3.
2449 */
2450PJ_DEF(void) pjsua_dump(pj_bool_t detail)
2451{
2452 unsigned old_decor;
2453 unsigned i;
Benny Prijonoda9785b2007-04-02 20:43:06 +00002454
2455 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2456
2457 old_decor = pj_log_get_decor();
2458 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2459
2460 if (detail)
2461 pj_dump_config();
2462
2463 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2464
2465 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2466
2467 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2468 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2469 pjsua_call *call = &pjsua_var.calls[i];
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002470 pjmedia_transport_info tpinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002471 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002472
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002473 /* MSVC complains about tpinfo not being initialized */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002474 //pj_bzero(&tpinfo, sizeof(tpinfo));
Benny Prijono4f093d22007-04-09 08:54:32 +00002475
Benny Prijono734fc2d2008-03-17 16:05:35 +00002476 pjmedia_transport_info_init(&tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002477 pjmedia_transport_get_info(call->med_tp, &tpinfo);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002478
Benny Prijono5186eae2007-12-03 14:38:25 +00002479 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002480 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002481 pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name, addr_buf,
Benny Prijono5186eae2007-12-03 14:38:25 +00002482 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002483 }
2484
2485 pjsip_tsx_layer_dump(detail);
2486 pjsip_ua_dump(detail);
2487
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002488// Dumping complete call states may require a 'large' buffer
2489// (about 3KB per call session, including RTCP XR).
2490#if 0
Benny Prijonoda9785b2007-04-02 20:43:06 +00002491 /* Dump all invite sessions: */
2492 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2493
2494 if (pjsua_call_get_count() == 0) {
2495
2496 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2497
2498 } else {
2499 unsigned i;
2500
2501 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2502 if (pjsua_call_is_active(i)) {
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002503 /* Tricky logging, since call states log string tends to be
2504 * longer than PJ_LOG_MAX_SIZE.
2505 */
2506 char buf[1024 * 3];
2507 unsigned call_dump_len;
2508 unsigned part_len;
2509 unsigned part_idx;
2510 unsigned log_decor;
2511
Benny Prijonoda9785b2007-04-02 20:43:06 +00002512 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002513 call_dump_len = strlen(buf);
2514
2515 log_decor = pj_log_get_decor();
2516 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE |
2517 PJ_LOG_HAS_CR));
2518 PJ_LOG(3,(THIS_FILE, "\n"));
2519 pj_log_set_decor(0);
2520
2521 part_idx = 0;
2522 part_len = PJ_LOG_MAX_SIZE-80;
2523 while (part_idx < call_dump_len) {
2524 char p_orig, *p;
2525
2526 p = &buf[part_idx];
2527 if (part_idx + part_len > call_dump_len)
2528 part_len = call_dump_len - part_idx;
2529 p_orig = p[part_len];
2530 p[part_len] = '\0';
2531 PJ_LOG(3,(THIS_FILE, "%s", p));
2532 p[part_len] = p_orig;
2533 part_idx += part_len;
2534 }
2535 pj_log_set_decor(log_decor);
Benny Prijonoda9785b2007-04-02 20:43:06 +00002536 }
2537 }
2538 }
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002539#endif
Benny Prijonoda9785b2007-04-02 20:43:06 +00002540
2541 /* Dump presence status */
2542 pjsua_pres_dump(detail);
2543
2544 pj_log_set_decor(old_decor);
2545 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2546}
2547