Asterisk - The Open Source Telephony Project GIT-master-80b953f
Loading...
Searching...
No Matches
res_pjsip_config_wizard.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2014, Fairview 5 Engineering, LLC
5 *
6 * George Joseph <george.joseph@fairview5.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 PJSIP Configuration Wizard
22 *
23 * \author George Joseph <george.joseph@fairview5.com>
24 */
25
26/*! \li \ref res_pjsip_config_wizard.c uses the configuration file \ref pjsip_wizard.conf
27 */
28
29/*!
30 * \page pjsip_wizard.conf pjsip_wizard.conf
31 * \verbinclude pjsip_wizard.conf.sample
32 */
33
34/*** MODULEINFO
35 <depend>pjproject</depend>
36 <depend>res_pjsip</depend>
37 <support_level>core</support_level>
38 ***/
39
40#include "asterisk.h"
41
42#include <regex.h>
43#include <pjsip.h>
44
45#include "asterisk/astobj2.h"
46#include "asterisk/cli.h"
47#include "asterisk/res_pjsip.h"
48#include "asterisk/module.h"
49#include "asterisk/pbx.h"
50#include "asterisk/sorcery.h"
51#include "asterisk/vector.h"
52#include "asterisk/stasis.h"
53#include "asterisk/acl.h"
55
56/*** DOCUMENTATION
57 <configInfo name="res_pjsip_config_wizard" language="en_US">
58 <synopsis>Module that provides simple configuration wizard capabilities.</synopsis>
59 <description><para>
60 <emphasis>PJSIP Configuration Wizard</emphasis>
61 </para>
62 <para>This module allows creation of common PJSIP configuration scenarios
63 without having to specify individual endpoint, aor, auth, identify and registration objects.
64 </para>
65 <para> </para>
66
67 <para>For example, the following configuration snippet would create the
68 endpoint, aor, contact, auth and phoneprov objects necessary for a phone to
69 get phone provisioning information, register, and make and receive calls.
70 A hint is also created in the default context for extension 1000.</para>
71 <example title="myphone">
72 [myphone]
73 type = wizard
74 sends_auth = no
75 accepts_auth = yes
76 sends_registrations = no
77 accepts_registrations = yes
78 has_phoneprov = yes
79 transport = ipv4
80 has_hint = yes
81 hint_exten = 1000
82 inbound_auth/username = testname
83 inbound_auth/password = test password
84 endpoint/allow = ulaw
85 endpoint/context = default
86 phoneprov/MAC = 001122aa4455
87 phoneprov/PROFILE = profile1
88 </example>
89
90 <para>The first 8 items are specific to the wizard. The rest of the items
91 are passed verbatim to the underlying objects.</para>
92 <para> </para>
93
94 <para>The following configuration snippet would create the
95 endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
96 to another pbx or ITSP that requires registration.</para>
97 <example title="mytrunk">
98 [mytrunk]
99 type = wizard
100 sends_auth = yes
101 accepts_auth = no
102 sends_registrations = yes
103 accepts_registrations = no
104 transport = ipv4
105 remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060
106 outbound_auth/username = testname
107 outbound_auth/password = test password
108 endpoint/allow = ulaw
109 endpoint/context = default
110 </example>
111
112 <para>Of course, any of the items in either example could be placed into
113 templates and shared among wizard objects.</para>
114
115 <para> </para>
116 <para>For more information, visit:</para>
117 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
118 </description>
119
120 <configFile name="pjsip_wizard.conf">
121 <configObject name="wizard">
122 <synopsis>Provides config wizard.</synopsis>
123 <description>
124 <para>For more information, visit:</para>
125 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
126 </description>
127 <configOption name="type">
128 <synopsis>Must be 'wizard'.</synopsis>
129 </configOption>
130 <configOption name="transport">
131 <synopsis>The name of a transport to use for this object.</synopsis>
132 <description><para>If not specified,
133 the default will be used.</para></description>
134 </configOption>
135 <configOption name="remote_hosts">
136 <synopsis>List of remote hosts.</synopsis>
137 <description><para>A comma-separated list of remote hosts in the form of
138 <replaceable>host</replaceable>[:<replaceable>port</replaceable>].
139 If set, an aor static contact and an identify match will be created for each
140 entry in the list. If send_registrations is also set, a registration will
141 also be created for each.</para></description>
142 </configOption>
143 <configOption name="outbound_proxy">
144 <synopsis>Shortcut for specifying proxy on individual objects.</synopsis>
145 <description><para>Shortcut for specifying endpoint/outbound_proxy,
146 aor/outbound_proxy, and registration/outbound_proxy individually.
147 </para></description>
148 </configOption>
149 <configOption name="sends_auth" default="no">
150 <synopsis>Send outbound authentication to remote hosts.</synopsis>
151 <description><para>At least outbound_auth/username is required.</para></description>
152 </configOption>
153 <configOption name="accepts_auth" default="no">
154 <synopsis>Accept incoming authentication from remote hosts.</synopsis>
155 <description><para>At least inbound_auth/username is required.</para></description>
156 </configOption>
157 <configOption name="sends_registrations" default="no">
158 <synopsis>Send outbound registrations to remote hosts.</synopsis>
159 <description><para>remote_hosts is required and a registration object will
160 be created for each host in the remote _hosts string. If authentication is required,
161 sends_auth and an outbound_auth/username must also be supplied.</para></description>
162 </configOption>
163 <configOption name="sends_line_with_registrations" default="no">
164 <synopsis>Sets "line" and "endpoint parameters on registrations.</synopsis>
165 <description><para>Setting this to true will cause the wizard to skip the
166 creation of an identify object to match incoming requests to the endpoint and
167 instead add the line and endpoint parameters to the outbound registration object.
168 </para></description>
169 </configOption>
170 <configOption name="accepts_registrations" default="no">
171 <synopsis>Accept inbound registration from remote hosts.</synopsis>
172 <description><para>An AOR with dynamic contacts will be created. If
173 the number of contacts nneds to be limited, set aor/max_contacts.</para></description>
174 </configOption>
175 <configOption name="has_phoneprov" default="no">
176 <synopsis>Create a phoneprov object for this endpoint.</synopsis>
177 <description><para>A phoneprov object will be created. phoneprov/MAC
178 must be specified.</para></description>
179 </configOption>
180 <configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
181 <synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
182 <description><para>
183 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
184 appropriate remote_host for each registration.</para></description>
185 </configOption>
186 <configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
187 <synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
188 <description><para>
189 The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
190 will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
191 </configOption>
192 <configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
193 <synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
194 <description><para>
195 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
196 appropriate remote_host for each contact.</para></description>
197 </configOption>
198 <configOption name="has_hint" default="no">
199 <synopsis>Create hint and optionally a default application.</synopsis>
200 <description><para>Create hint and optionally a default application.</para></description>
201 </configOption>
202 <configOption name="hint_context" default="endpoint/context or 'default'">
203 <synopsis>The context in which to place hints.</synopsis>
204 <description>
205 <para>Ignored if <literal>hint_exten</literal> is not specified otherwise specifies the
206 context into which the dialplan hints will be placed. If not specified,
207 defaults to the endpoint's context or <literal>default</literal> if that isn't
208 found.
209 </para></description>
210 </configOption>
211 <configOption name="hint_exten">
212 <synopsis>Extension to map a PJSIP hint to.</synopsis>
213 <description>
214 <para>Will create the following entry in <literal>hint_context</literal>:</para>
215 <para> <literal>exten =&gt; &lt;hint_exten&gt;,hint,PJSIP/&lt;wizard_id&gt;</literal></para>
216 <para> </para>
217 <para>Normal dialplan precedence rules apply so if there's already a hint for
218 this extension in <literal>hint_context</literal>, this one will be ignored.
219 For more information, visit: </para>
220 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
221 </description>
222 </configOption>
223 <configOption name="hint_application">
224 <synopsis>Application to call when 'hint_exten' is dialed.</synopsis>
225 <description>
226 <para>Ignored if <literal>hint_exten</literal> isn't specified otherwise
227 will create the following priority 1 extension in <literal>hint_context</literal>:</para>
228 <para> <literal>exten =&gt; &lt;hint_exten&gt;,1,&lt;hint_application&gt;</literal></para>
229 <para> </para>
230 <para>You can specify any valid extensions.conf application expression.</para>
231 <example title="Valid expressions">
232 Dial(${HINT})
233 Gosub(stdexten,${EXTEN},1(${HINT}))
234 </example>
235 <para>Any extensions.conf style variables specified are passed directly to the
236 dialplan.</para>
237 <para> </para>
238 <para>Normal dialplan precedence rules apply so if there's already a priority 1
239 application for this specific extension in <literal>hint_context</literal>,
240 this one will be ignored. For more information, visit: </para>
241 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
242 </description>
243 </configOption>
244 <configOption name="endpoint&#47;*">
245 <synopsis>Variables to be passed directly to the endpoint.</synopsis>
246 </configOption>
247 <configOption name="aor&#47;*">
248 <synopsis>Variables to be passed directly to the aor.</synopsis>
249 <description><para>If an aor/contact is explicitly defined then remote_hosts
250 will not be used to create contacts automatically.</para></description>
251 </configOption>
252 <configOption name="inbound_auth&#47;*">
253 <synopsis>Variables to be passed directly to the inbound auth.</synopsis>
254 </configOption>
255 <configOption name="outbound_auth&#47;*">
256 <synopsis>Variables to be passed directly to the outbound auth.</synopsis>
257 </configOption>
258 <configOption name="identify&#47;*">
259 <synopsis>Variables to be passed directly to the identify.</synopsis>
260 <description><para>If an identify/match is explicitly defined then remote_hosts
261 will not be used to create matches automatically.</para></description>
262 </configOption>
263 <configOption name="registration&#47;*">
264 <synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
265 </configOption>
266 <configOption name="phoneprov&#47;*">
267 <synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
268 <description><para>
269 To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
270 </configOption>
271 </configObject>
272 </configFile>
273 </configInfo>
274 ***/
275
276 /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
277#define MAX_ID_SUFFIX 20
278
279#define BASE_REGISTRAR "res_pjsip_config_wizard"
280
281/*! \brief A generic char * vector definition. */
283
284/*! \brief Keeps track of the sorcery wizard and last config for each object type */
292static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_type_wizards;
293
294/*! \brief Callbacks for vector deletes */
295#define NOT_EQUALS(a, b) (a != b)
296#define OTW_DELETE_CB(otw) ({ \
297 ast_config_destroy(otw->last_config); \
298 ast_free(otw); \
299})
300
301const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
302
303static int acl_change_detected = 0;
305
306/*! \brief Callback for Named ACL changed */
308{
310 return;
311 }
312
313 ast_debug(3, "PJSIP Wizard: Named ACL change detected via Stasis. Triggering reload.\n");
317}
318
319static int is_one_of(const char *needle, const char *haystack[])
320{
321 int i;
322 for (i = 0; haystack[i]; i++) {
323 if (!strcmp(needle, haystack[i])) {
324 return 1;
325 }
326 }
327
328 return 0;
329}
330
331/*! \brief Finds the otw for the object type */
332static struct object_type_wizard *find_wizard(const char *object_type)
333{
334 int idx;
335
336 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
337 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
338 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
339 if (!strcmp(otw->object_type, object_type)) {
340 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
341 return otw;
342 }
343 }
344 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
345
346 return NULL;
347}
348
349/*! \brief Creates a sorcery object and applies a variable list */
350static void *create_object(const struct ast_sorcery *sorcery,
351 const char *id, const char *type, struct ast_variable *vars)
352{
354
355 if (!obj) {
356 ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
357 return NULL;
358 }
359
360 if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
361 ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'. Check preceeding errors.\n", type, id);
362 ao2_ref(obj, -1);
363 return NULL;
364 }
365
366 return obj;
367}
368
369/*! \brief Finds the last variable in a list and tests it */
370static int is_variable_true(struct ast_variable *vars, const char *name)
371{
373}
374
375/*! \brief Appends a variable to the end of an existing list */
376static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
377{
378 struct ast_variable *new = ast_variable_new(name, value, "");
379
380 if (!new) {
381 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
382 return -1;
383 }
384
385 ast_variable_list_append(existing, new);
386
387 return 0;
388}
389
390/*! \brief Appends a variable to the end of an existing list. On failure, cause the calling
391 * function to return -1 */
392#define variable_list_append_return(existing, name, value) ({ \
393 struct ast_variable *new = ast_variable_new(name, value, ""); \
394 if (!new) { \
395 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
396 return -1; \
397 } \
398 ast_variable_list_append(existing, new); \
399})
400
401/*! \brief We need to strip off the prefix from the name of each variable
402 * so they're suitable for objectset_apply.
403 * I.E. will transform outbound_auth/username to username.
404 */
405static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
406{
407 struct ast_variable *return_vars = NULL;
408 struct ast_variable *v = vars;
409 int plen = strlen(prefix);
410
411 for(; v; v = v->next) {
412 if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
413 if (variable_list_append(&return_vars, v->name + plen, v->value)) {
414 ast_variables_destroy(return_vars);
415 return NULL;
416 }
417 }
418 }
419
420 return return_vars;
421}
422
423/* Don't call while holding context locks. */
424static int delete_extens(const char *context, const char *exten)
425{
426 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
427
428 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) {
430 }
431
432 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
434 }
435
436 return 0;
437}
438
439static int add_extension(struct ast_context *context, const char *exten,
440 int priority, const char *application)
441{
442 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
443 struct ast_exten *existing_exten;
444 char *data = NULL;
445 char *app = NULL;
446 void *free_ptr = NULL;
447 const char *paren;
448 const char *context_name;
449
450 if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) {
451 return -1;
452 }
453
454 /* The incoming application has to be split into the app name and the
455 * arguments (data). The app name can be any storage type as add_extension
456 * copies it into its own buffer. Data however, needs to be dynamically
457 * allocated and a free function provided.
458 */
459
460 paren = strchr(application, '(');
461 if (!paren) {
462 app = (char *)application;
463 } else {
464 app = ast_strdupa(application);
465 app[paren - application] = '\0';
466 data = ast_strdup(paren + 1);
467 if (!data) {
468 return -1;
469 }
470 data[strlen(data) - 1] = '\0';
471 free_ptr = ast_free_ptr;
473 ast_free(data);
474 return -1;
475 }
476 }
477
478 /* Don't disturb existing, exact-match, entries. */
480 if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten,
481 priority, NULL, NULL, E_MATCH))) {
482 const char *existing_app = ast_get_extension_app(existing_exten);
483 const char *existing_data = ast_get_extension_app_data(existing_exten);
484 if (!strcmp(existing_app, app)
485 && !strcmp(existing_data ? existing_data : "", data ? data : "")) {
486 ast_free(data);
487 return 0;
488 }
489
491 }
492
494 app, data, free_ptr, BASE_REGISTRAR, NULL, 0)) {
495 return -1;
496 }
497
498 return 0;
499}
500
501static int add_hints(const char *context, const char *exten, const char *application, const char *id)
502{
503 struct ast_context *hint_context;
504 char *hint_device;
505
506 hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1);
507 sprintf(hint_device, "PJSIP/%s", id);
508
509 /* We need the contexts list locked to safely be able to both read and lock the specific context within */
510 if (ast_wrlock_contexts()) {
511 ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
512 return -1;
513 }
514
515 if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) {
516 ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context);
517 if (ast_unlock_contexts()) {
518 ast_assert(0);
519 }
520 return -1;
521 }
522
523 /* Transfer the all-contexts lock to the specific context */
524 if (ast_wrlock_context(hint_context)) {
526 ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
527 return -1;
528 }
530
531 if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) {
532 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
533 exten, context);
534 }
535
536 if (!ast_strlen_zero(application)) {
537 if (add_extension(hint_context, exten, 1, application)) {
538 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
539 exten, context);
540 }
541 } else {
542 ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1);
543 }
544
545 ast_unlock_context(hint_context);
546
547 return 0;
548}
549
550static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
551 struct ast_category *wiz, char *direction)
552{
553 struct ast_variable *wizvars = ast_category_first(wiz);
554 struct ast_sorcery_object *obj = NULL;
555 const char *id = ast_category_get_name(wiz);
556 char new_id[strlen(id) + MAX_ID_SUFFIX];
557 char prefix[strlen(direction) + strlen("_auth/") + 1];
558 char *test_variable = NULL;
560
561 snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
562 vars = get_object_variables(wizvars, prefix);
563
564 if (!strcmp(direction, "outbound")) {
565 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
566 test_variable = "sends_auth";
567 } else {
568 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
569 test_variable = "accepts_auth";
570 }
571
572 if (is_variable_true(wizvars, test_variable)) {
573 if (!ast_variable_find_last_in_list(vars, "username")) {
575 "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
576 return -1;
577 }
578 } else {
579 /* Delete auth if sends or accepts is now false. */
580 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
581 if (obj) {
582 otw->wizard->delete(sorcery, otw->wizard_data, obj);
583 ao2_ref(obj, -1);
584 }
585 return 0;
586 }
587
588 variable_list_append_return(&vars, "@pjsip_wizard", id);
589
590 /* If the user set auth_type, don't override it. */
591 if (!ast_variable_find_last_in_list(vars, "auth_type")) {
592 variable_list_append_return(&vars, "auth_type", "userpass");
593 }
594
595 obj = create_object(sorcery, new_id, "auth", vars);
596 if (!obj) {
597 return -1;
598 }
599
600 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
601 otw->wizard->create(sorcery, otw->wizard_data, obj);
602 }
603 ao2_ref(obj, -1);
604
605 return 0;
606}
607
608static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
609 struct ast_category *wiz)
610{
611 int rc;
612
613 if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
614 return rc;
615 }
616
617 return handle_auth(sorcery, otw, wiz, "inbound");
618}
619
620static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
621 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
622{
623 struct ast_variable *wizvars = ast_category_first(wiz);
624 struct ast_sorcery_object *obj = NULL;
625 const char *id = ast_category_get_name(wiz);
626 const char *contact_pattern;
627 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
628 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
629 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
630
631 variable_list_append(&vars, "@pjsip_wizard", id);
632
633 if (!ast_strlen_zero(outbound_proxy)) {
634 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
635 }
636
637 /* If the user explicitly specified an aor/contact, don't use remote hosts. */
638 if (!ast_variable_find_last_in_list(vars, "contact")) {
639 if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
640 contact_pattern = "sip:${REMOTE_HOST}";
641 }
642
643 if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
644 int host_counter;
645
646 /* ast_str_substitute_variables operate on a varshead list so we have
647 * to create one to hold the REPORT_HOST substitution, do the substitution,
648 * then append the result to the ast_variable list.
649 */
650 for (host_counter = 0; host_counter < host_count; host_counter++) {
651 RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
653 struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
654 AST_VECTOR_GET(remote_hosts_vector, host_counter));
655
656 AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
657 ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
658 contact_pattern);
659
660 variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
661 }
662 }
663 }
664
665 obj = create_object(sorcery, id, "aor", vars);
666 if (!obj) {
667 return -1;
668 }
669
670 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
671 otw->wizard->create(sorcery, otw->wizard_data, obj);
672 }
673 ao2_ref(obj, -1);
674
675 return 0;
676}
677
678static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
679 struct ast_category *wiz)
680{
681 struct ast_variable *wizvars = ast_category_first(wiz);
682 struct ast_sorcery_object *obj = NULL;
683 const char *id = ast_category_get_name(wiz);
684 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
685 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
686 const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
687 const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
688 const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
689 char new_id[strlen(id) + MAX_ID_SUFFIX];
690 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
691
692 variable_list_append_return(&vars, "@pjsip_wizard", id);
693 variable_list_append_return(&vars, "aors", id);
694
695 if (!ast_strlen_zero(outbound_proxy)) {
696 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
697 }
698
699 if (ast_strlen_zero(hint_context)) {
700 hint_context = ast_variable_find_last_in_list(vars, "context");
701 }
702
703 if (ast_strlen_zero(hint_context)) {
704 hint_context = "default";
705 }
706
707 if (!ast_strlen_zero(hint_exten)) {
708 /* These are added so we can find and delete the hints when the endpoint gets deleted */
709 variable_list_append_return(&vars, "@hint_context", hint_context);
710 variable_list_append_return(&vars, "@hint_exten", hint_exten);
711 }
712
713 if (!ast_strlen_zero(transport)) {
714 variable_list_append_return(&vars, "transport", transport);
715 }
716
717 if (is_variable_true(wizvars, "sends_auth")) {
718 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
719 variable_list_append_return(&vars, "outbound_auth", new_id);
720 }
721
722 if (is_variable_true(wizvars, "accepts_auth")) {
723 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
724 variable_list_append_return(&vars, "auth", new_id);
725 }
726
727 obj = create_object(sorcery, id, "endpoint", vars);
728 if (!obj) {
729 return -1;
730 }
731
732 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
733 otw->wizard->create(sorcery, otw->wizard_data, obj);
734 }
735 ao2_ref(obj, -1);
736
737 if (!ast_strlen_zero(hint_exten)) {
738 if (is_variable_true(wizvars, "has_hint")) {
739 add_hints(hint_context, hint_exten, hint_application, id);
740 } else {
741 delete_extens(hint_context, hint_exten);
742 }
743 }
744
745 return 0;
746}
747
748static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
749 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
750{
751 struct ast_variable *wizvars = ast_category_first(wiz);
752 struct ast_sorcery_object *obj = NULL;
753 const char *id = ast_category_get_name(wiz);
754 char new_id[strlen(id) + MAX_ID_SUFFIX];
755 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
756 int host_counter;
757 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
758
759 snprintf(new_id, sizeof(new_id), "%s-identify", id);
760
761 /* If accepting registrations or we're sending line, we don't need an identify. */
762 if (is_variable_true(wizvars, "accepts_registrations")
763 || is_variable_true(wizvars, "sends_line_with_registrations")) {
764 /* If one exists, delete it. */
765 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
766 if (obj) {
767 otw->wizard->delete(sorcery, otw->wizard_data, obj);
768 ao2_ref(obj, -1);
769 }
770 return 0;
771 }
772
773 if (!host_count) {
775 "Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
776 return -1;
777 }
778
779 variable_list_append_return(&vars, "endpoint", id);
780 variable_list_append_return(&vars, "@pjsip_wizard", id);
781
782 if (!ast_variable_find_last_in_list(vars, "match")) {
783 for (host_counter = 0; host_counter < host_count; host_counter++) {
784 variable_list_append_return(&vars, "match",
785 AST_VECTOR_GET(remote_hosts_vector, host_counter));
786 }
787 }
788
789 obj = create_object(sorcery, new_id, "identify", vars);
790 if (!obj) {
791 return -1;
792 }
793
794 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
795 otw->wizard->create(sorcery, otw->wizard_data, obj);
796 }
797 ao2_ref(obj, -1);
798
799 return 0;
800}
801
802static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
803 struct ast_category *wiz)
804{
805 struct ast_variable *wizvars = ast_category_first(wiz);
806 struct ast_sorcery_object *obj = NULL;
807 const char *id = ast_category_get_name(wiz);
808 char new_id[strlen(id) + MAX_ID_SUFFIX];
809 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
810
811 snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
812
813 if (!is_variable_true(wizvars, "has_phoneprov")) {
814 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
815 if (obj) {
816 otw->wizard->delete(sorcery, otw->wizard_data, obj);
817 ao2_ref(obj, -1);
818 }
819 return 0;
820 }
821
822 if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
824 "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
825 return -1;
826 }
827
828 variable_list_append_return(&vars, "endpoint", id);
829 variable_list_append_return(&vars, "@pjsip_wizard", id);
830
831 obj = create_object(sorcery, new_id, "phoneprov", vars);
832 if (!obj) {
833 return -1;
834 }
835
836 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
837 otw->wizard->create(sorcery, otw->wizard_data, obj);
838 }
839 ao2_ref(obj, -1);
840
841 return 0;
842}
843
844static int delete_existing_cb(void *obj, void *arg, int flags)
845{
846 struct object_type_wizard *otw = arg;
847
848 if (!strcmp(otw->object_type, "endpoint")) {
849 const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
850 const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
851 if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) {
852 delete_extens(context, exten);
853 }
854 }
855
856 otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
857
858 return CMP_MATCH;
859}
860
861static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
862 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
863{
864 struct ast_variable *search;
865 struct ast_variable *wizvars = ast_category_first(wiz);
866 const char *id = ast_category_get_name(wiz);
867 const char *server_uri_pattern;
868 const char *client_uri_pattern;
869 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
870 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
871 const char *username;
872 char new_id[strlen(id) + MAX_ID_SUFFIX];
873 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
874 int host_counter;
875 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
876 RAII_VAR(struct ao2_container *, existing,
878
879 if (!existing) {
880 return -1;
881 }
882
883 /* Find any existing registrations. */
884 search = ast_variable_new("@pjsip_wizard", id, "");
885 if (!search) {
886 return -1;
887 }
888
889 if (!ast_strlen_zero(outbound_proxy)) {
890 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
891 }
892
893 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
894 ast_variables_destroy(search);
895
896 /* If not sending registrations, delete ALL existing registrations for this wizard. */
897 if (!is_variable_true(wizvars, "sends_registrations")) {
898 if (ao2_container_count(existing) > 0) {
900 }
901 return 0;
902 }
903
904 if (!host_count) {
905 ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
906 return -1;
907 }
908
909 variable_list_append_return(&vars, "@pjsip_wizard", id);
910
911 if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
912 server_uri_pattern = "sip:${REMOTE_HOST}";
913 }
914
915 if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
916 client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
917 }
918
919 if (is_variable_true(wizvars, "sends_auth")) {
920 if (!(username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username"))) {
921 ast_log(LOG_ERROR, "Wizard '%s' must have 'outbound_auth/username' if it sends"
922 " authentication.\n", id);
923 return -1;
924 }
925 } else {
926 username = id;
927 }
928
929
930 /* Unlike aor and identify, we need to create a separate registration object
931 * for each remote host.
932 */
933 for (host_counter = 0; host_counter < host_count; host_counter++) {
934 struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
935 AST_VECTOR_GET(remote_hosts_vector, host_counter));
936 struct ast_var_t *un = ast_var_assign("USERNAME", username);
937 struct ast_sorcery_object *obj;
938 RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
940 RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
941
942 AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
943 AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
944
945 if (!ast_strlen_zero(server_uri_pattern)) {
946 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
947 server_uri_pattern);
948 variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
949 }
950
951 if (!ast_strlen_zero(client_uri_pattern)) {
952 ast_str_reset(uri);
953 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
954 client_uri_pattern);
955 variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
956 }
957
958 if (is_variable_true(wizvars, "sends_auth")) {
959 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
960 variable_list_append_return(&registration_vars, "outbound_auth", new_id);
961 }
962
963 if (!ast_strlen_zero(transport)) {
964 variable_list_append_return(&registration_vars, "transport", transport);
965 }
966
967 if (is_variable_true(wizvars, "sends_line_with_registrations")) {
968 variable_list_append_return(&registration_vars, "line", "yes");
969 variable_list_append_return(&registration_vars, "endpoint", id);
970 }
971
972 snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
973
974 obj = create_object(sorcery, new_id, "registration", registration_vars);
975 if (!obj) {
976 return -1;
977 }
978
979 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
980 otw->wizard->create(sorcery, otw->wizard_data, obj);
981 }
982 ao2_ref(obj, -1);
983
984 /* Unlink it from the 'existing' container. Any left will be deleted from
985 * sorcery. If it wasn't in the existing container, no harm.
986 */
988 }
989
990 /* If there are any excess registrations, delete them. */
991 if (ao2_container_count(existing) > 0) {
993 }
994
995 return 0;
996}
997
998static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
999 struct ast_category *wiz)
1000{
1001 struct ast_variable *wizvars = ast_category_first(wiz);
1002 struct string_vector remote_hosts_vector;
1003 const char *remote_hosts;
1004 int rc = -1;
1005
1006 AST_VECTOR_INIT(&remote_hosts_vector, 16);
1007 remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
1008
1009 if (!ast_strlen_zero(remote_hosts)) {
1010 char *host;
1011 char *hosts = ast_strdupa(remote_hosts);
1012
1013 while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
1014 host = ast_strdup(host);
1015 if (host && AST_VECTOR_APPEND(&remote_hosts_vector, host)) {
1016 ast_free(host);
1017 }
1018 }
1019 }
1020
1021 ast_debug(4, "%s handler starting.\n", otw->object_type);
1022
1023 if (!strcmp(otw->object_type, "auth")) {
1024 rc = handle_auths(sorcery, otw, wiz);
1025 } else if (!strcmp(otw->object_type, "aor")) {
1026 rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
1027 } else if (!strcmp(otw->object_type, "endpoint")) {
1028 rc = handle_endpoint(sorcery, otw, wiz);
1029 } else if (!strcmp(otw->object_type, "identify")) {
1030 rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
1031 } else if (!strcmp(otw->object_type, "phoneprov")) {
1032 rc = handle_phoneprov(sorcery, otw, wiz);
1033 } else if (!strcmp(otw->object_type, "registration")) {
1034 rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
1035 }
1036
1038 AST_VECTOR_FREE(&remote_hosts_vector);
1039
1040 ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
1041
1042 return rc;
1043}
1044
1045/*
1046 * Everything below are the sorcery observers.
1047 */
1048static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
1049static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
1050static void object_type_loaded_observer(const char *name,
1051 const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
1052static void object_type_registered_observer(const char *name,
1053 struct ast_sorcery *sorcery, const char *object_type);
1054
1059
1064
1065/*! \brief Called after an object type is loaded/reloaded */
1066static void object_type_loaded_observer(const char *name,
1067 const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1068{
1069 struct ast_category *category = NULL;
1070 struct object_type_wizard *otw = NULL;
1071 char *filename = "pjsip_wizard.conf";
1072 struct ast_flags flags = { 0 };
1073 struct ast_config *cfg;
1074
1075 if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
1076 /* Not interested. */
1077 return;
1078 }
1079
1080 otw = find_wizard(object_type);
1081 if (!otw) {
1082 ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
1083 return;
1084 }
1085
1086 /* Only use the FILEUNCHANGED optimization if the ACLs haven't changed.
1087 * If ACLs changed, we force a reload of the config file to re-evaluate rules. */
1088 if (reloaded && otw->last_config && !acl_change_detected) {
1090 }
1091
1092 cfg = ast_config_load2(filename, object_type, flags);
1093
1094 if (!cfg) {
1095 ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
1096 return;
1097 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1098 ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
1099 return;
1100 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1101 ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
1102 return;
1103 }
1104
1105 while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
1106 const char *id = ast_category_get_name(category);
1107 struct ast_category *last_cat = NULL;
1108 int changes = 0;
1109
1110 if (otw->last_config) {
1111 last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
1112 changes = !ast_variable_lists_match(ast_category_first(category), ast_category_first(last_cat), 1);
1113
1114 /* If the ACL has changed, we assume EVERYTHING might have changed.
1115 * We force an update for all wizard objects. */
1116 if (!changes && reloaded && acl_change_detected) {
1117 ast_debug(3, "Forcing update of wizard '%s' due to global ACL change.\n", id);
1118 changes = 1;
1119 }
1120
1121 if (last_cat) {
1122 ast_category_delete(otw->last_config, last_cat);
1123 }
1124 }
1125
1126 if (!last_cat || changes) {
1127 ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
1128 if (wizard_apply_handler(sorcery, otw, category)) {
1129 ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
1130 }
1131 }
1132 }
1133
1134 if (!otw->last_config) {
1135 otw->last_config = cfg;
1136 return;
1137 }
1138
1139 /* Only wizards that weren't in the new config are left in last_config now so we need to delete
1140 * all objects belonging to them.
1141 */
1142 category = NULL;
1143 while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
1144 const char *id = ast_category_get_name(category);
1145 struct ast_variable *search;
1146 RAII_VAR(struct ao2_container *, existing,
1148
1149 if (!existing) {
1150 ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
1151 break;
1152 }
1153
1154 search = ast_variable_new("@pjsip_wizard", id, "");
1155 if (!search) {
1156 ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
1157 break;
1158 }
1159 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
1160 ast_variables_destroy(search);
1161
1162 if (ao2_container_count(existing) > 0) {
1163 ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
1164 reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
1166 delete_existing_cb, otw);
1167 }
1168 }
1169
1171 otw->last_config = cfg;
1172}
1173
1174/*! \brief When each object type is registered, map a memory wizard to it. */
1176 struct ast_sorcery *sorcery, const char *object_type)
1177{
1178 struct ast_sorcery_wizard *wizard;
1179 void *wizard_data;
1180 struct object_type_wizard *otw;
1181
1182 if (is_one_of(object_type, object_types)) {
1186 ast_log(LOG_ERROR, "Unable to apply sangoma wizard to object type '%s'\n", object_type);
1187 return;
1188 }
1189
1190 otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
1191 if (!otw) {
1192 return;
1193 }
1194
1195 otw->sorcery = sorcery;
1196 otw->wizard = wizard;
1197 otw->wizard_data = wizard_data;
1198 otw->last_config = NULL;
1199 strcpy(otw->object_type, object_type); /* Safe */
1200 AST_VECTOR_RW_WRLOCK(&object_type_wizards);
1201 if (AST_VECTOR_APPEND(&object_type_wizards, otw)) {
1202 ast_free(otw);
1203 } else {
1204 ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
1205 }
1206 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1207
1208 }
1209}
1210
1211/*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
1212 * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
1213 * destroyed.
1214 */
1215static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
1216{
1217 if (strcmp(name, "res_pjsip")) {
1218 return;
1219 }
1222}
1223
1224/*! \brief When the res_pjsip instance is destroyed, remove the observer
1225 * and unref the module. This should then allow this module to unload cleanly.
1226 */
1227static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
1228{
1229 if (strcmp(name, "res_pjsip")) {
1230 return;
1231 }
1232
1235}
1236
1237static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1238{
1239 struct ast_sorcery *sorcery;
1240 int idx;
1241 FILE *f = NULL;
1242 const char *fn = NULL;
1243
1244 switch (cmd) {
1245 case CLI_INIT:
1246 e->command = "pjsip export config_wizard primitives [to]";
1247 e->usage =
1248 "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
1249 " Export the config_wizard objects as pjsip primitives to\n"
1250 " the console or to <filename>\n";
1251 return NULL;
1252 case CLI_GENERATE:
1253 return NULL;
1254 }
1255
1256 if (a->argc > 5) {
1257 char date[256]="";
1258 time_t t;
1259 fn = a->argv[5];
1260
1261 time(&t);
1262 ast_copy_string(date, ctime(&t), sizeof(date));
1263 f = fopen(fn, "w");
1264 if (!f) {
1265 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
1266 return CLI_FAILURE;
1267 }
1268
1269 fprintf(f, ";!\n");
1270 fprintf(f, ";! Automatically generated configuration file\n");
1271 fprintf(f, ";! Filename: %s\n", fn);
1272 fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
1273 fprintf(f, ";! Creation Date: %s", date);
1274 fprintf(f, ";!\n");
1275 }
1276
1278
1279 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
1280 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
1281 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
1282 struct ao2_container *container;
1283 struct ao2_iterator i;
1284 void *o;
1285
1287 if (!container) {
1288 continue;
1289 }
1290
1292 while ((o = ao2_iterator_next(&i))) {
1293 struct ast_variable *vars;
1294 struct ast_variable *v;
1295
1297 if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
1298 if (f) {
1299 fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1300 } else {
1301 ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1302 }
1303 for (v = vars; v; v = v->next) {
1304 if (!ast_strlen_zero(v->value)) {
1305 if (f) {
1306 fprintf(f, "%s = %s\n", v->name, v->value);
1307 } else {
1308 ast_cli(a->fd, "%s = %s\n", v->name, v->value);
1309 }
1310 }
1311 }
1312 }
1314 ao2_ref(o, -1);
1315 }
1318 }
1319 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1320
1321 if (f) {
1322 fclose(f);
1323 ast_cli(a->fd, "Wrote configuration to %s\n", fn);
1324 }
1325
1326
1327 return CLI_SUCCESS;
1328}
1329
1331 AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
1332};
1333
1334static int reload_module(void)
1335{
1337 return 0;
1338}
1339
1340static int load_module(void)
1341{
1342 AST_VECTOR_RW_INIT(&object_type_wizards, 12);
1345
1347
1348 /* If the PJSIP sorcery instance exists it means that we have been explicitly
1349 * loaded and things are potentially already set up. Since we won't receive any
1350 * observer callbacks informing us of this we add ourselves to the instance
1351 * as an observer in case it goes away, and determine which object types have
1352 * been registered and synthesize the observer callbacks for them. Once done
1353 * we force a reload so we are aware of the state of things.
1354 */
1355 if (ast_sip_get_sorcery()) {
1356 int i;
1357
1360
1361 /* See which object types already exist */
1362 for (i = 0; i < ARRAY_LEN(object_types); ++i) {
1363 if (!object_types[i]) {
1364 break;
1365 } else if (!ast_sorcery_get_object_type(ast_sip_get_sorcery(), object_types[i])) {
1366 continue;
1367 }
1368
1370 object_types[i]);
1371 }
1372
1374 }
1375
1377}
1378
1392
1394 .support_level = AST_MODULE_SUPPORT_CORE,
1395 .load = load_module,
1397 .unload = unload_module,
1398 .load_pri = AST_MODPRI_REALTIME_DRIVER,
Access Control of various sorts.
struct stasis_message_type * ast_named_acl_change_type(void)
a stasis_message_type for changes against a named ACL or the set of all named ACLs
#define paren
Definition ael_lex.c:962
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
static const char app[]
enum queue_result id
Definition app_queue.c:1790
#define var
Definition ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
void ast_free_ptr(void *ptr)
free() wrapper
Definition astmm.c:1739
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
@ CMP_MATCH
Definition astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition astobj2.h:1327
static int priority
static rc_handle * rh
Definition cdr_radius.c:96
static const char type[]
static struct ast_sorcery * sorcery
void ast_var_list_destroy(struct varshead *head)
Definition chanvars.c:109
static void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var)
Definition chanvars.h:51
#define ast_var_assign(name, value)
Definition chanvars.h:40
struct varshead * ast_var_list_create(void)
Definition chanvars.c:97
Standard Command Line Interface.
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define CLI_FAILURE
Definition cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
@ E_MATCH
Definition extconf.h:217
static const char name[]
Definition format_mp3.c:68
direction
static const char context_name[]
static char prefix[MAX_PREFIX]
Definition http.c:145
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *cat)
Delete a category.
const char * ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
Gets the value of the LAST occurrence of a variable from a variable list.
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
struct ast_category * ast_category_browse_filtered(struct ast_config *config, const char *category_name, struct ast_category *prev, const char *filter)
Browse categories with filters.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition module.h:330
#define ast_module_unref(mod)
Release a reference to the module.
Definition module.h:483
#define ast_module_ref(mod)
Hold a reference to the module.
Definition module.h:457
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_REALTIME_DRIVER
Definition module.h:337
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
Core PBX routines and definitions.
void * ast_get_extension_app_data(struct ast_exten *e)
Definition pbx.c:8606
int ast_wrlock_contexts(void)
Write locks the context list.
Definition pbx.c:8507
int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar, int already_locked)
This functionc locks given context, search for the right extension and fires out all peer in this ext...
Definition pbx.c:5014
int ast_wrlock_context(struct ast_context *con)
Write locks a given context.
Definition pbx.c:8525
const char * ast_get_extension_app(struct ast_exten *e)
Definition pbx.c:8601
struct ast_context * ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
Register a new context or find an existing one.
Definition pbx.c:6185
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition pbx.c:4984
int ast_unlock_context(struct ast_context *con)
Definition pbx.c:8535
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
const char * ast_get_context_name(struct ast_context *con)
Definition ael_main.c:421
#define PRIORITY_HINT
Definition pbx.h:54
int ast_add_extension2_nolock(struct ast_context *con, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar, const char *registrar_file, int registrar_line)
Same as ast_add_extension2, but assumes you have already locked context.
Definition pbx.c:7310
struct ast_exten * pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, struct pbx_find_info *q, const char *context, const char *exten, int priority, const char *label, const char *callerid, enum ext_match_t action)
Definition ael_main.c:152
int ast_unlock_contexts(void)
Unlocks contexts.
Definition pbx.c:8517
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
static int reload(void)
struct ao2_container * container
Definition res_fax.c:603
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
When the res_pjsip instance is destroyed, remove the observer and unref the module....
static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
#define BASE_REGISTRAR
#define NOT_EQUALS(a, b)
static int add_hints(const char *context, const char *exten, const char *application, const char *id)
static int is_variable_true(struct ast_variable *vars, const char *name)
Finds the last variable in a list and tests it.
static struct stasis_subscription * acl_change_sub
static int delete_existing_cb(void *obj, void *arg, int flags)
#define OTW_DELETE_CB(otw)
static int add_extension(struct ast_context *context, const char *exten, int priority, const char *application)
static struct object_type_wizard * find_wizard(const char *object_type)
Finds the otw for the object type.
static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
Appends a variable to the end of an existing list.
static const struct ast_sorcery_global_observer global_observer
static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, char *direction)
static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
When the res_pjsip instance is created, add an observer to it and initialize the wizard vector....
static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static void object_type_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
Called after an object type is loaded/reloaded.
#define MAX_ID_SUFFIX
Defines the maximum number of characters that can be added to a wizard id.
static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int is_one_of(const char *needle, const char *haystack[])
static char * handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void object_type_registered_observer(const char *name, struct ast_sorcery *sorcery, const char *object_type)
When each object type is registered, map a memory wizard to it.
static int reload_module(void)
#define variable_list_append_return(existing, name, value)
Appends a variable to the end of an existing list. On failure, cause the calling function to return -...
static struct ast_cli_entry config_wizard_cli[]
static void * create_object(const struct ast_sorcery *sorcery, const char *id, const char *type, struct ast_variable *vars)
Creates a sorcery object and applies a variable list.
static struct ast_variable * get_object_variables(struct ast_variable *vars, char *prefix)
We need to strip off the prefix from the name of each variable so they're suitable for objectset_appl...
struct ast_sorcery_instance_observer observer
static int delete_extens(const char *context, const char *exten)
static int load_module(void)
static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int unload_module(void)
static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static int acl_change_detected
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Callback for Named ACL changed.
static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
#define NULL
Definition resample.c:96
Security Event Reporting API.
struct stasis_topic * ast_security_topic(void)
A stasis_topic which publishes messages for security related issues.
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_extended(const void *object, const char *name)
Get an extended field value from a sorcery object.
Definition sorcery.c:2399
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition sorcery.c:2381
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition sorcery.h:1137
void ast_sorcery_instance_observer_remove(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Remove an observer from a sorcery instance.
Definition sorcery.c:601
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition sorcery.h:120
struct ast_sorcery_object_type * ast_sorcery_get_object_type(const struct ast_sorcery *sorcery, const char *type)
Get the sorcery object type given a type name.
Definition sorcery.c:2558
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition sorcery.c:1472
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition sorcery.c:2528
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition sorcery.c:1808
int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
Apply an object set (KVP list) to an object.
Definition sorcery.c:1696
void ast_sorcery_global_observer_remove(const struct ast_sorcery_global_observer *callbacks)
Remove a global observer from sorcery.
Definition sorcery.c:578
int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer *callbacks)
Add a global observer to sorcery.
Definition sorcery.c:562
int ast_sorcery_instance_observer_add(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Add an observer to a sorcery instance.
Definition sorcery.c:584
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition sorcery.c:1961
@ AST_SORCERY_WIZARD_APPLY_ALLOW_DUPLICATE
Definition sorcery.h:583
@ AST_SORCERY_WIZARD_APPLY_READONLY
Definition sorcery.h:581
@ AST_SORCERY_APPLY_SUCCESS
Definition sorcery.h:427
#define ast_sorcery_object_type_apply_wizard(sorcery, object_type_name, wizard_type_name, wizard_args, flags, wizard, wizard_data)
Apply additional object wizard mappings returning wizard information.
Definition sorcery.h:678
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition stasis.c:1201
#define stasis_subscribe(topic, callback, data)
Definition stasis.h:649
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2233
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
@ AST_STRSEP_TRIM
Definition strings.h:256
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition strings.h:97
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition utils.c:1869
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
ast_context: An extension context
Definition pbx.c:299
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition pbx.c:252
char * exten
Definition pbx.c:253
void * data
Definition pbx.c:263
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
struct ast_module * self
Definition module.h:356
Interface for the global sorcery observer.
Definition sorcery.h:220
void(* instance_created)(const char *name, struct ast_sorcery *sorcery)
Callback after an instance is created.
Definition sorcery.h:222
Interface for the sorcery instance observer.
Definition sorcery.h:237
void(* object_type_registered)(const char *name, struct ast_sorcery *sorcery, const char *object_type)
Callback after any object_type is registered.
Definition sorcery.h:252
Structure for internal sorcery object information.
Definition sorcery.c:128
Interface for a sorcery wizard.
Definition sorcery.h:276
void(* retrieve_multiple)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
Optional callback for retrieving multiple objects using some optional field criteria.
Definition sorcery.h:313
void *(* retrieve_id)(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
Callback for retrieving an object using an id.
Definition sorcery.h:296
int(* create)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for creating an object.
Definition sorcery.h:293
int(* delete)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for deleting an object.
Definition sorcery.h:319
int(* update)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for updating an object.
Definition sorcery.h:316
Full structure for sorcery.
Definition sorcery.c:231
Support for dynamic strings.
Definition strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Keeps track of the sorcery wizard and last config for each object type.
struct ast_config * last_config
struct ast_sorcery * sorcery
struct ast_sorcery_wizard * wizard
A generic char * vector definition.
int value
Definition syslog.c:37
static struct test_val a
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
#define ARRAY_LEN(a)
Definition utils.h:706
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620
#define AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove all elements from a vector that matches the given comparison.
Definition vector.h:472
#define AST_VECTOR_RW_WRLOCK(vec)
Obtain write lock on vector.
Definition vector.h:898
#define AST_VECTOR_RW_UNLOCK(vec)
Unlock vector.
Definition vector.h:908
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_RW_FREE(vec)
Deallocates this locked vector.
Definition vector.h:213
#define AST_VECTOR_RW(name, type)
Define a vector structure with a read/write lock.
Definition vector.h:104
#define AST_VECTOR_RW_RDLOCK(vec)
Obtain read lock on vector.
Definition vector.h:888
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR(name, type)
Define a vector structure.
Definition vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691
#define AST_VECTOR_RW_INIT(vec, size)
Initialize a vector with a read/write lock.
Definition vector.h:169