blob: 321d34057eb33474eec35ddfe9d88a965a39ca06 [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 Prijonoeebe9af2006-06-13 22:57:13 +000030/* Display error */
31PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
32 pj_status_t status)
Benny Prijono268ca612006-02-07 12:34:11 +000033{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000034 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoa91a0032006-02-26 21:23:45 +000035
Benny Prijonoeebe9af2006-06-13 22:57:13 +000036 pj_strerror(status, errmsg, sizeof(errmsg));
37 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
Benny Prijono268ca612006-02-07 12:34:11 +000038}
39
40
Benny Prijonoeebe9af2006-06-13 22:57:13 +000041static void init_data()
Benny Prijonodc39fe82006-05-26 12:17:46 +000042{
43 unsigned i;
44
Benny Prijonoc97608e2007-03-23 16:34:20 +000045 pj_bzero(&pjsua_var, sizeof(pjsua_var));
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
48 pjsua_var.acc[i].index = i;
49
50 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
51 pjsua_var.tpdata[i].index = i;
Benny Prijonoc97608e2007-03-23 16:34:20 +000052
53 pjsua_var.stun_status = PJ_EUNKNOWN;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054}
Benny Prijonodc39fe82006-05-26 12:17:46 +000055
56
Benny Prijono1f61a8f2007-08-16 10:11:44 +000057PJ_DEF(void) pjsua_logging_config_default(pjsua_logging_config *cfg)
58{
59 pj_bzero(cfg, sizeof(*cfg));
60
61 cfg->msg_logging = PJ_TRUE;
62 cfg->level = 5;
63 cfg->console_level = 4;
64 cfg->decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
65 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
66}
67
68PJ_DEF(void) pjsua_logging_config_dup(pj_pool_t *pool,
69 pjsua_logging_config *dst,
70 const pjsua_logging_config *src)
71{
72 pj_memcpy(dst, src, sizeof(*src));
73 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
74}
75
76PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
77{
78 pj_bzero(cfg, sizeof(*cfg));
79
80 cfg->max_calls = 4;
81 cfg->thread_cnt = 1;
82}
83
Benny Prijono1f61a8f2007-08-16 10:11:44 +000084PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
85 pjsua_config *dst,
86 const pjsua_config *src)
87{
88 unsigned i;
89
90 pj_memcpy(dst, src, sizeof(*src));
91
92 for (i=0; i<src->outbound_proxy_cnt; ++i) {
93 pj_strdup_with_null(pool, &dst->outbound_proxy[i],
94 &src->outbound_proxy[i]);
95 }
96
97 for (i=0; i<src->cred_count; ++i) {
98 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
99 }
100
101 pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
102 pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
103 pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
104 pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
105}
106
107PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
108{
109 pj_bzero(msg_data, sizeof(*msg_data));
110 pj_list_init(&msg_data->hdr_list);
111}
112
113PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
114{
115 pj_bzero(cfg, sizeof(*cfg));
116 pjsip_tls_setting_default(&cfg->tls_setting);
117}
118
119PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
120 pjsua_transport_config *dst,
121 const pjsua_transport_config *src)
122{
123 PJ_UNUSED_ARG(pool);
124 pj_memcpy(dst, src, sizeof(*src));
125}
126
127PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
128{
129 pj_bzero(cfg, sizeof(*cfg));
130
131 cfg->reg_timeout = PJSUA_REG_INTERVAL;
132 cfg->transport_id = PJSUA_INVALID_ID;
Benny Prijono15b02302007-09-27 14:07:07 +0000133 cfg->auto_update_nat = PJ_TRUE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000134 cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000135}
136
137PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
138{
139 pj_bzero(cfg, sizeof(*cfg));
140}
141
142PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
143{
144 pj_bzero(cfg, sizeof(*cfg));
145
146 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijonocf0b4b22007-10-06 17:31:09 +0000147 cfg->audio_frame_ptime = 10;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000148 cfg->max_media_ports = 32;
149 cfg->has_ioqueue = PJ_TRUE;
150 cfg->thread_cnt = 1;
151 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
152 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
153 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
154 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
155}
156
Benny Prijonodc39fe82006-05-26 12:17:46 +0000157
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000158/*****************************************************************************
159 * This is a very simple PJSIP module, whose sole purpose is to display
160 * incoming and outgoing messages to log. This module will have priority
161 * higher than transport layer, which means:
162 *
163 * - incoming messages will come to this module first before reaching
164 * transaction layer.
165 *
166 * - outgoing messages will come to this module last, after the message
167 * has been 'printed' to contiguous buffer by transport layer and
168 * appropriate transport instance has been decided for this message.
169 *
170 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172/* Notification on incoming messages */
173static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
174{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000175 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
176 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000177 "--end msg--",
178 rdata->msg_info.len,
179 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000180 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 rdata->pkt_info.src_name,
182 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000183 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000184 rdata->msg_info.msg_buf));
185
186 /* Always return false, otherwise messages will not get processed! */
187 return PJ_FALSE;
188}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190/* Notification on outgoing messages */
191static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
192{
193
194 /* Important note:
195 * tp_info field is only valid after outgoing messages has passed
196 * transport layer. So don't try to access tp_info when the module
197 * has lower priority than transport layer.
198 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000199
Benny Prijonob4a17c92006-07-10 14:40:21 +0000200 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
201 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 "--end msg--",
203 (tdata->buf.cur - tdata->buf.start),
204 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000205 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 tdata->tp_info.dst_name,
207 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000208 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000212 return PJ_SUCCESS;
213}
214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215/* The module instance. */
216static pjsip_module pjsua_msg_logger =
217{
218 NULL, NULL, /* prev, next. */
219 { "mod-pjsua-log", 13 }, /* Name. */
220 -1, /* Id */
221 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
222 NULL, /* load() */
223 NULL, /* start() */
224 NULL, /* stop() */
225 NULL, /* unload() */
226 &logging_on_rx_msg, /* on_rx_request() */
227 &logging_on_rx_msg, /* on_rx_response() */
228 &logging_on_tx_msg, /* on_tx_request. */
229 &logging_on_tx_msg, /* on_tx_response() */
230 NULL, /* on_tsx_state() */
231
232};
233
234
235/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000236 * Another simple module to handle incoming OPTIONS request
237 */
238
239/* Notification on incoming request */
240static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
241{
242 pjsip_tx_data *tdata;
243 pjsip_response_addr res_addr;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000244 pjmedia_sock_info skinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000245 pjmedia_sdp_session *sdp;
246 const pjsip_hdr *cap_hdr;
247 pj_status_t status;
248
249 /* Only want to handle OPTIONS requests */
250 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000251 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000252 {
253 return PJ_FALSE;
254 }
255
256 /* Create basic response. */
257 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
258 &tdata);
259 if (status != PJ_SUCCESS) {
260 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
261 return PJ_TRUE;
262 }
263
264 /* Add Allow header */
265 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
266 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000267 pjsip_msg_add_hdr(tdata->msg,
268 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000269 }
270
271 /* Add Accept header */
272 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
273 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000274 pjsip_msg_add_hdr(tdata->msg,
275 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000276 }
277
278 /* Add Supported header */
279 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
280 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000281 pjsip_msg_add_hdr(tdata->msg,
282 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000283 }
284
285 /* Add Allow-Events header from the evsub module */
286 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
287 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000288 pjsip_msg_add_hdr(tdata->msg,
289 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000290 }
291
292 /* Add User-Agent header */
293 if (pjsua_var.ua_cfg.user_agent.slen) {
294 const pj_str_t USER_AGENT = { "User-Agent", 10};
295 pjsip_hdr *h;
296
297 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
298 &USER_AGENT,
299 &pjsua_var.ua_cfg.user_agent);
300 pjsip_msg_add_hdr(tdata->msg, h);
301 }
302
Benny Prijono617c5bc2007-04-02 19:51:21 +0000303 /* Get media socket info */
304 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &skinfo);
305
Benny Prijono56315612006-07-18 14:39:40 +0000306 /* Add SDP body, using call0's RTP address */
307 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000308 &skinfo, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000309 if (status == PJ_SUCCESS) {
310 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
311 }
312
313 /* Send response statelessly */
314 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
315 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
316 if (status != PJ_SUCCESS)
317 pjsip_tx_data_dec_ref(tdata);
318
319 return PJ_TRUE;
320}
321
322
323/* The module instance. */
324static pjsip_module pjsua_options_handler =
325{
326 NULL, NULL, /* prev, next. */
327 { "mod-pjsua-options", 17 }, /* Name. */
328 -1, /* Id */
329 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
330 NULL, /* load() */
331 NULL, /* start() */
332 NULL, /* stop() */
333 NULL, /* unload() */
334 &options_on_rx_request, /* on_rx_request() */
335 NULL, /* on_rx_response() */
336 NULL, /* on_tx_request. */
337 NULL, /* on_tx_response() */
338 NULL, /* on_tsx_state() */
339
340};
341
342
343/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000344 * These two functions are the main callbacks registered to PJSIP stack
345 * to receive SIP request and response messages that are outside any
346 * dialogs and any transactions.
347 */
Benny Prijono268ca612006-02-07 12:34:11 +0000348
349/*
350 * Handler for receiving incoming requests.
351 *
352 * This handler serves multiple purposes:
353 * - it receives requests outside dialogs.
354 * - it receives requests inside dialogs, when the requests are
355 * unhandled by other dialog usages. Example of these
356 * requests are: MESSAGE.
357 */
358static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
359{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000360 pj_bool_t processed = PJ_FALSE;
361
362 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000363
Benny Prijono84126ab2006-02-09 09:30:09 +0000364 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000365
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000366 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000367 }
368
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000369 PJSUA_UNLOCK();
370
371 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000372}
373
374
375/*
376 * Handler for receiving incoming responses.
377 *
378 * This handler serves multiple purposes:
379 * - it receives strayed responses (i.e. outside any dialog and
380 * outside any transactions).
381 * - it receives responses coming to a transaction, when pjsua
382 * module is set as transaction user for the transaction.
383 * - it receives responses inside a dialog, when these responses
384 * are unhandled by other dialog usages.
385 */
386static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
387{
388 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000389 return PJ_FALSE;
390}
391
392
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393/*****************************************************************************
394 * Logging.
395 */
396
397/* Log callback */
398static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000399{
Benny Prijono572d4852006-11-23 21:50:02 +0000400 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000401
402 if (pjsua_var.log_file) {
403 pj_ssize_t size = len;
404 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000405 /* This will slow things down considerably! Don't do it!
406 pj_file_flush(pjsua_var.log_file);
407 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000408 }
409
Benny Prijono572d4852006-11-23 21:50:02 +0000410 if (level <= (int)pjsua_var.log_cfg.console_level) {
411 if (pjsua_var.log_cfg.cb)
412 (*pjsua_var.log_cfg.cb)(level, buffer, len);
413 else
414 pj_log_write(level, buffer, len);
415 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416}
417
418
419/*
420 * Application can call this function at any time (after pjsua_create(), of
421 * course) to change logging settings.
422 */
423PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
424{
425 pj_status_t status;
426
427 /* Save config. */
428 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
429
430 /* Redirect log function to ours */
431 pj_log_set_log_func( &log_writer );
432
Benny Prijono9cb09a22007-08-30 09:35:10 +0000433 /* Set decor */
434 pj_log_set_decor(pjsua_var.log_cfg.decor);
435
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 /* Close existing file, if any */
437 if (pjsua_var.log_file) {
438 pj_file_close(pjsua_var.log_file);
439 pjsua_var.log_file = NULL;
440 }
441
442 /* If output log file is desired, create the file: */
443 if (pjsua_var.log_cfg.log_filename.slen) {
444
445 status = pj_file_open(pjsua_var.pool,
446 pjsua_var.log_cfg.log_filename.ptr,
447 PJ_O_WRONLY,
448 &pjsua_var.log_file);
449
450 if (status != PJ_SUCCESS) {
451 pjsua_perror(THIS_FILE, "Error creating log file", status);
452 return status;
453 }
454 }
455
456 /* Unregister msg logging if it's previously registered */
457 if (pjsua_msg_logger.id >= 0) {
458 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
459 pjsua_msg_logger.id = -1;
460 }
461
462 /* Enable SIP message logging */
463 if (pjsua_var.log_cfg.msg_logging)
464 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
465
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 return PJ_SUCCESS;
467}
468
469
470/*****************************************************************************
471 * PJSUA Base API.
472 */
473
474/* Worker thread function. */
475static int worker_thread(void *arg)
476{
477 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000478
Benny Prijono268ca612006-02-07 12:34:11 +0000479 PJ_UNUSED_ARG(arg);
480
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000481 while (!pjsua_var.thread_quit_flag) {
482 int count;
483
484 count = pjsua_handle_events(TIMEOUT);
485 if (count < 0)
486 pj_thread_sleep(TIMEOUT);
487 }
Benny Prijono268ca612006-02-07 12:34:11 +0000488
489 return 0;
490}
491
Benny Prijonodc39fe82006-05-26 12:17:46 +0000492
Benny Prijono268ca612006-02-07 12:34:11 +0000493/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000494 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000495 */
496PJ_DEF(pj_status_t) pjsua_create(void)
497{
498 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000499
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 /* Init pjsua data */
501 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000502
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 /* Set default logging settings */
504 pjsua_logging_config_default(&pjsua_var.log_cfg);
505
506 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000507 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
509
Benny Prijono268ca612006-02-07 12:34:11 +0000510
Benny Prijonofccab712006-02-22 22:23:22 +0000511 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000512 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000513 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000514
Benny Prijonoec921342007-03-24 13:00:30 +0000515 /* Init PJNATH */
516 status = pjnath_init();
517 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000518
Benny Prijono094d3ad2006-11-21 08:41:00 +0000519 /* Set default sound device ID */
520 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
521
Benny Prijono268ca612006-02-07 12:34:11 +0000522 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000523 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000524
525 /* Create memory pool for application. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
527
528 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000529
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000530 /* Create mutex */
531 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
532 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000533 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000534 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000535 return status;
536 }
537
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538 /* Must create SIP endpoint to initialize SIP parser. The parser
539 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000540 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000541 status = pjsip_endpt_create(&pjsua_var.cp.factory,
542 pj_gethostname()->ptr,
543 &pjsua_var.endpt);
544 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000545
546
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000547 return PJ_SUCCESS;
548}
549
550
551/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000552 * Initialize pjsua with the specified settings. All the settings are
553 * optional, and the default values will be used when the config is not
554 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000555 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000556PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
557 const pjsua_logging_config *log_cfg,
558 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000559{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 pjsua_config default_cfg;
561 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000562 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000563 pj_status_t status;
564
565
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000566 /* Create default configurations when the config is not supplied */
567
568 if (ua_cfg == NULL) {
569 pjsua_config_default(&default_cfg);
570 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571 }
572
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000573 if (media_cfg == NULL) {
574 pjsua_media_config_default(&default_media_cfg);
575 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000576 }
577
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000578 /* Initialize logging first so that info/errors can be captured */
579 if (log_cfg) {
580 status = pjsua_reconfigure_logging(log_cfg);
581 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000582 }
583
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000584 /* If nameserver is configured, create DNS resolver instance and
585 * set it to be used by SIP resolver.
586 */
587 if (ua_cfg->nameserver_count) {
588#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000589 unsigned i;
590
591 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000592 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
593 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000594 if (status != PJ_SUCCESS) {
595 pjsua_perror(THIS_FILE, "Error creating resolver", status);
596 return status;
597 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000598
599 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000600 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
601 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000602 ua_cfg->nameserver, NULL);
603 if (status != PJ_SUCCESS) {
604 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
605 return status;
606 }
607
608 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000609 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000610 if (status != PJ_SUCCESS) {
611 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
612 return status;
613 }
614
615 /* Print nameservers */
616 for (i=0; i<ua_cfg->nameserver_count; ++i) {
617 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
618 (int)ua_cfg->nameserver[i].slen,
619 ua_cfg->nameserver[i].ptr));
620 }
621#else
622 PJ_LOG(2,(THIS_FILE,
623 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
624#endif
625 }
626
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000627 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000628
629 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000630 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
631 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000632
Benny Prijonodc39fe82006-05-26 12:17:46 +0000633
634 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000635 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
636 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000637
Benny Prijonodc39fe82006-05-26 12:17:46 +0000638
Benny Prijono053f5222006-11-11 16:16:04 +0000639 /* Initialize Replaces support. */
640 status = pjsip_replaces_init_module( pjsua_var.endpt );
641 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
642
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000643 /* Initialize 100rel support */
644 status = pjsip_100rel_init_module(pjsua_var.endpt);
645 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000646
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000647 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000648 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000649 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000650 {
651 NULL, NULL, /* prev, next. */
652 { "mod-pjsua", 9 }, /* Name. */
653 -1, /* Id */
654 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
655 NULL, /* load() */
656 NULL, /* start() */
657 NULL, /* stop() */
658 NULL, /* unload() */
659 &mod_pjsua_on_rx_request, /* on_rx_request() */
660 &mod_pjsua_on_rx_response, /* on_rx_response() */
661 NULL, /* on_tx_request. */
662 NULL, /* on_tx_response() */
663 NULL, /* on_tsx_state() */
664 };
665
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000666 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000667
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000668 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
669 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000670 }
671
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000672
Benny Prijonodc39fe82006-05-26 12:17:46 +0000673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674 /* Initialize PJSUA call subsystem: */
675 status = pjsua_call_subsys_init(ua_cfg);
676 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000677 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000678
679
Benny Prijonoc97608e2007-03-23 16:34:20 +0000680 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000681
Benny Prijonoc97608e2007-03-23 16:34:20 +0000682 status = pjsua_resolve_stun_server(PJ_FALSE);
683 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
684 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
685 return status;
686 }
687
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 /* Initialize PJSUA media subsystem */
689 status = pjsua_media_subsys_init(media_cfg);
690 if (status != PJ_SUCCESS)
691 goto on_error;
692
Benny Prijonodc39fe82006-05-26 12:17:46 +0000693
694 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000695 status = pjsip_evsub_init_module(pjsua_var.endpt);
696 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000697
Benny Prijonodc39fe82006-05-26 12:17:46 +0000698
699 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000700 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
701 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000702
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000703 /* Init PUBLISH module */
704 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000705
706 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000707 status = pjsip_xfer_init_module( pjsua_var.endpt );
708 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000709
710 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000711 status = pjsua_pres_init();
712 if (status != PJ_SUCCESS)
713 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000714
715 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000716 status = pjsua_im_init();
717 if (status != PJ_SUCCESS)
718 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000719
Benny Prijonoc8141a82006-08-20 09:12:19 +0000720 /* Register OPTIONS handler */
721 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
722
723 /* Add OPTIONS in Allow header */
724 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
725 NULL, 1, &STR_OPTIONS);
726
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000727 /* Start worker thread if needed. */
728 if (pjsua_var.ua_cfg.thread_cnt) {
729 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000730
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000731 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
732 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000733
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000734 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
735 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
736 NULL, 0, 0, &pjsua_var.thread[i]);
737 if (status != PJ_SUCCESS)
738 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000739 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000740 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
741 pjsua_var.ua_cfg.thread_cnt));
742 } else {
743 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000744 }
745
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000746 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000747
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000748 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000749 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000750
Benny Prijono268ca612006-02-07 12:34:11 +0000751 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000752
753on_error:
754 pjsua_destroy();
755 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000756}
757
758
Benny Prijono834aee32006-02-19 01:38:06 +0000759/* Sleep with polling */
760static void busy_sleep(unsigned msec)
761{
Benny Prijonob2c96822007-05-03 13:31:21 +0000762#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000763 /* Ideally we shouldn't call pj_thread_sleep() and rather
764 * CActiveScheduler::WaitForAnyRequest() here, but that will
765 * drag in Symbian header and it doesn't look pretty.
766 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000767 pj_thread_sleep(msec);
768#else
Benny Prijono834aee32006-02-19 01:38:06 +0000769 pj_time_val timeout, now;
770
771 pj_gettimeofday(&timeout);
772 timeout.msec += msec;
773 pj_time_val_normalize(&timeout);
774
775 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000776 while (pjsua_handle_events(10) > 0)
777 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000778 pj_gettimeofday(&now);
779 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000780#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000781}
782
Benny Prijonoebbf6892007-03-24 17:37:25 +0000783
Benny Prijonoebbf6892007-03-24 17:37:25 +0000784/*
785 * Callback function to receive notification from the resolver
786 * when the resolution process completes.
787 */
788static void stun_dns_srv_resolver_cb(void *user_data,
789 pj_status_t status,
790 const pj_dns_srv_record *rec)
791{
792 unsigned i;
793
794 PJ_UNUSED_ARG(user_data);
795
796 pjsua_var.stun_status = status;
797
798 if (status != PJ_SUCCESS) {
799 /* DNS SRV resolution failed. If stun_host is specified, resolve
800 * it with gethostbyname()
801 */
802 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000803 pj_str_t str_host, str_port;
804 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000805 pj_hostent he;
806
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000807 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
808 if (str_port.ptr != NULL) {
809 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
810 str_host.slen = (str_port.ptr - str_host.ptr);
811 str_port.ptr++;
812 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
813 str_host.slen - 1;
814 port = (int)pj_strtoul(&str_port);
815 if (port < 1 || port > 65535) {
816 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
817 pjsua_var.stun_status = PJ_EINVAL;
818 return;
819 }
820 } else {
821 str_host = pjsua_var.ua_cfg.stun_host;
822 port = 3478;
823 }
824
825 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000826
827 if (pjsua_var.stun_status == PJ_SUCCESS) {
828 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
829 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000830 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000831
832 PJ_LOG(3,(THIS_FILE,
833 "STUN server %.*s resolved, address is %s:%d",
834 (int)pjsua_var.ua_cfg.stun_host.slen,
835 pjsua_var.ua_cfg.stun_host.ptr,
836 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
837 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
838 }
839 } else {
840 char errmsg[PJ_ERR_MSG_SIZE];
841
842 pj_strerror(status, errmsg, sizeof(errmsg));
843 PJ_LOG(1,(THIS_FILE,
844 "DNS SRV resolution failed for STUN server %.*s: %s",
845 (int)pjsua_var.ua_cfg.stun_domain.slen,
846 pjsua_var.ua_cfg.stun_domain.ptr,
847 errmsg));
848 }
849 return;
850 }
851
Benny Prijonof5afa922007-06-11 16:51:18 +0000852 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
853 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
854 rec->entry[0].port);
855 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
856 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000857
858 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
859 (int)pjsua_var.ua_cfg.stun_domain.slen,
860 pjsua_var.ua_cfg.stun_domain.ptr,
861 rec->count));
862
863 for (i=0; i<rec->count; ++i) {
864 PJ_LOG(3,(THIS_FILE,
865 " %d: prio=%d, weight=%d %s:%d",
866 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000867 pj_inet_ntoa(rec->entry[i].server.addr[0]),
868 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000869 }
870
871}
872
Benny Prijono268ca612006-02-07 12:34:11 +0000873/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000874 * Resolve STUN server.
875 */
876pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
877{
878 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
879 /* Initialize STUN configuration */
880 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
881 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
882 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
883
884 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000885
886 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000887
Benny Prijonoebbf6892007-03-24 17:37:25 +0000888 /* If stun_domain is specified, resolve STUN servers with DNS
889 * SRV resolution.
890 */
891 if (pjsua_var.ua_cfg.stun_domain.slen) {
892 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000893 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000894
895 /* Fail if resolver is not configured */
896 if (pjsua_var.resolver == NULL) {
897 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
898 "stun_domain is specified"));
899 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
900 return PJLIB_UTIL_EDNSNONS;
901 }
902 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000903 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000904 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
905 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000906 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000907 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000908 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
909 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000910 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000911 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000912 } else {
913 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000914 }
915 }
916 /* Otherwise if stun_host is specified, resolve STUN server with
917 * gethostbyname().
918 */
919 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000920 pj_str_t str_host, str_port;
921 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000922 pj_hostent he;
923
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000924 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
925 if (str_port.ptr != NULL) {
926 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
927 str_host.slen = (str_port.ptr - str_host.ptr);
928 str_port.ptr++;
929 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
930 str_host.slen - 1;
931 port = (int)pj_strtoul(&str_port);
932 if (port < 1 || port > 65535) {
933 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
934 pjsua_var.stun_status = PJ_EINVAL;
935 return pjsua_var.stun_status;
936 }
937 } else {
938 str_host = pjsua_var.ua_cfg.stun_host;
939 port = 3478;
940 }
941
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000942 pjsua_var.stun_status =
943 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
944 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000945
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000946 if (pjsua_var.stun_status != PJ_SUCCESS) {
947 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000948
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000949 if (pjsua_var.stun_status == PJ_SUCCESS) {
950 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
951 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
952 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
953 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000954 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000955
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000956 PJ_LOG(3,(THIS_FILE,
957 "STUN server %.*s resolved, address is %s:%d",
958 (int)pjsua_var.ua_cfg.stun_host.slen,
959 pjsua_var.ua_cfg.stun_host.ptr,
960 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
961 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
962
Benny Prijonoc97608e2007-03-23 16:34:20 +0000963 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000964 /* Otherwise disable STUN. */
965 else {
966 pjsua_var.stun_status = PJ_SUCCESS;
967 }
968
969
Benny Prijonoc97608e2007-03-23 16:34:20 +0000970 return pjsua_var.stun_status;
971
972 } else if (pjsua_var.stun_status == PJ_EPENDING) {
973 /* STUN server resolution has been started, wait for the
974 * result.
975 */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000976 if (wait) {
977 while (pjsua_var.stun_status == PJ_EPENDING)
978 pjsua_handle_events(10);
979 }
980
981 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000982
983 } else {
984 /* STUN server has been resolved, return the status */
985 return pjsua_var.stun_status;
986 }
987}
988
989/*
Benny Prijono268ca612006-02-07 12:34:11 +0000990 * Destroy pjsua.
991 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000992PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +0000993{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000994 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +0000995
996 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000997 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +0000998
999 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001000 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1001 if (pjsua_var.thread[i]) {
1002 pj_thread_join(pjsua_var.thread[i]);
1003 pj_thread_destroy(pjsua_var.thread[i]);
1004 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001005 }
Benny Prijono268ca612006-02-07 12:34:11 +00001006 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001007
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001008 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001009 /* Terminate all calls. */
1010 pjsua_call_hangup_all();
1011
1012 /* Terminate all presence subscriptions. */
1013 pjsua_pres_shutdown();
1014
1015 /* Unregister, if required: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001016 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1017 if (!pjsua_var.acc[i].valid)
1018 continue;
1019
1020 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001021 pjsua_acc_set_registration(i, PJ_FALSE);
1022 }
1023 }
1024
1025 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001026 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1027 busy_sleep(1000);
1028 }
Benny Prijono834aee32006-02-19 01:38:06 +00001029
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001030 /* Destroy media */
1031 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001032
Benny Prijono268ca612006-02-07 12:34:11 +00001033 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001034 if (pjsua_var.endpt) {
1035 pjsip_endpt_destroy(pjsua_var.endpt);
1036 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001037 }
Benny Prijono268ca612006-02-07 12:34:11 +00001038
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001039 /* Destroy mutex */
1040 if (pjsua_var.mutex) {
1041 pj_mutex_destroy(pjsua_var.mutex);
1042 pjsua_var.mutex = NULL;
1043 }
1044
1045 /* Destroy pool and pool factory. */
1046 if (pjsua_var.pool) {
1047 pj_pool_release(pjsua_var.pool);
1048 pjsua_var.pool = NULL;
1049 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001050
1051 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1052
1053 /* End logging */
1054 if (pjsua_var.log_file) {
1055 pj_file_close(pjsua_var.log_file);
1056 pjsua_var.log_file = NULL;
1057 }
1058
1059 /* Shutdown PJLIB */
1060 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061 }
Benny Prijono268ca612006-02-07 12:34:11 +00001062
Benny Prijonof762ee72006-12-01 11:14:37 +00001063 /* Clear pjsua_var */
1064 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1065
Benny Prijono268ca612006-02-07 12:34:11 +00001066 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001067 return PJ_SUCCESS;
1068}
1069
1070
1071/**
1072 * Application is recommended to call this function after all initialization
1073 * is done, so that the library can do additional checking set up
1074 * additional
1075 *
1076 * @return PJ_SUCCESS on success, or the appropriate error code.
1077 */
1078PJ_DEF(pj_status_t) pjsua_start(void)
1079{
1080 pj_status_t status;
1081
1082 status = pjsua_call_subsys_start();
1083 if (status != PJ_SUCCESS)
1084 return status;
1085
1086 status = pjsua_media_subsys_start();
1087 if (status != PJ_SUCCESS)
1088 return status;
1089
1090 status = pjsua_pres_start();
1091 if (status != PJ_SUCCESS)
1092 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001093
1094 return PJ_SUCCESS;
1095}
1096
Benny Prijono9fc735d2006-05-28 14:58:12 +00001097
1098/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001099 * Poll pjsua for events, and if necessary block the caller thread for
1100 * the specified maximum interval (in miliseconds).
1101 */
1102PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1103{
Benny Prijono897f9f82007-05-03 19:56:21 +00001104#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1105 /* Ideally we shouldn't call pj_thread_sleep() and rather
1106 * CActiveScheduler::WaitForAnyRequest() here, but that will
1107 * drag in Symbian header and it doesn't look pretty.
1108 */
1109 pj_thread_sleep(msec_timeout);
1110 return msec_timeout;
1111#else
1112
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 unsigned count = 0;
1114 pj_time_val tv;
1115 pj_status_t status;
1116
1117 tv.sec = 0;
1118 tv.msec = msec_timeout;
1119 pj_time_val_normalize(&tv);
1120
1121 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1122
1123 if (status != PJ_SUCCESS)
1124 return -status;
1125
1126 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001127#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001128}
1129
1130
1131/*
1132 * Create memory pool.
1133 */
1134PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1135 pj_size_t increment)
1136{
1137 /* Pool factory is thread safe, no need to lock */
1138 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1139 NULL);
1140}
1141
1142
1143/*
1144 * Internal function to get SIP endpoint instance of pjsua, which is
1145 * needed for example to register module, create transports, etc.
1146 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001147 */
1148PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1149{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001150 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001151}
1152
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001153/*
1154 * Internal function to get media endpoint instance.
1155 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001156 */
1157PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1158{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001160}
1161
Benny Prijono97b87172006-08-24 14:25:14 +00001162/*
1163 * Internal function to get PJSUA pool factory.
1164 */
1165PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1166{
1167 return &pjsua_var.cp.factory;
1168}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169
1170/*****************************************************************************
1171 * PJSUA SIP Transport API.
1172 */
1173
1174/*
1175 * Create and initialize SIP socket (and possibly resolve public
1176 * address via STUN, depending on config).
1177 */
1178static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
1179 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001180 pj_sock_t *p_sock,
1181 pj_sockaddr_in *p_pub_addr)
1182{
Benny Prijonoc97608e2007-03-23 16:34:20 +00001183 char ip_addr[32];
1184 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 pj_sock_t sock;
1186 pj_status_t status;
1187
Benny Prijonoc97608e2007-03-23 16:34:20 +00001188 /* Make sure STUN server resolution has completed */
1189 status = pjsua_resolve_stun_server(PJ_TRUE);
1190 if (status != PJ_SUCCESS) {
1191 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1192 return status;
1193 }
1194
Benny Prijono8ab968f2007-07-20 08:08:30 +00001195 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001196 if (status != PJ_SUCCESS) {
1197 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001198 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001199 }
1200
Benny Prijonof90ef4f2007-02-12 14:59:57 +00001201 status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.s_addr),
1202 (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001203 if (status != PJ_SUCCESS) {
1204 pjsua_perror(THIS_FILE, "bind() error", status);
1205 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001206 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 }
1208
Benny Prijonoe347cb02007-02-14 14:36:13 +00001209 /* If port is zero, get the bound port */
1210 if (port == 0) {
1211 pj_sockaddr_in bound_addr;
1212 int namelen = sizeof(bound_addr);
1213 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1214 if (status != PJ_SUCCESS) {
1215 pjsua_perror(THIS_FILE, "getsockname() error", status);
1216 pj_sock_close(sock);
1217 return status;
1218 }
1219
1220 port = pj_ntohs(bound_addr.sin_port);
1221 }
1222
Benny Prijonoc97608e2007-03-23 16:34:20 +00001223 if (pjsua_var.stun_srv.addr.sa_family != 0) {
1224 pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1225 stun_srv = pj_str(ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001226 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001227 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001228 }
1229
1230 /* Get the published address, either by STUN or by resolving
1231 * the name of local host.
1232 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001233 if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001234 /*
1235 * STUN is specified, resolve the address with STUN.
1236 */
Benny Prijono14c2b862007-02-21 00:40:05 +00001237 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001238 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1239 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijonoc97608e2007-03-23 16:34:20 +00001240 p_pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001242 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001243 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001244 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 }
1246
Benny Prijono0a5cad82006-09-26 13:21:02 +00001247 } else if (p_pub_addr->sin_addr.s_addr != 0) {
1248 /*
1249 * Public address is already specified, no need to resolve the
1250 * address, only set the port.
1251 */
Benny Prijonoe347cb02007-02-14 14:36:13 +00001252 if (p_pub_addr->sin_port == 0)
1253 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001254
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001255 } else {
1256
Benny Prijono594e4c52006-09-14 18:51:01 +00001257 pj_bzero(p_pub_addr, sizeof(pj_sockaddr_in));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258
Benny Prijono594e4c52006-09-14 18:51:01 +00001259 status = pj_gethostip(&p_pub_addr->sin_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001260 if (status != PJ_SUCCESS) {
Benny Prijonob43bad72007-01-20 05:11:08 +00001261 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001263 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 }
1265
Benny Prijono8ab968f2007-07-20 08:08:30 +00001266 p_pub_addr->sin_family = pj_AF_INET();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268 }
1269
1270 *p_sock = sock;
1271
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001272 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
1273 pj_inet_ntoa(p_pub_addr->sin_addr),
1274 (int)pj_ntohs(p_pub_addr->sin_port)));
1275
Benny Prijonod8410532006-06-15 11:04:33 +00001276 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001277}
1278
1279
1280/*
1281 * Create SIP transport.
1282 */
1283PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1284 const pjsua_transport_config *cfg,
1285 pjsua_transport_id *p_id)
1286{
1287 pjsip_transport *tp;
1288 unsigned id;
1289 pj_status_t status;
1290
1291 PJSUA_LOCK();
1292
1293 /* Find empty transport slot */
1294 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001295 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001296 break;
1297 }
1298
1299 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1300 status = PJ_ETOOMANY;
1301 pjsua_perror(THIS_FILE, "Error creating transport", status);
1302 goto on_return;
1303 }
1304
1305 /* Create the transport */
1306 if (type == PJSIP_TRANSPORT_UDP) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001307 /*
1308 * Create UDP transport.
1309 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001310 pjsua_transport_config config;
Benny Prijonod8410532006-06-15 11:04:33 +00001311 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001312 pj_sockaddr_in bound_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001313 pj_sockaddr_in pub_addr;
1314 pjsip_host_port addr_name;
1315
1316 /* Supply default config if it's not specified */
1317 if (cfg == NULL) {
1318 pjsua_transport_config_default(&config);
1319 cfg = &config;
1320 }
1321
Benny Prijono0a5cad82006-09-26 13:21:02 +00001322 /* Initialize bound address, if any */
1323 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
1324 if (cfg->bound_addr.slen) {
1325 status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr);
1326 if (status != PJ_SUCCESS) {
1327 pjsua_perror(THIS_FILE,
1328 "Unable to resolve transport bound address",
1329 status);
1330 goto on_return;
1331 }
1332 }
1333
1334 /* Initialize the public address from the config, if any */
1335 pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port);
1336 if (cfg->public_addr.slen) {
1337 status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr);
1338 if (status != PJ_SUCCESS) {
1339 pjsua_perror(THIS_FILE,
1340 "Unable to resolve transport public address",
1341 status);
1342 goto on_return;
1343 }
1344 }
1345
1346 /* Create the socket and possibly resolve the address with STUN
1347 * (only when public address is not specified).
1348 */
1349 status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001350 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351 if (status != PJ_SUCCESS)
1352 goto on_return;
1353
1354 addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr));
1355 addr_name.port = pj_ntohs(pub_addr.sin_port);
1356
1357 /* Create UDP transport */
1358 status = pjsip_udp_transport_attach( pjsua_var.endpt, sock,
1359 &addr_name, 1,
1360 &tp);
1361 if (status != PJ_SUCCESS) {
1362 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1363 status);
1364 pj_sock_close(sock);
1365 goto on_return;
1366 }
1367
Benny Prijonoe93e2872006-06-28 16:46:49 +00001368
1369 /* Save the transport */
1370 pjsua_var.tpdata[id].type = type;
1371 pjsua_var.tpdata[id].local_name = tp->local_name;
1372 pjsua_var.tpdata[id].data.tp = tp;
1373
Benny Prijono3569c0d2007-04-06 10:29:20 +00001374#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1375
Benny Prijonoe93e2872006-06-28 16:46:49 +00001376 } else if (type == PJSIP_TRANSPORT_TCP) {
1377 /*
1378 * Create TCP transport.
1379 */
1380 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001381 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001382 pjsip_tpfactory *tcp;
1383 pj_sockaddr_in local_addr;
1384
1385 /* Supply default config if it's not specified */
1386 if (cfg == NULL) {
1387 pjsua_transport_config_default(&config);
1388 cfg = &config;
1389 }
1390
1391 /* Init local address */
1392 pj_sockaddr_in_init(&local_addr, 0, 0);
1393
1394 if (cfg->port)
1395 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1396
Benny Prijono0a5cad82006-09-26 13:21:02 +00001397 if (cfg->bound_addr.slen) {
1398 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1399 if (status != PJ_SUCCESS) {
1400 pjsua_perror(THIS_FILE,
1401 "Unable to resolve transport bound address",
1402 status);
1403 goto on_return;
1404 }
1405 }
1406
1407 /* Init published name */
1408 pj_bzero(&a_name, sizeof(pjsip_host_port));
1409 if (cfg->public_addr.slen)
1410 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001411
1412 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001413 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1414 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001415
1416 if (status != PJ_SUCCESS) {
1417 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1418 status);
1419 goto on_return;
1420 }
1421
1422 /* Save the transport */
1423 pjsua_var.tpdata[id].type = type;
1424 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1425 pjsua_var.tpdata[id].data.factory = tcp;
1426
Benny Prijono3569c0d2007-04-06 10:29:20 +00001427#endif /* PJ_HAS_TCP */
1428
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001429#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1430 } else if (type == PJSIP_TRANSPORT_TLS) {
1431 /*
1432 * Create TLS transport.
1433 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001434 /*
1435 * Create TCP transport.
1436 */
1437 pjsua_transport_config config;
1438 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001439 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001440 pj_sockaddr_in local_addr;
1441
1442 /* Supply default config if it's not specified */
1443 if (cfg == NULL) {
1444 pjsua_transport_config_default(&config);
1445 config.port = 5061;
1446 cfg = &config;
1447 }
1448
1449 /* Init local address */
1450 pj_sockaddr_in_init(&local_addr, 0, 0);
1451
1452 if (cfg->port)
1453 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1454
1455 if (cfg->bound_addr.slen) {
1456 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1457 if (status != PJ_SUCCESS) {
1458 pjsua_perror(THIS_FILE,
1459 "Unable to resolve transport bound address",
1460 status);
1461 goto on_return;
1462 }
1463 }
1464
1465 /* Init published name */
1466 pj_bzero(&a_name, sizeof(pjsip_host_port));
1467 if (cfg->public_addr.slen)
1468 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001469
1470 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001471 &cfg->tls_setting,
1472 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001473 if (status != PJ_SUCCESS) {
1474 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1475 status);
1476 goto on_return;
1477 }
1478
1479 /* Save the transport */
1480 pjsua_var.tpdata[id].type = type;
1481 pjsua_var.tpdata[id].local_name = tls->addr_name;
1482 pjsua_var.tpdata[id].data.factory = tls;
1483#endif
1484
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001485 } else {
1486 status = PJSIP_EUNSUPTRANSPORT;
1487 pjsua_perror(THIS_FILE, "Error creating transport", status);
1488 goto on_return;
1489 }
1490
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001491
1492 /* Return the ID */
1493 if (p_id) *p_id = id;
1494
1495 status = PJ_SUCCESS;
1496
1497on_return:
1498
1499 PJSUA_UNLOCK();
1500
Benny Prijonod8410532006-06-15 11:04:33 +00001501 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502}
1503
1504
1505/*
1506 * Register transport that has been created by application.
1507 */
1508PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1509 pjsua_transport_id *p_id)
1510{
1511 unsigned id;
1512
1513 PJSUA_LOCK();
1514
1515 /* Find empty transport slot */
1516 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001517 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 break;
1519 }
1520
1521 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1522 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1523 PJSUA_UNLOCK();
1524 return PJ_ETOOMANY;
1525 }
1526
1527 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001528 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001529 pjsua_var.tpdata[id].local_name = tp->local_name;
1530 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531
1532 /* Return the ID */
1533 if (p_id) *p_id = id;
1534
1535 PJSUA_UNLOCK();
1536
1537 return PJ_SUCCESS;
1538}
1539
1540
1541/*
1542 * Enumerate all transports currently created in the system.
1543 */
1544PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1545 unsigned *p_count )
1546{
1547 unsigned i, count;
1548
1549 PJSUA_LOCK();
1550
1551 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1552 ++i)
1553 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001554 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555 continue;
1556
1557 id[count++] = i;
1558 }
1559
1560 *p_count = count;
1561
1562 PJSUA_UNLOCK();
1563
1564 return PJ_SUCCESS;
1565}
1566
1567
1568/*
1569 * Get information about transports.
1570 */
1571PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1572 pjsua_transport_info *info)
1573{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001574 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001575 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001576
Benny Prijonoac623b32006-07-03 15:19:31 +00001577 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578
1579 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001580 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1581 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582
1583 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001584 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001585
1586 PJSUA_LOCK();
1587
Benny Prijonoe93e2872006-06-28 16:46:49 +00001588 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1589
1590 pjsip_transport *tp = t->data.tp;
1591
1592 if (tp == NULL) {
1593 PJSUA_UNLOCK();
1594 return PJ_EINVALIDOP;
1595 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596
Benny Prijonoe93e2872006-06-28 16:46:49 +00001597 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001598 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001599 info->type_name = pj_str(tp->type_name);
1600 info->info = pj_str(tp->info);
1601 info->flag = tp->flag;
1602 info->addr_len = tp->addr_len;
1603 info->local_addr = tp->local_addr;
1604 info->local_name = tp->local_name;
1605 info->usage_count = pj_atomic_get(tp->ref_cnt);
1606
Benny Prijono0a5cad82006-09-26 13:21:02 +00001607 status = PJ_SUCCESS;
1608
Benny Prijonoe93e2872006-06-28 16:46:49 +00001609 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1610
1611 pjsip_tpfactory *factory = t->data.factory;
1612
1613 if (factory == NULL) {
1614 PJSUA_UNLOCK();
1615 return PJ_EINVALIDOP;
1616 }
1617
1618 info->id = id;
1619 info->type = t->type;
1620 info->type_name = pj_str("TCP");
1621 info->info = pj_str("TCP transport");
1622 info->flag = factory->flag;
1623 info->addr_len = sizeof(factory->local_addr);
1624 info->local_addr = factory->local_addr;
1625 info->local_name = factory->addr_name;
1626 info->usage_count = 0;
1627
Benny Prijono0a5cad82006-09-26 13:21:02 +00001628 status = PJ_SUCCESS;
1629
1630 } else {
1631 pj_assert(!"Unsupported transport");
1632 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001633 }
1634
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635
1636 PJSUA_UNLOCK();
1637
Benny Prijono0a5cad82006-09-26 13:21:02 +00001638 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639}
1640
1641
1642/*
1643 * Disable a transport or re-enable it.
1644 */
1645PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1646 pj_bool_t enabled)
1647{
1648 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001649 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1650 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001651
1652 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001653 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001654
1655
1656 /* To be done!! */
1657 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001658 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001659
1660 return PJ_EINVALIDOP;
1661}
1662
1663
1664/*
1665 * Close the transport.
1666 */
1667PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1668 pj_bool_t force )
1669{
Benny Prijono5ff61872007-02-01 03:37:11 +00001670 pj_status_t status;
1671
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001672 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001673 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1674 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001675
1676 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001677 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001678
Benny Prijono0a5cad82006-09-26 13:21:02 +00001679 /* Note: destroy() may not work if there are objects still referencing
1680 * the transport.
1681 */
1682 if (force) {
1683 switch (pjsua_var.tpdata[id].type) {
1684 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001685 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1686 if (status != PJ_SUCCESS)
1687 return status;
1688 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1689 if (status != PJ_SUCCESS)
1690 return status;
1691 break;
1692
1693 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001694 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001695 /* This will close the TCP listener, but existing TCP/TLS
1696 * connections (if any) will still linger
1697 */
1698 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1699 (pjsua_var.tpdata[id].data.factory);
1700 if (status != PJ_SUCCESS)
1701 return status;
1702
Benny Prijono0a5cad82006-09-26 13:21:02 +00001703 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001704
Benny Prijono1ebd6142006-10-19 15:48:02 +00001705 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001706 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001707 }
1708
1709 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001710 /* If force is not specified, transports will be closed at their
1711 * convenient time. However this will leak PJSUA-API transport
1712 * descriptors as PJSUA-API wouldn't know when exactly the
1713 * transport is closed thus it can't cleanup PJSUA transport
1714 * descriptor.
1715 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001716 switch (pjsua_var.tpdata[id].type) {
1717 case PJSIP_TRANSPORT_UDP:
1718 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001719 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001720 case PJSIP_TRANSPORT_TCP:
1721 return (*pjsua_var.tpdata[id].data.factory->destroy)
1722 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001723 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001724 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001725 }
1726 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727
Benny Prijono5ff61872007-02-01 03:37:11 +00001728 /* Cleanup pjsua data when force is applied */
1729 if (force) {
1730 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1731 pjsua_var.tpdata[id].data.ptr = NULL;
1732 }
1733
1734 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735}
1736
1737
1738/*
1739 * Add additional headers etc in msg_data specified by application
1740 * when sending requests.
1741 */
1742void pjsua_process_msg_data(pjsip_tx_data *tdata,
1743 const pjsua_msg_data *msg_data)
1744{
1745 pj_bool_t allow_body;
1746 const pjsip_hdr *hdr;
1747
Benny Prijono56315612006-07-18 14:39:40 +00001748 /* Always add User-Agent */
1749 if (pjsua_var.ua_cfg.user_agent.slen &&
1750 tdata->msg->type == PJSIP_REQUEST_MSG)
1751 {
1752 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1753 pjsip_hdr *h;
1754 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1755 &STR_USER_AGENT,
1756 &pjsua_var.ua_cfg.user_agent);
1757 pjsip_msg_add_hdr(tdata->msg, h);
1758 }
1759
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001760 if (!msg_data)
1761 return;
1762
1763 hdr = msg_data->hdr_list.next;
1764 while (hdr && hdr != &msg_data->hdr_list) {
1765 pjsip_hdr *new_hdr;
1766
Benny Prijonoa1e69682007-05-11 15:14:34 +00001767 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001768 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1769
1770 hdr = hdr->next;
1771 }
1772
1773 allow_body = (tdata->msg->body == NULL);
1774
1775 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1776 pjsip_media_type ctype;
1777 pjsip_msg_body *body;
1778
1779 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1780 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1781 &msg_data->msg_body);
1782 tdata->msg->body = body;
1783 }
1784}
1785
1786
1787/*
1788 * Add route_set to outgoing requests
1789 */
1790void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1791 const pjsip_route_hdr *route_set )
1792{
1793 const pjsip_route_hdr *r;
1794
1795 r = route_set->next;
1796 while (r != route_set) {
1797 pjsip_route_hdr *new_r;
1798
Benny Prijonoa1e69682007-05-11 15:14:34 +00001799 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001800 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1801
1802 r = r->next;
1803 }
1804}
1805
1806
1807/*
1808 * Simple version of MIME type parsing (it doesn't support parameters)
1809 */
1810void pjsua_parse_media_type( pj_pool_t *pool,
1811 const pj_str_t *mime,
1812 pjsip_media_type *media_type)
1813{
1814 pj_str_t tmp;
1815 char *pos;
1816
Benny Prijonoac623b32006-07-03 15:19:31 +00001817 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818
1819 pj_strdup_with_null(pool, &tmp, mime);
1820
1821 pos = pj_strchr(&tmp, '/');
1822 if (pos) {
1823 media_type->type.ptr = tmp.ptr;
1824 media_type->type.slen = (pos-tmp.ptr);
1825 media_type->subtype.ptr = pos+1;
1826 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1827 } else {
1828 media_type->type = tmp;
1829 }
1830}
1831
1832
1833/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001834 * Internal function to init transport selector from transport id.
1835 */
1836void pjsua_init_tpselector(pjsua_transport_id tp_id,
1837 pjsip_tpselector *sel)
1838{
1839 pjsua_transport_data *tpdata;
1840 unsigned flag;
1841
1842 pj_bzero(sel, sizeof(*sel));
1843 if (tp_id == PJSUA_INVALID_ID)
1844 return;
1845
Benny Prijonoa1e69682007-05-11 15:14:34 +00001846 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001847 tpdata = &pjsua_var.tpdata[tp_id];
1848
1849 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1850
1851 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1852 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1853 sel->u.transport = tpdata->data.tp;
1854 } else {
1855 sel->type = PJSIP_TPSELECTOR_LISTENER;
1856 sel->u.listener = tpdata->data.factory;
1857 }
1858}
1859
1860
1861/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001862 * Detect NAT type.
1863 */
1864PJ_DEF(pj_status_t) pjsua_detect_nat_type( void *user_data,
1865 pj_stun_nat_detect_cb *cb)
1866{
1867 pj_status_t status;
1868
1869 /* Make sure STUN server resolution has completed */
1870 status = pjsua_resolve_stun_server(PJ_TRUE);
1871 if (status != PJ_SUCCESS) {
1872 return status;
1873 }
1874
1875 /* Make sure we have STUN */
1876 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
1877 return PJ_EINVALIDOP;
1878 }
1879
1880 return pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
1881 &pjsua_var.stun_cfg,
1882 user_data, cb);
1883}
1884
1885
1886/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001887 * Verify that valid SIP url is given.
1888 */
1889PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
1890{
1891 pjsip_uri *p;
1892 pj_pool_t *pool;
1893 char *url;
1894 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
1895
1896 if (!len) return -1;
1897
1898 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
1899 if (!pool) return -1;
1900
Benny Prijonoa1e69682007-05-11 15:14:34 +00001901 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001902 pj_ansi_strcpy(url, c_url);
1903
1904 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001905 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
1906 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
1907 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001908 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001909 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001910
1911 pj_pool_release(pool);
1912 return p ? 0 : -1;
1913}
Benny Prijonoda9785b2007-04-02 20:43:06 +00001914
1915
1916/*
1917 * This is a utility function to dump the stack states to log, using
1918 * verbosity level 3.
1919 */
1920PJ_DEF(void) pjsua_dump(pj_bool_t detail)
1921{
1922 unsigned old_decor;
1923 unsigned i;
1924 char buf[1024];
1925
1926 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
1927
1928 old_decor = pj_log_get_decor();
1929 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
1930
1931 if (detail)
1932 pj_dump_config();
1933
1934 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
1935
1936 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
1937
1938 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
1939 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1940 pjsua_call *call = &pjsua_var.calls[i];
1941 pjmedia_sock_info skinfo;
1942
Benny Prijono4f093d22007-04-09 08:54:32 +00001943 /* MSVC complains about skinfo not being initialized */
1944 pj_bzero(&skinfo, sizeof(skinfo));
1945
Benny Prijonoda9785b2007-04-02 20:43:06 +00001946 pjmedia_transport_get_info(call->med_tp, &skinfo);
1947
1948 PJ_LOG(3,(THIS_FILE, " %s: %s:%d",
1949 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
1950 pj_inet_ntoa(skinfo.rtp_addr_name.sin_addr),
1951 (int)pj_ntohs(skinfo.rtp_addr_name.sin_port)));
1952 }
1953
1954 pjsip_tsx_layer_dump(detail);
1955 pjsip_ua_dump(detail);
1956
1957
1958 /* Dump all invite sessions: */
1959 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
1960
1961 if (pjsua_call_get_count() == 0) {
1962
1963 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
1964
1965 } else {
1966 unsigned i;
1967
1968 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
1969 if (pjsua_call_is_active(i)) {
1970 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
1971 PJ_LOG(3,(THIS_FILE, "%s", buf));
1972 }
1973 }
1974 }
1975
1976 /* Dump presence status */
1977 pjsua_pres_dump(detail);
1978
1979 pj_log_set_decor(old_decor);
1980 PJ_LOG(3,(THIS_FILE, "Dump complete"));
1981}
1982