blob: a60301ade2db61cee5d60da6c3dd67ec49255ba1 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono268ca612006-02-07 12:34:11 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijono268ca612006-02-07 12:34:11 +000021
Benny Prijono268ca612006-02-07 12:34:11 +000022
Benny Prijono84126ab2006-02-09 09:30:09 +000023#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000024
25
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026/* PJSUA application instance. */
27struct pjsua_data pjsua_var;
Benny Prijono84126ab2006-02-09 09:30:09 +000028
29
Benny Prijono44e88ea2007-10-30 15:42:35 +000030PJ_DEF(struct pjsua_data*) pjsua_get_var(void)
31{
32 return &pjsua_var;
33}
34
35
Benny Prijonoeebe9af2006-06-13 22:57:13 +000036/* Display error */
37PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
38 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000039{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000041
Benny Prijonoeebe9af2006-06-13 22:57:13 +000042 pj_strerror(status, errmsg, sizeof(errmsg));
43 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000044}
45
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000048{
49 unsigned i;
50
Benny Prijonoc97608e2007-03-23 16:34:20 +000051 pj_bzero(&pjsua_var, sizeof(pjsua_var));
52
Benny Prijonoeebe9af2006-06-13 22:57:13 +000053 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
54 pjsua_var.acc[i].index = i;
55
56 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
57 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000058
59 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijono6ba8c542007-10-16 01:34:14 +000060 pjsua_var.nat_status = PJ_EPENDING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000061}
Benny Prijonodc39fe82006-05-26 12:17:46 +000062
63
Benny Prijono1f61a8f2007-08-16 10:11:44 +000064PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
65{
66 pj_bzero(cfg, sizeof(*cfg));
67
68 cfg->msg_logging = PJ_TRUE;
69 cfg->level = 5;
70 cfg->console_level = 4;
71 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
72 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
73}
74
75PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
76 pjsua_logging_config *dst,
77 const pjsua_logging_config *src)
78{
79 pj_memcpy(dst, src, sizeof(*src));
80 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
81}
82
83PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
84{
85 pj_bzero(cfg, sizeof(*cfg));
86
87 cfg->max_calls = 4;
88 cfg->thread_cnt = 1;
Benny Prijono6ba8c542007-10-16 01:34:14 +000089 cfg->nat_type_in_sdp = 2;
Benny Prijono1f61a8f2007-08-16 10:11:44 +000090}
91
Benny Prijono1f61a8f2007-08-16 10:11:44 +000092PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
93 pjsua_config *dst,
94 const pjsua_config *src)
95{
96 unsigned i;
97
98 pj_memcpy(dst, src, sizeof(*src));
99
100 for (i=0; i<src->outbound_proxy_cnt; ++i) {
101 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
102 &src->outbound_proxy[i]);
103 }
104
105 for (i=0; i<src->cred_count; ++i) {
106 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
107 }
108
109 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
110 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
111 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
112 pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
113}
114
115PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
116{
117 pj_bzero(msg_data, sizeof(*msg_data));
118 pj_list_init(&msg_data->hdr_list);
119}
120
121PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
122{
123 pj_bzero(cfg, sizeof(*cfg));
124 pjsip_tls_setting_default(&cfg->tls_setting);
125}
126
127PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
128 pjsua_transport_config *dst,
129 const pjsua_transport_config *src)
130{
131 PJ_UNUSED_ARG(pool);
132 pj_memcpy(dst, src, sizeof(*src));
133}
134
135PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
136{
137 pj_bzero(cfg, sizeof(*cfg));
138
139 cfg->reg_timeout = PJSUA_REG_INTERVAL;
140 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijono15b02302007-09-27 14:07:07 +0000141 cfg->auto_update_nat = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000142 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000143}
144
145PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
146{
147 pj_bzero(cfg, sizeof(*cfg));
148}
149
150PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
151{
152 pj_bzero(cfg, sizeof(*cfg));
153
154 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijonocf0b4b22007-10-06 17:31:09 +0000155 cfg->audio_frame_ptime = 10;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000156 cfg->max_media_ports = 32;
157 cfg->has_ioqueue = PJ_TRUE;
158 cfg->thread_cnt = 1;
159 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
160 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
161 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
162 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
163}
164
Benny Prijonodc39fe82006-05-26 12:17:46 +0000165
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000166/*****************************************************************************
167 * This is a very simple PJSIP module, whose sole purpose is to display
168 * incoming and outgoing messages to log. This module will have priority
169 * higher than transport layer, which means:
170 *
171 * - incoming messages will come to this module first before reaching
172 * transaction layer.
173 *
174 * - outgoing messages will come to this module last, after the message
175 * has been 'printed' to contiguous buffer by transport layer and
176 * appropriate transport instance has been decided for this message.
177 *
178 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000179
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000180/* Notification on incoming messages */
181static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
182{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000183 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
184 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 "--end msg--",
186 rdata->msg_info.len,
187 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000188 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 rdata->pkt_info.src_name,
190 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000191 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000192 rdata->msg_info.msg_buf));
193
194 /* Always return false, otherwise messages will not get processed! */
195 return PJ_FALSE;
196}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000197
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198/* Notification on outgoing messages */
199static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
200{
201
202 /* Important note:
203 * tp_info field is only valid after outgoing messages has passed
204 * transport layer. So don't try to access tp_info when the module
205 * has lower priority than transport layer.
206 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000207
Benny Prijonob4a17c92006-07-10 14:40:21 +0000208 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
209 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 "--end msg--",
211 (tdata->buf.cur - tdata->buf.start),
212 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000213 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000214 tdata->tp_info.dst_name,
215 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000216 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000220 return PJ_SUCCESS;
221}
222
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000223/* The module instance. */
224static pjsip_module pjsua_msg_logger =
225{
226 NULL, NULL, /* prev, next. */
227 { "mod-pjsua-log", 13 }, /* Name. */
228 -1, /* Id */
229 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
230 NULL, /* load() */
231 NULL, /* start() */
232 NULL, /* stop() */
233 NULL, /* unload() */
234 &logging_on_rx_msg, /* on_rx_request() */
235 &logging_on_rx_msg, /* on_rx_response() */
236 &logging_on_tx_msg, /* on_tx_request. */
237 &logging_on_tx_msg, /* on_tx_response() */
238 NULL, /* on_tsx_state() */
239
240};
241
242
243/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000244 * Another simple module to handle incoming OPTIONS request
245 */
246
247/* Notification on incoming request */
248static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
249{
250 pjsip_tx_data *tdata;
251 pjsip_response_addr res_addr;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000252 pjmedia_sock_info skinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000253 pjmedia_sdp_session *sdp;
254 const pjsip_hdr *cap_hdr;
255 pj_status_t status;
256
257 /* Only want to handle OPTIONS requests */
258 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000259 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000260 {
261 return PJ_FALSE;
262 }
263
264 /* Create basic response. */
265 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
266 &tdata);
267 if (status != PJ_SUCCESS) {
268 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
269 return PJ_TRUE;
270 }
271
272 /* Add Allow header */
273 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
274 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000275 pjsip_msg_add_hdr(tdata->msg,
276 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000277 }
278
279 /* Add Accept header */
280 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
281 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000282 pjsip_msg_add_hdr(tdata->msg,
283 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000284 }
285
286 /* Add Supported header */
287 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
288 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000289 pjsip_msg_add_hdr(tdata->msg,
290 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000291 }
292
293 /* Add Allow-Events header from the evsub module */
294 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
295 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000296 pjsip_msg_add_hdr(tdata->msg,
297 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000298 }
299
300 /* Add User-Agent header */
301 if (pjsua_var.ua_cfg.user_agent.slen) {
302 const pj_str_t USER_AGENT = { "User-Agent", 10};
303 pjsip_hdr *h;
304
305 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
306 &USER_AGENT,
307 &pjsua_var.ua_cfg.user_agent);
308 pjsip_msg_add_hdr(tdata->msg, h);
309 }
310
Benny Prijono617c5bc2007-04-02 19:51:21 +0000311 /* Get media socket info */
312 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &skinfo);
313
Benny Prijono56315612006-07-18 14:39:40 +0000314 /* Add SDP body, using call0's RTP address */
315 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000316 &skinfo, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000317 if (status == PJ_SUCCESS) {
318 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
319 }
320
321 /* Send response statelessly */
322 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
323 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
324 if (status != PJ_SUCCESS)
325 pjsip_tx_data_dec_ref(tdata);
326
327 return PJ_TRUE;
328}
329
330
331/* The module instance. */
332static pjsip_module pjsua_options_handler =
333{
334 NULL, NULL, /* prev, next. */
335 { "mod-pjsua-options", 17 }, /* Name. */
336 -1, /* Id */
337 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
338 NULL, /* load() */
339 NULL, /* start() */
340 NULL, /* stop() */
341 NULL, /* unload() */
342 &options_on_rx_request, /* on_rx_request() */
343 NULL, /* on_rx_response() */
344 NULL, /* on_tx_request. */
345 NULL, /* on_tx_response() */
346 NULL, /* on_tsx_state() */
347
348};
349
350
351/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352 * These two functions are the main callbacks registered to PJSIP stack
353 * to receive SIP request and response messages that are outside any
354 * dialogs and any transactions.
355 */
Benny Prijono268ca612006-02-07 12:34:11 +0000356
357/*
358 * Handler for receiving incoming requests.
359 *
360 * This handler serves multiple purposes:
361 * - it receives requests outside dialogs.
362 * - it receives requests inside dialogs, when the requests are
363 * unhandled by other dialog usages. Example of these
364 * requests are: MESSAGE.
365 */
366static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
367{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 pj_bool_t processed = PJ_FALSE;
369
370 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000371
Benny Prijono84126ab2006-02-09 09:30:09 +0000372 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000373
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000374 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000375 }
376
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000377 PJSUA_UNLOCK();
378
379 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000380}
381
382
383/*
384 * Handler for receiving incoming responses.
385 *
386 * This handler serves multiple purposes:
387 * - it receives strayed responses (i.e. outside any dialog and
388 * outside any transactions).
389 * - it receives responses coming to a transaction, when pjsua
390 * module is set as transaction user for the transaction.
391 * - it receives responses inside a dialog, when these responses
392 * are unhandled by other dialog usages.
393 */
394static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
395{
396 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000397 return PJ_FALSE;
398}
399
400
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000401/*****************************************************************************
402 * Logging.
403 */
404
405/* Log callback */
406static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000407{
Benny Prijono572d4852006-11-23 21:50:02 +0000408 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000409
410 if (pjsua_var.log_file) {
411 pj_ssize_t size = len;
412 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000413 /* This will slow things down considerably! Don't do it!
414 pj_file_flush(pjsua_var.log_file);
415 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 }
417
Benny Prijono572d4852006-11-23 21:50:02 +0000418 if (level <= (int)pjsua_var.log_cfg.console_level) {
419 if (pjsua_var.log_cfg.cb)
420 (*pjsua_var.log_cfg.cb)(level, buffer, len);
421 else
422 pj_log_write(level, buffer, len);
423 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424}
425
426
427/*
428 * Application can call this function at any time (after pjsua_create(), of
429 * course) to change logging settings.
430 */
431PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
432{
433 pj_status_t status;
434
435 /* Save config. */
436 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
437
438 /* Redirect log function to ours */
439 pj_log_set_log_func( &log_writer );
440
Benny Prijono9cb09a22007-08-30 09:35:10 +0000441 /* Set decor */
442 pj_log_set_decor(pjsua_var.log_cfg.decor);
443
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000444 /* Close existing file, if any */
445 if (pjsua_var.log_file) {
446 pj_file_close(pjsua_var.log_file);
447 pjsua_var.log_file = NULL;
448 }
449
450 /* If output log file is desired, create the file: */
451 if (pjsua_var.log_cfg.log_filename.slen) {
452
453 status = pj_file_open(pjsua_var.pool,
454 pjsua_var.log_cfg.log_filename.ptr,
455 PJ_O_WRONLY,
456 &pjsua_var.log_file);
457
458 if (status != PJ_SUCCESS) {
459 pjsua_perror(THIS_FILE, "Error creating log file", status);
460 return status;
461 }
462 }
463
464 /* Unregister msg logging if it's previously registered */
465 if (pjsua_msg_logger.id >= 0) {
466 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
467 pjsua_msg_logger.id = -1;
468 }
469
470 /* Enable SIP message logging */
471 if (pjsua_var.log_cfg.msg_logging)
472 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
473
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000474 return PJ_SUCCESS;
475}
476
477
478/*****************************************************************************
479 * PJSUA Base API.
480 */
481
482/* Worker thread function. */
483static int worker_thread(void *arg)
484{
485 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000486
Benny Prijono268ca612006-02-07 12:34:11 +0000487 PJ_UNUSED_ARG(arg);
488
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489 while (!pjsua_var.thread_quit_flag) {
490 int count;
491
492 count = pjsua_handle_events(TIMEOUT);
493 if (count < 0)
494 pj_thread_sleep(TIMEOUT);
495 }
Benny Prijono268ca612006-02-07 12:34:11 +0000496
497 return 0;
498}
499
Benny Prijonodc39fe82006-05-26 12:17:46 +0000500
Benny Prijono268ca612006-02-07 12:34:11 +0000501/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000502 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000503 */
504PJ_DEF(pj_status_t) pjsua_create(void)
505{
506 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000507
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 /* Init pjsua data */
509 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000510
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000511 /* Set default logging settings */
512 pjsua_logging_config_default(&pjsua_var.log_cfg);
513
514 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000515 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
517
Benny Prijono268ca612006-02-07 12:34:11 +0000518
Benny Prijonofccab712006-02-22 22:23:22 +0000519 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000520 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000522
Benny Prijonoec921342007-03-24 13:00:30 +0000523 /* Init PJNATH */
524 status = pjnath_init();
525 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000526
Benny Prijono094d3ad2006-11-21 08:41:00 +0000527 /* Set default sound device ID */
528 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
529
Benny Prijono268ca612006-02-07 12:34:11 +0000530 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000531 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000532
533 /* Create memory pool for application. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000534 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
535
536 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000537
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538 /* Create mutex */
539 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
540 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000541 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000543 return status;
544 }
545
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000546 /* Must create SIP endpoint to initialize SIP parser. The parser
547 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000548 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000549 status = pjsip_endpt_create(&pjsua_var.cp.factory,
550 pj_gethostname()->ptr,
551 &pjsua_var.endpt);
552 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000553
554
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000555 return PJ_SUCCESS;
556}
557
558
559/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 * Initialize pjsua with the specified settings. All the settings are
561 * optional, and the default values will be used when the config is not
562 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000563 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000564PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
565 const pjsua_logging_config *log_cfg,
566 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000567{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 pjsua_config default_cfg;
569 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000570 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571 pj_status_t status;
572
573
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 /* Create default configurations when the config is not supplied */
575
576 if (ua_cfg == NULL) {
577 pjsua_config_default(&default_cfg);
578 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000579 }
580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 if (media_cfg == NULL) {
582 pjsua_media_config_default(&default_media_cfg);
583 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000584 }
585
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 /* Initialize logging first so that info/errors can be captured */
587 if (log_cfg) {
588 status = pjsua_reconfigure_logging(log_cfg);
589 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000590 }
591
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000592 /* If nameserver is configured, create DNS resolver instance and
593 * set it to be used by SIP resolver.
594 */
595 if (ua_cfg->nameserver_count) {
596#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000597 unsigned i;
598
599 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000600 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
601 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000602 if (status != PJ_SUCCESS) {
603 pjsua_perror(THIS_FILE, "Error creating resolver", status);
604 return status;
605 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000606
607 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000608 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
609 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000610 ua_cfg->nameserver, NULL);
611 if (status != PJ_SUCCESS) {
612 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
613 return status;
614 }
615
616 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000617 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000618 if (status != PJ_SUCCESS) {
619 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
620 return status;
621 }
622
623 /* Print nameservers */
624 for (i=0; i<ua_cfg->nameserver_count; ++i) {
625 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
626 (int)ua_cfg->nameserver[i].slen,
627 ua_cfg->nameserver[i].ptr));
628 }
629#else
630 PJ_LOG(2,(THIS_FILE,
631 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
632#endif
633 }
634
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000635 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000636
637 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000638 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
639 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000640
Benny Prijonodc39fe82006-05-26 12:17:46 +0000641
642 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
644 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000645
Benny Prijonodc39fe82006-05-26 12:17:46 +0000646
Benny Prijono053f5222006-11-11 16:16:04 +0000647 /* Initialize Replaces support. */
648 status = pjsip_replaces_init_module( pjsua_var.endpt );
649 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
650
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000651 /* Initialize 100rel support */
652 status = pjsip_100rel_init_module(pjsua_var.endpt);
653 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000654
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000655 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000656 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000658 {
659 NULL, NULL, /* prev, next. */
660 { "mod-pjsua", 9 }, /* Name. */
661 -1, /* Id */
662 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
663 NULL, /* load() */
664 NULL, /* start() */
665 NULL, /* stop() */
666 NULL, /* unload() */
667 &mod_pjsua_on_rx_request, /* on_rx_request() */
668 &mod_pjsua_on_rx_response, /* on_rx_response() */
669 NULL, /* on_tx_request. */
670 NULL, /* on_tx_response() */
671 NULL, /* on_tsx_state() */
672 };
673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000675
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
677 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000678 }
679
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680
Benny Prijonodc39fe82006-05-26 12:17:46 +0000681
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682 /* Initialize PJSUA call subsystem: */
683 status = pjsua_call_subsys_init(ua_cfg);
684 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000685 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000686
687
Benny Prijonoc97608e2007-03-23 16:34:20 +0000688 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000689
Benny Prijonoc97608e2007-03-23 16:34:20 +0000690 status = pjsua_resolve_stun_server(PJ_FALSE);
691 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
692 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
693 return status;
694 }
695
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000696 /* Initialize PJSUA media subsystem */
697 status = pjsua_media_subsys_init(media_cfg);
698 if (status != PJ_SUCCESS)
699 goto on_error;
700
Benny Prijonodc39fe82006-05-26 12:17:46 +0000701
702 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000703 status = pjsip_evsub_init_module(pjsua_var.endpt);
704 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000705
Benny Prijonodc39fe82006-05-26 12:17:46 +0000706
707 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000708 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
709 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000710
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000711 /* Init PUBLISH module */
712 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000713
714 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000715 status = pjsip_xfer_init_module( pjsua_var.endpt );
716 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000717
718 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000719 status = pjsua_pres_init();
720 if (status != PJ_SUCCESS)
721 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000722
723 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000724 status = pjsua_im_init();
725 if (status != PJ_SUCCESS)
726 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000727
Benny Prijonoc8141a82006-08-20 09:12:19 +0000728 /* Register OPTIONS handler */
729 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
730
731 /* Add OPTIONS in Allow header */
732 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
733 NULL, 1, &STR_OPTIONS);
734
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000735 /* Start worker thread if needed. */
736 if (pjsua_var.ua_cfg.thread_cnt) {
737 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000738
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000739 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
740 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000741
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000742 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
743 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
744 NULL, 0, 0, &pjsua_var.thread[i]);
745 if (status != PJ_SUCCESS)
746 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000747 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000748 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
749 pjsua_var.ua_cfg.thread_cnt));
750 } else {
751 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000752 }
753
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000754 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000755
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000756 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000757 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000758
Benny Prijono268ca612006-02-07 12:34:11 +0000759 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000760
761on_error:
762 pjsua_destroy();
763 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000764}
765
766
Benny Prijono834aee32006-02-19 01:38:06 +0000767/* Sleep with polling */
768static void busy_sleep(unsigned msec)
769{
Benny Prijonob2c96822007-05-03 13:31:21 +0000770#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000771 /* Ideally we shouldn't call pj_thread_sleep() and rather
772 * CActiveScheduler::WaitForAnyRequest() here, but that will
773 * drag in Symbian header and it doesn't look pretty.
774 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000775 pj_thread_sleep(msec);
776#else
Benny Prijono834aee32006-02-19 01:38:06 +0000777 pj_time_val timeout, now;
778
779 pj_gettimeofday(&timeout);
780 timeout.msec += msec;
781 pj_time_val_normalize(&timeout);
782
783 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000784 while (pjsua_handle_events(10) > 0)
785 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000786 pj_gettimeofday(&now);
787 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000788#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000789}
790
Benny Prijonoebbf6892007-03-24 17:37:25 +0000791
Benny Prijonoebbf6892007-03-24 17:37:25 +0000792/*
793 * Callback function to receive notification from the resolver
794 * when the resolution process completes.
795 */
796static void stun_dns_srv_resolver_cb(void *user_data,
797 pj_status_t status,
798 const pj_dns_srv_record *rec)
799{
800 unsigned i;
801
802 PJ_UNUSED_ARG(user_data);
803
804 pjsua_var.stun_status = status;
805
806 if (status != PJ_SUCCESS) {
807 /* DNS SRV resolution failed. If stun_host is specified, resolve
808 * it with gethostbyname()
809 */
810 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000811 pj_str_t str_host, str_port;
812 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000813 pj_hostent he;
814
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000815 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
816 if (str_port.ptr != NULL) {
817 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
818 str_host.slen = (str_port.ptr - str_host.ptr);
819 str_port.ptr++;
820 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
821 str_host.slen - 1;
822 port = (int)pj_strtoul(&str_port);
823 if (port < 1 || port > 65535) {
824 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
825 pjsua_var.stun_status = PJ_EINVAL;
826 return;
827 }
828 } else {
829 str_host = pjsua_var.ua_cfg.stun_host;
830 port = 3478;
831 }
832
833 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000834
835 if (pjsua_var.stun_status == PJ_SUCCESS) {
836 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
837 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000838 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000839
840 PJ_LOG(3,(THIS_FILE,
841 "STUN server %.*s resolved, address is %s:%d",
842 (int)pjsua_var.ua_cfg.stun_host.slen,
843 pjsua_var.ua_cfg.stun_host.ptr,
844 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
845 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
846 }
847 } else {
848 char errmsg[PJ_ERR_MSG_SIZE];
849
850 pj_strerror(status, errmsg, sizeof(errmsg));
851 PJ_LOG(1,(THIS_FILE,
852 "DNS SRV resolution failed for STUN server %.*s: %s",
853 (int)pjsua_var.ua_cfg.stun_domain.slen,
854 pjsua_var.ua_cfg.stun_domain.ptr,
855 errmsg));
856 }
857 return;
858 }
859
Benny Prijonof5afa922007-06-11 16:51:18 +0000860 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
861 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
862 rec->entry[0].port);
863 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
864 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000865
866 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
867 (int)pjsua_var.ua_cfg.stun_domain.slen,
868 pjsua_var.ua_cfg.stun_domain.ptr,
869 rec->count));
870
871 for (i=0; i<rec->count; ++i) {
872 PJ_LOG(3,(THIS_FILE,
873 " %d: prio=%d, weight=%d %s:%d",
874 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000875 pj_inet_ntoa(rec->entry[i].server.addr[0]),
876 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000877 }
878
879}
880
Benny Prijono268ca612006-02-07 12:34:11 +0000881/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000882 * Resolve STUN server.
883 */
884pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
885{
886 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
887 /* Initialize STUN configuration */
888 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
889 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
890 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
891
892 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000893
894 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000895
Benny Prijonoebbf6892007-03-24 17:37:25 +0000896 /* If stun_domain is specified, resolve STUN servers with DNS
897 * SRV resolution.
898 */
899 if (pjsua_var.ua_cfg.stun_domain.slen) {
900 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000901 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000902
903 /* Fail if resolver is not configured */
904 if (pjsua_var.resolver == NULL) {
905 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
906 "stun_domain is specified"));
907 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
908 return PJLIB_UTIL_EDNSNONS;
909 }
910 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000911 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000912 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
913 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000914 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000915 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000916 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
917 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000918 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000919 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000920 } else {
921 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000922 }
923 }
924 /* Otherwise if stun_host is specified, resolve STUN server with
925 * gethostbyname().
926 */
927 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000928 pj_str_t str_host, str_port;
929 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000930 pj_hostent he;
931
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000932 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
933 if (str_port.ptr != NULL) {
934 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
935 str_host.slen = (str_port.ptr - str_host.ptr);
936 str_port.ptr++;
937 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
938 str_host.slen - 1;
939 port = (int)pj_strtoul(&str_port);
940 if (port < 1 || port > 65535) {
941 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
942 pjsua_var.stun_status = PJ_EINVAL;
943 return pjsua_var.stun_status;
944 }
945 } else {
946 str_host = pjsua_var.ua_cfg.stun_host;
947 port = 3478;
948 }
949
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000950 pjsua_var.stun_status =
951 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
952 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000953
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000954 if (pjsua_var.stun_status != PJ_SUCCESS) {
955 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000956
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000957 if (pjsua_var.stun_status == PJ_SUCCESS) {
958 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
959 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
960 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
961 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000962 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000963
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000964 PJ_LOG(3,(THIS_FILE,
965 "STUN server %.*s resolved, address is %s:%d",
966 (int)pjsua_var.ua_cfg.stun_host.slen,
967 pjsua_var.ua_cfg.stun_host.ptr,
968 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
969 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
970
Benny Prijonoc97608e2007-03-23 16:34:20 +0000971 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000972 /* Otherwise disable STUN. */
973 else {
974 pjsua_var.stun_status = PJ_SUCCESS;
975 }
976
977
Benny Prijonoc97608e2007-03-23 16:34:20 +0000978 return pjsua_var.stun_status;
979
980 } else if (pjsua_var.stun_status == PJ_EPENDING) {
981 /* STUN server resolution has been started, wait for the
982 * result.
983 */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000984 if (wait) {
985 while (pjsua_var.stun_status == PJ_EPENDING)
986 pjsua_handle_events(10);
987 }
988
989 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000990
991 } else {
992 /* STUN server has been resolved, return the status */
993 return pjsua_var.stun_status;
994 }
995}
996
997/*
Benny Prijono268ca612006-02-07 12:34:11 +0000998 * Destroy pjsua.
999 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001000PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001001{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001002 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001003
1004 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001005 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001006
1007 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001008 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1009 if (pjsua_var.thread[i]) {
1010 pj_thread_join(pjsua_var.thread[i]);
1011 pj_thread_destroy(pjsua_var.thread[i]);
1012 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001013 }
Benny Prijono268ca612006-02-07 12:34:11 +00001014 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001015
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001016 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001017 /* Terminate all calls. */
1018 pjsua_call_hangup_all();
1019
1020 /* Terminate all presence subscriptions. */
1021 pjsua_pres_shutdown();
1022
1023 /* Unregister, if required: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001024 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1025 if (!pjsua_var.acc[i].valid)
1026 continue;
1027
1028 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001029 pjsua_acc_set_registration(i, PJ_FALSE);
1030 }
1031 }
1032
1033 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001034 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1035 busy_sleep(1000);
1036 }
Benny Prijono834aee32006-02-19 01:38:06 +00001037
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001038 /* Destroy media */
1039 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001040
Benny Prijono268ca612006-02-07 12:34:11 +00001041 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001042 if (pjsua_var.endpt) {
1043 pjsip_endpt_destroy(pjsua_var.endpt);
1044 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001045 }
Benny Prijono268ca612006-02-07 12:34:11 +00001046
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001047 /* Destroy mutex */
1048 if (pjsua_var.mutex) {
1049 pj_mutex_destroy(pjsua_var.mutex);
1050 pjsua_var.mutex = NULL;
1051 }
1052
1053 /* Destroy pool and pool factory. */
1054 if (pjsua_var.pool) {
1055 pj_pool_release(pjsua_var.pool);
1056 pjsua_var.pool = NULL;
1057 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001058
1059 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1060
1061 /* End logging */
1062 if (pjsua_var.log_file) {
1063 pj_file_close(pjsua_var.log_file);
1064 pjsua_var.log_file = NULL;
1065 }
1066
1067 /* Shutdown PJLIB */
1068 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001069 }
Benny Prijono268ca612006-02-07 12:34:11 +00001070
Benny Prijonof762ee72006-12-01 11:14:37 +00001071 /* Clear pjsua_var */
1072 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1073
Benny Prijono268ca612006-02-07 12:34:11 +00001074 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001075 return PJ_SUCCESS;
1076}
1077
1078
1079/**
1080 * Application is recommended to call this function after all initialization
1081 * is done, so that the library can do additional checking set up
1082 * additional
1083 *
1084 * @return PJ_SUCCESS on success, or the appropriate error code.
1085 */
1086PJ_DEF(pj_status_t) pjsua_start(void)
1087{
1088 pj_status_t status;
1089
1090 status = pjsua_call_subsys_start();
1091 if (status != PJ_SUCCESS)
1092 return status;
1093
1094 status = pjsua_media_subsys_start();
1095 if (status != PJ_SUCCESS)
1096 return status;
1097
1098 status = pjsua_pres_start();
1099 if (status != PJ_SUCCESS)
1100 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001101
1102 return PJ_SUCCESS;
1103}
1104
Benny Prijono9fc735d2006-05-28 14:58:12 +00001105
1106/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001107 * Poll pjsua for events, and if necessary block the caller thread for
1108 * the specified maximum interval (in miliseconds).
1109 */
1110PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1111{
Benny Prijono897f9f82007-05-03 19:56:21 +00001112#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1113 /* Ideally we shouldn't call pj_thread_sleep() and rather
1114 * CActiveScheduler::WaitForAnyRequest() here, but that will
1115 * drag in Symbian header and it doesn't look pretty.
1116 */
1117 pj_thread_sleep(msec_timeout);
1118 return msec_timeout;
1119#else
1120
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001121 unsigned count = 0;
1122 pj_time_val tv;
1123 pj_status_t status;
1124
1125 tv.sec = 0;
1126 tv.msec = msec_timeout;
1127 pj_time_val_normalize(&tv);
1128
1129 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1130
1131 if (status != PJ_SUCCESS)
1132 return -status;
1133
1134 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001135#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001136}
1137
1138
1139/*
1140 * Create memory pool.
1141 */
1142PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1143 pj_size_t increment)
1144{
1145 /* Pool factory is thread safe, no need to lock */
1146 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1147 NULL);
1148}
1149
1150
1151/*
1152 * Internal function to get SIP endpoint instance of pjsua, which is
1153 * needed for example to register module, create transports, etc.
1154 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001155 */
1156PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1157{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001158 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001159}
1160
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001161/*
1162 * Internal function to get media endpoint instance.
1163 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001164 */
1165PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1166{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001167 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001168}
1169
Benny Prijono97b87172006-08-24 14:25:14 +00001170/*
1171 * Internal function to get PJSUA pool factory.
1172 */
1173PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1174{
1175 return &pjsua_var.cp.factory;
1176}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001177
1178/*****************************************************************************
1179 * PJSUA SIP Transport API.
1180 */
1181
1182/*
1183 * Create and initialize SIP socket (and possibly resolve public
1184 * address via STUN, depending on config).
1185 */
1186static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
1187 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001188 pj_sock_t *p_sock,
1189 pj_sockaddr_in *p_pub_addr)
1190{
Benny Prijonoc97608e2007-03-23 16:34:20 +00001191 char ip_addr[32];
1192 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001193 pj_sock_t sock;
1194 pj_status_t status;
1195
Benny Prijonoc97608e2007-03-23 16:34:20 +00001196 /* Make sure STUN server resolution has completed */
1197 status = pjsua_resolve_stun_server(PJ_TRUE);
1198 if (status != PJ_SUCCESS) {
1199 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1200 return status;
1201 }
1202
Benny Prijono8ab968f2007-07-20 08:08:30 +00001203 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001204 if (status != PJ_SUCCESS) {
1205 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001206 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 }
1208
Benny Prijonof90ef4f2007-02-12 14:59:57 +00001209 status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.s_addr),
1210 (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001211 if (status != PJ_SUCCESS) {
1212 pjsua_perror(THIS_FILE, "bind() error", status);
1213 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001214 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215 }
1216
Benny Prijonoe347cb02007-02-14 14:36:13 +00001217 /* If port is zero, get the bound port */
1218 if (port == 0) {
1219 pj_sockaddr_in bound_addr;
1220 int namelen = sizeof(bound_addr);
1221 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1222 if (status != PJ_SUCCESS) {
1223 pjsua_perror(THIS_FILE, "getsockname() error", status);
1224 pj_sock_close(sock);
1225 return status;
1226 }
1227
1228 port = pj_ntohs(bound_addr.sin_port);
1229 }
1230
Benny Prijonoc97608e2007-03-23 16:34:20 +00001231 if (pjsua_var.stun_srv.addr.sa_family != 0) {
1232 pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1233 stun_srv = pj_str(ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001234 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001235 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001236 }
1237
1238 /* Get the published address, either by STUN or by resolving
1239 * the name of local host.
1240 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001241 if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001242 /*
1243 * STUN is specified, resolve the address with STUN.
1244 */
Benny Prijono14c2b862007-02-21 00:40:05 +00001245 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001246 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1247 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijonoc97608e2007-03-23 16:34:20 +00001248 p_pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001249 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001250 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001252 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253 }
1254
Benny Prijono0a5cad82006-09-26 13:21:02 +00001255 } else if (p_pub_addr->sin_addr.s_addr != 0) {
1256 /*
1257 * Public address is already specified, no need to resolve the
1258 * address, only set the port.
1259 */
Benny Prijonoe347cb02007-02-14 14:36:13 +00001260 if (p_pub_addr->sin_port == 0)
1261 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001262
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001263 } else {
1264
Benny Prijono594e4c52006-09-14 18:51:01 +00001265 pj_bzero(p_pub_addr, sizeof(pj_sockaddr_in));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266
Benny Prijono594e4c52006-09-14 18:51:01 +00001267 status = pj_gethostip(&p_pub_addr->sin_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268 if (status != PJ_SUCCESS) {
Benny Prijonob43bad72007-01-20 05:11:08 +00001269 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001270 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001271 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001272 }
1273
Benny Prijono8ab968f2007-07-20 08:08:30 +00001274 p_pub_addr->sin_family = pj_AF_INET();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001275 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001276 }
1277
1278 *p_sock = sock;
1279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
1281 pj_inet_ntoa(p_pub_addr->sin_addr),
1282 (int)pj_ntohs(p_pub_addr->sin_port)));
1283
Benny Prijonod8410532006-06-15 11:04:33 +00001284 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001285}
1286
1287
1288/*
1289 * Create SIP transport.
1290 */
1291PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1292 const pjsua_transport_config *cfg,
1293 pjsua_transport_id *p_id)
1294{
1295 pjsip_transport *tp;
1296 unsigned id;
1297 pj_status_t status;
1298
1299 PJSUA_LOCK();
1300
1301 /* Find empty transport slot */
1302 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001303 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001304 break;
1305 }
1306
1307 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1308 status = PJ_ETOOMANY;
1309 pjsua_perror(THIS_FILE, "Error creating transport", status);
1310 goto on_return;
1311 }
1312
1313 /* Create the transport */
1314 if (type == PJSIP_TRANSPORT_UDP) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001315 /*
1316 * Create UDP transport.
1317 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001318 pjsua_transport_config config;
Benny Prijonod8410532006-06-15 11:04:33 +00001319 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001320 pj_sockaddr_in bound_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001321 pj_sockaddr_in pub_addr;
1322 pjsip_host_port addr_name;
1323
1324 /* Supply default config if it's not specified */
1325 if (cfg == NULL) {
1326 pjsua_transport_config_default(&config);
1327 cfg = &config;
1328 }
1329
Benny Prijono0a5cad82006-09-26 13:21:02 +00001330 /* Initialize bound address, if any */
1331 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
1332 if (cfg->bound_addr.slen) {
1333 status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr);
1334 if (status != PJ_SUCCESS) {
1335 pjsua_perror(THIS_FILE,
1336 "Unable to resolve transport bound address",
1337 status);
1338 goto on_return;
1339 }
1340 }
1341
1342 /* Initialize the public address from the config, if any */
1343 pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port);
1344 if (cfg->public_addr.slen) {
1345 status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr);
1346 if (status != PJ_SUCCESS) {
1347 pjsua_perror(THIS_FILE,
1348 "Unable to resolve transport public address",
1349 status);
1350 goto on_return;
1351 }
1352 }
1353
1354 /* Create the socket and possibly resolve the address with STUN
1355 * (only when public address is not specified).
1356 */
1357 status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001358 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359 if (status != PJ_SUCCESS)
1360 goto on_return;
1361
1362 addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr));
1363 addr_name.port = pj_ntohs(pub_addr.sin_port);
1364
1365 /* Create UDP transport */
1366 status = pjsip_udp_transport_attach( pjsua_var.endpt, sock,
1367 &addr_name, 1,
1368 &tp);
1369 if (status != PJ_SUCCESS) {
1370 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1371 status);
1372 pj_sock_close(sock);
1373 goto on_return;
1374 }
1375
Benny Prijonoe93e2872006-06-28 16:46:49 +00001376
1377 /* Save the transport */
1378 pjsua_var.tpdata[id].type = type;
1379 pjsua_var.tpdata[id].local_name = tp->local_name;
1380 pjsua_var.tpdata[id].data.tp = tp;
1381
Benny Prijono3569c0d2007-04-06 10:29:20 +00001382#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1383
Benny Prijonoe93e2872006-06-28 16:46:49 +00001384 } else if (type == PJSIP_TRANSPORT_TCP) {
1385 /*
1386 * Create TCP transport.
1387 */
1388 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001389 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001390 pjsip_tpfactory *tcp;
1391 pj_sockaddr_in local_addr;
1392
1393 /* Supply default config if it's not specified */
1394 if (cfg == NULL) {
1395 pjsua_transport_config_default(&config);
1396 cfg = &config;
1397 }
1398
1399 /* Init local address */
1400 pj_sockaddr_in_init(&local_addr, 0, 0);
1401
1402 if (cfg->port)
1403 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1404
Benny Prijono0a5cad82006-09-26 13:21:02 +00001405 if (cfg->bound_addr.slen) {
1406 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1407 if (status != PJ_SUCCESS) {
1408 pjsua_perror(THIS_FILE,
1409 "Unable to resolve transport bound address",
1410 status);
1411 goto on_return;
1412 }
1413 }
1414
1415 /* Init published name */
1416 pj_bzero(&a_name, sizeof(pjsip_host_port));
1417 if (cfg->public_addr.slen)
1418 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001419
1420 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001421 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1422 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001423
1424 if (status != PJ_SUCCESS) {
1425 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1426 status);
1427 goto on_return;
1428 }
1429
1430 /* Save the transport */
1431 pjsua_var.tpdata[id].type = type;
1432 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1433 pjsua_var.tpdata[id].data.factory = tcp;
1434
Benny Prijono3569c0d2007-04-06 10:29:20 +00001435#endif /* PJ_HAS_TCP */
1436
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001437#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1438 } else if (type == PJSIP_TRANSPORT_TLS) {
1439 /*
1440 * Create TLS transport.
1441 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001442 /*
1443 * Create TCP transport.
1444 */
1445 pjsua_transport_config config;
1446 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001447 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001448 pj_sockaddr_in local_addr;
1449
1450 /* Supply default config if it's not specified */
1451 if (cfg == NULL) {
1452 pjsua_transport_config_default(&config);
1453 config.port = 5061;
1454 cfg = &config;
1455 }
1456
1457 /* Init local address */
1458 pj_sockaddr_in_init(&local_addr, 0, 0);
1459
1460 if (cfg->port)
1461 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1462
1463 if (cfg->bound_addr.slen) {
1464 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1465 if (status != PJ_SUCCESS) {
1466 pjsua_perror(THIS_FILE,
1467 "Unable to resolve transport bound address",
1468 status);
1469 goto on_return;
1470 }
1471 }
1472
1473 /* Init published name */
1474 pj_bzero(&a_name, sizeof(pjsip_host_port));
1475 if (cfg->public_addr.slen)
1476 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001477
1478 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001479 &cfg->tls_setting,
1480 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001481 if (status != PJ_SUCCESS) {
1482 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1483 status);
1484 goto on_return;
1485 }
1486
1487 /* Save the transport */
1488 pjsua_var.tpdata[id].type = type;
1489 pjsua_var.tpdata[id].local_name = tls->addr_name;
1490 pjsua_var.tpdata[id].data.factory = tls;
1491#endif
1492
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493 } else {
1494 status = PJSIP_EUNSUPTRANSPORT;
1495 pjsua_perror(THIS_FILE, "Error creating transport", status);
1496 goto on_return;
1497 }
1498
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499
1500 /* Return the ID */
1501 if (p_id) *p_id = id;
1502
1503 status = PJ_SUCCESS;
1504
1505on_return:
1506
1507 PJSUA_UNLOCK();
1508
Benny Prijonod8410532006-06-15 11:04:33 +00001509 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001510}
1511
1512
1513/*
1514 * Register transport that has been created by application.
1515 */
1516PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1517 pjsua_transport_id *p_id)
1518{
1519 unsigned id;
1520
1521 PJSUA_LOCK();
1522
1523 /* Find empty transport slot */
1524 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001525 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001526 break;
1527 }
1528
1529 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1530 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1531 PJSUA_UNLOCK();
1532 return PJ_ETOOMANY;
1533 }
1534
1535 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001536 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001537 pjsua_var.tpdata[id].local_name = tp->local_name;
1538 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539
1540 /* Return the ID */
1541 if (p_id) *p_id = id;
1542
1543 PJSUA_UNLOCK();
1544
1545 return PJ_SUCCESS;
1546}
1547
1548
1549/*
1550 * Enumerate all transports currently created in the system.
1551 */
1552PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1553 unsigned *p_count )
1554{
1555 unsigned i, count;
1556
1557 PJSUA_LOCK();
1558
1559 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1560 ++i)
1561 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001562 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001563 continue;
1564
1565 id[count++] = i;
1566 }
1567
1568 *p_count = count;
1569
1570 PJSUA_UNLOCK();
1571
1572 return PJ_SUCCESS;
1573}
1574
1575
1576/*
1577 * Get information about transports.
1578 */
1579PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1580 pjsua_transport_info *info)
1581{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001582 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001583 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001584
Benny Prijonoac623b32006-07-03 15:19:31 +00001585 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586
1587 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001588 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1589 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001590
1591 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001592 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593
1594 PJSUA_LOCK();
1595
Benny Prijonoe93e2872006-06-28 16:46:49 +00001596 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1597
1598 pjsip_transport *tp = t->data.tp;
1599
1600 if (tp == NULL) {
1601 PJSUA_UNLOCK();
1602 return PJ_EINVALIDOP;
1603 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604
Benny Prijonoe93e2872006-06-28 16:46:49 +00001605 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001606 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001607 info->type_name = pj_str(tp->type_name);
1608 info->info = pj_str(tp->info);
1609 info->flag = tp->flag;
1610 info->addr_len = tp->addr_len;
1611 info->local_addr = tp->local_addr;
1612 info->local_name = tp->local_name;
1613 info->usage_count = pj_atomic_get(tp->ref_cnt);
1614
Benny Prijono0a5cad82006-09-26 13:21:02 +00001615 status = PJ_SUCCESS;
1616
Benny Prijonoe93e2872006-06-28 16:46:49 +00001617 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1618
1619 pjsip_tpfactory *factory = t->data.factory;
1620
1621 if (factory == NULL) {
1622 PJSUA_UNLOCK();
1623 return PJ_EINVALIDOP;
1624 }
1625
1626 info->id = id;
1627 info->type = t->type;
1628 info->type_name = pj_str("TCP");
1629 info->info = pj_str("TCP transport");
1630 info->flag = factory->flag;
1631 info->addr_len = sizeof(factory->local_addr);
1632 info->local_addr = factory->local_addr;
1633 info->local_name = factory->addr_name;
1634 info->usage_count = 0;
1635
Benny Prijono0a5cad82006-09-26 13:21:02 +00001636 status = PJ_SUCCESS;
1637
1638 } else {
1639 pj_assert(!"Unsupported transport");
1640 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001641 }
1642
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001643
1644 PJSUA_UNLOCK();
1645
Benny Prijono0a5cad82006-09-26 13:21:02 +00001646 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001647}
1648
1649
1650/*
1651 * Disable a transport or re-enable it.
1652 */
1653PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1654 pj_bool_t enabled)
1655{
1656 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001657 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1658 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659
1660 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001661 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001662
1663
1664 /* To be done!! */
1665 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001666 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667
1668 return PJ_EINVALIDOP;
1669}
1670
1671
1672/*
1673 * Close the transport.
1674 */
1675PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1676 pj_bool_t force )
1677{
Benny Prijono5ff61872007-02-01 03:37:11 +00001678 pj_status_t status;
1679
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001681 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1682 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683
1684 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001685 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001686
Benny Prijono0a5cad82006-09-26 13:21:02 +00001687 /* Note: destroy() may not work if there are objects still referencing
1688 * the transport.
1689 */
1690 if (force) {
1691 switch (pjsua_var.tpdata[id].type) {
1692 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001693 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1694 if (status != PJ_SUCCESS)
1695 return status;
1696 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1697 if (status != PJ_SUCCESS)
1698 return status;
1699 break;
1700
1701 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001702 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001703 /* This will close the TCP listener, but existing TCP/TLS
1704 * connections (if any) will still linger
1705 */
1706 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1707 (pjsua_var.tpdata[id].data.factory);
1708 if (status != PJ_SUCCESS)
1709 return status;
1710
Benny Prijono0a5cad82006-09-26 13:21:02 +00001711 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001712
Benny Prijono1ebd6142006-10-19 15:48:02 +00001713 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001714 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001715 }
1716
1717 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001718 /* If force is not specified, transports will be closed at their
1719 * convenient time. However this will leak PJSUA-API transport
1720 * descriptors as PJSUA-API wouldn't know when exactly the
1721 * transport is closed thus it can't cleanup PJSUA transport
1722 * descriptor.
1723 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001724 switch (pjsua_var.tpdata[id].type) {
1725 case PJSIP_TRANSPORT_UDP:
1726 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001727 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001728 case PJSIP_TRANSPORT_TCP:
1729 return (*pjsua_var.tpdata[id].data.factory->destroy)
1730 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001731 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001732 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001733 }
1734 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735
Benny Prijono5ff61872007-02-01 03:37:11 +00001736 /* Cleanup pjsua data when force is applied */
1737 if (force) {
1738 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1739 pjsua_var.tpdata[id].data.ptr = NULL;
1740 }
1741
1742 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001743}
1744
1745
1746/*
1747 * Add additional headers etc in msg_data specified by application
1748 * when sending requests.
1749 */
1750void pjsua_process_msg_data(pjsip_tx_data *tdata,
1751 const pjsua_msg_data *msg_data)
1752{
1753 pj_bool_t allow_body;
1754 const pjsip_hdr *hdr;
1755
Benny Prijono56315612006-07-18 14:39:40 +00001756 /* Always add User-Agent */
1757 if (pjsua_var.ua_cfg.user_agent.slen &&
1758 tdata->msg->type == PJSIP_REQUEST_MSG)
1759 {
1760 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1761 pjsip_hdr *h;
1762 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1763 &STR_USER_AGENT,
1764 &pjsua_var.ua_cfg.user_agent);
1765 pjsip_msg_add_hdr(tdata->msg, h);
1766 }
1767
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001768 if (!msg_data)
1769 return;
1770
1771 hdr = msg_data->hdr_list.next;
1772 while (hdr && hdr != &msg_data->hdr_list) {
1773 pjsip_hdr *new_hdr;
1774
Benny Prijonoa1e69682007-05-11 15:14:34 +00001775 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1777
1778 hdr = hdr->next;
1779 }
1780
1781 allow_body = (tdata->msg->body == NULL);
1782
1783 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1784 pjsip_media_type ctype;
1785 pjsip_msg_body *body;
1786
1787 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1788 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1789 &msg_data->msg_body);
1790 tdata->msg->body = body;
1791 }
1792}
1793
1794
1795/*
1796 * Add route_set to outgoing requests
1797 */
1798void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1799 const pjsip_route_hdr *route_set )
1800{
1801 const pjsip_route_hdr *r;
1802
1803 r = route_set->next;
1804 while (r != route_set) {
1805 pjsip_route_hdr *new_r;
1806
Benny Prijonoa1e69682007-05-11 15:14:34 +00001807 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001808 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1809
1810 r = r->next;
1811 }
1812}
1813
1814
1815/*
1816 * Simple version of MIME type parsing (it doesn't support parameters)
1817 */
1818void pjsua_parse_media_type( pj_pool_t *pool,
1819 const pj_str_t *mime,
1820 pjsip_media_type *media_type)
1821{
1822 pj_str_t tmp;
1823 char *pos;
1824
Benny Prijonoac623b32006-07-03 15:19:31 +00001825 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001826
1827 pj_strdup_with_null(pool, &tmp, mime);
1828
1829 pos = pj_strchr(&tmp, '/');
1830 if (pos) {
1831 media_type->type.ptr = tmp.ptr;
1832 media_type->type.slen = (pos-tmp.ptr);
1833 media_type->subtype.ptr = pos+1;
1834 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1835 } else {
1836 media_type->type = tmp;
1837 }
1838}
1839
1840
1841/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001842 * Internal function to init transport selector from transport id.
1843 */
1844void pjsua_init_tpselector(pjsua_transport_id tp_id,
1845 pjsip_tpselector *sel)
1846{
1847 pjsua_transport_data *tpdata;
1848 unsigned flag;
1849
1850 pj_bzero(sel, sizeof(*sel));
1851 if (tp_id == PJSUA_INVALID_ID)
1852 return;
1853
Benny Prijonoa1e69682007-05-11 15:14:34 +00001854 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001855 tpdata = &pjsua_var.tpdata[tp_id];
1856
1857 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1858
1859 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1860 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1861 sel->u.transport = tpdata->data.tp;
1862 } else {
1863 sel->type = PJSIP_TPSELECTOR_LISTENER;
1864 sel->u.listener = tpdata->data.factory;
1865 }
1866}
1867
1868
Benny Prijono6ba8c542007-10-16 01:34:14 +00001869/* Callback upon NAT detection completion */
1870static void nat_detect_cb(void *user_data,
1871 const pj_stun_nat_detect_result *res)
1872{
1873 PJ_UNUSED_ARG(user_data);
1874
1875 pjsua_var.nat_in_progress = PJ_FALSE;
1876 pjsua_var.nat_status = res->status;
1877 pjsua_var.nat_type = res->nat_type;
1878
1879 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1880 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1881 }
1882}
1883
1884
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001885/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001886 * Detect NAT type.
1887 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00001888PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001889{
1890 pj_status_t status;
1891
Benny Prijono6ba8c542007-10-16 01:34:14 +00001892 if (pjsua_var.nat_in_progress)
1893 return PJ_SUCCESS;
1894
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001895 /* Make sure STUN server resolution has completed */
1896 status = pjsua_resolve_stun_server(PJ_TRUE);
1897 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001898 pjsua_var.nat_status = status;
1899 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001900 return status;
1901 }
1902
1903 /* Make sure we have STUN */
1904 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001905 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
1906 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001907 }
1908
Benny Prijono6ba8c542007-10-16 01:34:14 +00001909 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
1910 &pjsua_var.stun_cfg,
1911 NULL, &nat_detect_cb);
1912
1913 if (status != PJ_SUCCESS) {
1914 pjsua_var.nat_status = status;
1915 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
1916 return status;
1917 }
1918
1919 pjsua_var.nat_in_progress = PJ_TRUE;
1920
1921 return PJ_SUCCESS;
1922}
1923
1924
1925/*
1926 * Get NAT type.
1927 */
1928PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
1929{
1930 *type = pjsua_var.nat_type;
1931 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001932}
1933
1934
1935/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001936 * Verify that valid SIP url is given.
1937 */
1938PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
1939{
1940 pjsip_uri *p;
1941 pj_pool_t *pool;
1942 char *url;
1943 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
1944
1945 if (!len) return -1;
1946
1947 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
1948 if (!pool) return -1;
1949
Benny Prijonoa1e69682007-05-11 15:14:34 +00001950 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 pj_ansi_strcpy(url, c_url);
1952
1953 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001954 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
1955 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
1956 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001957 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001958 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001959
1960 pj_pool_release(pool);
1961 return p ? 0 : -1;
1962}
Benny Prijonoda9785b2007-04-02 20:43:06 +00001963
1964
1965/*
1966 * This is a utility function to dump the stack states to log, using
1967 * verbosity level 3.
1968 */
1969PJ_DEF(void) pjsua_dump(pj_bool_t detail)
1970{
1971 unsigned old_decor;
1972 unsigned i;
1973 char buf[1024];
1974
1975 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
1976
1977 old_decor = pj_log_get_decor();
1978 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
1979
1980 if (detail)
1981 pj_dump_config();
1982
1983 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
1984
1985 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
1986
1987 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
1988 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1989 pjsua_call *call = &pjsua_var.calls[i];
1990 pjmedia_sock_info skinfo;
1991
Benny Prijono4f093d22007-04-09 08:54:32 +00001992 /* MSVC complains about skinfo not being initialized */
1993 pj_bzero(&skinfo, sizeof(skinfo));
1994
Benny Prijonoda9785b2007-04-02 20:43:06 +00001995 pjmedia_transport_get_info(call->med_tp, &skinfo);
1996
1997 PJ_LOG(3,(THIS_FILE, " %s: %s:%d",
1998 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
1999 pj_inet_ntoa(skinfo.rtp_addr_name.sin_addr),
2000 (int)pj_ntohs(skinfo.rtp_addr_name.sin_port)));
2001 }
2002
2003 pjsip_tsx_layer_dump(detail);
2004 pjsip_ua_dump(detail);
2005
2006
2007 /* Dump all invite sessions: */
2008 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2009
2010 if (pjsua_call_get_count() == 0) {
2011
2012 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2013
2014 } else {
2015 unsigned i;
2016
2017 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2018 if (pjsua_call_is_active(i)) {
2019 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
2020 PJ_LOG(3,(THIS_FILE, "%s", buf));
2021 }
2022 }
2023 }
2024
2025 /* Dump presence status */
2026 pjsua_pres_dump(detail);
2027
2028 pj_log_set_decor(old_decor);
2029 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2030}
2031