Removing registrar_expire from basic-pbx config
[asterisk/asterisk.git] / res / res_pjproject.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Bridge PJPROJECT logging to Asterisk logging.
22  * \author David M. Lee, II <dlee@digium.com>
23  *
24  * PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is
25  * not too bad. PJPROJECT log levels are identified by a single int. Limits are
26  * not specified by PJPROJECT, but their implementation used 1 through 6.
27  *
28  * The mapping is as follows:
29  *  - 0: LOG_ERROR
30  *  - 1: LOG_ERROR
31  *  - 2: LOG_WARNING
32  *  - 3 and above: equivalent to ast_debug(level, ...) for res_pjproject.so
33  */
34
35 /*** MODULEINFO
36         <depend>pjproject</depend>
37         <support_level>core</support_level>
38  ***/
39
40 /*** DOCUMENTATION
41         <configInfo name="res_pjproject" language="en_US">
42                 <synopsis>pjproject common configuration</synopsis>
43                 <configFile name="pjproject.conf">
44                         <configObject name="startup">
45                                 <synopsis>Asterisk startup time options for PJPROJECT</synopsis>
46                                 <description>
47                                         <note><para>The id of this object, as well as its type, must be
48                                         'startup' or it won't be found.</para></note>
49                                 </description>
50                                 <configOption name="type">
51                                         <synopsis>Must be of type 'startup'.</synopsis>
52                                 </configOption>
53                                 <configOption name="log_level" default="2">
54                                         <synopsis>Initial maximum pjproject logging level to log.</synopsis>
55                                         <description>
56                                                 <para>Valid values are: 0-6, and default</para>
57                                         <note><para>
58                                                 This option is needed very early in the startup process
59                                                 so it can only be read from config files because the
60                                                 modules for other methods have not been loaded yet.
61                                         </para></note>
62                                         </description>
63                                 </configOption>
64                         </configObject>
65                         <configObject name="log_mappings">
66                                 <synopsis>PJPROJECT to Asterisk Log Level Mapping</synopsis>
67                                 <description><para>Warnings and errors in the pjproject libraries are generally handled
68                                         by Asterisk.  In many cases, Asterisk wouldn't even consider them to
69                                         be warnings or errors so the messages emitted by pjproject directly
70                                         are either superfluous or misleading.  The 'log_mappings'
71                                         object allows mapping the pjproject levels to Asterisk levels, or nothing.
72                                         </para>
73                                         <note><para>The id of this object, as well as its type, must be
74                                         'log_mappings' or it won't be found.</para></note>
75                                 </description>
76                                 <configOption name="type">
77                                         <synopsis>Must be of type 'log_mappings'.</synopsis>
78                                 </configOption>
79                                 <configOption name="asterisk_error" default="0,1">
80                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_ERROR.</synopsis>
81                                 </configOption>
82                                 <configOption name="asterisk_warning" default="2">
83                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_WARNING.</synopsis>
84                                 </configOption>
85                                 <configOption name="asterisk_notice" default="">
86                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_NOTICE.</synopsis>
87                                 </configOption>
88                                 <configOption name="asterisk_debug" default="3,4,5,6">
89                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_DEBUG.</synopsis>
90                                 </configOption>
91                                 <configOption name="asterisk_verbose" default="">
92                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_VERBOSE.</synopsis>
93                                 </configOption>
94                         </configObject>
95                 </configFile>
96         </configInfo>
97  ***/
98
99 #include "asterisk.h"
100
101 #include <stdarg.h>
102 #include <pjlib.h>
103 #include <pjsip.h>
104 #include <pj/log.h>
105
106 #include "asterisk/options.h"
107 #include "asterisk/logger.h"
108 #include "asterisk/module.h"
109 #include "asterisk/cli.h"
110 #include "asterisk/res_pjproject.h"
111 #include "asterisk/vector.h"
112 #include "asterisk/sorcery.h"
113 #include "asterisk/test.h"
114 #include "asterisk/netsock2.h"
115
116 static struct ast_sorcery *pjproject_sorcery;
117 static pj_log_func *log_cb_orig;
118 static unsigned decor_orig;
119
120 static AST_VECTOR(buildopts, char *) buildopts;
121
122 /*! Protection from other log intercept instances.  There can be only one at a time. */
123 AST_MUTEX_DEFINE_STATIC(pjproject_log_intercept_lock);
124
125 struct pjproject_log_intercept_data {
126         pthread_t thread;
127         int fd;
128 };
129
130 static struct pjproject_log_intercept_data pjproject_log_intercept = {
131         .thread = AST_PTHREADT_NULL,
132         .fd = -1,
133 };
134
135 struct log_mappings {
136         /*! Sorcery object details */
137         SORCERY_OBJECT(details);
138         /*! These are all comma-separated lists of pjproject log levels */
139         AST_DECLARE_STRING_FIELDS(
140                 /*! pjproject log levels mapped to Asterisk ERROR */
141                 AST_STRING_FIELD(asterisk_error);
142                 /*! pjproject log levels mapped to Asterisk WARNING */
143                 AST_STRING_FIELD(asterisk_warning);
144                 /*! pjproject log levels mapped to Asterisk NOTICE */
145                 AST_STRING_FIELD(asterisk_notice);
146                 /*! pjproject log levels mapped to Asterisk VERBOSE */
147                 AST_STRING_FIELD(asterisk_verbose);
148                 /*! pjproject log levels mapped to Asterisk DEBUG */
149                 AST_STRING_FIELD(asterisk_debug);
150         );
151 };
152
153 static struct log_mappings *default_log_mappings;
154
155 static struct log_mappings *get_log_mappings(void)
156 {
157         struct log_mappings *mappings;
158
159         mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
160         if (!mappings) {
161                 return ao2_bump(default_log_mappings);
162         }
163
164         return mappings;
165 }
166
167 #define __LOG_SUPPRESS -1
168
169 static int get_log_level(int pj_level)
170 {
171         int mapped_level;
172         unsigned char l;
173         struct log_mappings *mappings;
174
175         mappings = get_log_mappings();
176         if (!mappings) {
177                 return __LOG_ERROR;
178         }
179
180         l = '0' + fmin(pj_level, 9);
181
182         if (strchr(mappings->asterisk_error, l)) {
183                 mapped_level = __LOG_ERROR;
184         } else if (strchr(mappings->asterisk_warning, l)) {
185                 mapped_level = __LOG_WARNING;
186         } else if (strchr(mappings->asterisk_notice, l)) {
187                 mapped_level = __LOG_NOTICE;
188         } else if (strchr(mappings->asterisk_verbose, l)) {
189                 mapped_level = __LOG_VERBOSE;
190         } else if (strchr(mappings->asterisk_debug, l)) {
191                 mapped_level = __LOG_DEBUG;
192         } else {
193                 mapped_level = __LOG_SUPPRESS;
194         }
195
196         ao2_ref(mappings, -1);
197         return mapped_level;
198 }
199
200 static void log_forwarder(int level, const char *data, int len)
201 {
202         int ast_level;
203         /* PJPROJECT doesn't provide much in the way of source info */
204         const char * log_source = "pjproject";
205         int log_line = 0;
206         const char *log_func = "<?>";
207
208         if (pjproject_log_intercept.fd != -1
209                 && pjproject_log_intercept.thread == pthread_self()) {
210                 /*
211                  * We are handling a CLI command intercepting PJPROJECT
212                  * log output.
213                  */
214                 ast_cli(pjproject_log_intercept.fd, "%s\n", data);
215                 return;
216         }
217
218         ast_level = get_log_level(level);
219
220         if (ast_level == __LOG_SUPPRESS) {
221                 return;
222         }
223
224         /* PJPROJECT uses indention to indicate function call depth. We'll prepend
225          * log statements with a tab so they'll have a better shot at lining
226          * up */
227         ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
228 }
229
230 static void capture_buildopts_cb(int level, const char *data, int len)
231 {
232         char *dup;
233
234         if (strstr(data, "Teluu") || strstr(data, "Dumping")) {
235                 return;
236         }
237
238         dup = ast_strdup(ast_skip_blanks(data));
239         if (dup && AST_VECTOR_ADD_SORTED(&buildopts, dup, strcmp)) {
240                 ast_free(dup);
241         }
242 }
243
244 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
245 int ast_pjproject_get_buildopt(char *option, char *format_string, ...)
246 {
247         int res = 0;
248         char *format_temp;
249         int i;
250
251         format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1);
252         sprintf(format_temp, "%s : %s", option, format_string);
253
254         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
255                 va_list arg_ptr;
256                 va_start(arg_ptr, format_string);
257                 res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr);
258                 va_end(arg_ptr);
259                 if (res) {
260                         break;
261                 }
262         }
263
264         return res;
265 }
266 #pragma GCC diagnostic warning "-Wformat-nonliteral"
267
268 void ast_pjproject_log_intercept_begin(int fd)
269 {
270         /* Protect from other CLI instances trying to do this at the same time. */
271         ast_mutex_lock(&pjproject_log_intercept_lock);
272
273         pjproject_log_intercept.thread = pthread_self();
274         pjproject_log_intercept.fd = fd;
275 }
276
277 void ast_pjproject_log_intercept_end(void)
278 {
279         pjproject_log_intercept.fd = -1;
280         pjproject_log_intercept.thread = AST_PTHREADT_NULL;
281
282         ast_mutex_unlock(&pjproject_log_intercept_lock);
283 }
284
285 static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
286 {
287         int i;
288
289         switch (cmd) {
290         case CLI_INIT:
291                 e->command = "pjproject show buildopts";
292                 e->usage =
293                         "Usage: pjproject show buildopts\n"
294                         "       Show the compile time config of the pjproject that Asterisk is\n"
295                         "       running against.\n";
296                 return NULL;
297         case CLI_GENERATE:
298                 return NULL;
299         }
300
301         ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
302
303         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
304                 ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i));
305         }
306
307         return CLI_SUCCESS;
308 }
309
310 static void mapping_destroy(void *object)
311 {
312         struct log_mappings *mappings = object;
313
314         ast_string_field_free_memory(mappings);
315 }
316
317 static void *mapping_alloc(const char *name)
318 {
319         struct log_mappings *mappings = ast_sorcery_generic_alloc(sizeof(*mappings), mapping_destroy);
320         if (!mappings) {
321                 return NULL;
322         }
323         ast_string_field_init(mappings, 128);
324
325         return mappings;
326 }
327
328 static char *handle_pjproject_show_log_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
329 {
330         struct ast_variable *objset;
331         struct ast_variable *i;
332         struct log_mappings *mappings;
333
334         switch (cmd) {
335         case CLI_INIT:
336                 e->command = "pjproject show log mappings";
337                 e->usage =
338                         "Usage: pjproject show log mappings\n"
339                         "       Show pjproject to Asterisk log mappings\n";
340                 return NULL;
341         case CLI_GENERATE:
342                 return NULL;
343         }
344
345         ast_cli(a->fd, "PJPROJECT to Asterisk log mappings:\n");
346         ast_cli(a->fd, "Asterisk Level   : PJPROJECT log levels\n");
347
348         mappings = get_log_mappings();
349         if (!mappings) {
350                 ast_log(LOG_ERROR, "Unable to retrieve pjproject log_mappings\n");
351                 return CLI_SUCCESS;
352         }
353
354         objset = ast_sorcery_objectset_create(pjproject_sorcery, mappings);
355         if (!objset) {
356                 ao2_ref(mappings, -1);
357                 return CLI_SUCCESS;
358         }
359
360         for (i = objset; i; i = i->next) {
361                 ast_cli(a->fd, "%-16s : %s\n", i->name, i->value);
362         }
363         ast_variables_destroy(objset);
364
365         ao2_ref(mappings, -1);
366         return CLI_SUCCESS;
367 }
368
369 struct max_pjproject_log_level_check {
370         /*!
371          * Compile time sanity check to determine if
372          * MAX_PJ_LOG_MAX_LEVEL matches CLI syntax.
373          */
374         char check[1 / (6 == MAX_PJ_LOG_MAX_LEVEL)];
375 };
376
377 static char *handle_pjproject_set_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
378 {
379         int level_new;
380         int level_old;
381
382         switch (cmd) {
383         case CLI_INIT:
384                 e->command = "pjproject set log level {default|0|1|2|3|4|5|6}";
385                 e->usage =
386                         "Usage: pjproject set log level {default|<level>}\n"
387                         "\n"
388                         "       Set the maximum active pjproject logging level.\n"
389                         "       See pjproject.conf.sample for additional information\n"
390                         "       about the various levels pjproject uses.\n";
391                 return NULL;
392         case CLI_GENERATE:
393                 return NULL;
394         }
395
396         if (a->argc != 5) {
397                 return CLI_SHOWUSAGE;
398         }
399
400         if (!strcasecmp(a->argv[4], "default")) {
401                 level_new = DEFAULT_PJ_LOG_MAX_LEVEL;
402         } else {
403                 if (sscanf(a->argv[4], "%30d", &level_new) != 1
404                         || level_new < 0 || MAX_PJ_LOG_MAX_LEVEL < level_new) {
405                         return CLI_SHOWUSAGE;
406                 }
407         }
408
409         /* Update pjproject logging level */
410         if (ast_pjproject_max_log_level < level_new) {
411                 level_new = ast_pjproject_max_log_level;
412                 ast_cli(a->fd,
413                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d.\n"
414                         "Lowering request to the max supported level.\n",
415                         ast_pjproject_max_log_level);
416         }
417         level_old = ast_option_pjproject_log_level;
418         if (level_old == level_new) {
419                 ast_cli(a->fd, "pjproject log level is still %d.\n", level_old);
420         } else {
421                 ast_cli(a->fd, "pjproject log level was %d and is now %d.\n",
422                         level_old, level_new);
423                 ast_option_pjproject_log_level = level_new;
424                 pj_log_set_level(level_new);
425         }
426
427         return CLI_SUCCESS;
428 }
429
430 static char *handle_pjproject_show_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
431 {
432         switch (cmd) {
433         case CLI_INIT:
434                 e->command = "pjproject show log level";
435                 e->usage =
436                         "Usage: pjproject show log level\n"
437                         "\n"
438                         "       Show the current maximum active pjproject logging level.\n"
439                         "       See pjproject.conf.sample for additional information\n"
440                         "       about the various levels pjproject uses.\n";
441                 return NULL;
442         case CLI_GENERATE:
443                 return NULL;
444         }
445
446         if (a->argc != 4) {
447                 return CLI_SHOWUSAGE;
448         }
449
450         ast_cli(a->fd, "pjproject log level is %d.%s\n",
451                 ast_option_pjproject_log_level,
452                 ast_option_pjproject_log_level == DEFAULT_PJ_LOG_MAX_LEVEL ? " (default)" : "");
453
454         return CLI_SUCCESS;
455 }
456
457 static struct ast_cli_entry pjproject_cli[] = {
458         AST_CLI_DEFINE(handle_pjproject_set_log_level, "Set the maximum active pjproject logging level"),
459         AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
460         AST_CLI_DEFINE(handle_pjproject_show_log_mappings, "Show pjproject to Asterisk log mappings"),
461         AST_CLI_DEFINE(handle_pjproject_show_log_level, "Show the maximum active pjproject logging level"),
462 };
463
464 void ast_pjproject_caching_pool_init(pj_caching_pool *cp,
465         const pj_pool_factory_policy *policy, pj_size_t max_capacity)
466 {
467         /* Passing a max_capacity of zero disables caching pools */
468         pj_caching_pool_init(cp, policy, ast_option_pjproject_cache_pools ? max_capacity : 0);
469 }
470
471 void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
472 {
473         pj_caching_pool_destroy(cp);
474 }
475
476 int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
477 {
478         if (addr->ss.ss_family == AF_INET) {
479                 struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
480                 pjaddr->ipv4.sin_family = pj_AF_INET();
481 #ifdef HAVE_PJPROJECT_BUNDLED
482                 pjaddr->ipv4.sin_addr = sin->sin_addr;
483 #else
484                 pjaddr->ipv4.sin_addr.s_addr = sin->sin_addr.s_addr;
485 #endif
486                 pjaddr->ipv4.sin_port   = sin->sin_port;
487         } else if (addr->ss.ss_family == AF_INET6) {
488                 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
489                 pjaddr->ipv6.sin6_family   = pj_AF_INET6();
490                 pjaddr->ipv6.sin6_port     = sin->sin6_port;
491                 pjaddr->ipv6.sin6_flowinfo = sin->sin6_flowinfo;
492                 pjaddr->ipv6.sin6_scope_id = sin->sin6_scope_id;
493                 memcpy(&pjaddr->ipv6.sin6_addr, &sin->sin6_addr, sizeof(pjaddr->ipv6.sin6_addr));
494         } else {
495                 memset(pjaddr, 0, sizeof(*pjaddr));
496                 return -1;
497         }
498         return 0;
499 }
500
501 int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
502 {
503         if (pjaddr->addr.sa_family == pj_AF_INET()) {
504                 struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
505                 sin->sin_family = AF_INET;
506 #ifdef HAVE_PJPROJECT_BUNDLED
507                 sin->sin_addr = pjaddr->ipv4.sin_addr;
508 #else
509                 sin->sin_addr.s_addr = pjaddr->ipv4.sin_addr.s_addr;
510 #endif
511                 sin->sin_port   = pjaddr->ipv4.sin_port;
512                 addr->len = sizeof(struct sockaddr_in);
513         } else if (pjaddr->addr.sa_family == pj_AF_INET6()) {
514                 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
515                 sin->sin6_family   = AF_INET6;
516                 sin->sin6_port     = pjaddr->ipv6.sin6_port;
517                 sin->sin6_flowinfo = pjaddr->ipv6.sin6_flowinfo;
518                 sin->sin6_scope_id = pjaddr->ipv6.sin6_scope_id;
519                 memcpy(&sin->sin6_addr, &pjaddr->ipv6.sin6_addr, sizeof(sin->sin6_addr));
520                 addr->len = sizeof(struct sockaddr_in6);
521         } else {
522                 memset(addr, 0, sizeof(*addr));
523                 return -1;
524         }
525         return 0;
526 }
527
528 #ifdef TEST_FRAMEWORK
529 static void fill_with_garbage(void *x, ssize_t len)
530 {
531         unsigned char *w = x;
532         while (len > 0) {
533                 int r = ast_random();
534                 memcpy(w, &r, len > sizeof(r) ? sizeof(r) : len);
535                 w += sizeof(r);
536                 len -= sizeof(r);
537         }
538 }
539
540 AST_TEST_DEFINE(ast_sockaddr_to_pj_sockaddr_test)
541 {
542         char *candidates[] = {
543                 "127.0.0.1:5555",
544                 "[::]:4444",
545                 "192.168.0.100:0",
546                 "[fec0::1:80]:0",
547                 "[fec0::1]:80",
548                 NULL,
549         }, **candidate = candidates;
550
551         switch (cmd) {
552         case TEST_INIT:
553                 info->name = "ast_sockaddr_to_pj_sockaddr_test";
554                 info->category = "/res/res_pjproject/";
555                 info->summary = "Validate conversions from an ast_sockaddr to a pj_sockaddr";
556                 info->description = "This test converts an ast_sockaddr to a pj_sockaddr and validates\n"
557                         "that the two evaluate to the same string when formatted.";
558                 return AST_TEST_NOT_RUN;
559         case TEST_EXECUTE:
560                 break;
561         }
562
563         while (*candidate) {
564                 struct ast_sockaddr addr = {{0,}};
565                 pj_sockaddr pjaddr;
566                 char buffer[512];
567
568                 fill_with_garbage(&pjaddr, sizeof(pj_sockaddr));
569
570                 if (!ast_sockaddr_parse(&addr, *candidate, 0)) {
571                         ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
572                         return AST_TEST_FAIL;
573                 }
574
575                 if (ast_sockaddr_to_pj_sockaddr(&addr, &pjaddr)) {
576                         ast_test_status_update(test, "Failed to convert ast_sockaddr to pj_sockaddr: %s\n", *candidate);
577                         return AST_TEST_FAIL;
578                 }
579
580                 pj_sockaddr_print(&pjaddr, buffer, sizeof(buffer), 1 | 2);
581
582                 if (strcmp(*candidate, buffer)) {
583                         ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
584                                 *candidate,
585                                 buffer);
586                         return AST_TEST_FAIL;
587                 }
588
589                 candidate++;
590         }
591
592         return AST_TEST_PASS;
593 }
594
595 AST_TEST_DEFINE(ast_sockaddr_from_pj_sockaddr_test)
596 {
597         char *candidates[] = {
598                 "127.0.0.1:5555",
599                 "[::]:4444",
600                 "192.168.0.100:0",
601                 "[fec0::1:80]:0",
602                 "[fec0::1]:80",
603                 NULL,
604         }, **candidate = candidates;
605
606         switch (cmd) {
607         case TEST_INIT:
608                 info->name = "ast_sockaddr_from_pj_sockaddr_test";
609                 info->category = "/res/res_pjproject/";
610                 info->summary = "Validate conversions from a pj_sockaddr to an ast_sockaddr";
611                 info->description = "This test converts a pj_sockaddr to an ast_sockaddr and validates\n"
612                         "that the two evaluate to the same string when formatted.";
613                 return AST_TEST_NOT_RUN;
614         case TEST_EXECUTE:
615                 break;
616         }
617
618         while (*candidate) {
619                 struct ast_sockaddr addr = {{0,}};
620                 pj_sockaddr pjaddr;
621                 pj_str_t t;
622                 char buffer[512];
623
624                 fill_with_garbage(&addr, sizeof(addr));
625
626                 pj_strset(&t, *candidate, strlen(*candidate));
627
628                 if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &t, &pjaddr) != PJ_SUCCESS) {
629                         ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
630                         return AST_TEST_FAIL;
631                 }
632
633                 if (ast_sockaddr_from_pj_sockaddr(&addr, &pjaddr)) {
634                         ast_test_status_update(test, "Failed to convert pj_sockaddr to ast_sockaddr: %s\n", *candidate);
635                         return AST_TEST_FAIL;
636                 }
637
638                 snprintf(buffer, sizeof(buffer), "%s", ast_sockaddr_stringify(&addr));
639
640                 if (strcmp(*candidate, buffer)) {
641                         ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
642                                 *candidate,
643                                 buffer);
644                         return AST_TEST_FAIL;
645                 }
646
647                 candidate++;
648         }
649
650         return AST_TEST_PASS;
651 }
652 #endif
653
654 static int load_module(void)
655 {
656         ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
657
658         if (!(pjproject_sorcery = ast_sorcery_open())) {
659                 ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
660                 return AST_MODULE_LOAD_DECLINE;
661         }
662
663         ast_sorcery_apply_default(pjproject_sorcery, "log_mappings", "config", "pjproject.conf,criteria=type=log_mappings");
664         if (ast_sorcery_object_register(pjproject_sorcery, "log_mappings", mapping_alloc, NULL, NULL)) {
665                 ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
666                 ast_sorcery_unref(pjproject_sorcery);
667                 pjproject_sorcery = NULL;
668                 return AST_MODULE_LOAD_DECLINE;
669         }
670
671         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
672         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_debug", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_debug));
673         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_error", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_error));
674         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_warning", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_warning));
675         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_notice", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_notice));
676         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_verbose", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_verbose));
677
678         default_log_mappings = ast_sorcery_alloc(pjproject_sorcery, "log_mappings", "log_mappings");
679         if (!default_log_mappings) {
680                 ast_log(LOG_ERROR, "Unable to allocate memory for pjproject log_mappings\n");
681                 return AST_MODULE_LOAD_DECLINE;
682         }
683         ast_string_field_set(default_log_mappings, asterisk_error, "0,1");
684         ast_string_field_set(default_log_mappings, asterisk_warning, "2");
685         ast_string_field_set(default_log_mappings, asterisk_debug, "3,4,5,6");
686
687         ast_sorcery_load(pjproject_sorcery);
688
689         AST_PJPROJECT_INIT_LOG_LEVEL();
690         pj_init();
691
692         decor_orig = pj_log_get_decor();
693         log_cb_orig = pj_log_get_log_func();
694
695         if (AST_VECTOR_INIT(&buildopts, 64)) {
696                 return AST_MODULE_LOAD_DECLINE;
697         }
698
699         /*
700          * On startup, we want to capture the dump once and store it.
701          */
702         pj_log_set_log_func(capture_buildopts_cb);
703         pj_log_set_decor(0);
704         pj_log_set_level(MAX_PJ_LOG_MAX_LEVEL);/* Set level to guarantee the dump output. */
705         pj_dump_config();
706         pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
707         pj_log_set_log_func(log_forwarder);
708         if (ast_pjproject_max_log_level < ast_option_pjproject_log_level) {
709                 ast_log(LOG_WARNING,
710                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low for startup level: %d.\n",
711                         ast_pjproject_max_log_level, ast_option_pjproject_log_level);
712                 ast_option_pjproject_log_level = ast_pjproject_max_log_level;
713         }
714         pj_log_set_level(ast_option_pjproject_log_level);
715         if (!AST_VECTOR_SIZE(&buildopts)) {
716                 ast_log(LOG_NOTICE,
717                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low to get buildopts.\n",
718                         ast_pjproject_max_log_level);
719         }
720
721         ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
722
723         AST_TEST_REGISTER(ast_sockaddr_to_pj_sockaddr_test);
724         AST_TEST_REGISTER(ast_sockaddr_from_pj_sockaddr_test);
725
726         return AST_MODULE_LOAD_SUCCESS;
727 }
728
729 #define NOT_EQUALS(a, b) (a != b)
730
731 static int unload_module(void)
732 {
733         ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
734         pj_log_set_log_func(log_cb_orig);
735         pj_log_set_decor(decor_orig);
736
737         AST_VECTOR_CALLBACK_VOID(&buildopts, ast_free);
738         AST_VECTOR_FREE(&buildopts);
739
740         ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
741
742         pj_shutdown();
743
744         ao2_cleanup(default_log_mappings);
745         default_log_mappings = NULL;
746
747         ast_sorcery_unref(pjproject_sorcery);
748
749         AST_TEST_UNREGISTER(ast_sockaddr_to_pj_sockaddr_test);
750         AST_TEST_UNREGISTER(ast_sockaddr_from_pj_sockaddr_test);
751
752         return 0;
753 }
754
755 static int reload_module(void)
756 {
757         if (pjproject_sorcery) {
758                 ast_sorcery_reload(pjproject_sorcery);
759         }
760
761         return AST_MODULE_LOAD_SUCCESS;
762 }
763
764 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
765         .support_level = AST_MODULE_SUPPORT_CORE,
766         .load = load_module,
767         .unload = unload_module,
768         .reload = reload_module,
769         .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
770 );