blob: 0bfb062cec2393cd260238167aa68ef5fcdb0160 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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 Prijono268ca612006-02-07 12:34:11 +000020
Benny Prijono84126ab2006-02-09 09:30:09 +000021/*
22 * pjsua_core.c
23 *
24 * Core application functionalities.
25 */
Benny Prijono268ca612006-02-07 12:34:11 +000026
Benny Prijono84126ab2006-02-09 09:30:09 +000027#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000028
29
Benny Prijono95196582006-02-09 00:13:40 +000030/*
Benny Prijono84126ab2006-02-09 09:30:09 +000031 * Global variable.
32 */
33struct pjsua pjsua;
34
35
36/*
37 * Default local URI, if none is specified in cmd-line
Benny Prijono95196582006-02-09 00:13:40 +000038 */
Benny Prijonoccf95622006-02-07 18:48:01 +000039#define PJSUA_LOCAL_URI "<sip:user@127.0.0.1>"
Benny Prijono268ca612006-02-07 12:34:11 +000040
Benny Prijono268ca612006-02-07 12:34:11 +000041
Benny Prijono268ca612006-02-07 12:34:11 +000042
43/*
44 * Init default application parameters.
45 */
46void pjsua_default(void)
47{
Benny Prijonoa91a0032006-02-26 21:23:45 +000048 unsigned i;
49
Benny Prijono268ca612006-02-07 12:34:11 +000050
51 /* Normally need another thread for console application, because main
52 * thread will be blocked in fgets().
53 */
54 pjsua.thread_cnt = 1;
55
56
57 /* Default transport settings: */
Benny Prijono268ca612006-02-07 12:34:11 +000058 pjsua.sip_port = 5060;
59
60
Benny Prijonocbf37402006-03-01 19:29:10 +000061 /* Default we start RTP at port 4000 */
62 pjsua.start_rtp_port = 4000;
63
64
Benny Prijono268ca612006-02-07 12:34:11 +000065 /* Default logging settings: */
Benny Prijono268ca612006-02-07 12:34:11 +000066 pjsua.log_level = 5;
67 pjsua.app_log_level = 4;
68 pjsua.log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
69 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
70
Benny Prijono268ca612006-02-07 12:34:11 +000071
Benny Prijono105217f2006-03-06 16:25:59 +000072 /* Default call settings. */
73 pjsua.uas_refresh = -1;
74 pjsua.uas_duration = -1;
75
Benny Prijonoa91a0032006-02-26 21:23:45 +000076 /* Default: do not use STUN: */
Benny Prijono268ca612006-02-07 12:34:11 +000077 pjsua.stun_port1 = pjsua.stun_port2 = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +000078
Benny Prijono1c2bf462006-03-05 11:54:02 +000079 /* Default for media: */
Benny Prijonofa137ca2006-03-20 17:42:37 +000080#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
81 pjsua.clock_rate = 44100;
82#else
Benny Prijono08e0d062006-03-04 14:52:44 +000083 pjsua.clock_rate = 8000;
Benny Prijonofa137ca2006-03-20 17:42:37 +000084#endif
Benny Prijonoccb03fa2006-03-06 13:35:47 +000085 pjsua.complexity = -1;
Benny Prijono1c2bf462006-03-05 11:54:02 +000086 pjsua.quality = 4;
Benny Prijonofa137ca2006-03-20 17:42:37 +000087 pjsua.has_wb = 0;
88 pjsua.has_uwb = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +000089
Benny Prijono08e0d062006-03-04 14:52:44 +000090
Benny Prijonoa91a0032006-02-26 21:23:45 +000091 /* Init accounts: */
92 pjsua.acc_cnt = 1;
93 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
94 pjsua.acc[i].index = i;
95 pjsua.acc[i].local_uri = pj_str(PJSUA_LOCAL_URI);
96 pjsua.acc[i].reg_timeout = 55;
Benny Prijonoccb03fa2006-03-06 13:35:47 +000097 pjsua.acc[i].online_status = PJ_TRUE;
Benny Prijonoa91a0032006-02-26 21:23:45 +000098 pj_list_init(&pjsua.acc[i].route_set);
99 pj_list_init(&pjsua.acc[i].pres_srv_list);
100 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000101
Benny Prijonoa91a0032006-02-26 21:23:45 +0000102 /* Init call array: */
Benny Prijono105217f2006-03-06 16:25:59 +0000103 for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000104 pjsua.calls[i].index = i;
Benny Prijono105217f2006-03-06 16:25:59 +0000105 pjsua.calls[i].refresh_tm._timer_id = -1;
106 pjsua.calls[i].hangup_tm._timer_id = -1;
Benny Prijono275fd682006-03-22 11:59:11 +0000107 pjsua.calls[i].conf_slot = 0;
Benny Prijono105217f2006-03-06 16:25:59 +0000108 }
Benny Prijono95196582006-02-09 00:13:40 +0000109
Benny Prijonoa91a0032006-02-26 21:23:45 +0000110 /* Default max nb of calls. */
111 pjsua.max_calls = 4;
Benny Prijono834aee32006-02-19 01:38:06 +0000112
113 /* Init server presence subscription list: */
114
Benny Prijono834aee32006-02-19 01:38:06 +0000115
Benny Prijono268ca612006-02-07 12:34:11 +0000116}
117
118
Benny Prijono268ca612006-02-07 12:34:11 +0000119
120/*
121 * Handler for receiving incoming requests.
122 *
123 * This handler serves multiple purposes:
124 * - it receives requests outside dialogs.
125 * - it receives requests inside dialogs, when the requests are
126 * unhandled by other dialog usages. Example of these
127 * requests are: MESSAGE.
128 */
129static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
130{
Benny Prijono38998232006-02-08 22:44:25 +0000131
Benny Prijono84126ab2006-02-09 09:30:09 +0000132 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000133
Benny Prijonoa91a0032006-02-26 21:23:45 +0000134 return pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000135 }
136
Benny Prijono268ca612006-02-07 12:34:11 +0000137 return PJ_FALSE;
138}
139
140
141/*
142 * Handler for receiving incoming responses.
143 *
144 * This handler serves multiple purposes:
145 * - it receives strayed responses (i.e. outside any dialog and
146 * outside any transactions).
147 * - it receives responses coming to a transaction, when pjsua
148 * module is set as transaction user for the transaction.
149 * - it receives responses inside a dialog, when these responses
150 * are unhandled by other dialog usages.
151 */
152static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
153{
154 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000155 return PJ_FALSE;
156}
157
158
Benny Prijono268ca612006-02-07 12:34:11 +0000159/*
160 * Initialize sockets and optionally get the public address via STUN.
161 */
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000162static pj_status_t init_sockets(pj_bool_t sip,
163 pjmedia_sock_info *skinfo)
Benny Prijono268ca612006-02-07 12:34:11 +0000164{
165 enum {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000166 RTP_RETRY = 100
Benny Prijono268ca612006-02-07 12:34:11 +0000167 };
168 enum {
169 SIP_SOCK,
170 RTP_SOCK,
171 RTCP_SOCK,
172 };
173 int i;
Benny Prijonocbf37402006-03-01 19:29:10 +0000174 static pj_uint16_t rtp_port;
Benny Prijono268ca612006-02-07 12:34:11 +0000175 pj_sock_t sock[3];
176 pj_sockaddr_in mapped_addr[3];
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000177 pj_status_t status = PJ_SUCCESS;
Benny Prijono268ca612006-02-07 12:34:11 +0000178
Benny Prijonocbf37402006-03-01 19:29:10 +0000179 if (rtp_port == 0)
180 rtp_port = (pj_uint16_t)pjsua.start_rtp_port;
181
Benny Prijono268ca612006-02-07 12:34:11 +0000182 for (i=0; i<3; ++i)
183 sock[i] = PJ_INVALID_SOCKET;
184
Benny Prijonofccab712006-02-22 22:23:22 +0000185 /* Create and bind SIP UDP socket. */
186 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[SIP_SOCK]);
187 if (status != PJ_SUCCESS) {
188 pjsua_perror(THIS_FILE, "socket() error", status);
189 goto on_error;
190 }
191
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000192 if (sip) {
Benny Prijonofccab712006-02-22 22:23:22 +0000193 status = pj_sock_bind_in(sock[SIP_SOCK], 0, pjsua.sip_port);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000194 if (status != PJ_SUCCESS) {
Benny Prijonofccab712006-02-22 22:23:22 +0000195 pjsua_perror(THIS_FILE, "bind() error", status);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000196 goto on_error;
197 }
Benny Prijonofccab712006-02-22 22:23:22 +0000198 } else {
199 status = pj_sock_bind_in(sock[SIP_SOCK], 0, 0);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000200 if (status != PJ_SUCCESS) {
201 pjsua_perror(THIS_FILE, "bind() error", status);
202 goto on_error;
203 }
Benny Prijono268ca612006-02-07 12:34:11 +0000204 }
205
Benny Prijono268ca612006-02-07 12:34:11 +0000206
207 /* Loop retry to bind RTP and RTCP sockets. */
208 for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
209
210 /* Create and bind RTP socket. */
211 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTP_SOCK]);
212 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000213 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000214 goto on_error;
215 }
216
217 status = pj_sock_bind_in(sock[RTP_SOCK], 0, rtp_port);
218 if (status != PJ_SUCCESS) {
219 pj_sock_close(sock[RTP_SOCK]);
220 sock[RTP_SOCK] = PJ_INVALID_SOCKET;
221 continue;
222 }
223
224 /* Create and bind RTCP socket. */
225 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTCP_SOCK]);
226 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000227 pjsua_perror(THIS_FILE, "socket() error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000228 goto on_error;
229 }
230
231 status = pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1));
232 if (status != PJ_SUCCESS) {
233 pj_sock_close(sock[RTP_SOCK]);
234 sock[RTP_SOCK] = PJ_INVALID_SOCKET;
235
236 pj_sock_close(sock[RTCP_SOCK]);
237 sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
238 continue;
239 }
240
241 /*
242 * If we're configured to use STUN, then find out the mapped address,
243 * and make sure that the mapped RTCP port is adjacent with the RTP.
244 */
245 if (pjsua.stun_port1 == 0) {
246 const pj_str_t *hostname;
247 pj_sockaddr_in addr;
248
249 /* Get local IP address. */
250 hostname = pj_gethostname();
251
252 pj_memset( &addr, 0, sizeof(addr));
253 addr.sin_family = PJ_AF_INET;
254 status = pj_sockaddr_in_set_str_addr( &addr, hostname);
255 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000256 pjsua_perror(THIS_FILE, "Unresolvable local hostname",
257 status);
Benny Prijono268ca612006-02-07 12:34:11 +0000258 goto on_error;
259 }
260
261 for (i=0; i<3; ++i)
262 pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
263
Benny Prijonoa91a0032006-02-26 21:23:45 +0000264 if (sip) {
265 mapped_addr[SIP_SOCK].sin_port =
266 pj_htons((pj_uint16_t)pjsua.sip_port);
267 }
268 mapped_addr[RTP_SOCK].sin_port=pj_htons((pj_uint16_t)rtp_port);
269 mapped_addr[RTCP_SOCK].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
Benny Prijono268ca612006-02-07 12:34:11 +0000270 break;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000271
Benny Prijono268ca612006-02-07 12:34:11 +0000272 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000273 status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 3, sock,
274 &pjsua.stun_srv1, pjsua.stun_port1,
275 &pjsua.stun_srv2, pjsua.stun_port2,
276 mapped_addr);
Benny Prijono268ca612006-02-07 12:34:11 +0000277 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000278 pjsua_perror(THIS_FILE, "STUN error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000279 goto on_error;
280 }
281
Benny Prijonoa91a0032006-02-26 21:23:45 +0000282 if (pj_ntohs(mapped_addr[2].sin_port) ==
283 pj_ntohs(mapped_addr[1].sin_port)+1)
284 {
Benny Prijono268ca612006-02-07 12:34:11 +0000285 break;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000286 }
Benny Prijono268ca612006-02-07 12:34:11 +0000287
Benny Prijonoa91a0032006-02-26 21:23:45 +0000288 pj_sock_close(sock[RTP_SOCK]);
289 sock[RTP_SOCK] = PJ_INVALID_SOCKET;
290
291 pj_sock_close(sock[RTCP_SOCK]);
292 sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
Benny Prijono268ca612006-02-07 12:34:11 +0000293 }
294 }
295
296 if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000297 PJ_LOG(1,(THIS_FILE,
298 "Unable to find appropriate RTP/RTCP ports combination"));
Benny Prijono268ca612006-02-07 12:34:11 +0000299 goto on_error;
300 }
301
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000302 if (sip) {
303 pjsua.sip_sock = sock[SIP_SOCK];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000304 pj_memcpy(&pjsua.sip_sock_name,
305 &mapped_addr[SIP_SOCK],
306 sizeof(pj_sockaddr_in));
Benny Prijonofccab712006-02-22 22:23:22 +0000307 } else {
308 pj_sock_close(sock[0]);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000309 }
Benny Prijono95196582006-02-09 00:13:40 +0000310
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000311 skinfo->rtp_sock = sock[RTP_SOCK];
312 pj_memcpy(&skinfo->rtp_addr_name,
Benny Prijono95196582006-02-09 00:13:40 +0000313 &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
314
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000315 skinfo->rtcp_sock = sock[RTCP_SOCK];
316 pj_memcpy(&skinfo->rtcp_addr_name,
Benny Prijono95196582006-02-09 00:13:40 +0000317 &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
Benny Prijono268ca612006-02-07 12:34:11 +0000318
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000319 if (sip) {
320 PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
321 pj_inet_ntoa(pjsua.sip_sock_name.sin_addr),
322 pj_ntohs(pjsua.sip_sock_name.sin_port)));
323 }
Benny Prijono268ca612006-02-07 12:34:11 +0000324 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000325 pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr),
326 pj_ntohs(skinfo->rtp_addr_name.sin_port)));
Benny Prijono268ca612006-02-07 12:34:11 +0000327 PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000328 pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr),
329 pj_ntohs(skinfo->rtcp_addr_name.sin_port)));
Benny Prijono268ca612006-02-07 12:34:11 +0000330
Benny Prijonofccab712006-02-22 22:23:22 +0000331 rtp_port += 2;
Benny Prijono268ca612006-02-07 12:34:11 +0000332 return PJ_SUCCESS;
333
334on_error:
335 for (i=0; i<3; ++i) {
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000336 if (sip && i==0)
337 continue;
Benny Prijono268ca612006-02-07 12:34:11 +0000338 if (sock[i] != PJ_INVALID_SOCKET)
339 pj_sock_close(sock[i]);
340 }
341 return status;
342}
343
344
345
346/*
347 * Initialize stack.
348 */
349static pj_status_t init_stack(void)
350{
351 pj_status_t status;
352
353 /* Create global endpoint: */
354
355 {
356 const pj_str_t *hostname;
357 const char *endpt_name;
358
359 /* Endpoint MUST be assigned a globally unique name.
360 * The name will be used as the hostname in Warning header.
361 */
362
363 /* For this implementation, we'll use hostname for simplicity */
364 hostname = pj_gethostname();
365 endpt_name = hostname->ptr;
366
367 /* Create the endpoint: */
368
369 status = pjsip_endpt_create(&pjsua.cp.factory, endpt_name,
370 &pjsua.endpt);
371 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000372 pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000373 return status;
374 }
375 }
376
377
378 /* Initialize transaction layer: */
379
Benny Prijono2f8992b2006-02-25 21:16:36 +0000380 status = pjsip_tsx_layer_init_module(pjsua.endpt);
Benny Prijono268ca612006-02-07 12:34:11 +0000381 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000382 pjsua_perror(THIS_FILE, "Transaction layer initialization error",
383 status);
Benny Prijono268ca612006-02-07 12:34:11 +0000384 goto on_error;
385 }
386
387 /* Initialize UA layer module: */
388
Benny Prijono2f8992b2006-02-25 21:16:36 +0000389 status = pjsip_ua_init_module( pjsua.endpt, NULL );
Benny Prijono268ca612006-02-07 12:34:11 +0000390 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000391 pjsua_perror(THIS_FILE, "UA layer initialization error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000392 goto on_error;
393 }
394
395 /* Initialize and register pjsua's application module: */
396
397 {
398 pjsip_module my_mod =
399 {
400 NULL, NULL, /* prev, next. */
401 { "mod-pjsua", 9 }, /* Name. */
402 -1, /* Id */
403 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
Benny Prijono268ca612006-02-07 12:34:11 +0000404 NULL, /* load() */
405 NULL, /* start() */
406 NULL, /* stop() */
407 NULL, /* unload() */
408 &mod_pjsua_on_rx_request, /* on_rx_request() */
409 &mod_pjsua_on_rx_response, /* on_rx_response() */
410 NULL, /* on_tx_request. */
411 NULL, /* on_tx_response() */
412 NULL, /* on_tsx_state() */
413 };
414
415 pjsua.mod = my_mod;
416
417 status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
418 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000419 pjsua_perror(THIS_FILE, "Unable to register pjsua module",
420 status);
Benny Prijono268ca612006-02-07 12:34:11 +0000421 goto on_error;
422 }
423 }
424
425 /* Initialize invite session module: */
426
Benny Prijonoa91a0032006-02-26 21:23:45 +0000427 status = pjsua_call_init();
428 if (status != PJ_SUCCESS) {
429 pjsua_perror(THIS_FILE, "Invite usage initialization error",
430 status);
431 goto on_error;
Benny Prijono268ca612006-02-07 12:34:11 +0000432 }
433
Benny Prijonoccf95622006-02-07 18:48:01 +0000434 /* Done */
Benny Prijono268ca612006-02-07 12:34:11 +0000435
436 return PJ_SUCCESS;
437
438
439on_error:
440 pjsip_endpt_destroy(pjsua.endpt);
441 pjsua.endpt = NULL;
442 return status;
443}
444
445
Benny Prijono834aee32006-02-19 01:38:06 +0000446static int PJ_THREAD_FUNC pjsua_poll(void *arg)
Benny Prijono268ca612006-02-07 12:34:11 +0000447{
Benny Prijonof8baa872006-02-27 23:54:23 +0000448 pj_status_t last_err = 0;
449
Benny Prijono268ca612006-02-07 12:34:11 +0000450 PJ_UNUSED_ARG(arg);
451
Benny Prijono834aee32006-02-19 01:38:06 +0000452 do {
Benny Prijono268ca612006-02-07 12:34:11 +0000453 pj_time_val timeout = { 0, 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000454 pj_status_t status;
455
456 status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
Benny Prijono080a2c42006-03-30 20:55:20 +0000457 if (status != PJ_SUCCESS && status != last_err) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000458 last_err = status;
459 pjsua_perror(THIS_FILE, "handle_events() returned error", status);
460 }
Benny Prijono834aee32006-02-19 01:38:06 +0000461 } while (!pjsua.quit_flag);
Benny Prijono268ca612006-02-07 12:34:11 +0000462
463 return 0;
464}
465
466/*
467 * Initialize pjsua application.
Benny Prijonoccf95622006-02-07 18:48:01 +0000468 * This will initialize all libraries, create endpoint instance, and register
469 * pjsip modules.
Benny Prijono268ca612006-02-07 12:34:11 +0000470 */
471pj_status_t pjsua_init(void)
472{
Benny Prijono268ca612006-02-07 12:34:11 +0000473 pj_status_t status;
474
475 /* Init PJLIB logging: */
476
477 pj_log_set_level(pjsua.log_level);
478 pj_log_set_decor(pjsua.log_decor);
479
480
481 /* Init PJLIB: */
482
483 status = pj_init();
484 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000485 pjsua_perror(THIS_FILE, "pj_init() error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000486 return status;
487 }
488
Benny Prijonofccab712006-02-22 22:23:22 +0000489 /* Init PJLIB-UTIL: */
490
491 status = pjlib_util_init();
492 if (status != PJ_SUCCESS) {
493 pjsua_perror(THIS_FILE, "pjlib_util_init() error", status);
494 return status;
495 }
496
Benny Prijono268ca612006-02-07 12:34:11 +0000497 /* Init memory pool: */
498
499 /* Init caching pool. */
500 pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
501
502 /* Create memory pool for application. */
503 pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
504
505
Benny Prijono834aee32006-02-19 01:38:06 +0000506 /* Init PJSIP : */
Benny Prijonoccf95622006-02-07 18:48:01 +0000507
508 status = init_stack();
509 if (status != PJ_SUCCESS) {
510 pj_caching_pool_destroy(&pjsua.cp);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000511 pjsua_perror(THIS_FILE, "Stack initialization has returned error",
512 status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000513 return status;
514 }
515
Benny Prijono95196582006-02-09 00:13:40 +0000516
Benny Prijono834aee32006-02-19 01:38:06 +0000517 /* Init core SIMPLE module : */
518
519 pjsip_evsub_init_module(pjsua.endpt);
520
521 /* Init presence module: */
522
523 pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
524
Benny Prijono26ff9062006-02-21 23:47:00 +0000525 /* Init xfer/REFER module */
526
527 pjsip_xfer_init_module( pjsua.endpt );
Benny Prijono834aee32006-02-19 01:38:06 +0000528
529 /* Init pjsua presence handler: */
530
531 pjsua_pres_init();
532
Benny Prijonob0808372006-03-02 21:18:58 +0000533 /* Init out-of-dialog MESSAGE request handler. */
534
535 pjsua_im_init();
536
Benny Prijono834aee32006-02-19 01:38:06 +0000537
Benny Prijono95196582006-02-09 00:13:40 +0000538 /* Init media endpoint: */
539
Benny Prijono275fd682006-03-22 11:59:11 +0000540 status = pjmedia_endpt_create(&pjsua.cp.factory,
541 pjsip_endpt_get_ioqueue(pjsua.endpt), 0,
542 &pjsua.med_endpt);
Benny Prijono95196582006-02-09 00:13:40 +0000543 if (status != PJ_SUCCESS) {
544 pj_caching_pool_destroy(&pjsua.cp);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000545 pjsua_perror(THIS_FILE,
546 "Media stack initialization has returned error",
547 status);
Benny Prijono95196582006-02-09 00:13:40 +0000548 return status;
549 }
550
Benny Prijonoccf95622006-02-07 18:48:01 +0000551 /* Done. */
552 return PJ_SUCCESS;
553}
554
555
Benny Prijonoa91a0032006-02-26 21:23:45 +0000556/*
557 * Find account for incoming request.
558 */
559int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
560{
561 pjsip_uri *uri;
562 pjsip_sip_uri *sip_uri;
563 int acc_index;
564
565 uri = rdata->msg_info.to->uri;
566
567 /* Just return account #0 if To URI is not SIP: */
568 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
569 !PJSIP_URI_SCHEME_IS_SIPS(uri))
570 {
571 return 0;
572 }
573
574
575 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
576
577 /* Find account which has matching username and domain. */
578 for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
579
580 pjsua_acc *acc = &pjsua.acc[acc_index];
581
582 if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
583 pj_stricmp(&acc->host_part, &sip_uri->host)==0)
584 {
585 /* Match ! */
586 return acc_index;
587 }
588 }
589
590 /* No matching, try match domain part only. */
591 for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
592
593 pjsua_acc *acc = &pjsua.acc[acc_index];
594
595 if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
596 /* Match ! */
597 return acc_index;
598 }
599 }
600
601 /* Still no match, just return account #0 */
602 return 0;
603}
604
605
606/*
607 * Find account for outgoing request.
608 */
609int pjsua_find_account_for_outgoing(const pj_str_t *url)
610{
611 PJ_UNUSED_ARG(url);
612
613 /* Just use account #0 */
614 return 0;
615}
616
Benny Prijonoccf95622006-02-07 18:48:01 +0000617
618/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000619 * Init media.
Benny Prijonoccf95622006-02-07 18:48:01 +0000620 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000621static pj_status_t init_media(void)
Benny Prijonoccf95622006-02-07 18:48:01 +0000622{
Benny Prijono8e3344c2006-03-08 12:37:22 +0000623 unsigned options;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000624 pj_status_t status;
625
626 /* If user doesn't specify any codecs, register all of them. */
627 if (pjsua.codec_cnt == 0) {
628
Benny Prijono4381efe2006-03-16 14:24:26 +0000629#if PJMEDIA_HAS_SPEEX_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000630 unsigned option = PJMEDIA_SPEEX_NO_WB | PJMEDIA_SPEEX_NO_UWB;
631
632 /* Register speex. */
Benny Prijonofa137ca2006-03-20 17:42:37 +0000633 if (pjsua.has_wb)
634 option &= ~PJMEDIA_SPEEX_NO_WB;
635 if (pjsua.has_uwb)
636 option &= ~PJMEDIA_SPEEX_NO_UWB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000637
Benny Prijono1c2bf462006-03-05 11:54:02 +0000638 status = pjmedia_codec_speex_init(pjsua.med_endpt, option,
639 pjsua.quality, pjsua.complexity );
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000640 if (status != PJ_SUCCESS) {
641 pjsua_perror(THIS_FILE, "Error initializing Speex codec",
642 status);
643 return status;
644 }
645
646 pjsua.codec_arg[pjsua.codec_cnt] = pj_str("speex");
647 pjsua.codec_deinit[pjsua.codec_cnt] = &pjmedia_codec_speex_deinit;
648 pjsua.codec_cnt++;
Benny Prijono4381efe2006-03-16 14:24:26 +0000649#endif /* PJMEDIA_HAS_SPEEX_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000650
Benny Prijono4381efe2006-03-16 14:24:26 +0000651#if PJMEDIA_HAS_GSM_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000652 /* Register GSM */
653 status = pjmedia_codec_gsm_init(pjsua.med_endpt);
654 if (status != PJ_SUCCESS) {
655 pjsua_perror(THIS_FILE, "Error initializing GSM codec",
656 status);
657 return status;
658 }
659
660 pjsua.codec_arg[pjsua.codec_cnt] = pj_str("gsm");
661 pjsua.codec_deinit[pjsua.codec_cnt] = &pjmedia_codec_gsm_deinit;
662 pjsua.codec_cnt++;
Benny Prijono4381efe2006-03-16 14:24:26 +0000663#endif /* PJMEDIA_HAS_GSM_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000664
Benny Prijono15953012006-04-27 22:37:08 +0000665#if PJMEDIA_HAS_L16_CODEC
666 /* Register L16 */
667 status = pjmedia_codec_l16_init(pjsua.med_endpt, 0);
668 if (status != PJ_SUCCESS) {
669 pjsua_perror(THIS_FILE, "Error initializing L16 codec",
670 status);
671 return status;
672 }
673
674 pjsua.codec_arg[pjsua.codec_cnt] = pj_str("l16");
675 pjsua.codec_deinit[pjsua.codec_cnt] = &pjmedia_codec_l16_deinit;
676 pjsua.codec_cnt++;
677#endif /* PJMEDIA_HAS_L16_CODEC */
678
679
Benny Prijono4381efe2006-03-16 14:24:26 +0000680#if PJMEDIA_HAS_G711_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000681 /* Register PCMA and PCMU */
682 status = pjmedia_codec_g711_init(pjsua.med_endpt);
683 if (status != PJ_SUCCESS) {
684 pjsua_perror(THIS_FILE, "Error initializing G711 codec",
685 status);
686 return status;
687 }
688
689 pjsua.codec_arg[pjsua.codec_cnt] = pj_str("pcmu");
690 pjsua.codec_deinit[pjsua.codec_cnt] = &pjmedia_codec_g711_deinit;
691 pjsua.codec_cnt++;
692 pjsua.codec_arg[pjsua.codec_cnt] = pj_str("pcma");
693 pjsua.codec_deinit[pjsua.codec_cnt] = &pjmedia_codec_g711_deinit;
694 pjsua.codec_cnt++;
Benny Prijono4381efe2006-03-16 14:24:26 +0000695#endif /* PJMEDIA_HAS_G711_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000696
697 } else {
698
699 /* If user specifies the exact codec to be used, then create only
700 * those codecs.
701 */
702 int i;
703
704 for (i=0; i<pjsua.codec_cnt; ++i) {
705
Benny Prijono4381efe2006-03-16 14:24:26 +0000706 if (0) {
707 /* Dummy */
708 }
709#if PJMEDIA_HAS_SPEEX_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000710 /* Is it speex? */
Benny Prijono4381efe2006-03-16 14:24:26 +0000711 else if (!pj_stricmp2(&pjsua.codec_arg[i], "speex")) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000712
713 unsigned option = PJMEDIA_SPEEX_NO_WB | PJMEDIA_SPEEX_NO_UWB;
714
715 /* Register speex. */
Benny Prijonofa137ca2006-03-20 17:42:37 +0000716 if (pjsua.has_wb)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000717 option &= ~(PJMEDIA_SPEEX_NO_WB);
Benny Prijonofa137ca2006-03-20 17:42:37 +0000718 if (pjsua.has_uwb)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000719 option &= ~(PJMEDIA_SPEEX_NO_UWB);
720
721 status = pjmedia_codec_speex_init(pjsua.med_endpt, option,
722 -1, -1);
723 if (status != PJ_SUCCESS) {
724 pjsua_perror(THIS_FILE, "Error initializing Speex codec",
725 status);
726 return status;
727 }
728
729 pjsua.codec_deinit[i] = &pjmedia_codec_speex_deinit;
730 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000731#endif /* PJMEDIA_HAS_SPEEX_CODEC */
732
733#if PJMEDIA_HAS_GSM_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000734 /* Is it gsm? */
735 else if (!pj_stricmp2(&pjsua.codec_arg[i], "gsm")) {
736
737 status = pjmedia_codec_gsm_init(pjsua.med_endpt);
738 if (status != PJ_SUCCESS) {
739 pjsua_perror(THIS_FILE, "Error initializing GSM codec",
740 status);
741 return status;
742 }
743
744 pjsua.codec_deinit[i] = &pjmedia_codec_gsm_deinit;
745
746 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000747#endif /* PJMEDIA_HAS_GSM_CODEC */
748
Benny Prijono15953012006-04-27 22:37:08 +0000749#if PJMEDIA_HAS_L16_CODEC
750 /* Is it l16? */
751 else if (!pj_stricmp2(&pjsua.codec_arg[i], "l16")) {
752
753 status = pjmedia_codec_l16_init(pjsua.med_endpt, 0);
754 if (status != PJ_SUCCESS) {
755 pjsua_perror(THIS_FILE, "Error initializing L16 codec",
756 status);
757 return status;
758 }
759
760 pjsua.codec_deinit[i] = &pjmedia_codec_l16_deinit;
761
762 pjsua.clock_rate = 44100;
763 }
764#endif /* PJMEDIA_HAS_L16_CODEC */
765
Benny Prijono4381efe2006-03-16 14:24:26 +0000766#if PJMEDIA_HAS_G711_CODEC
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000767 /* Is it pcma/pcmu? */
768 else if (!pj_stricmp2(&pjsua.codec_arg[i], "pcmu") ||
769 !pj_stricmp2(&pjsua.codec_arg[i], "pcma"))
770 {
771
772 status = pjmedia_codec_g711_init(pjsua.med_endpt);
773 if (status != PJ_SUCCESS) {
774 pjsua_perror(THIS_FILE, "Error initializing G711 codec",
775 status);
776 return status;
777 }
778
779 pjsua.codec_deinit[i] = &pjmedia_codec_g711_deinit;
780
781 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000782#endif /* PJMEDIA_HAS_G711_CODEC */
783
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000784 /* Don't know about this codec... */
785 else {
786
787 PJ_LOG(1,(THIS_FILE, "Error: unsupported codecs %s",
788 pjsua.codec_arg[i].ptr));
789 return PJMEDIA_CODEC_EUNSUP;
790 }
791 }
792 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000793
Benny Prijono8e3344c2006-03-08 12:37:22 +0000794 /* Init options for conference bridge. */
795 options = 0;
796 if (pjsua.no_mic)
797 options |= PJMEDIA_CONF_NO_MIC;
798
Benny Prijonof8baa872006-02-27 23:54:23 +0000799 /* Init conference bridge. */
800
801 status = pjmedia_conf_create(pjsua.pool,
802 pjsua.max_calls+PJSUA_CONF_MORE_PORTS,
Benny Prijono08e0d062006-03-04 14:52:44 +0000803 pjsua.clock_rate,
Benny Prijonod0659a32006-03-16 19:03:07 +0000804 1, /* mono */
Benny Prijono08e0d062006-03-04 14:52:44 +0000805 pjsua.clock_rate * 20 / 1000, 16,
Benny Prijono8e3344c2006-03-08 12:37:22 +0000806 options,
Benny Prijono08e0d062006-03-04 14:52:44 +0000807 &pjsua.mconf);
Benny Prijonof8baa872006-02-27 23:54:23 +0000808 if (status != PJ_SUCCESS) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000809 pjsua_perror(THIS_FILE,
810 "Media stack initialization has returned error",
811 status);
812 return status;
813 }
814
Benny Prijonode380582006-03-15 19:32:41 +0000815 /* Add NULL port to the bridge. */
816 status = pjmedia_null_port_create( pjsua.pool, pjsua.clock_rate,
Benny Prijonod0659a32006-03-16 19:03:07 +0000817 1, /* mono */
Benny Prijonode380582006-03-15 19:32:41 +0000818 pjsua.clock_rate * 20 / 1000, 16,
819 &pjsua.null_port);
820 pjmedia_conf_add_port( pjsua.mconf, pjsua.pool, pjsua.null_port,
821 &pjsua.null_port->info.name, NULL );
822
Benny Prijono39879152006-02-23 02:09:10 +0000823 /* Create WAV file player if required: */
824
825 if (pjsua.wav_file) {
Benny Prijono39879152006-02-23 02:09:10 +0000826 pj_str_t port_name;
827
828 /* Create the file player port. */
Benny Prijono15953012006-04-27 22:37:08 +0000829 status = pjmedia_wav_player_port_create( pjsua.pool, pjsua.wav_file,
830 0, 0, -1, NULL,
Benny Prijonode380582006-03-15 19:32:41 +0000831 &pjsua.file_port);
Benny Prijono39879152006-02-23 02:09:10 +0000832 if (status != PJ_SUCCESS) {
833 pjsua_perror(THIS_FILE,
834 "Error playing media file",
835 status);
836 return status;
837 }
838
839 /* Add port to conference bridge: */
Benny Prijonode380582006-03-15 19:32:41 +0000840 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
841 pjsua.file_port,
Benny Prijono39879152006-02-23 02:09:10 +0000842 pj_cstr(&port_name, pjsua.wav_file),
843 &pjsua.wav_slot);
844 if (status != PJ_SUCCESS) {
845 pjsua_perror(THIS_FILE,
846 "Unable to add file player to conference bridge",
847 status);
848 return status;
849 }
850 }
851
852
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000853 return PJ_SUCCESS;
854}
855
856
857/*
858 * Start pjsua stack.
859 * This will start the registration process, if registration is configured.
860 */
861pj_status_t pjsua_start(void)
862{
863 int i; /* Must be signed */
864 pjsip_transport *udp_transport;
865 pj_status_t status = PJ_SUCCESS;
866
867 /*
868 * Init media subsystem (codecs, conference bridge, et all).
869 */
870 status = init_media();
871 if (status != PJ_SUCCESS)
872 return status;
873
Benny Prijono268ca612006-02-07 12:34:11 +0000874 /* Init sockets (STUN etc): */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000875 for (i=0; i<(int)pjsua.max_calls; ++i) {
876 status = init_sockets(i==0, &pjsua.calls[i].skinfo);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000877 if (status != PJ_SUCCESS) {
878 pjsua_perror(THIS_FILE, "init_sockets() has returned error",
879 status);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000880 --i;
881 if (i >= 0)
882 pj_sock_close(pjsua.sip_sock);
883 while (i >= 0) {
884 pj_sock_close(pjsua.calls[i].skinfo.rtp_sock);
885 pj_sock_close(pjsua.calls[i].skinfo.rtcp_sock);
886 }
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000887 return status;
888 }
Benny Prijono268ca612006-02-07 12:34:11 +0000889 }
890
Benny Prijonoccf95622006-02-07 18:48:01 +0000891 /* Add UDP transport: */
Benny Prijono268ca612006-02-07 12:34:11 +0000892
Benny Prijonoccf95622006-02-07 18:48:01 +0000893 {
894 /* Init the published name for the transport.
895 * Depending whether STUN is used, this may be the STUN mapped
896 * address, or socket's bound address.
897 */
898 pjsip_host_port addr_name;
899
900 addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr);
Benny Prijonof3195072006-02-14 21:15:30 +0000901 addr_name.host.slen = pj_ansi_strlen(addr_name.host.ptr);
Benny Prijonoccf95622006-02-07 18:48:01 +0000902 addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port);
903
904 /* Create UDP transport from previously created UDP socket: */
905
906 status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
907 &addr_name, 1,
908 &udp_transport);
909 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000910 pjsua_perror(THIS_FILE, "Unable to start UDP transport",
911 status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000912 return status;
913 }
Benny Prijono268ca612006-02-07 12:34:11 +0000914 }
915
Benny Prijonoccf95622006-02-07 18:48:01 +0000916 /* Initialize Contact URI, if one is not specified: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000917 for (i=0; i<pjsua.acc_cnt; ++i) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000918
919 pjsip_uri *uri;
920 pjsip_sip_uri *sip_uri;
Benny Prijonoccf95622006-02-07 18:48:01 +0000921
Benny Prijonoccf95622006-02-07 18:48:01 +0000922 /* Need to parse local_uri to get the elements: */
923
Benny Prijonoa91a0032006-02-26 21:23:45 +0000924 uri = pjsip_parse_uri(pjsua.pool, pjsua.acc[i].local_uri.ptr,
925 pjsua.acc[i].local_uri.slen, 0);
Benny Prijonoccf95622006-02-07 18:48:01 +0000926 if (uri == NULL) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000927 pjsua_perror(THIS_FILE, "Invalid local URI",
928 PJSIP_EINVALIDURI);
Benny Prijonoccf95622006-02-07 18:48:01 +0000929 return PJSIP_EINVALIDURI;
930 }
931
Benny Prijonoccf95622006-02-07 18:48:01 +0000932 /* Local URI MUST be a SIP or SIPS: */
933
Benny Prijonoa91a0032006-02-26 21:23:45 +0000934 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
935 !PJSIP_URI_SCHEME_IS_SIPS(uri))
936 {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000937 pjsua_perror(THIS_FILE, "Invalid local URI",
938 PJSIP_EINVALIDSCHEME);
Benny Prijonoccf95622006-02-07 18:48:01 +0000939 return PJSIP_EINVALIDSCHEME;
940 }
941
942
943 /* Get the SIP URI object: */
944
945 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
946
Benny Prijonoa91a0032006-02-26 21:23:45 +0000947 pjsua.acc[i].user_part = sip_uri->user;
948 pjsua.acc[i].host_part = sip_uri->host;
Benny Prijonoccf95622006-02-07 18:48:01 +0000949
Benny Prijonoa91a0032006-02-26 21:23:45 +0000950 if (pjsua.acc[i].contact_uri.slen == 0 &&
951 pjsua.acc[i].local_uri.slen)
952 {
953 char contact[128];
954 int len;
Benny Prijonoccf95622006-02-07 18:48:01 +0000955
Benny Prijonoa91a0032006-02-26 21:23:45 +0000956 /* The local Contact is the username@ip-addr, where
957 * - username is taken from the local URI,
958 * - ip-addr in UDP transport's address name (which may have been
959 * resolved from STUN.
960 */
961
962 /* Build temporary contact string. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000963
Benny Prijonoa91a0032006-02-26 21:23:45 +0000964 if (sip_uri->user.slen) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000965
Benny Prijonoa91a0032006-02-26 21:23:45 +0000966 /* With the user part. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000967 len = pj_ansi_snprintf(contact, sizeof(contact),
Benny Prijonoa91a0032006-02-26 21:23:45 +0000968 "<sip:%.*s@%.*s:%d>",
969 (int)sip_uri->user.slen,
970 sip_uri->user.ptr,
971 (int)udp_transport->local_name.host.slen,
972 udp_transport->local_name.host.ptr,
973 udp_transport->local_name.port);
974 } else {
975
976 /* Without user part */
977
Benny Prijonoed811d72006-03-10 12:57:12 +0000978 len = pj_ansi_snprintf(contact, sizeof(contact),
Benny Prijonoa91a0032006-02-26 21:23:45 +0000979 "<sip:%.*s:%d>",
980 (int)udp_transport->local_name.host.slen,
981 udp_transport->local_name.host.ptr,
982 udp_transport->local_name.port);
983 }
984
985 if (len < 1 || len >= sizeof(contact)) {
986 pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
987 return PJSIP_EURITOOLONG;
988 }
989
990 /* Duplicate Contact uri. */
991
992 pj_strdup2(pjsua.pool, &pjsua.acc[i].contact_uri, contact);
993
Benny Prijonoccf95622006-02-07 18:48:01 +0000994 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000995 }
996
Benny Prijono95196582006-02-09 00:13:40 +0000997 /* If outbound_proxy is specified, put it in the route_set: */
Benny Prijonoccf95622006-02-07 18:48:01 +0000998
Benny Prijono95196582006-02-09 00:13:40 +0000999 if (pjsua.outbound_proxy.slen) {
1000
1001 pjsip_route_hdr *route;
1002 const pj_str_t hname = { "Route", 5 };
1003 int parsed_len;
1004
1005 route = pjsip_parse_hdr( pjsua.pool, &hname,
1006 pjsua.outbound_proxy.ptr,
1007 pjsua.outbound_proxy.slen,
1008 &parsed_len);
1009 if (route == NULL) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001010 pjsua_perror(THIS_FILE, "Invalid outbound proxy URL",
1011 PJSIP_EINVALIDURI);
Benny Prijono95196582006-02-09 00:13:40 +00001012 return PJSIP_EINVALIDURI;
1013 }
1014
Benny Prijonoa91a0032006-02-26 21:23:45 +00001015 for (i=0; i<pjsua.acc_cnt; ++i) {
1016 pj_list_push_front(&pjsua.acc[i].route_set, route);
1017 }
Benny Prijono95196582006-02-09 00:13:40 +00001018 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001019
1020
Benny Prijono268ca612006-02-07 12:34:11 +00001021 /* Create worker thread(s), if required: */
1022
1023 for (i=0; i<pjsua.thread_cnt; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001024 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
Benny Prijono268ca612006-02-07 12:34:11 +00001025 NULL, 0, 0, &pjsua.threads[i]);
1026 if (status != PJ_SUCCESS) {
1027 pjsua.quit_flag = 1;
1028 for (--i; i>=0; --i) {
1029 pj_thread_join(pjsua.threads[i]);
1030 pj_thread_destroy(pjsua.threads[i]);
1031 }
Benny Prijono268ca612006-02-07 12:34:11 +00001032 return status;
1033 }
1034 }
1035
Benny Prijonoccf95622006-02-07 18:48:01 +00001036 /* Start registration: */
1037
1038 /* Create client registration session: */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001039 for (i=0; i<pjsua.acc_cnt; ++i) {
1040 status = pjsua_regc_init(i);
1041 if (status != PJ_SUCCESS)
1042 return status;
Benny Prijonoccf95622006-02-07 18:48:01 +00001043
Benny Prijonoa91a0032006-02-26 21:23:45 +00001044 /* Perform registration, if required. */
1045 if (pjsua.acc[i].regc) {
1046 pjsua_regc_update(i, 1);
1047 }
1048 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001049
Benny Prijonoa91a0032006-02-26 21:23:45 +00001050
1051 /* Find account for outgoing preence subscription */
1052 for (i=0; i<pjsua.buddy_cnt; ++i) {
1053 pjsua.buddies[i].acc_index =
1054 pjsua_find_account_for_outgoing(&pjsua.buddies[i].uri);
Benny Prijonoccf95622006-02-07 18:48:01 +00001055 }
1056
1057
Benny Prijono834aee32006-02-19 01:38:06 +00001058 PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
Benny Prijono268ca612006-02-07 12:34:11 +00001059 return PJ_SUCCESS;
1060}
1061
1062
Benny Prijono834aee32006-02-19 01:38:06 +00001063/* Sleep with polling */
1064static void busy_sleep(unsigned msec)
1065{
1066 pj_time_val timeout, now;
1067
1068 pj_gettimeofday(&timeout);
1069 timeout.msec += msec;
1070 pj_time_val_normalize(&timeout);
1071
1072 do {
1073 pjsua_poll(NULL);
1074 pj_gettimeofday(&now);
1075 } while (PJ_TIME_VAL_LT(now, timeout));
1076}
1077
Benny Prijono268ca612006-02-07 12:34:11 +00001078/*
1079 * Destroy pjsua.
1080 */
1081pj_status_t pjsua_destroy(void)
1082{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001083 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001084
1085 /* Signal threads to quit: */
Benny Prijono268ca612006-02-07 12:34:11 +00001086 pjsua.quit_flag = 1;
1087
Benny Prijonoa91a0032006-02-26 21:23:45 +00001088 /* Terminate all calls. */
Benny Prijono1a174142006-03-01 20:46:13 +00001089 pjsua_call_hangup_all();
Benny Prijonoa91a0032006-02-26 21:23:45 +00001090
1091 /* Terminate all presence subscriptions. */
1092 pjsua_pres_shutdown();
1093
1094 /* Unregister, if required: */
1095 for (i=0; i<pjsua.acc_cnt; ++i) {
1096 if (pjsua.acc[i].regc) {
1097 pjsua_regc_update(i, 0);
1098 }
1099 }
1100
Benny Prijono268ca612006-02-07 12:34:11 +00001101 /* Wait worker threads to quit: */
Benny Prijono268ca612006-02-07 12:34:11 +00001102 for (i=0; i<pjsua.thread_cnt; ++i) {
1103
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001104 if (pjsua.threads[i]) {
1105 pj_thread_join(pjsua.threads[i]);
1106 pj_thread_destroy(pjsua.threads[i]);
1107 pjsua.threads[i] = NULL;
1108 }
Benny Prijono268ca612006-02-07 12:34:11 +00001109 }
1110
Benny Prijono834aee32006-02-19 01:38:06 +00001111
Benny Prijono834aee32006-02-19 01:38:06 +00001112 /* Wait for some time to allow unregistration to complete: */
1113 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1114 busy_sleep(1000);
1115
Benny Prijono26ff9062006-02-21 23:47:00 +00001116 /* Destroy conference bridge. */
1117 if (pjsua.mconf)
1118 pjmedia_conf_destroy(pjsua.mconf);
1119
Benny Prijonode380582006-03-15 19:32:41 +00001120 /* Destroy file port */
Benny Prijono4381efe2006-03-16 14:24:26 +00001121 if (pjsua.file_port)
1122 pjmedia_port_destroy(pjsua.file_port);
Benny Prijonode380582006-03-15 19:32:41 +00001123
1124 /* Destroy null port. */
Benny Prijono4381efe2006-03-16 14:24:26 +00001125 if (pjsua.null_port)
1126 pjmedia_port_destroy(pjsua.null_port);
Benny Prijonode380582006-03-15 19:32:41 +00001127
1128
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001129 /* Shutdown all codecs: */
1130 for (i = pjsua.codec_cnt-1; i >= 0; --i) {
1131 (*pjsua.codec_deinit[i])();
1132 }
1133
1134 /* Destroy media endpoint. */
1135
1136 pjmedia_endpt_destroy(pjsua.med_endpt);
1137
Benny Prijono268ca612006-02-07 12:34:11 +00001138 /* Destroy endpoint. */
Benny Prijono84126ab2006-02-09 09:30:09 +00001139
Benny Prijono268ca612006-02-07 12:34:11 +00001140 pjsip_endpt_destroy(pjsua.endpt);
1141 pjsua.endpt = NULL;
1142
1143 /* Destroy caching pool. */
Benny Prijono84126ab2006-02-09 09:30:09 +00001144
Benny Prijono268ca612006-02-07 12:34:11 +00001145 pj_caching_pool_destroy(&pjsua.cp);
1146
1147
1148 /* Done. */
1149
1150 return PJ_SUCCESS;
1151}
1152