blob: ff9b36fad628747c8a940448c6fdcab44605a0f6 [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 Prijono91a6a172007-10-31 08:59:29 +000089 cfg->nat_type_in_sdp = 1;
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 Prijonobddef2c2007-10-31 13:28:08 +0000143 cfg->ka_interval = 15;
144 cfg->ka_data = pj_str("\r\n");
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000145}
146
147PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
148{
149 pj_bzero(cfg, sizeof(*cfg));
150}
151
152PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
153{
154 pj_bzero(cfg, sizeof(*cfg));
155
156 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE;
Benny Prijonocf0b4b22007-10-06 17:31:09 +0000157 cfg->audio_frame_ptime = 10;
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000158 cfg->max_media_ports = 32;
159 cfg->has_ioqueue = PJ_TRUE;
160 cfg->thread_cnt = 1;
161 cfg->quality = PJSUA_DEFAULT_CODEC_QUALITY;
162 cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
163 cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
164 cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
165}
166
Benny Prijonodc39fe82006-05-26 12:17:46 +0000167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168/*****************************************************************************
169 * This is a very simple PJSIP module, whose sole purpose is to display
170 * incoming and outgoing messages to log. This module will have priority
171 * higher than transport layer, which means:
172 *
173 * - incoming messages will come to this module first before reaching
174 * transaction layer.
175 *
176 * - outgoing messages will come to this module last, after the message
177 * has been 'printed' to contiguous buffer by transport layer and
178 * appropriate transport instance has been decided for this message.
179 *
180 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000181
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182/* Notification on incoming messages */
183static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
184{
Benny Prijonob4a17c92006-07-10 14:40:21 +0000185 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
186 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 "--end msg--",
188 rdata->msg_info.len,
189 pjsip_rx_data_get_info(rdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000190 rdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191 rdata->pkt_info.src_name,
192 rdata->pkt_info.src_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000193 (int)rdata->msg_info.len,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194 rdata->msg_info.msg_buf));
195
196 /* Always return false, otherwise messages will not get processed! */
197 return PJ_FALSE;
198}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200/* Notification on outgoing messages */
201static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
202{
203
204 /* Important note:
205 * tp_info field is only valid after outgoing messages has passed
206 * transport layer. So don't try to access tp_info when the module
207 * has lower priority than transport layer.
208 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000209
Benny Prijonob4a17c92006-07-10 14:40:21 +0000210 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
211 "%.*s\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 "--end msg--",
213 (tdata->buf.cur - tdata->buf.start),
214 pjsip_tx_data_get_info(tdata),
Benny Prijonob4a17c92006-07-10 14:40:21 +0000215 tdata->tp_info.transport->type_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 tdata->tp_info.dst_name,
217 tdata->tp_info.dst_port,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000218 (int)(tdata->buf.cur - tdata->buf.start),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 tdata->buf.start));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 /* Always return success, otherwise message will not get sent! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000222 return PJ_SUCCESS;
223}
224
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000225/* The module instance. */
226static pjsip_module pjsua_msg_logger =
227{
228 NULL, NULL, /* prev, next. */
229 { "mod-pjsua-log", 13 }, /* Name. */
230 -1, /* Id */
231 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
232 NULL, /* load() */
233 NULL, /* start() */
234 NULL, /* stop() */
235 NULL, /* unload() */
236 &logging_on_rx_msg, /* on_rx_request() */
237 &logging_on_rx_msg, /* on_rx_response() */
238 &logging_on_tx_msg, /* on_tx_request. */
239 &logging_on_tx_msg, /* on_tx_response() */
240 NULL, /* on_tsx_state() */
241
242};
243
244
245/*****************************************************************************
Benny Prijono56315612006-07-18 14:39:40 +0000246 * Another simple module to handle incoming OPTIONS request
247 */
248
249/* Notification on incoming request */
250static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
251{
252 pjsip_tx_data *tdata;
253 pjsip_response_addr res_addr;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000254 pjmedia_sock_info skinfo;
Benny Prijono56315612006-07-18 14:39:40 +0000255 pjmedia_sdp_session *sdp;
256 const pjsip_hdr *cap_hdr;
257 pj_status_t status;
258
259 /* Only want to handle OPTIONS requests */
260 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000261 pjsip_get_options_method()) != 0)
Benny Prijono56315612006-07-18 14:39:40 +0000262 {
263 return PJ_FALSE;
264 }
265
266 /* Create basic response. */
267 status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
268 &tdata);
269 if (status != PJ_SUCCESS) {
270 pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
271 return PJ_TRUE;
272 }
273
274 /* Add Allow header */
275 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
276 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000277 pjsip_msg_add_hdr(tdata->msg,
278 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000279 }
280
281 /* Add Accept header */
282 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
283 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000284 pjsip_msg_add_hdr(tdata->msg,
285 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000286 }
287
288 /* Add Supported header */
289 cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
290 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000291 pjsip_msg_add_hdr(tdata->msg,
292 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000293 }
294
295 /* Add Allow-Events header from the evsub module */
296 cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
297 if (cap_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000298 pjsip_msg_add_hdr(tdata->msg,
299 (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr));
Benny Prijono56315612006-07-18 14:39:40 +0000300 }
301
302 /* Add User-Agent header */
303 if (pjsua_var.ua_cfg.user_agent.slen) {
304 const pj_str_t USER_AGENT = { "User-Agent", 10};
305 pjsip_hdr *h;
306
307 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
308 &USER_AGENT,
309 &pjsua_var.ua_cfg.user_agent);
310 pjsip_msg_add_hdr(tdata->msg, h);
311 }
312
Benny Prijono617c5bc2007-04-02 19:51:21 +0000313 /* Get media socket info */
314 pjmedia_transport_get_info(pjsua_var.calls[0].med_tp, &skinfo);
315
Benny Prijono56315612006-07-18 14:39:40 +0000316 /* Add SDP body, using call0's RTP address */
317 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000318 &skinfo, &sdp);
Benny Prijono56315612006-07-18 14:39:40 +0000319 if (status == PJ_SUCCESS) {
320 pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
321 }
322
323 /* Send response statelessly */
324 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
325 status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
326 if (status != PJ_SUCCESS)
327 pjsip_tx_data_dec_ref(tdata);
328
329 return PJ_TRUE;
330}
331
332
333/* The module instance. */
334static pjsip_module pjsua_options_handler =
335{
336 NULL, NULL, /* prev, next. */
337 { "mod-pjsua-options", 17 }, /* Name. */
338 -1, /* Id */
339 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
340 NULL, /* load() */
341 NULL, /* start() */
342 NULL, /* stop() */
343 NULL, /* unload() */
344 &options_on_rx_request, /* on_rx_request() */
345 NULL, /* on_rx_response() */
346 NULL, /* on_tx_request. */
347 NULL, /* on_tx_response() */
348 NULL, /* on_tsx_state() */
349
350};
351
352
353/*****************************************************************************
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 * These two functions are the main callbacks registered to PJSIP stack
355 * to receive SIP request and response messages that are outside any
356 * dialogs and any transactions.
357 */
Benny Prijono268ca612006-02-07 12:34:11 +0000358
359/*
360 * Handler for receiving incoming requests.
361 *
362 * This handler serves multiple purposes:
363 * - it receives requests outside dialogs.
364 * - it receives requests inside dialogs, when the requests are
365 * unhandled by other dialog usages. Example of these
366 * requests are: MESSAGE.
367 */
368static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
369{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 pj_bool_t processed = PJ_FALSE;
371
372 PJSUA_LOCK();
Benny Prijono38998232006-02-08 22:44:25 +0000373
Benny Prijono84126ab2006-02-09 09:30:09 +0000374 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000375
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 processed = pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000377 }
378
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000379 PJSUA_UNLOCK();
380
381 return processed;
Benny Prijono268ca612006-02-07 12:34:11 +0000382}
383
384
385/*
386 * Handler for receiving incoming responses.
387 *
388 * This handler serves multiple purposes:
389 * - it receives strayed responses (i.e. outside any dialog and
390 * outside any transactions).
391 * - it receives responses coming to a transaction, when pjsua
392 * module is set as transaction user for the transaction.
393 * - it receives responses inside a dialog, when these responses
394 * are unhandled by other dialog usages.
395 */
396static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
397{
398 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000399 return PJ_FALSE;
400}
401
402
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403/*****************************************************************************
404 * Logging.
405 */
406
407/* Log callback */
408static void log_writer(int level, const char *buffer, int len)
Benny Prijono268ca612006-02-07 12:34:11 +0000409{
Benny Prijono572d4852006-11-23 21:50:02 +0000410 /* Write to file, stdout or application callback. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000411
412 if (pjsua_var.log_file) {
413 pj_ssize_t size = len;
414 pj_file_write(pjsua_var.log_file, buffer, &size);
Benny Prijono980ca0a2007-04-03 17:55:08 +0000415 /* This will slow things down considerably! Don't do it!
416 pj_file_flush(pjsua_var.log_file);
417 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418 }
419
Benny Prijono572d4852006-11-23 21:50:02 +0000420 if (level <= (int)pjsua_var.log_cfg.console_level) {
421 if (pjsua_var.log_cfg.cb)
422 (*pjsua_var.log_cfg.cb)(level, buffer, len);
423 else
424 pj_log_write(level, buffer, len);
425 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426}
427
428
429/*
430 * Application can call this function at any time (after pjsua_create(), of
431 * course) to change logging settings.
432 */
433PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
434{
435 pj_status_t status;
436
437 /* Save config. */
438 pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
439
440 /* Redirect log function to ours */
441 pj_log_set_log_func( &log_writer );
442
Benny Prijono9cb09a22007-08-30 09:35:10 +0000443 /* Set decor */
444 pj_log_set_decor(pjsua_var.log_cfg.decor);
445
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446 /* Close existing file, if any */
447 if (pjsua_var.log_file) {
448 pj_file_close(pjsua_var.log_file);
449 pjsua_var.log_file = NULL;
450 }
451
452 /* If output log file is desired, create the file: */
453 if (pjsua_var.log_cfg.log_filename.slen) {
454
455 status = pj_file_open(pjsua_var.pool,
456 pjsua_var.log_cfg.log_filename.ptr,
457 PJ_O_WRONLY,
458 &pjsua_var.log_file);
459
460 if (status != PJ_SUCCESS) {
461 pjsua_perror(THIS_FILE, "Error creating log file", status);
462 return status;
463 }
464 }
465
466 /* Unregister msg logging if it's previously registered */
467 if (pjsua_msg_logger.id >= 0) {
468 pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
469 pjsua_msg_logger.id = -1;
470 }
471
472 /* Enable SIP message logging */
473 if (pjsua_var.log_cfg.msg_logging)
474 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
475
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000476 return PJ_SUCCESS;
477}
478
479
480/*****************************************************************************
481 * PJSUA Base API.
482 */
483
484/* Worker thread function. */
485static int worker_thread(void *arg)
486{
487 enum { TIMEOUT = 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000488
Benny Prijono268ca612006-02-07 12:34:11 +0000489 PJ_UNUSED_ARG(arg);
490
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 while (!pjsua_var.thread_quit_flag) {
492 int count;
493
494 count = pjsua_handle_events(TIMEOUT);
495 if (count < 0)
496 pj_thread_sleep(TIMEOUT);
497 }
Benny Prijono268ca612006-02-07 12:34:11 +0000498
499 return 0;
500}
501
Benny Prijonodc39fe82006-05-26 12:17:46 +0000502
Benny Prijono268ca612006-02-07 12:34:11 +0000503/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 * Instantiate pjsua application.
Benny Prijonodc39fe82006-05-26 12:17:46 +0000505 */
506PJ_DEF(pj_status_t) pjsua_create(void)
507{
508 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000509
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 /* Init pjsua data */
511 init_data();
Benny Prijono268ca612006-02-07 12:34:11 +0000512
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000513 /* Set default logging settings */
514 pjsua_logging_config_default(&pjsua_var.log_cfg);
515
516 /* Init PJLIB: */
Benny Prijono268ca612006-02-07 12:34:11 +0000517 status = pj_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
519
Benny Prijono268ca612006-02-07 12:34:11 +0000520
Benny Prijonofccab712006-02-22 22:23:22 +0000521 /* Init PJLIB-UTIL: */
Benny Prijonofccab712006-02-22 22:23:22 +0000522 status = pjlib_util_init();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonofccab712006-02-22 22:23:22 +0000524
Benny Prijonoec921342007-03-24 13:00:30 +0000525 /* Init PJNATH */
526 status = pjnath_init();
527 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono268ca612006-02-07 12:34:11 +0000528
Benny Prijono094d3ad2006-11-21 08:41:00 +0000529 /* Set default sound device ID */
530 pjsua_var.cap_dev = pjsua_var.play_dev = -1;
531
Benny Prijono268ca612006-02-07 12:34:11 +0000532 /* Init caching pool. */
Benny Prijono106f5b72007-08-30 13:49:33 +0000533 pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
Benny Prijono268ca612006-02-07 12:34:11 +0000534
535 /* Create memory pool for application. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
537
538 PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
Benny Prijono268ca612006-02-07 12:34:11 +0000539
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 /* Create mutex */
541 status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
542 &pjsua_var.mutex);
Benny Prijonoccf95622006-02-07 18:48:01 +0000543 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 pjsua_perror(THIS_FILE, "Unable to create mutex", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000545 return status;
546 }
547
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000548 /* Must create SIP endpoint to initialize SIP parser. The parser
549 * is needed for example when application needs to call pjsua_verify_url().
Benny Prijonob8c25182006-04-29 08:31:09 +0000550 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 status = pjsip_endpt_create(&pjsua_var.cp.factory,
552 pj_gethostname()->ptr,
553 &pjsua_var.endpt);
554 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono39879152006-02-23 02:09:10 +0000555
556
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000557 return PJ_SUCCESS;
558}
559
560
561/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 * Initialize pjsua with the specified settings. All the settings are
563 * optional, and the default values will be used when the config is not
564 * specified.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000565 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000566PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
567 const pjsua_logging_config *log_cfg,
568 const pjsua_media_config *media_cfg)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000569{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000570 pjsua_config default_cfg;
571 pjsua_media_config default_media_cfg;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000572 const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573 pj_status_t status;
574
575
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000576 /* Create default configurations when the config is not supplied */
577
578 if (ua_cfg == NULL) {
579 pjsua_config_default(&default_cfg);
580 ua_cfg = &default_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000581 }
582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 if (media_cfg == NULL) {
584 pjsua_media_config_default(&default_media_cfg);
585 media_cfg = &default_media_cfg;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000586 }
587
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588 /* Initialize logging first so that info/errors can be captured */
589 if (log_cfg) {
590 status = pjsua_reconfigure_logging(log_cfg);
591 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000592 }
593
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000594 /* If nameserver is configured, create DNS resolver instance and
595 * set it to be used by SIP resolver.
596 */
597 if (ua_cfg->nameserver_count) {
598#if PJSIP_HAS_RESOLVER
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000599 unsigned i;
600
601 /* Create DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000602 status = pjsip_endpt_create_resolver(pjsua_var.endpt,
603 &pjsua_var.resolver);
Benny Prijono318f3842007-05-15 20:27:08 +0000604 if (status != PJ_SUCCESS) {
605 pjsua_perror(THIS_FILE, "Error creating resolver", status);
606 return status;
607 }
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000608
609 /* Configure nameserver for the DNS resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000610 status = pj_dns_resolver_set_ns(pjsua_var.resolver,
611 ua_cfg->nameserver_count,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000612 ua_cfg->nameserver, NULL);
613 if (status != PJ_SUCCESS) {
614 pjsua_perror(THIS_FILE, "Error setting nameserver", status);
615 return status;
616 }
617
618 /* Set this DNS resolver to be used by the SIP resolver */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000619 status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000620 if (status != PJ_SUCCESS) {
621 pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
622 return status;
623 }
624
625 /* Print nameservers */
626 for (i=0; i<ua_cfg->nameserver_count; ++i) {
627 PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
628 (int)ua_cfg->nameserver[i].slen,
629 ua_cfg->nameserver[i].ptr));
630 }
631#else
632 PJ_LOG(2,(THIS_FILE,
633 "DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
634#endif
635 }
636
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000637 /* Init SIP UA: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000638
639 /* Initialize transaction layer: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000640 status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
641 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000642
Benny Prijonodc39fe82006-05-26 12:17:46 +0000643
644 /* Initialize UA layer module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000645 status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
646 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000647
Benny Prijonodc39fe82006-05-26 12:17:46 +0000648
Benny Prijono053f5222006-11-11 16:16:04 +0000649 /* Initialize Replaces support. */
650 status = pjsip_replaces_init_module( pjsua_var.endpt );
651 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
652
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000653 /* Initialize 100rel support */
654 status = pjsip_100rel_init_module(pjsua_var.endpt);
655 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono053f5222006-11-11 16:16:04 +0000656
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000657 /* Initialize and register PJSUA application module. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000658 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000659 const pjsip_module mod_initializer =
Benny Prijonodc39fe82006-05-26 12:17:46 +0000660 {
661 NULL, NULL, /* prev, next. */
662 { "mod-pjsua", 9 }, /* Name. */
663 -1, /* Id */
664 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
665 NULL, /* load() */
666 NULL, /* start() */
667 NULL, /* stop() */
668 NULL, /* unload() */
669 &mod_pjsua_on_rx_request, /* on_rx_request() */
670 &mod_pjsua_on_rx_response, /* on_rx_response() */
671 NULL, /* on_tx_request. */
672 NULL, /* on_tx_response() */
673 NULL, /* on_tsx_state() */
674 };
675
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 pjsua_var.mod = mod_initializer;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000677
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000678 status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
679 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000680 }
681
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682
Benny Prijonodc39fe82006-05-26 12:17:46 +0000683
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000684 /* Initialize PJSUA call subsystem: */
685 status = pjsua_call_subsys_init(ua_cfg);
686 if (status != PJ_SUCCESS)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000687 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688
689
Benny Prijonoc97608e2007-03-23 16:34:20 +0000690 /* Start resolving STUN server */
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000691
Benny Prijonoc97608e2007-03-23 16:34:20 +0000692 status = pjsua_resolve_stun_server(PJ_FALSE);
693 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
694 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
695 return status;
696 }
697
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000698 /* Initialize PJSUA media subsystem */
699 status = pjsua_media_subsys_init(media_cfg);
700 if (status != PJ_SUCCESS)
701 goto on_error;
702
Benny Prijonodc39fe82006-05-26 12:17:46 +0000703
704 /* Init core SIMPLE module : */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000705 status = pjsip_evsub_init_module(pjsua_var.endpt);
706 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000707
Benny Prijonodc39fe82006-05-26 12:17:46 +0000708
709 /* Init presence module: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000710 status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
711 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000712
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000713 /* Init PUBLISH module */
714 pjsip_publishc_init_module(pjsua_var.endpt);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000715
716 /* Init xfer/REFER module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000717 status = pjsip_xfer_init_module( pjsua_var.endpt );
718 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000719
720 /* Init pjsua presence handler: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000721 status = pjsua_pres_init();
722 if (status != PJ_SUCCESS)
723 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000724
725 /* Init out-of-dialog MESSAGE request handler. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000726 status = pjsua_im_init();
727 if (status != PJ_SUCCESS)
728 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000729
Benny Prijonoc8141a82006-08-20 09:12:19 +0000730 /* Register OPTIONS handler */
731 pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
732
733 /* Add OPTIONS in Allow header */
734 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
735 NULL, 1, &STR_OPTIONS);
736
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000737 /* Start worker thread if needed. */
738 if (pjsua_var.ua_cfg.thread_cnt) {
739 unsigned i;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000740
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000741 if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
742 pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000743
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000744 for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
745 status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
746 NULL, 0, 0, &pjsua_var.thread[i]);
747 if (status != PJ_SUCCESS)
748 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000749 }
Benny Prijonof521eb02006-08-06 23:07:25 +0000750 PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
751 pjsua_var.ua_cfg.thread_cnt));
752 } else {
753 PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
Benny Prijonodc39fe82006-05-26 12:17:46 +0000754 }
755
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000756 /* Done! */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000757
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000758 PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
Benny Prijono106f5b72007-08-30 13:49:33 +0000759 pj_get_version(), PJ_OS_NAME));
Benny Prijonoccf95622006-02-07 18:48:01 +0000760
Benny Prijono268ca612006-02-07 12:34:11 +0000761 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000762
763on_error:
764 pjsua_destroy();
765 return status;
Benny Prijono268ca612006-02-07 12:34:11 +0000766}
767
768
Benny Prijono834aee32006-02-19 01:38:06 +0000769/* Sleep with polling */
770static void busy_sleep(unsigned msec)
771{
Benny Prijonob2c96822007-05-03 13:31:21 +0000772#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
Benny Prijono897f9f82007-05-03 19:56:21 +0000773 /* Ideally we shouldn't call pj_thread_sleep() and rather
774 * CActiveScheduler::WaitForAnyRequest() here, but that will
775 * drag in Symbian header and it doesn't look pretty.
776 */
Benny Prijonob2c96822007-05-03 13:31:21 +0000777 pj_thread_sleep(msec);
778#else
Benny Prijono834aee32006-02-19 01:38:06 +0000779 pj_time_val timeout, now;
780
781 pj_gettimeofday(&timeout);
782 timeout.msec += msec;
783 pj_time_val_normalize(&timeout);
784
785 do {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000786 while (pjsua_handle_events(10) > 0)
787 ;
Benny Prijono834aee32006-02-19 01:38:06 +0000788 pj_gettimeofday(&now);
789 } while (PJ_TIME_VAL_LT(now, timeout));
Benny Prijonob2c96822007-05-03 13:31:21 +0000790#endif
Benny Prijono834aee32006-02-19 01:38:06 +0000791}
792
Benny Prijonoebbf6892007-03-24 17:37:25 +0000793
Benny Prijonoebbf6892007-03-24 17:37:25 +0000794/*
795 * Callback function to receive notification from the resolver
796 * when the resolution process completes.
797 */
798static void stun_dns_srv_resolver_cb(void *user_data,
799 pj_status_t status,
800 const pj_dns_srv_record *rec)
801{
802 unsigned i;
803
804 PJ_UNUSED_ARG(user_data);
805
806 pjsua_var.stun_status = status;
807
808 if (status != PJ_SUCCESS) {
809 /* DNS SRV resolution failed. If stun_host is specified, resolve
810 * it with gethostbyname()
811 */
812 if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000813 pj_str_t str_host, str_port;
814 int port;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000815 pj_hostent he;
816
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000817 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
818 if (str_port.ptr != NULL) {
819 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
820 str_host.slen = (str_port.ptr - str_host.ptr);
821 str_port.ptr++;
822 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
823 str_host.slen - 1;
824 port = (int)pj_strtoul(&str_port);
825 if (port < 1 || port > 65535) {
826 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
827 pjsua_var.stun_status = PJ_EINVAL;
828 return;
829 }
830 } else {
831 str_host = pjsua_var.ua_cfg.stun_host;
832 port = 3478;
833 }
834
835 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000836
837 if (pjsua_var.stun_status == PJ_SUCCESS) {
838 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
839 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000840 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
Benny Prijonoebbf6892007-03-24 17:37:25 +0000841
842 PJ_LOG(3,(THIS_FILE,
843 "STUN server %.*s resolved, address is %s:%d",
844 (int)pjsua_var.ua_cfg.stun_host.slen,
845 pjsua_var.ua_cfg.stun_host.ptr,
846 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
847 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
848 }
849 } else {
850 char errmsg[PJ_ERR_MSG_SIZE];
851
852 pj_strerror(status, errmsg, sizeof(errmsg));
853 PJ_LOG(1,(THIS_FILE,
854 "DNS SRV resolution failed for STUN server %.*s: %s",
855 (int)pjsua_var.ua_cfg.stun_domain.slen,
856 pjsua_var.ua_cfg.stun_domain.ptr,
857 errmsg));
858 }
859 return;
860 }
861
Benny Prijonof5afa922007-06-11 16:51:18 +0000862 pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
863 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
864 rec->entry[0].port);
865 pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
866 rec->entry[0].server.addr[0].s_addr;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000867
868 PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
869 (int)pjsua_var.ua_cfg.stun_domain.slen,
870 pjsua_var.ua_cfg.stun_domain.ptr,
871 rec->count));
872
873 for (i=0; i<rec->count; ++i) {
874 PJ_LOG(3,(THIS_FILE,
875 " %d: prio=%d, weight=%d %s:%d",
876 i, rec->entry[i].priority, rec->entry[i].weight,
Benny Prijonof5afa922007-06-11 16:51:18 +0000877 pj_inet_ntoa(rec->entry[i].server.addr[0]),
878 (int)rec->entry[i].port));
Benny Prijonoebbf6892007-03-24 17:37:25 +0000879 }
880
881}
882
Benny Prijono268ca612006-02-07 12:34:11 +0000883/*
Benny Prijonoc97608e2007-03-23 16:34:20 +0000884 * Resolve STUN server.
885 */
886pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
887{
888 if (pjsua_var.stun_status == PJ_EUNKNOWN) {
889 /* Initialize STUN configuration */
890 pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
891 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
892 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
893
894 /* Start STUN server resolution */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000895
896 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000897
Benny Prijonoebbf6892007-03-24 17:37:25 +0000898 /* If stun_domain is specified, resolve STUN servers with DNS
899 * SRV resolution.
900 */
901 if (pjsua_var.ua_cfg.stun_domain.slen) {
902 pj_str_t res_type;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000903 pj_status_t status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000904
905 /* Fail if resolver is not configured */
906 if (pjsua_var.resolver == NULL) {
907 PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
908 "stun_domain is specified"));
909 pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
910 return PJLIB_UTIL_EDNSNONS;
911 }
912 res_type = pj_str("_stun._udp");
Benny Prijonoda9785b2007-04-02 20:43:06 +0000913 status =
Benny Prijonoebbf6892007-03-24 17:37:25 +0000914 pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
915 3478, pjsua_var.pool, pjsua_var.resolver,
Benny Prijonof5afa922007-06-11 16:51:18 +0000916 0, NULL, &stun_dns_srv_resolver_cb, NULL);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000917 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +0000918 pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
919 pjsua_var.stun_status);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000920 pjsua_var.stun_status = status;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000921 return pjsua_var.stun_status;
Benny Prijonoda9785b2007-04-02 20:43:06 +0000922 } else {
923 pjsua_var.stun_status = PJ_EPENDING;
Benny Prijonoebbf6892007-03-24 17:37:25 +0000924 }
925 }
926 /* Otherwise if stun_host is specified, resolve STUN server with
927 * gethostbyname().
928 */
929 else if (pjsua_var.ua_cfg.stun_host.slen) {
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000930 pj_str_t str_host, str_port;
931 int port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000932 pj_hostent he;
933
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000934 str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
935 if (str_port.ptr != NULL) {
936 str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
937 str_host.slen = (str_port.ptr - str_host.ptr);
938 str_port.ptr++;
939 str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
940 str_host.slen - 1;
941 port = (int)pj_strtoul(&str_port);
942 if (port < 1 || port > 65535) {
943 pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
944 pjsua_var.stun_status = PJ_EINVAL;
945 return pjsua_var.stun_status;
946 }
947 } else {
948 str_host = pjsua_var.ua_cfg.stun_host;
949 port = 3478;
950 }
951
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000952 pjsua_var.stun_status =
953 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, &str_host,
954 (pj_uint16_t)port);
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000955
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000956 if (pjsua_var.stun_status != PJ_SUCCESS) {
957 pjsua_var.stun_status = pj_gethostbyname(&str_host, &he);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000958
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000959 if (pjsua_var.stun_status == PJ_SUCCESS) {
960 pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
961 pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
962 pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)port);
963 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000964 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000965
Benny Prijono4ab9fbb2007-10-12 12:14:27 +0000966 PJ_LOG(3,(THIS_FILE,
967 "STUN server %.*s resolved, address is %s:%d",
968 (int)pjsua_var.ua_cfg.stun_host.slen,
969 pjsua_var.ua_cfg.stun_host.ptr,
970 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
971 (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
972
Benny Prijonoc97608e2007-03-23 16:34:20 +0000973 }
Benny Prijonoebbf6892007-03-24 17:37:25 +0000974 /* Otherwise disable STUN. */
975 else {
976 pjsua_var.stun_status = PJ_SUCCESS;
977 }
978
979
Benny Prijonoc97608e2007-03-23 16:34:20 +0000980 return pjsua_var.stun_status;
981
982 } else if (pjsua_var.stun_status == PJ_EPENDING) {
983 /* STUN server resolution has been started, wait for the
984 * result.
985 */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000986 if (wait) {
987 while (pjsua_var.stun_status == PJ_EPENDING)
988 pjsua_handle_events(10);
989 }
990
991 return pjsua_var.stun_status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000992
993 } else {
994 /* STUN server has been resolved, return the status */
995 return pjsua_var.stun_status;
996 }
997}
998
999/*
Benny Prijono268ca612006-02-07 12:34:11 +00001000 * Destroy pjsua.
1001 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001002PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001003{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001004 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001005
1006 /* Signal threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001007 pjsua_var.thread_quit_flag = 1;
Benny Prijono268ca612006-02-07 12:34:11 +00001008
1009 /* Wait worker threads to quit: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001010 for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
1011 if (pjsua_var.thread[i]) {
1012 pj_thread_join(pjsua_var.thread[i]);
1013 pj_thread_destroy(pjsua_var.thread[i]);
1014 pjsua_var.thread[i] = NULL;
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001015 }
Benny Prijono268ca612006-02-07 12:34:11 +00001016 }
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001017
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001018 if (pjsua_var.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001019 /* Terminate all calls. */
1020 pjsua_call_hangup_all();
1021
1022 /* Terminate all presence subscriptions. */
1023 pjsua_pres_shutdown();
1024
1025 /* Unregister, if required: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001026 for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
1027 if (!pjsua_var.acc[i].valid)
1028 continue;
1029
1030 if (pjsua_var.acc[i].regc) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001031 pjsua_acc_set_registration(i, PJ_FALSE);
1032 }
1033 }
1034
1035 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001036 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1037 busy_sleep(1000);
1038 }
Benny Prijono834aee32006-02-19 01:38:06 +00001039
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001040 /* Destroy media */
1041 pjsua_media_subsys_destroy();
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001042
Benny Prijono268ca612006-02-07 12:34:11 +00001043 /* Destroy endpoint. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001044 if (pjsua_var.endpt) {
1045 pjsip_endpt_destroy(pjsua_var.endpt);
1046 pjsua_var.endpt = NULL;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001047 }
Benny Prijono268ca612006-02-07 12:34:11 +00001048
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001049 /* Destroy mutex */
1050 if (pjsua_var.mutex) {
1051 pj_mutex_destroy(pjsua_var.mutex);
1052 pjsua_var.mutex = NULL;
1053 }
1054
1055 /* Destroy pool and pool factory. */
1056 if (pjsua_var.pool) {
1057 pj_pool_release(pjsua_var.pool);
1058 pjsua_var.pool = NULL;
1059 pj_caching_pool_destroy(&pjsua_var.cp);
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00001060
1061 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1062
1063 /* End logging */
1064 if (pjsua_var.log_file) {
1065 pj_file_close(pjsua_var.log_file);
1066 pjsua_var.log_file = NULL;
1067 }
1068
1069 /* Shutdown PJLIB */
1070 pj_shutdown();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071 }
Benny Prijono268ca612006-02-07 12:34:11 +00001072
Benny Prijonof762ee72006-12-01 11:14:37 +00001073 /* Clear pjsua_var */
1074 pj_bzero(&pjsua_var, sizeof(pjsua_var));
1075
Benny Prijono268ca612006-02-07 12:34:11 +00001076 /* Done. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001077 return PJ_SUCCESS;
1078}
1079
1080
1081/**
1082 * Application is recommended to call this function after all initialization
1083 * is done, so that the library can do additional checking set up
1084 * additional
1085 *
1086 * @return PJ_SUCCESS on success, or the appropriate error code.
1087 */
1088PJ_DEF(pj_status_t) pjsua_start(void)
1089{
1090 pj_status_t status;
1091
1092 status = pjsua_call_subsys_start();
1093 if (status != PJ_SUCCESS)
1094 return status;
1095
1096 status = pjsua_media_subsys_start();
1097 if (status != PJ_SUCCESS)
1098 return status;
1099
1100 status = pjsua_pres_start();
1101 if (status != PJ_SUCCESS)
1102 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001103
1104 return PJ_SUCCESS;
1105}
1106
Benny Prijono9fc735d2006-05-28 14:58:12 +00001107
1108/**
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001109 * Poll pjsua for events, and if necessary block the caller thread for
1110 * the specified maximum interval (in miliseconds).
1111 */
1112PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
1113{
Benny Prijono897f9f82007-05-03 19:56:21 +00001114#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
1115 /* Ideally we shouldn't call pj_thread_sleep() and rather
1116 * CActiveScheduler::WaitForAnyRequest() here, but that will
1117 * drag in Symbian header and it doesn't look pretty.
1118 */
1119 pj_thread_sleep(msec_timeout);
1120 return msec_timeout;
1121#else
1122
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001123 unsigned count = 0;
1124 pj_time_val tv;
1125 pj_status_t status;
1126
1127 tv.sec = 0;
1128 tv.msec = msec_timeout;
1129 pj_time_val_normalize(&tv);
1130
1131 status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
1132
1133 if (status != PJ_SUCCESS)
1134 return -status;
1135
1136 return count;
Benny Prijono897f9f82007-05-03 19:56:21 +00001137#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001138}
1139
1140
1141/*
1142 * Create memory pool.
1143 */
1144PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
1145 pj_size_t increment)
1146{
1147 /* Pool factory is thread safe, no need to lock */
1148 return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
1149 NULL);
1150}
1151
1152
1153/*
1154 * Internal function to get SIP endpoint instance of pjsua, which is
1155 * needed for example to register module, create transports, etc.
1156 * Probably is only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001157 */
1158PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1159{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001160 return pjsua_var.endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001161}
1162
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001163/*
1164 * Internal function to get media endpoint instance.
1165 * Only valid after #pjsua_init() is called.
Benny Prijono9fc735d2006-05-28 14:58:12 +00001166 */
1167PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1168{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001169 return pjsua_var.med_endpt;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001170}
1171
Benny Prijono97b87172006-08-24 14:25:14 +00001172/*
1173 * Internal function to get PJSUA pool factory.
1174 */
1175PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void)
1176{
1177 return &pjsua_var.cp.factory;
1178}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001179
1180/*****************************************************************************
1181 * PJSUA SIP Transport API.
1182 */
1183
1184/*
Benny Prijono23674a32007-12-01 08:59:25 +00001185 * Tools to get address string.
1186 */
1187static const char *addr_string(const pj_sockaddr_t *addr)
1188{
1189 static char str[128];
1190 str[0] = '\0';
1191 pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family,
1192 pj_sockaddr_get_addr(addr),
1193 str, sizeof(str));
1194 return str;
1195}
1196
1197/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001198 * Create and initialize SIP socket (and possibly resolve public
1199 * address via STUN, depending on config).
1200 */
Benny Prijono23674a32007-12-01 08:59:25 +00001201static pj_status_t create_sip_udp_sock(int af,
1202 const pj_str_t *bind_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001203 int port,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001204 pj_sock_t *p_sock,
Benny Prijono23674a32007-12-01 08:59:25 +00001205 pj_sockaddr *p_pub_addr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206{
Benny Prijono23674a32007-12-01 08:59:25 +00001207 char stun_ip_addr[PJ_INET6_ADDRSTRLEN];
Benny Prijonoc97608e2007-03-23 16:34:20 +00001208 pj_str_t stun_srv;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209 pj_sock_t sock;
Benny Prijono23674a32007-12-01 08:59:25 +00001210 pj_sockaddr bind_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001211 pj_status_t status;
1212
Benny Prijonoc97608e2007-03-23 16:34:20 +00001213 /* Make sure STUN server resolution has completed */
1214 status = pjsua_resolve_stun_server(PJ_TRUE);
1215 if (status != PJ_SUCCESS) {
1216 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
1217 return status;
1218 }
1219
Benny Prijono23674a32007-12-01 08:59:25 +00001220 /* Initialize bound address */
1221 if (bind_param->slen) {
1222 status = pj_sockaddr_init(af, &bind_addr, bind_param,
1223 (pj_uint16_t)port);
1224 if (status != PJ_SUCCESS) {
1225 pjsua_perror(THIS_FILE,
1226 "Unable to resolve transport bound address",
1227 status);
1228 return status;
1229 }
1230 } else {
1231 pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port);
1232 }
1233
1234 status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001235 if (status != PJ_SUCCESS) {
1236 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijonod8410532006-06-15 11:04:33 +00001237 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001238 }
1239
Benny Prijono23674a32007-12-01 08:59:25 +00001240 status = pj_sock_bind(sock, &bind_addr, sizeof(bind_addr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241 if (status != PJ_SUCCESS) {
1242 pjsua_perror(THIS_FILE, "bind() error", status);
1243 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001244 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245 }
1246
Benny Prijonoe347cb02007-02-14 14:36:13 +00001247 /* If port is zero, get the bound port */
1248 if (port == 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001249 pj_sockaddr bound_addr;
Benny Prijonoe347cb02007-02-14 14:36:13 +00001250 int namelen = sizeof(bound_addr);
1251 status = pj_sock_getsockname(sock, &bound_addr, &namelen);
1252 if (status != PJ_SUCCESS) {
1253 pjsua_perror(THIS_FILE, "getsockname() error", status);
1254 pj_sock_close(sock);
1255 return status;
1256 }
1257
Benny Prijono23674a32007-12-01 08:59:25 +00001258 port = pj_sockaddr_get_port(&bound_addr);
Benny Prijonoe347cb02007-02-14 14:36:13 +00001259 }
1260
Benny Prijonoc97608e2007-03-23 16:34:20 +00001261 if (pjsua_var.stun_srv.addr.sa_family != 0) {
Benny Prijono23674a32007-12-01 08:59:25 +00001262 pj_ansi_strcpy(stun_ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
1263 stun_srv = pj_str(stun_ip_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 } else {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001265 stun_srv.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001266 }
1267
1268 /* Get the published address, either by STUN or by resolving
1269 * the name of local host.
1270 */
Benny Prijono23674a32007-12-01 08:59:25 +00001271 if (pj_sockaddr_has_addr(p_pub_addr)) {
Benny Prijono744aca02007-11-11 03:06:07 +00001272 /*
1273 * Public address is already specified, no need to resolve the
1274 * address, only set the port.
1275 */
Benny Prijono23674a32007-12-01 08:59:25 +00001276 if (pj_sockaddr_get_port(p_pub_addr) == 0)
1277 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijono744aca02007-11-11 03:06:07 +00001278
1279 } else if (stun_srv.slen) {
Benny Prijono0a5cad82006-09-26 13:21:02 +00001280 /*
1281 * STUN is specified, resolve the address with STUN.
1282 */
Benny Prijono23674a32007-12-01 08:59:25 +00001283 if (af != pj_AF_INET()) {
1284 pjsua_perror(THIS_FILE, "Cannot use STUN", PJ_EAFNOTSUP);
1285 pj_sock_close(sock);
1286 return PJ_EAFNOTSUP;
1287 }
1288
Benny Prijono14c2b862007-02-21 00:40:05 +00001289 status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001290 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
1291 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijono23674a32007-12-01 08:59:25 +00001292 &p_pub_addr->ipv4);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001293 if (status != PJ_SUCCESS) {
Benny Prijonoebbf6892007-03-24 17:37:25 +00001294 pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001295 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001296 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001297 }
1298
1299 } else {
Benny Prijono23674a32007-12-01 08:59:25 +00001300 pj_bzero(p_pub_addr, sizeof(pj_sockaddr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001301
Benny Prijono23674a32007-12-01 08:59:25 +00001302 status = pj_gethostip(af, p_pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001303 if (status != PJ_SUCCESS) {
Benny Prijonob43bad72007-01-20 05:11:08 +00001304 pjsua_perror(THIS_FILE, "Unable to get local host IP", status);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001305 pj_sock_close(sock);
Benny Prijonod8410532006-06-15 11:04:33 +00001306 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001307 }
1308
Benny Prijono23674a32007-12-01 08:59:25 +00001309 p_pub_addr->addr.sa_family = (pj_uint16_t)af;
1310 pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001311 }
1312
1313 *p_sock = sock;
1314
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001315 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
Benny Prijono23674a32007-12-01 08:59:25 +00001316 addr_string(p_pub_addr),
1317 (int)pj_sockaddr_get_port(p_pub_addr)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001318
Benny Prijonod8410532006-06-15 11:04:33 +00001319 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001320}
1321
1322
1323/*
1324 * Create SIP transport.
1325 */
1326PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
1327 const pjsua_transport_config *cfg,
1328 pjsua_transport_id *p_id)
1329{
1330 pjsip_transport *tp;
1331 unsigned id;
1332 pj_status_t status;
1333
1334 PJSUA_LOCK();
1335
1336 /* Find empty transport slot */
1337 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001338 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001339 break;
1340 }
1341
1342 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1343 status = PJ_ETOOMANY;
1344 pjsua_perror(THIS_FILE, "Error creating transport", status);
1345 goto on_return;
1346 }
1347
1348 /* Create the transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001349 if (type & PJSIP_TRANSPORT_UDP) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001350 /*
Benny Prijono23674a32007-12-01 08:59:25 +00001351 * Create UDP transport (IPv4 or IPv6).
Benny Prijonoe93e2872006-06-28 16:46:49 +00001352 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353 pjsua_transport_config config;
Benny Prijono23674a32007-12-01 08:59:25 +00001354 char hostbuf[PJ_INET6_ADDRSTRLEN];
Benny Prijonod8410532006-06-15 11:04:33 +00001355 pj_sock_t sock = PJ_INVALID_SOCKET;
Benny Prijono23674a32007-12-01 08:59:25 +00001356 pj_sockaddr pub_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357 pjsip_host_port addr_name;
1358
1359 /* Supply default config if it's not specified */
1360 if (cfg == NULL) {
1361 pjsua_transport_config_default(&config);
1362 cfg = &config;
1363 }
1364
Benny Prijono0a5cad82006-09-26 13:21:02 +00001365 /* Initialize the public address from the config, if any */
Benny Prijono23674a32007-12-01 08:59:25 +00001366 pj_sockaddr_init(pjsip_transport_type_get_af(type), &pub_addr,
1367 NULL, (pj_uint16_t)cfg->port);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001368 if (cfg->public_addr.slen) {
Benny Prijono23674a32007-12-01 08:59:25 +00001369 status = pj_sockaddr_set_str_addr(pjsip_transport_type_get_af(type),
1370 &pub_addr, &cfg->public_addr);
Benny Prijono0a5cad82006-09-26 13:21:02 +00001371 if (status != PJ_SUCCESS) {
1372 pjsua_perror(THIS_FILE,
1373 "Unable to resolve transport public address",
1374 status);
1375 goto on_return;
1376 }
1377 }
1378
1379 /* Create the socket and possibly resolve the address with STUN
1380 * (only when public address is not specified).
1381 */
Benny Prijono23674a32007-12-01 08:59:25 +00001382 status = create_sip_udp_sock(pjsip_transport_type_get_af(type),
1383 &cfg->bound_addr, cfg->port,
Benny Prijono0a5cad82006-09-26 13:21:02 +00001384 &sock, &pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385 if (status != PJ_SUCCESS)
1386 goto on_return;
1387
Benny Prijono23674a32007-12-01 08:59:25 +00001388 pj_ansi_strcpy(hostbuf, addr_string(&pub_addr));
1389 addr_name.host = pj_str(hostbuf);
1390 addr_name.port = pj_sockaddr_get_port(&pub_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391
1392 /* Create UDP transport */
Benny Prijono23674a32007-12-01 08:59:25 +00001393 status = pjsip_udp_transport_attach2(pjsua_var.endpt, type, sock,
1394 &addr_name, 1, &tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 if (status != PJ_SUCCESS) {
1396 pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
1397 status);
1398 pj_sock_close(sock);
1399 goto on_return;
1400 }
1401
Benny Prijonoe93e2872006-06-28 16:46:49 +00001402
1403 /* Save the transport */
1404 pjsua_var.tpdata[id].type = type;
1405 pjsua_var.tpdata[id].local_name = tp->local_name;
1406 pjsua_var.tpdata[id].data.tp = tp;
1407
Benny Prijono3569c0d2007-04-06 10:29:20 +00001408#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
1409
Benny Prijonoe93e2872006-06-28 16:46:49 +00001410 } else if (type == PJSIP_TRANSPORT_TCP) {
1411 /*
1412 * Create TCP transport.
1413 */
1414 pjsua_transport_config config;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001415 pjsip_host_port a_name;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001416 pjsip_tpfactory *tcp;
1417 pj_sockaddr_in local_addr;
1418
1419 /* Supply default config if it's not specified */
1420 if (cfg == NULL) {
1421 pjsua_transport_config_default(&config);
1422 cfg = &config;
1423 }
1424
1425 /* Init local address */
1426 pj_sockaddr_in_init(&local_addr, 0, 0);
1427
1428 if (cfg->port)
1429 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1430
Benny Prijono0a5cad82006-09-26 13:21:02 +00001431 if (cfg->bound_addr.slen) {
1432 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1433 if (status != PJ_SUCCESS) {
1434 pjsua_perror(THIS_FILE,
1435 "Unable to resolve transport bound address",
1436 status);
1437 goto on_return;
1438 }
1439 }
1440
1441 /* Init published name */
1442 pj_bzero(&a_name, sizeof(pjsip_host_port));
1443 if (cfg->public_addr.slen)
1444 a_name.host = cfg->public_addr;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001445
1446 /* Create the TCP transport */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001447 status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
1448 &a_name, 1, &tcp);
Benny Prijonoe93e2872006-06-28 16:46:49 +00001449
1450 if (status != PJ_SUCCESS) {
1451 pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
1452 status);
1453 goto on_return;
1454 }
1455
1456 /* Save the transport */
1457 pjsua_var.tpdata[id].type = type;
1458 pjsua_var.tpdata[id].local_name = tcp->addr_name;
1459 pjsua_var.tpdata[id].data.factory = tcp;
1460
Benny Prijono3569c0d2007-04-06 10:29:20 +00001461#endif /* PJ_HAS_TCP */
1462
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001463#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1464 } else if (type == PJSIP_TRANSPORT_TLS) {
1465 /*
1466 * Create TLS transport.
1467 */
Benny Prijonof3bbc132006-12-25 06:43:59 +00001468 /*
1469 * Create TCP transport.
1470 */
1471 pjsua_transport_config config;
1472 pjsip_host_port a_name;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001473 pjsip_tpfactory *tls;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001474 pj_sockaddr_in local_addr;
1475
1476 /* Supply default config if it's not specified */
1477 if (cfg == NULL) {
1478 pjsua_transport_config_default(&config);
1479 config.port = 5061;
1480 cfg = &config;
1481 }
1482
1483 /* Init local address */
1484 pj_sockaddr_in_init(&local_addr, 0, 0);
1485
1486 if (cfg->port)
1487 local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
1488
1489 if (cfg->bound_addr.slen) {
1490 status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
1491 if (status != PJ_SUCCESS) {
1492 pjsua_perror(THIS_FILE,
1493 "Unable to resolve transport bound address",
1494 status);
1495 goto on_return;
1496 }
1497 }
1498
1499 /* Init published name */
1500 pj_bzero(&a_name, sizeof(pjsip_host_port));
1501 if (cfg->public_addr.slen)
1502 a_name.host = cfg->public_addr;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001503
1504 status = pjsip_tls_transport_start(pjsua_var.endpt,
Benny Prijonof3bbc132006-12-25 06:43:59 +00001505 &cfg->tls_setting,
1506 &local_addr, &a_name, 1, &tls);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001507 if (status != PJ_SUCCESS) {
1508 pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
1509 status);
1510 goto on_return;
1511 }
1512
1513 /* Save the transport */
1514 pjsua_var.tpdata[id].type = type;
1515 pjsua_var.tpdata[id].local_name = tls->addr_name;
1516 pjsua_var.tpdata[id].data.factory = tls;
1517#endif
1518
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519 } else {
1520 status = PJSIP_EUNSUPTRANSPORT;
1521 pjsua_perror(THIS_FILE, "Error creating transport", status);
1522 goto on_return;
1523 }
1524
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525
1526 /* Return the ID */
1527 if (p_id) *p_id = id;
1528
1529 status = PJ_SUCCESS;
1530
1531on_return:
1532
1533 PJSUA_UNLOCK();
1534
Benny Prijonod8410532006-06-15 11:04:33 +00001535 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536}
1537
1538
1539/*
1540 * Register transport that has been created by application.
1541 */
1542PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
1543 pjsua_transport_id *p_id)
1544{
1545 unsigned id;
1546
1547 PJSUA_LOCK();
1548
1549 /* Find empty transport slot */
1550 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001551 if (pjsua_var.tpdata[id].data.ptr == NULL)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552 break;
1553 }
1554
1555 if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
1556 pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
1557 PJSUA_UNLOCK();
1558 return PJ_ETOOMANY;
1559 }
1560
1561 /* Save the transport */
Benny Prijonod17a5e92007-05-29 11:51:45 +00001562 pjsua_var.tpdata[id].type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001563 pjsua_var.tpdata[id].local_name = tp->local_name;
1564 pjsua_var.tpdata[id].data.tp = tp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565
1566 /* Return the ID */
1567 if (p_id) *p_id = id;
1568
1569 PJSUA_UNLOCK();
1570
1571 return PJ_SUCCESS;
1572}
1573
1574
1575/*
1576 * Enumerate all transports currently created in the system.
1577 */
1578PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
1579 unsigned *p_count )
1580{
1581 unsigned i, count;
1582
1583 PJSUA_LOCK();
1584
1585 for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
1586 ++i)
1587 {
Benny Prijonoe93e2872006-06-28 16:46:49 +00001588 if (!pjsua_var.tpdata[i].data.ptr)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589 continue;
1590
1591 id[count++] = i;
1592 }
1593
1594 *p_count = count;
1595
1596 PJSUA_UNLOCK();
1597
1598 return PJ_SUCCESS;
1599}
1600
1601
1602/*
1603 * Get information about transports.
1604 */
1605PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
1606 pjsua_transport_info *info)
1607{
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001608 pjsua_transport_data *t = &pjsua_var.tpdata[id];
Benny Prijono0a5cad82006-09-26 13:21:02 +00001609 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610
Benny Prijonoac623b32006-07-03 15:19:31 +00001611 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612
1613 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001614 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1615 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616
1617 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001618 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619
1620 PJSUA_LOCK();
1621
Benny Prijonoe93e2872006-06-28 16:46:49 +00001622 if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_UDP) {
1623
1624 pjsip_transport *tp = t->data.tp;
1625
1626 if (tp == NULL) {
1627 PJSUA_UNLOCK();
1628 return PJ_EINVALIDOP;
1629 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001630
Benny Prijonoe93e2872006-06-28 16:46:49 +00001631 info->id = id;
Benny Prijonod17a5e92007-05-29 11:51:45 +00001632 info->type = (pjsip_transport_type_e) tp->key.type;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001633 info->type_name = pj_str(tp->type_name);
1634 info->info = pj_str(tp->info);
1635 info->flag = tp->flag;
1636 info->addr_len = tp->addr_len;
1637 info->local_addr = tp->local_addr;
1638 info->local_name = tp->local_name;
1639 info->usage_count = pj_atomic_get(tp->ref_cnt);
1640
Benny Prijono0a5cad82006-09-26 13:21:02 +00001641 status = PJ_SUCCESS;
1642
Benny Prijonoe93e2872006-06-28 16:46:49 +00001643 } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
1644
1645 pjsip_tpfactory *factory = t->data.factory;
1646
1647 if (factory == NULL) {
1648 PJSUA_UNLOCK();
1649 return PJ_EINVALIDOP;
1650 }
1651
1652 info->id = id;
1653 info->type = t->type;
1654 info->type_name = pj_str("TCP");
1655 info->info = pj_str("TCP transport");
1656 info->flag = factory->flag;
1657 info->addr_len = sizeof(factory->local_addr);
1658 info->local_addr = factory->local_addr;
1659 info->local_name = factory->addr_name;
1660 info->usage_count = 0;
1661
Benny Prijono0a5cad82006-09-26 13:21:02 +00001662 status = PJ_SUCCESS;
1663
1664 } else {
1665 pj_assert(!"Unsupported transport");
1666 status = PJ_EINVALIDOP;
Benny Prijonoe93e2872006-06-28 16:46:49 +00001667 }
1668
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001669
1670 PJSUA_UNLOCK();
1671
Benny Prijono0a5cad82006-09-26 13:21:02 +00001672 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673}
1674
1675
1676/*
1677 * Disable a transport or re-enable it.
1678 */
1679PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
1680 pj_bool_t enabled)
1681{
1682 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001683 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1684 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685
1686 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001687 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001688
1689
1690 /* To be done!! */
1691 PJ_TODO(pjsua_transport_set_enable);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001692 PJ_UNUSED_ARG(enabled);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001693
1694 return PJ_EINVALIDOP;
1695}
1696
1697
1698/*
1699 * Close the transport.
1700 */
1701PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
1702 pj_bool_t force )
1703{
Benny Prijono5ff61872007-02-01 03:37:11 +00001704 pj_status_t status;
1705
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001706 /* Make sure id is in range. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001707 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
1708 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001709
1710 /* Make sure that transport exists */
Benny Prijonoe93e2872006-06-28 16:46:49 +00001711 PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001712
Benny Prijono0a5cad82006-09-26 13:21:02 +00001713 /* Note: destroy() may not work if there are objects still referencing
1714 * the transport.
1715 */
1716 if (force) {
1717 switch (pjsua_var.tpdata[id].type) {
1718 case PJSIP_TRANSPORT_UDP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001719 status = pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
1720 if (status != PJ_SUCCESS)
1721 return status;
1722 status = pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
1723 if (status != PJ_SUCCESS)
1724 return status;
1725 break;
1726
1727 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001728 case PJSIP_TRANSPORT_TCP:
Benny Prijono5ff61872007-02-01 03:37:11 +00001729 /* This will close the TCP listener, but existing TCP/TLS
1730 * connections (if any) will still linger
1731 */
1732 status = (*pjsua_var.tpdata[id].data.factory->destroy)
1733 (pjsua_var.tpdata[id].data.factory);
1734 if (status != PJ_SUCCESS)
1735 return status;
1736
Benny Prijono0a5cad82006-09-26 13:21:02 +00001737 break;
Benny Prijono5ff61872007-02-01 03:37:11 +00001738
Benny Prijono1ebd6142006-10-19 15:48:02 +00001739 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001740 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001741 }
1742
1743 } else {
Benny Prijono5ff61872007-02-01 03:37:11 +00001744 /* If force is not specified, transports will be closed at their
1745 * convenient time. However this will leak PJSUA-API transport
1746 * descriptors as PJSUA-API wouldn't know when exactly the
1747 * transport is closed thus it can't cleanup PJSUA transport
1748 * descriptor.
1749 */
Benny Prijono0a5cad82006-09-26 13:21:02 +00001750 switch (pjsua_var.tpdata[id].type) {
1751 case PJSIP_TRANSPORT_UDP:
1752 return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
Benny Prijono5ff61872007-02-01 03:37:11 +00001753 case PJSIP_TRANSPORT_TLS:
Benny Prijono0a5cad82006-09-26 13:21:02 +00001754 case PJSIP_TRANSPORT_TCP:
1755 return (*pjsua_var.tpdata[id].data.factory->destroy)
1756 (pjsua_var.tpdata[id].data.factory);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001757 default:
Benny Prijono5ff61872007-02-01 03:37:11 +00001758 return PJ_EINVAL;
Benny Prijono0a5cad82006-09-26 13:21:02 +00001759 }
1760 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761
Benny Prijono5ff61872007-02-01 03:37:11 +00001762 /* Cleanup pjsua data when force is applied */
1763 if (force) {
1764 pjsua_var.tpdata[id].type = PJSIP_TRANSPORT_UNSPECIFIED;
1765 pjsua_var.tpdata[id].data.ptr = NULL;
1766 }
1767
1768 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001769}
1770
1771
1772/*
1773 * Add additional headers etc in msg_data specified by application
1774 * when sending requests.
1775 */
1776void pjsua_process_msg_data(pjsip_tx_data *tdata,
1777 const pjsua_msg_data *msg_data)
1778{
1779 pj_bool_t allow_body;
1780 const pjsip_hdr *hdr;
1781
Benny Prijono56315612006-07-18 14:39:40 +00001782 /* Always add User-Agent */
1783 if (pjsua_var.ua_cfg.user_agent.slen &&
1784 tdata->msg->type == PJSIP_REQUEST_MSG)
1785 {
1786 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1787 pjsip_hdr *h;
1788 h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
1789 &STR_USER_AGENT,
1790 &pjsua_var.ua_cfg.user_agent);
1791 pjsip_msg_add_hdr(tdata->msg, h);
1792 }
1793
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001794 if (!msg_data)
1795 return;
1796
1797 hdr = msg_data->hdr_list.next;
1798 while (hdr && hdr != &msg_data->hdr_list) {
1799 pjsip_hdr *new_hdr;
1800
Benny Prijonoa1e69682007-05-11 15:14:34 +00001801 new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001802 pjsip_msg_add_hdr(tdata->msg, new_hdr);
1803
1804 hdr = hdr->next;
1805 }
1806
1807 allow_body = (tdata->msg->body == NULL);
1808
1809 if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
1810 pjsip_media_type ctype;
1811 pjsip_msg_body *body;
1812
1813 pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
1814 body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
1815 &msg_data->msg_body);
1816 tdata->msg->body = body;
1817 }
1818}
1819
1820
1821/*
1822 * Add route_set to outgoing requests
1823 */
1824void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
1825 const pjsip_route_hdr *route_set )
1826{
1827 const pjsip_route_hdr *r;
1828
1829 r = route_set->next;
1830 while (r != route_set) {
1831 pjsip_route_hdr *new_r;
1832
Benny Prijonoa1e69682007-05-11 15:14:34 +00001833 new_r = (pjsip_route_hdr*) pjsip_hdr_clone(tdata->pool, r);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001834 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
1835
1836 r = r->next;
1837 }
1838}
1839
1840
1841/*
1842 * Simple version of MIME type parsing (it doesn't support parameters)
1843 */
1844void pjsua_parse_media_type( pj_pool_t *pool,
1845 const pj_str_t *mime,
1846 pjsip_media_type *media_type)
1847{
1848 pj_str_t tmp;
1849 char *pos;
1850
Benny Prijonoac623b32006-07-03 15:19:31 +00001851 pj_bzero(media_type, sizeof(*media_type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001852
1853 pj_strdup_with_null(pool, &tmp, mime);
1854
1855 pos = pj_strchr(&tmp, '/');
1856 if (pos) {
1857 media_type->type.ptr = tmp.ptr;
1858 media_type->type.slen = (pos-tmp.ptr);
1859 media_type->subtype.ptr = pos+1;
1860 media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
1861 } else {
1862 media_type->type = tmp;
1863 }
1864}
1865
1866
1867/*
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001868 * Internal function to init transport selector from transport id.
1869 */
1870void pjsua_init_tpselector(pjsua_transport_id tp_id,
1871 pjsip_tpselector *sel)
1872{
1873 pjsua_transport_data *tpdata;
1874 unsigned flag;
1875
1876 pj_bzero(sel, sizeof(*sel));
1877 if (tp_id == PJSUA_INVALID_ID)
1878 return;
1879
Benny Prijonoa1e69682007-05-11 15:14:34 +00001880 pj_assert(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata));
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001881 tpdata = &pjsua_var.tpdata[tp_id];
1882
1883 flag = pjsip_transport_get_flag_from_type(tpdata->type);
1884
1885 if (flag & PJSIP_TRANSPORT_DATAGRAM) {
1886 sel->type = PJSIP_TPSELECTOR_TRANSPORT;
1887 sel->u.transport = tpdata->data.tp;
1888 } else {
1889 sel->type = PJSIP_TPSELECTOR_LISTENER;
1890 sel->u.listener = tpdata->data.factory;
1891 }
1892}
1893
1894
Benny Prijono6ba8c542007-10-16 01:34:14 +00001895/* Callback upon NAT detection completion */
1896static void nat_detect_cb(void *user_data,
1897 const pj_stun_nat_detect_result *res)
1898{
1899 PJ_UNUSED_ARG(user_data);
1900
1901 pjsua_var.nat_in_progress = PJ_FALSE;
1902 pjsua_var.nat_status = res->status;
1903 pjsua_var.nat_type = res->nat_type;
1904
1905 if (pjsua_var.ua_cfg.cb.on_nat_detect) {
1906 (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
1907 }
1908}
1909
1910
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001911/*
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001912 * Detect NAT type.
1913 */
Benny Prijono6ba8c542007-10-16 01:34:14 +00001914PJ_DEF(pj_status_t) pjsua_detect_nat_type()
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001915{
1916 pj_status_t status;
1917
Benny Prijono6ba8c542007-10-16 01:34:14 +00001918 if (pjsua_var.nat_in_progress)
1919 return PJ_SUCCESS;
1920
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001921 /* Make sure STUN server resolution has completed */
1922 status = pjsua_resolve_stun_server(PJ_TRUE);
1923 if (status != PJ_SUCCESS) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001924 pjsua_var.nat_status = status;
1925 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001926 return status;
1927 }
1928
1929 /* Make sure we have STUN */
1930 if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
Benny Prijono6ba8c542007-10-16 01:34:14 +00001931 pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
1932 return PJNATH_ESTUNINSERVER;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001933 }
1934
Benny Prijono6ba8c542007-10-16 01:34:14 +00001935 status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
1936 &pjsua_var.stun_cfg,
1937 NULL, &nat_detect_cb);
1938
1939 if (status != PJ_SUCCESS) {
1940 pjsua_var.nat_status = status;
1941 pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
1942 return status;
1943 }
1944
1945 pjsua_var.nat_in_progress = PJ_TRUE;
1946
1947 return PJ_SUCCESS;
1948}
1949
1950
1951/*
1952 * Get NAT type.
1953 */
1954PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
1955{
1956 *type = pjsua_var.nat_type;
1957 return pjsua_var.nat_status;
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00001958}
1959
1960
1961/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001962 * Verify that valid SIP url is given.
1963 */
1964PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
1965{
1966 pjsip_uri *p;
1967 pj_pool_t *pool;
1968 char *url;
1969 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
1970
1971 if (!len) return -1;
1972
1973 pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
1974 if (!pool) return -1;
1975
Benny Prijonoa1e69682007-05-11 15:14:34 +00001976 url = (char*) pj_pool_alloc(pool, len+1);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001977 pj_ansi_strcpy(url, c_url);
1978
1979 p = pjsip_parse_uri(pool, url, len, 0);
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001980 if (!p || (pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0 &&
1981 pj_stricmp2(pjsip_uri_get_scheme(p), "sips") != 0))
1982 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 p = NULL;
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001984 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001985
1986 pj_pool_release(pool);
1987 return p ? 0 : -1;
1988}
Benny Prijonoda9785b2007-04-02 20:43:06 +00001989
1990
1991/*
1992 * This is a utility function to dump the stack states to log, using
1993 * verbosity level 3.
1994 */
1995PJ_DEF(void) pjsua_dump(pj_bool_t detail)
1996{
1997 unsigned old_decor;
1998 unsigned i;
1999 char buf[1024];
2000
2001 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
2002
2003 old_decor = pj_log_get_decor();
2004 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
2005
2006 if (detail)
2007 pj_dump_config();
2008
2009 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
2010
2011 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
2012
2013 PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
2014 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2015 pjsua_call *call = &pjsua_var.calls[i];
2016 pjmedia_sock_info skinfo;
Benny Prijono5186eae2007-12-03 14:38:25 +00002017 char addr_buf[80];
Benny Prijonoda9785b2007-04-02 20:43:06 +00002018
Benny Prijono4f093d22007-04-09 08:54:32 +00002019 /* MSVC complains about skinfo not being initialized */
2020 pj_bzero(&skinfo, sizeof(skinfo));
2021
Benny Prijonoda9785b2007-04-02 20:43:06 +00002022 pjmedia_transport_get_info(call->med_tp, &skinfo);
2023
Benny Prijono5186eae2007-12-03 14:38:25 +00002024 PJ_LOG(3,(THIS_FILE, " %s: %s",
Benny Prijonoda9785b2007-04-02 20:43:06 +00002025 (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
Benny Prijono5186eae2007-12-03 14:38:25 +00002026 pj_sockaddr_print(&skinfo.rtp_addr_name, addr_buf,
2027 sizeof(addr_buf), 3)));
Benny Prijonoda9785b2007-04-02 20:43:06 +00002028 }
2029
2030 pjsip_tsx_layer_dump(detail);
2031 pjsip_ua_dump(detail);
2032
2033
2034 /* Dump all invite sessions: */
2035 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
2036
2037 if (pjsua_call_get_count() == 0) {
2038
2039 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
2040
2041 } else {
2042 unsigned i;
2043
2044 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2045 if (pjsua_call_is_active(i)) {
2046 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
2047 PJ_LOG(3,(THIS_FILE, "%s", buf));
2048 }
2049 }
2050 }
2051
2052 /* Dump presence status */
2053 pjsua_pres_dump(detail);
2054
2055 pj_log_set_decor(old_decor);
2056 PJ_LOG(3,(THIS_FILE, "Dump complete"));
2057}
2058