diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-05-26 12:17:46 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-05-26 12:17:46 +0000 |
commit | c01fdece34cb0eac0c1fdbafb5c1cc242ec01933 (patch) | |
tree | e48d300bf63489fd01dd66db8b75b8b49681be6d /pjsip/src/pjsua-lib/pjsua_core.c | |
parent | b2627727779145512c9c414cb532cb7e18133ddd (diff) |
First stage in pjsua library re-arrangements towards creating an easy to use high level API
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@476 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsua-lib/pjsua_core.c')
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 1112 |
1 files changed, 657 insertions, 455 deletions
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 5959d065..dba563ce 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pjsua-lib/pjsua.h> +#include "pjsua_imp.h" /* * pjsua_core.c @@ -43,75 +44,147 @@ struct pjsua pjsua; /* * Init default application parameters. */ -void pjsua_default(void) +PJ_DEF(void) pjsua_default_config(pjsua_config *cfg) { unsigned i; + pj_memset(cfg, 0, sizeof(pjsua_config)); - /* Normally need another thread for console application, because main - * thread will be blocked in fgets(). - */ - pjsua.thread_cnt = 1; + cfg->thread_cnt = 1; + cfg->udp_port = 5060; + cfg->start_rtp_port = 4000; + cfg->max_calls = 4; + cfg->conf_ports = 0; +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 + pjsua.clock_rate = 44100; +#endif - /* Default transport settings: */ - pjsua.sip_port = 5060; + cfg->complexity = 10; + cfg->quality = 10; + + cfg->auto_answer = 100; + cfg->uas_duration = 3600; + /* Default logging settings: */ + cfg->log_level = 5; + cfg->app_log_level = 4; + cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE; - /* Default we start RTP at port 4000 */ - pjsua.start_rtp_port = 4000; + /* Also init logging settings in pjsua.config, because log + * may be written before pjsua_init() is called. + */ + pjsua.config.log_level = 5; + pjsua.config.app_log_level = 4; - /* Default logging settings: */ - pjsua.log_level = 5; - pjsua.app_log_level = 4; - pjsua.log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE; + /* Init accounts: */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + cfg->acc_config[i].reg_timeout = 55; + } +} - /* Default call settings. */ - pjsua.uas_refresh = -1; - pjsua.uas_duration = -1; - /* Default: do not use STUN: */ - pjsua.stun_port1 = pjsua.stun_port2 = 0; +#define strncpy_with_null(dst,src,len) \ +do { \ + strncpy(dst, src, len); \ + dst[len-1] = '\0'; \ +} while (0) - /* Default for media: */ -#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 - pjsua.clock_rate = 44100; -#endif - pjsua.complexity = -1; - pjsua.quality = 4; - /* Init accounts: */ - pjsua.acc_cnt = 1; - for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { - pjsua.acc[i].index = i; - pjsua.acc[i].local_uri = pj_str(PJSUA_LOCAL_URI); - pjsua.acc[i].reg_timeout = 55; - pjsua.acc[i].online_status = PJ_TRUE; - pj_list_init(&pjsua.acc[i].route_set); - pj_list_init(&pjsua.acc[i].pres_srv_list); +PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg, + char *errmsg, + int len) +{ + unsigned i; + + /* If UDP port is zero, then sip_host and sip_port must be specified */ + if (cfg->udp_port == 0) { + if (cfg->sip_host.slen==0 || cfg->sip_port==0) { + strncpy_with_null(errmsg, + "sip_host and sip_port must be specified", + len); + return -1; + } } - /* Init call array: */ - for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) { - pjsua.calls[i].index = i; - pjsua.calls[i].refresh_tm._timer_id = -1; - pjsua.calls[i].hangup_tm._timer_id = -1; - pjsua.calls[i].conf_slot = 0; + if (cfg->max_calls < 1) { + strncpy_with_null(errmsg, + "max_calls needs to be at least 1", + len); + return -1; } - /* Default max nb of calls. */ - pjsua.max_calls = 4; + /* STUN */ + if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 || + cfg->stun_srv2.slen) + { + if (cfg->stun_port1 == 0) { + strncpy_with_null(errmsg, "stun_port1 required", len); + return -1; + } + if (cfg->stun_srv1.slen == 0) { + strncpy_with_null(errmsg, "stun_srv1 required", len); + return -1; + } + if (cfg->stun_port2 == 0) { + strncpy_with_null(errmsg, "stun_port2 required", len); + return -1; + } + if (cfg->stun_srv2.slen == 0) { + strncpy_with_null(errmsg, "stun_srv2 required", len); + return -1; + } + } - /* Init server presence subscription list: */ - + /* Verify accounts */ + for (i=0; i<cfg->acc_cnt; ++i) { + const pjsua_acc_config *acc_cfg = &cfg->acc_config[i]; + unsigned j; -} + if (acc_cfg->id.slen == 0) { + strncpy_with_null(errmsg, "missing account ID", len); + return -1; + } + + if (acc_cfg->id.slen == 0) { + strncpy_with_null(errmsg, "missing registrar URI", len); + return -1; + } + + if (acc_cfg->reg_timeout == 0) { + strncpy_with_null(errmsg, "missing registration timeout", len); + return -1; + } + for (j=0; j<acc_cfg->cred_count; ++j) { + + if (acc_cfg->cred_info[j].scheme.slen == 0) { + strncpy_with_null(errmsg, "missing auth scheme in account", + len); + return -1; + } + + if (acc_cfg->cred_info[j].realm.slen == 0) { + strncpy_with_null(errmsg, "missing realm in account", len); + return -1; + } + + if (acc_cfg->cred_info[j].username.slen == 0) { + strncpy_with_null(errmsg, "missing username in account", len); + return -1; + } + + } + } + + return PJ_SUCCESS; +} + /* * Handler for receiving incoming requests. @@ -152,85 +225,146 @@ static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata) } -/* - * Initialize sockets and optionally get the public address via STUN. - */ -static pj_status_t init_sockets(pj_bool_t sip, - pjmedia_sock_info *skinfo) +static int PJ_THREAD_FUNC pjsua_poll(void *arg) { - enum { - RTP_RETRY = 100 - }; - enum { - SIP_SOCK, - RTP_SOCK, - RTCP_SOCK, - }; - int i; - static pj_uint16_t rtp_port; - pj_sock_t sock[3]; - pj_sockaddr_in mapped_addr[3]; - pj_status_t status = PJ_SUCCESS; + pj_status_t last_err = 0; - if (rtp_port == 0) - rtp_port = (pj_uint16_t)pjsua.start_rtp_port; + PJ_UNUSED_ARG(arg); - for (i=0; i<3; ++i) - sock[i] = PJ_INVALID_SOCKET; + do { + pj_time_val timeout = { 0, 10 }; + pj_status_t status; + + status = pjsip_endpt_handle_events (pjsua.endpt, &timeout); + if (status != PJ_SUCCESS && status != last_err) { + last_err = status; + pjsua_perror(THIS_FILE, "handle_events() returned error", status); + } + } while (!pjsua.quit_flag); + + return 0; +} + + + +#define pjsua_has_stun() (pjsua.config.stun_port1 && \ + pjsua.config.stun_port2) + + +/* + * Create and initialize SIP socket (and possibly resolve public + * address via STUN, depending on config). + */ +static pj_status_t create_sip_udp_sock(int port, + pj_sock_t *p_sock, + pj_sockaddr_in *p_pub_addr) +{ + pj_sock_t sock; + pj_status_t status; - /* Create and bind SIP UDP socket. */ - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[SIP_SOCK]); + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); - goto on_error; + return status; + } + + status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "bind() error", status); + pj_sock_close(sock); + return status; } - if (sip) { - status = pj_sock_bind_in(sock[SIP_SOCK], 0, pjsua.sip_port); + if (pjsua_has_stun()) { + status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock, + &pjsua.config.stun_srv1, + pjsua.config.stun_port1, + &pjsua.config.stun_srv2, + pjsua.config.stun_port2, + p_pub_addr); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "bind() error", status); - goto on_error; + pjsua_perror(THIS_FILE, "STUN resolve error", status); + pj_sock_close(sock); + return status; } + } else { - status = pj_sock_bind_in(sock[SIP_SOCK], 0, 0); + + const pj_str_t *hostname = pj_gethostname(); + struct pj_hostent he; + + status = pj_gethostbyname(hostname, &he); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "bind() error", status); - goto on_error; + pjsua_perror(THIS_FILE, "Unable to resolve local host", status); + pj_sock_close(sock); + return status; } + + pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in)); + p_pub_addr->sin_family = PJ_AF_INET; + p_pub_addr->sin_port = pj_htons((pj_uint16_t)port); + p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr; } + *p_sock = sock; + return PJ_SUCCESS; +} + + +/* + * Create RTP and RTCP socket pair, and possibly resolve their public + * address via STUN. + */ +static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo) +{ + enum { + RTP_RETRY = 100 + }; + int i; + static pj_uint16_t rtp_port; + pj_sockaddr_in mapped_addr[2]; + pj_status_t status = PJ_SUCCESS; + pj_sock_t sock[2]; + + if (rtp_port == 0) + rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port; + + for (i=0; i<2; ++i) + sock[i] = PJ_INVALID_SOCKET; + /* Loop retry to bind RTP and RTCP sockets. */ for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) { /* Create and bind RTP socket. */ - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTP_SOCK]); + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); - goto on_error; + return status; } - status = pj_sock_bind_in(sock[RTP_SOCK], 0, rtp_port); + status = pj_sock_bind_in(sock[0], 0, rtp_port); if (status != PJ_SUCCESS) { - pj_sock_close(sock[RTP_SOCK]); - sock[RTP_SOCK] = PJ_INVALID_SOCKET; + pj_sock_close(sock[0]); + sock[0] = PJ_INVALID_SOCKET; continue; } /* Create and bind RTCP socket. */ - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTCP_SOCK]); + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); - goto on_error; + pj_sock_close(sock[0]); + return status; } - status = pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)); + status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1)); if (status != PJ_SUCCESS) { - pj_sock_close(sock[RTP_SOCK]); - sock[RTP_SOCK] = PJ_INVALID_SOCKET; + pj_sock_close(sock[0]); + sock[0] = PJ_INVALID_SOCKET; - pj_sock_close(sock[RTCP_SOCK]); - sock[RTCP_SOCK] = PJ_INVALID_SOCKET; + pj_sock_close(sock[1]); + sock[1] = PJ_INVALID_SOCKET; continue; } @@ -238,7 +372,32 @@ static pj_status_t init_sockets(pj_bool_t sip, * If we're configured to use STUN, then find out the mapped address, * and make sure that the mapped RTCP port is adjacent with the RTP. */ - if (pjsua.stun_port1 == 0) { + if (pjsua_has_stun()) { + status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock, + &pjsua.config.stun_srv1, + pjsua.config.stun_port1, + &pjsua.config.stun_srv2, + pjsua.config.stun_port2, + mapped_addr); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "STUN resolve error", status); + goto on_error; + } + + if (pj_ntohs(mapped_addr[1].sin_port) == + pj_ntohs(mapped_addr[0].sin_port)+1) + { + /* Success! */ + break; + } + + pj_sock_close(sock[0]); + sock[0] = PJ_INVALID_SOCKET; + + pj_sock_close(sock[1]); + sock[1] = PJ_INVALID_SOCKET; + + } else { const pj_str_t *hostname; pj_sockaddr_in addr; @@ -254,69 +413,30 @@ static pj_status_t init_sockets(pj_bool_t sip, goto on_error; } - for (i=0; i<3; ++i) + for (i=0; i<2; ++i) pj_memcpy(&mapped_addr[i], &addr, sizeof(addr)); - if (sip) { - mapped_addr[SIP_SOCK].sin_port = - pj_htons((pj_uint16_t)pjsua.sip_port); - } - mapped_addr[RTP_SOCK].sin_port=pj_htons((pj_uint16_t)rtp_port); - mapped_addr[RTCP_SOCK].sin_port=pj_htons((pj_uint16_t)(rtp_port+1)); + mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port); + mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1)); break; - - } else { - status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 3, sock, - &pjsua.stun_srv1, pjsua.stun_port1, - &pjsua.stun_srv2, pjsua.stun_port2, - mapped_addr); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "STUN error", status); - goto on_error; - } - - if (pj_ntohs(mapped_addr[2].sin_port) == - pj_ntohs(mapped_addr[1].sin_port)+1) - { - break; - } - - pj_sock_close(sock[RTP_SOCK]); - sock[RTP_SOCK] = PJ_INVALID_SOCKET; - - pj_sock_close(sock[RTCP_SOCK]); - sock[RTCP_SOCK] = PJ_INVALID_SOCKET; } } - if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) { + if (sock[0] == PJ_INVALID_SOCKET) { PJ_LOG(1,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination")); goto on_error; } - if (sip) { - pjsua.sip_sock = sock[SIP_SOCK]; - pj_memcpy(&pjsua.sip_sock_name, - &mapped_addr[SIP_SOCK], - sizeof(pj_sockaddr_in)); - } else { - pj_sock_close(sock[0]); - } - skinfo->rtp_sock = sock[RTP_SOCK]; + skinfo->rtp_sock = sock[0]; pj_memcpy(&skinfo->rtp_addr_name, - &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in)); + &mapped_addr[0], sizeof(pj_sockaddr_in)); - skinfo->rtcp_sock = sock[RTCP_SOCK]; + skinfo->rtcp_sock = sock[1]; pj_memcpy(&skinfo->rtcp_addr_name, - &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in)); + &mapped_addr[1], sizeof(pj_sockaddr_in)); - if (sip) { - PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", - pj_inet_ntoa(pjsua.sip_sock_name.sin_addr), - pj_ntohs(pjsua.sip_sock_name.sin_port))); - } PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr), pj_ntohs(skinfo->rtp_addr_name.sin_port))); @@ -328,9 +448,7 @@ static pj_status_t init_sockets(pj_bool_t sip, return PJ_SUCCESS; on_error: - for (i=0; i<3; ++i) { - if (sip && i==0) - continue; + for (i=0; i<2; ++i) { if (sock[i] != PJ_INVALID_SOCKET) pj_sock_close(sock[i]); } @@ -339,141 +457,15 @@ on_error: -/* - * Initialize stack. - */ -static pj_status_t init_stack(void) -{ - pj_status_t status; - - /* Create global endpoint: */ - - { - const pj_str_t *hostname; - const char *endpt_name; - - /* Endpoint MUST be assigned a globally unique name. - * The name will be used as the hostname in Warning header. - */ - - /* For this implementation, we'll use hostname for simplicity */ - hostname = pj_gethostname(); - endpt_name = hostname->ptr; - - /* Create the endpoint: */ - - status = pjsip_endpt_create(&pjsua.cp.factory, endpt_name, - &pjsua.endpt); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status); - return status; - } - } - - - /* Initialize transaction layer: */ - - status = pjsip_tsx_layer_init_module(pjsua.endpt); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Transaction layer initialization error", - status); - goto on_error; - } - - /* Initialize UA layer module: */ - - status = pjsip_ua_init_module( pjsua.endpt, NULL ); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "UA layer initialization error", status); - goto on_error; - } - - /* Initialize and register pjsua's application module: */ - - { - pjsip_module my_mod = - { - NULL, NULL, /* prev, next. */ - { "mod-pjsua", 9 }, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - &mod_pjsua_on_rx_request, /* on_rx_request() */ - &mod_pjsua_on_rx_response, /* on_rx_response() */ - NULL, /* on_tx_request. */ - NULL, /* on_tx_response() */ - NULL, /* on_tsx_state() */ - }; - - pjsua.mod = my_mod; - - status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to register pjsua module", - status); - goto on_error; - } - } - - /* Initialize invite session module: */ - - status = pjsua_call_init(); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Invite usage initialization error", - status); - goto on_error; - } - - /* Done */ - - return PJ_SUCCESS; - - -on_error: - pjsip_endpt_destroy(pjsua.endpt); - pjsua.endpt = NULL; - return status; -} - - -static int PJ_THREAD_FUNC pjsua_poll(void *arg) -{ - pj_status_t last_err = 0; - - PJ_UNUSED_ARG(arg); - - do { - pj_time_val timeout = { 0, 10 }; - pj_status_t status; - - status = pjsip_endpt_handle_events (pjsua.endpt, &timeout); - if (status != PJ_SUCCESS && status != last_err) { - last_err = status; - pjsua_perror(THIS_FILE, "handle_events() returned error", status); - } - } while (!pjsua.quit_flag); - - return 0; -} - -/* - * Initialize pjsua application. - * This will initialize all libraries, create endpoint instance, and register - * pjsip modules. +/** + * Create pjsua application. + * This initializes pjlib/pjlib-util, and creates memory pool factory to + * be used by application. */ -pj_status_t pjsua_init(void) +PJ_DEF(pj_status_t) pjsua_create(void) { pj_status_t status; - /* Init PJLIB logging: */ - - pj_log_set_level(pjsua.log_level); - pj_log_set_decor(pjsua.log_decor); - - /* Init PJLIB: */ status = pj_init(); @@ -498,118 +490,33 @@ pj_status_t pjsua_init(void) /* Create memory pool for application. */ pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL); + /* Must create endpoint to initialize SIP parser. */ + /* Create global endpoint: */ - /* Init PJSIP : */ - - status = init_stack(); + status = pjsip_endpt_create(&pjsua.cp.factory, + pj_gethostname()->ptr, + &pjsua.endpt); if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror(THIS_FILE, "Stack initialization has returned error", - status); + pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status); return status; } - - /* Init core SIMPLE module : */ - - pjsip_evsub_init_module(pjsua.endpt); - - /* Init presence module: */ - - pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); - - /* Init xfer/REFER module */ - - pjsip_xfer_init_module( pjsua.endpt ); - - /* Init pjsua presence handler: */ - - pjsua_pres_init(); - - /* Init out-of-dialog MESSAGE request handler. */ - - pjsua_im_init(); - - - /* Init media endpoint: */ - + /* Must create media endpoint too */ status = pjmedia_endpt_create(&pjsua.cp.factory, pjsip_endpt_get_ioqueue(pjsua.endpt), 0, &pjsua.med_endpt); if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); pjsua_perror(THIS_FILE, "Media stack initialization has returned error", status); return status; } - /* Done. */ - return PJ_SUCCESS; -} - - -/* - * Find account for incoming request. - */ -int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) -{ - pjsip_uri *uri; - pjsip_sip_uri *sip_uri; - int acc_index; - - uri = rdata->msg_info.to->uri; - - /* Just return account #0 if To URI is not SIP: */ - if (!PJSIP_URI_SCHEME_IS_SIP(uri) && - !PJSIP_URI_SCHEME_IS_SIPS(uri)) - { - return 0; - } - - - sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri); - - /* Find account which has matching username and domain. */ - for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) { - - pjsua_acc *acc = &pjsua.acc[acc_index]; - if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 && - pj_stricmp(&acc->host_part, &sip_uri->host)==0) - { - /* Match ! */ - return acc_index; - } - } - - /* No matching, try match domain part only. */ - for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) { - - pjsua_acc *acc = &pjsua.acc[acc_index]; - - if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) { - /* Match ! */ - return acc_index; - } - } - - /* Still no match, just return account #0 */ - return 0; + return PJ_SUCCESS; } -/* - * Find account for outgoing request. - */ -int pjsua_find_account_for_outgoing(const pj_str_t *url) -{ - PJ_UNUSED_ARG(url); - - /* Just use account #0 */ - return 0; -} - /* * Init media. @@ -628,7 +535,8 @@ static pj_status_t init_media(void) /* Register speex. */ status = pjmedia_codec_speex_init(pjsua.med_endpt, PJMEDIA_SPEEX_NO_UWB, - pjsua.quality, pjsua.complexity ); + pjsua.config.quality, + pjsua.config.complexity ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing Speex codec", status); @@ -686,24 +594,27 @@ static pj_status_t init_media(void) /* Enable those codecs that user put with "--add-codec", and move * the priority to top */ - for (i=0; i<pjsua.codec_cnt; ++i) { + for (i=0; i<(int)pjsua.config.codec_cnt; ++i) { pjmedia_codec_mgr_set_codec_priority( pjmedia_endpt_get_codec_mgr(pjsua.med_endpt), - &pjsua.codec_arg[i], + &pjsua.config.codec_arg[i], PJMEDIA_CODEC_PRIO_HIGHEST); } /* Init options for conference bridge. */ options = 0; - if (pjsua.no_mic) - options |= PJMEDIA_CONF_NO_MIC; + + /* Calculate maximum number of ports, if it's not specified */ + if (pjsua.config.conf_ports == 0) { + pjsua.config.conf_ports = 3 * pjsua.config.max_calls; + } /* Init conference bridge. */ - clock_rate = pjsua.clock_rate ? pjsua.clock_rate : 16000; + clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000; samples_per_frame = clock_rate * 10 / 1000; status = pjmedia_conf_create(pjsua.pool, - pjsua.max_calls+PJSUA_CONF_MORE_PORTS, + pjsua.config.conf_ports, clock_rate, 1, /* mono */ samples_per_frame, @@ -717,21 +628,14 @@ static pj_status_t init_media(void) return status; } - /* Add NULL port to the bridge. */ - status = pjmedia_null_port_create( pjsua.pool, clock_rate, - 1, /* mono */ - samples_per_frame, 16, - &pjsua.null_port); - pjmedia_conf_add_port( pjsua.mconf, pjsua.pool, pjsua.null_port, - &pjsua.null_port->info.name, NULL ); - /* Create WAV file player if required: */ - if (pjsua.wav_file) { + if (pjsua.config.wav_file.slen) { pj_str_t port_name; /* Create the file player port. */ - status = pjmedia_wav_player_port_create( pjsua.pool, pjsua.wav_file, + status = pjmedia_wav_player_port_create( pjsua.pool, + pjsua.config.wav_file.ptr, 0, 0, -1, NULL, &pjsua.file_port); if (status != PJ_SUCCESS) { @@ -742,9 +646,10 @@ static pj_status_t init_media(void) } /* Add port to conference bridge: */ + port_name = pjsua.config.wav_file; status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, pjsua.file_port, - pj_cstr(&port_name, pjsua.wav_file), + &port_name, &pjsua.wav_slot); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, @@ -760,60 +665,332 @@ static pj_status_t init_media(void) /* - * Start pjsua stack. - * This will start the registration process, if registration is configured. + * Copy configuration. */ -pj_status_t pjsua_start(void) +static void copy_config(pj_pool_t *pool, pjsua_config *dst, + const pjsua_config *src) { - int i; /* Must be signed */ - pjsip_transport *udp_transport; - pj_status_t status = PJ_SUCCESS; + unsigned i; - /* - * Init media subsystem (codecs, conference bridge, et all). - */ + /* Plain memcpy */ + pj_memcpy(dst, src, sizeof(pjsua_config)); + + /* Duplicate strings */ + pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host); + pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1); + pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2); + pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file); + + for (i=0; i<src->codec_cnt; ++i) { + pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]); + } + + pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy); + pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call); + + for (i=0; i<src->acc_cnt; ++i) { + pjsua_acc_config *dst_acc = &dst->acc_config[i]; + const pjsua_acc_config *src_acc = &src->acc_config[i]; + unsigned j; + + pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id); + pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri); + pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact); + pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy); + + for (j=0; j<src_acc->cred_count; ++j) { + pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, + &src_acc->cred_info[j].realm); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, + &src_acc->cred_info[j].scheme); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, + &src_acc->cred_info[j].username); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, + &src_acc->cred_info[j].data); + } + } + + pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename); + + for (i=0; i<src->buddy_cnt; ++i) { + pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]); + } +} + + +/* + * Initialize pjsua application. + * This will initialize all libraries, create endpoint instance, and register + * pjsip modules. + */ +PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, + const pjsua_callback *cb) +{ + char errmsg[80]; + unsigned i; + pj_status_t status; + + + /* Init accounts: */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + pjsua.acc[i].index = i; + pjsua.acc[i].online_status = PJ_TRUE; + pj_list_init(&pjsua.acc[i].route_set); + pj_list_init(&pjsua.acc[i].pres_srv_list); + } + + /* Init call array: */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) { + pjsua.calls[i].index = i; + pjsua.calls[i].refresh_tm._timer_id = -1; + pjsua.calls[i].hangup_tm._timer_id = -1; + pjsua.calls[i].conf_slot = 0; + } + + /* Copy configuration */ + copy_config(pjsua.pool, &pjsua.config, cfg); + + /* Copy callback */ + pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback)); + + /* Test configuration */ + if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) { + PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg)); + return -1; + } + + + /* Init PJLIB logging: */ + + pj_log_set_level(pjsua.config.log_level); + pj_log_set_decor(pjsua.config.log_decor); + + + /* Create SIP UDP socket */ + if (pjsua.config.udp_port) { + + status = create_sip_udp_sock( pjsua.config.udp_port, + &pjsua.sip_sock, + &pjsua.sip_sock_name); + if (status != PJ_SUCCESS) + return status; + + pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host, + pj_inet_ntoa(pjsua.sip_sock_name.sin_addr)); + pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port); + + } else { + + /* Check that SIP host and port is configured */ + if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) { + PJ_LOG(1,(THIS_FILE, + "Error: sip_host and sip_port must be specified")); + return PJ_EINVAL; + } + + pjsua.sip_sock = PJ_INVALID_SOCKET; + } + + + /* Init media endpoint */ status = init_media(); if (status != PJ_SUCCESS) return status; - /* Init sockets (STUN etc): */ - for (i=0; i<(int)pjsua.max_calls; ++i) { - status = init_sockets(i==0, &pjsua.calls[i].skinfo); - if (status == PJ_SUCCESS) - status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL, - &pjsua.calls[i].skinfo, - &pjsua.calls[i].med_tp); + + /* Init RTP sockets, only when UDP transport is enabled */ + for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) { + status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "init_sockets() has returned error", - status); - --i; - if (i >= 0) - pj_sock_close(pjsua.sip_sock); - while (i >= 0) { - pjmedia_transport_udp_close(pjsua.calls[i].med_tp); + unsigned j; + for (j=0; j<i; ++j) { + pjmedia_transport_udp_close(pjsua.calls[j].med_tp); } return status; } + status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL, + &pjsua.calls[i].skinfo, + &pjsua.calls[i].med_tp); } - /* Add UDP transport: */ + /* Init PJSIP : */ + + /* Initialize transaction layer: */ + + status = pjsip_tsx_layer_init_module(pjsua.endpt); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Transaction layer initialization error", + status); + goto on_error; + } + + /* Initialize UA layer module: */ + + status = pjsip_ua_init_module( pjsua.endpt, NULL ); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "UA layer initialization error", status); + goto on_error; + } + + /* Initialize and register pjsua's application module: */ + + { + pjsip_module my_mod = + { + NULL, NULL, /* prev, next. */ + { "mod-pjsua", 9 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &mod_pjsua_on_rx_request, /* on_rx_request() */ + &mod_pjsua_on_rx_response, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + }; + + pjsua.mod = my_mod; + + status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to register pjsua module", + status); + goto on_error; + } + } + + /* Initialize invite session module: */ + + status = pjsua_call_init(); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Invite usage initialization error", + status); + goto on_error; + } + + /* Init core SIMPLE module : */ + + pjsip_evsub_init_module(pjsua.endpt); + + /* Init presence module: */ + + pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); + + /* Init xfer/REFER module */ + + pjsip_xfer_init_module( pjsua.endpt ); + + /* Init pjsua presence handler: */ + + pjsua_pres_init(); + + /* Init out-of-dialog MESSAGE request handler. */ + + pjsua_im_init(); + + + /* Done. */ + return PJ_SUCCESS; + +on_error: + pj_caching_pool_destroy(&pjsua.cp); + return status; +} + + +/* + * Find account for incoming request. + */ +int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) +{ + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + unsigned acc_index; + + uri = rdata->msg_info.to->uri; + /* Just return last account if To URI is not SIP: */ + if (!PJSIP_URI_SCHEME_IS_SIP(uri) && + !PJSIP_URI_SCHEME_IS_SIPS(uri)) { + return pjsua.config.acc_cnt; + } + + + sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri); + + /* Find account which has matching username and domain. */ + for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) { + + pjsua_acc *acc = &pjsua.acc[acc_index]; + + if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 && + pj_stricmp(&acc->host_part, &sip_uri->host)==0) + { + /* Match ! */ + return acc_index; + } + } + + /* No matching, try match domain part only. */ + for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) { + + pjsua_acc *acc = &pjsua.acc[acc_index]; + + if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) { + /* Match ! */ + return acc_index; + } + } + + /* Still no match, just return last account */ + return pjsua.config.acc_cnt; +} + + +/* + * Find account for outgoing request. + */ +int pjsua_find_account_for_outgoing(const pj_str_t *url) +{ + PJ_UNUSED_ARG(url); + + /* Just use account #0 */ + return 0; +} + + + +/* + * Start pjsua stack. + * This will start the registration process, if registration is configured. + */ +PJ_DEF(pj_status_t) pjsua_start(void) +{ + int i; /* Must be signed */ + pj_status_t status = PJ_SUCCESS; + + + /* Add UDP transport: */ + if (pjsua.sip_sock > 0) { + /* Init the published name for the transport. * Depending whether STUN is used, this may be the STUN mapped * address, or socket's bound address. */ pjsip_host_port addr_name; - addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr); - addr_name.host.slen = pj_ansi_strlen(addr_name.host.ptr); - addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port); + addr_name.host = pjsua.config.sip_host; + addr_name.port = pjsua.config.sip_port; /* Create UDP transport from previously created UDP socket: */ status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock, &addr_name, 1, - &udp_transport); + NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to start UDP transport", status); @@ -821,16 +998,37 @@ pj_status_t pjsua_start(void) } } - /* Initialize Contact URI, if one is not specified: */ - for (i=0; i<pjsua.acc_cnt; ++i) { + /* The last account is default account to be used when nothing match + * any configured accounts. + */ + { + char buf[80]; + pj_str_t tmp; + pjsua_acc_config *acc_cfg = + &pjsua.config.acc_config[pjsua.config.acc_cnt]; + + tmp.ptr = buf; + tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>", + pjsua.config.sip_host.ptr, + pjsua.config.sip_port); + + pj_strdup_with_null( pjsua.pool, &acc_cfg->id, &tmp); + acc_cfg->contact = acc_cfg->id; + } + + + /* Initialize accounts: */ + for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { + pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[i]; + pjsua_acc *acc = &pjsua.acc[i]; pjsip_uri *uri; pjsip_sip_uri *sip_uri; /* Need to parse local_uri to get the elements: */ - uri = pjsip_parse_uri(pjsua.pool, pjsua.acc[i].local_uri.ptr, - pjsua.acc[i].local_uri.slen, 0); + uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr, + acc_cfg->id.slen, 0); if (uri == NULL) { pjsua_perror(THIS_FILE, "Invalid local URI", PJSIP_EINVALIDURI); @@ -852,15 +1050,20 @@ pj_status_t pjsua_start(void) sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); - pjsua.acc[i].user_part = sip_uri->user; - pjsua.acc[i].host_part = sip_uri->host; + acc->user_part = sip_uri->user; + acc->host_part = sip_uri->host; - if (pjsua.acc[i].contact_uri.slen == 0 && - pjsua.acc[i].local_uri.slen) - { + /* Build Contact header */ + + if (acc_cfg->contact.slen == 0) { char contact[128]; + const char *addr; + int port; int len; + addr = pjsua.config.sip_host.ptr; + port = pjsua.config.sip_port; + /* The local Contact is the username@ip-addr, where * - username is taken from the local URI, * - ip-addr in UDP transport's address name (which may have been @@ -873,21 +1076,17 @@ pj_status_t pjsua_start(void) /* With the user part. */ len = pj_ansi_snprintf(contact, sizeof(contact), - "<sip:%.*s@%.*s:%d>", + "<sip:%.*s@%s:%d>", (int)sip_uri->user.slen, sip_uri->user.ptr, - (int)udp_transport->local_name.host.slen, - udp_transport->local_name.host.ptr, - udp_transport->local_name.port); + addr, port); } else { /* Without user part */ len = pj_ansi_snprintf(contact, sizeof(contact), - "<sip:%.*s:%d>", - (int)udp_transport->local_name.host.slen, - udp_transport->local_name.host.ptr, - udp_transport->local_name.port); + "<sip:%s:%d>", + addr, port); } if (len < 1 || len >= sizeof(contact)) { @@ -897,38 +1096,39 @@ pj_status_t pjsua_start(void) /* Duplicate Contact uri. */ - pj_strdup2(pjsua.pool, &pjsua.acc[i].contact_uri, contact); + pj_strdup2(pjsua.pool, &acc_cfg->contact, contact); } - } - /* If outbound_proxy is specified, put it in the route_set: */ - if (pjsua.outbound_proxy.slen) { + /* Build route-set for this account */ + if (pjsua.config.outbound_proxy.slen) { + pj_str_t hname = { "Route", 5}; + pjsip_route_hdr *r; + pj_str_t tmp; - pjsip_route_hdr *route; - const pj_str_t hname = { "Route", 5 }; - int parsed_len; - - route = pjsip_parse_hdr( pjsua.pool, &hname, - pjsua.outbound_proxy.ptr, - pjsua.outbound_proxy.slen, - &parsed_len); - if (route == NULL) { - pjsua_perror(THIS_FILE, "Invalid outbound proxy URL", - PJSIP_EINVALIDURI); - return PJSIP_EINVALIDURI; + pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy); + r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); + pj_list_push_back(&acc->route_set, r); } - for (i=0; i<pjsua.acc_cnt; ++i) { - pj_list_push_front(&pjsua.acc[i].route_set, route); + if (acc_cfg->proxy.slen) { + pj_str_t hname = { "Route", 5}; + pjsip_route_hdr *r; + pj_str_t tmp; + + pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy); + r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); + pj_list_push_back(&acc->route_set, r); } } + + /* Create worker thread(s), if required: */ - for (i=0; i<pjsua.thread_cnt; ++i) { + for (i=0; i<(int)pjsua.config.thread_cnt; ++i) { status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll, NULL, 0, 0, &pjsua.threads[i]); if (status != PJ_SUCCESS) { @@ -944,7 +1144,7 @@ pj_status_t pjsua_start(void) /* Start registration: */ /* Create client registration session: */ - for (i=0; i<pjsua.acc_cnt; ++i) { + for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { status = pjsua_regc_init(i); if (status != PJ_SUCCESS) return status; @@ -956,6 +1156,12 @@ pj_status_t pjsua_start(void) } + /* Init buddies */ + for (i=0; i<(int)pjsua.config.buddy_cnt; ++i) { + pjsua.buddies[i].uri = pjsua.config.buddy_uri[i]; + } + pjsua.buddy_cnt = pjsua.config.buddy_cnt; + /* Find account for outgoing preence subscription */ for (i=0; i<pjsua.buddy_cnt; ++i) { pjsua.buddies[i].acc_index = @@ -986,7 +1192,7 @@ static void busy_sleep(unsigned msec) /* * Destroy pjsua. */ -pj_status_t pjsua_destroy(void) +PJ_DEF(pj_status_t) pjsua_destroy(void) { int i; /* Must be signed */ @@ -1000,14 +1206,14 @@ pj_status_t pjsua_destroy(void) pjsua_pres_shutdown(); /* Unregister, if required: */ - for (i=0; i<pjsua.acc_cnt; ++i) { + for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { if (pjsua.acc[i].regc) { pjsua_regc_update(i, 0); } } /* Wait worker threads to quit: */ - for (i=0; i<pjsua.thread_cnt; ++i) { + for (i=0; i<(int)pjsua.config.thread_cnt; ++i) { if (pjsua.threads[i]) { pj_thread_join(pjsua.threads[i]); @@ -1029,10 +1235,6 @@ pj_status_t pjsua_destroy(void) if (pjsua.file_port) pjmedia_port_destroy(pjsua.file_port); - /* Destroy null port. */ - if (pjsua.null_port) - pjmedia_port_destroy(pjsua.null_port); - /* Shutdown all codecs: */ #if PJMEDIA_HAS_SPEEX_CODEC @@ -1053,7 +1255,7 @@ pj_status_t pjsua_destroy(void) /* Close transports */ - for (i=0; i<pjsua.call_cnt; ++i) { + for (i=0; pjsua.config.start_rtp_port && i<(int)pjsua.config.max_calls; ++i) { pjmedia_transport_udp_close(pjsua.calls[i].med_tp); } |