Asterisk - The Open Source Telephony Project GIT-master-d856a3e
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
53/*** DOCUMENTATION
54 <configInfo name="res_pjsip_config_wizard" language="en_US">
55 <synopsis>Module that provides simple configuration wizard capabilities.</synopsis>
56 <description><para>
57 <emphasis>PJSIP Configuration Wizard</emphasis>
58 </para>
59 <para>This module allows creation of common PJSIP configuration scenarios
60 without having to specify individual endpoint, aor, auth, identify and registration objects.
61 </para>
62 <para> </para>
63
64 <para>For example, the following configuration snippet would create the
65 endpoint, aor, contact, auth and phoneprov objects necessary for a phone to
66 get phone provisioning information, register, and make and receive calls.
67 A hint is also created in the default context for extension 1000.</para>
68 <example title="myphone">
69 [myphone]
70 type = wizard
71 sends_auth = no
72 accepts_auth = yes
73 sends_registrations = no
74 accepts_registrations = yes
75 has_phoneprov = yes
76 transport = ipv4
77 has_hint = yes
78 hint_exten = 1000
79 inbound_auth/username = testname
80 inbound_auth/password = test password
81 endpoint/allow = ulaw
82 endpoint/context = default
83 phoneprov/MAC = 001122aa4455
84 phoneprov/PROFILE = profile1
85 </example>
86
87 <para>The first 8 items are specific to the wizard. The rest of the items
88 are passed verbatim to the underlying objects.</para>
89 <para> </para>
90
91 <para>The following configuration snippet would create the
92 endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
93 to another pbx or ITSP that requires registration.</para>
94 <example title="mytrunk">
95 [mytrunk]
96 type = wizard
97 sends_auth = yes
98 accepts_auth = no
99 sends_registrations = yes
100 accepts_registrations = no
101 transport = ipv4
102 remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060
103 outbound_auth/username = testname
104 outbound_auth/password = test password
105 endpoint/allow = ulaw
106 endpoint/context = default
107 </example>
108
109 <para>Of course, any of the items in either example could be placed into
110 templates and shared among wizard objects.</para>
111
112 <para> </para>
113 <para>For more information, visit:</para>
114 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
115 </description>
116
117 <configFile name="pjsip_wizard.conf">
118 <configObject name="wizard">
119 <synopsis>Provides config wizard.</synopsis>
120 <description>
121 <para>For more information, visit:</para>
122 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
123 </description>
124 <configOption name="type">
125 <synopsis>Must be 'wizard'.</synopsis>
126 </configOption>
127 <configOption name="transport">
128 <synopsis>The name of a transport to use for this object.</synopsis>
129 <description><para>If not specified,
130 the default will be used.</para></description>
131 </configOption>
132 <configOption name="remote_hosts">
133 <synopsis>List of remote hosts.</synopsis>
134 <description><para>A comma-separated list of remote hosts in the form of
135 <replaceable>host</replaceable>[:<replaceable>port</replaceable>].
136 If set, an aor static contact and an identify match will be created for each
137 entry in the list. If send_registrations is also set, a registration will
138 also be created for each.</para></description>
139 </configOption>
140 <configOption name="outbound_proxy">
141 <synopsis>Shortcut for specifying proxy on individual objects.</synopsis>
142 <description><para>Shortcut for specifying endpoint/outbound_proxy,
143 aor/outbound_proxy, and registration/outbound_proxy individually.
144 </para></description>
145 </configOption>
146 <configOption name="sends_auth" default="no">
147 <synopsis>Send outbound authentication to remote hosts.</synopsis>
148 <description><para>At least outbound_auth/username is required.</para></description>
149 </configOption>
150 <configOption name="accepts_auth" default="no">
151 <synopsis>Accept incoming authentication from remote hosts.</synopsis>
152 <description><para>At least inbound_auth/username is required.</para></description>
153 </configOption>
154 <configOption name="sends_registrations" default="no">
155 <synopsis>Send outbound registrations to remote hosts.</synopsis>
156 <description><para>remote_hosts is required and a registration object will
157 be created for each host in the remote _hosts string. If authentication is required,
158 sends_auth and an outbound_auth/username must also be supplied.</para></description>
159 </configOption>
160 <configOption name="sends_line_with_registrations" default="no">
161 <synopsis>Sets "line" and "endpoint parameters on registrations.</synopsis>
162 <description><para>Setting this to true will cause the wizard to skip the
163 creation of an identify object to match incoming requests to the endpoint and
164 instead add the line and endpoint parameters to the outbound registration object.
165 </para></description>
166 </configOption>
167 <configOption name="accepts_registrations" default="no">
168 <synopsis>Accept inbound registration from remote hosts.</synopsis>
169 <description><para>An AOR with dynamic contacts will be created. If
170 the number of contacts nneds to be limited, set aor/max_contacts.</para></description>
171 </configOption>
172 <configOption name="has_phoneprov" default="no">
173 <synopsis>Create a phoneprov object for this endpoint.</synopsis>
174 <description><para>A phoneprov object will be created. phoneprov/MAC
175 must be specified.</para></description>
176 </configOption>
177 <configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
178 <synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
179 <description><para>
180 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
181 appropriate remote_host for each registration.</para></description>
182 </configOption>
183 <configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
184 <synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
185 <description><para>
186 The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
187 will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
188 </configOption>
189 <configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
190 <synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
191 <description><para>
192 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
193 appropriate remote_host for each contact.</para></description>
194 </configOption>
195 <configOption name="has_hint" default="no">
196 <synopsis>Create hint and optionally a default application.</synopsis>
197 <description><para>Create hint and optionally a default application.</para></description>
198 </configOption>
199 <configOption name="hint_context" default="endpoint/context or 'default'">
200 <synopsis>The context in which to place hints.</synopsis>
201 <description>
202 <para>Ignored if <literal>hint_exten</literal> is not specified otherwise specifies the
203 context into which the dialplan hints will be placed. If not specified,
204 defaults to the endpoint's context or <literal>default</literal> if that isn't
205 found.
206 </para></description>
207 </configOption>
208 <configOption name="hint_exten">
209 <synopsis>Extension to map a PJSIP hint to.</synopsis>
210 <description>
211 <para>Will create the following entry in <literal>hint_context</literal>:</para>
212 <para> <literal>exten =&gt; &lt;hint_exten&gt;,hint,PJSIP/&lt;wizard_id&gt;</literal></para>
213 <para> </para>
214 <para>Normal dialplan precedence rules apply so if there's already a hint for
215 this extension in <literal>hint_context</literal>, this one will be ignored.
216 For more information, visit: </para>
217 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
218 </description>
219 </configOption>
220 <configOption name="hint_application">
221 <synopsis>Application to call when 'hint_exten' is dialed.</synopsis>
222 <description>
223 <para>Ignored if <literal>hint_exten</literal> isn't specified otherwise
224 will create the following priority 1 extension in <literal>hint_context</literal>:</para>
225 <para> <literal>exten =&gt; &lt;hint_exten&gt;,1,&lt;hint_application&gt;</literal></para>
226 <para> </para>
227 <para>You can specify any valid extensions.conf application expression.</para>
228 <example title="Valid expressions">
229 Dial(${HINT})
230 Gosub(stdexten,${EXTEN},1(${HINT}))
231 </example>
232 <para>Any extensions.conf style variables specified are passed directly to the
233 dialplan.</para>
234 <para> </para>
235 <para>Normal dialplan precedence rules apply so if there's already a priority 1
236 application for this specific extension in <literal>hint_context</literal>,
237 this one will be ignored. For more information, visit: </para>
238 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
239 </description>
240 </configOption>
241 <configOption name="endpoint&#47;*">
242 <synopsis>Variables to be passed directly to the endpoint.</synopsis>
243 </configOption>
244 <configOption name="aor&#47;*">
245 <synopsis>Variables to be passed directly to the aor.</synopsis>
246 <description><para>If an aor/contact is explicitly defined then remote_hosts
247 will not be used to create contacts automatically.</para></description>
248 </configOption>
249 <configOption name="inbound_auth&#47;*">
250 <synopsis>Variables to be passed directly to the inbound auth.</synopsis>
251 </configOption>
252 <configOption name="outbound_auth&#47;*">
253 <synopsis>Variables to be passed directly to the outbound auth.</synopsis>
254 </configOption>
255 <configOption name="identify&#47;*">
256 <synopsis>Variables to be passed directly to the identify.</synopsis>
257 <description><para>If an identify/match is explicitly defined then remote_hosts
258 will not be used to create matches automatically.</para></description>
259 </configOption>
260 <configOption name="registration&#47;*">
261 <synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
262 </configOption>
263 <configOption name="phoneprov&#47;*">
264 <synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
265 <description><para>
266 To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
267 </configOption>
268 </configObject>
269 </configFile>
270 </configInfo>
271 ***/
272
273 /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
274#define MAX_ID_SUFFIX 20
275
276#define BASE_REGISTRAR "res_pjsip_config_wizard"
277
278/*! \brief A generic char * vector definition. */
280
281/*! \brief Keeps track of the sorcery wizard and last config for each object type */
288};
289static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_type_wizards;
290
291/*! \brief Callbacks for vector deletes */
292#define NOT_EQUALS(a, b) (a != b)
293#define OTW_DELETE_CB(otw) ({ \
294 ast_config_destroy(otw->last_config); \
295 ast_free(otw); \
296})
297
298const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
299
300static int is_one_of(const char *needle, const char *haystack[])
301{
302 int i;
303 for (i = 0; haystack[i]; i++) {
304 if (!strcmp(needle, haystack[i])) {
305 return 1;
306 }
307 }
308
309 return 0;
310}
311
312/*! \brief Finds the otw for the object type */
313static struct object_type_wizard *find_wizard(const char *object_type)
314{
315 int idx;
316
317 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
318 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
319 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
320 if (!strcmp(otw->object_type, object_type)) {
321 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
322 return otw;
323 }
324 }
325 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
326
327 return NULL;
328}
329
330/*! \brief Creates a sorcery object and applies a variable list */
331static void *create_object(const struct ast_sorcery *sorcery,
332 const char *id, const char *type, struct ast_variable *vars)
333{
335
336 if (!obj) {
337 ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
338 return NULL;
339 }
340
341 if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
342 ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'. Check preceeding errors.\n", type, id);
343 ao2_ref(obj, -1);
344 return NULL;
345 }
346
347 return obj;
348}
349
350/*! \brief Finds the last variable in a list and tests it */
351static int is_variable_true(struct ast_variable *vars, const char *name)
352{
354}
355
356/*! \brief Appends a variable to the end of an existing list */
357static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
358{
359 struct ast_variable *new = ast_variable_new(name, value, "");
360
361 if (!new) {
362 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
363 return -1;
364 }
365
366 ast_variable_list_append(existing, new);
367
368 return 0;
369}
370
371/*! \brief Appends a variable to the end of an existing list. On failure, cause the calling
372 * function to return -1 */
373#define variable_list_append_return(existing, name, value) ({ \
374 struct ast_variable *new = ast_variable_new(name, value, ""); \
375 if (!new) { \
376 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
377 return -1; \
378 } \
379 ast_variable_list_append(existing, new); \
380})
381
382/*! \brief We need to strip off the prefix from the name of each variable
383 * so they're suitable for objectset_apply.
384 * I.E. will transform outbound_auth/username to username.
385 */
386static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
387{
388 struct ast_variable *return_vars = NULL;
389 struct ast_variable *v = vars;
390 int plen = strlen(prefix);
391
392 for(; v; v = v->next) {
393 if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
394 if (variable_list_append(&return_vars, v->name + plen, v->value)) {
395 ast_variables_destroy(return_vars);
396 return NULL;
397 }
398 }
399 }
400
401 return return_vars;
402}
403
404/* Don't call while holding context locks. */
405static int delete_extens(const char *context, const char *exten)
406{
407 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
408
409 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) {
411 }
412
413 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
415 }
416
417 return 0;
418}
419
420static int add_extension(struct ast_context *context, const char *exten,
421 int priority, const char *application)
422{
423 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
424 struct ast_exten *existing_exten;
425 char *data = NULL;
426 char *app = NULL;
427 void *free_ptr = NULL;
428 char *paren;
429 const char *context_name;
430
431 if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) {
432 return -1;
433 }
434
435 /* The incoming application has to be split into the app name and the
436 * arguments (data). The app name can be any storage type as add_extension
437 * copies it into its own buffer. Data however, needs to be dynamically
438 * allocated and a free function provided.
439 */
440
441 paren = strchr(application, '(');
442 if (!paren) {
443 app = (char *)application;
444 } else {
445 app = ast_strdupa(application);
446 app[paren - application] = '\0';
447 data = ast_strdup(paren + 1);
448 if (!data) {
449 return -1;
450 }
451 data[strlen(data) - 1] = '\0';
452 free_ptr = ast_free_ptr;
454 ast_free(data);
455 return -1;
456 }
457 }
458
459 /* Don't disturb existing, exact-match, entries. */
461 if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten,
462 priority, NULL, NULL, E_MATCH))) {
463 const char *existing_app = ast_get_extension_app(existing_exten);
464 const char *existing_data = ast_get_extension_app_data(existing_exten);
465 if (!strcmp(existing_app, app)
466 && !strcmp(existing_data ? existing_data : "", data ? data : "")) {
467 ast_free(data);
468 return 0;
469 }
470
472 }
473
475 app, data, free_ptr, BASE_REGISTRAR, NULL, 0)) {
476 return -1;
477 }
478
479 return 0;
480}
481
482static int add_hints(const char *context, const char *exten, const char *application, const char *id)
483{
484 struct ast_context *hint_context;
485 char *hint_device;
486
487 hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1);
488 sprintf(hint_device, "PJSIP/%s", id);
489
490 /* We need the contexts list locked to safely be able to both read and lock the specific context within */
491 if (ast_wrlock_contexts()) {
492 ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
493 return -1;
494 }
495
496 if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) {
497 ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context);
498 if (ast_unlock_contexts()) {
499 ast_assert(0);
500 }
501 return -1;
502 }
503
504 /* Transfer the all-contexts lock to the specific context */
505 if (ast_wrlock_context(hint_context)) {
507 ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
508 return -1;
509 }
511
512 if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) {
513 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
514 exten, context);
515 }
516
517 if (!ast_strlen_zero(application)) {
518 if (add_extension(hint_context, exten, 1, application)) {
519 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
520 exten, context);
521 }
522 } else {
523 ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1);
524 }
525
526 ast_unlock_context(hint_context);
527
528 return 0;
529}
530
531static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
532 struct ast_category *wiz, char *direction)
533{
534 struct ast_variable *wizvars = ast_category_first(wiz);
535 struct ast_sorcery_object *obj = NULL;
536 const char *id = ast_category_get_name(wiz);
537 char new_id[strlen(id) + MAX_ID_SUFFIX];
538 char prefix[strlen(direction) + strlen("_auth/") + 1];
539 char *test_variable = NULL;
541
542 snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
543 vars = get_object_variables(wizvars, prefix);
544
545 if (!strcmp(direction, "outbound")) {
546 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
547 test_variable = "sends_auth";
548 } else {
549 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
550 test_variable = "accepts_auth";
551 }
552
553 if (is_variable_true(wizvars, test_variable)) {
554 if (!ast_variable_find_last_in_list(vars, "username")) {
556 "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
557 return -1;
558 }
559 } else {
560 /* Delete auth if sends or accepts is now false. */
561 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
562 if (obj) {
563 otw->wizard->delete(sorcery, otw->wizard_data, obj);
564 ao2_ref(obj, -1);
565 }
566 return 0;
567 }
568
569 variable_list_append_return(&vars, "@pjsip_wizard", id);
570
571 /* If the user set auth_type, don't override it. */
572 if (!ast_variable_find_last_in_list(vars, "auth_type")) {
573 variable_list_append_return(&vars, "auth_type", "userpass");
574 }
575
576 obj = create_object(sorcery, new_id, "auth", vars);
577 if (!obj) {
578 return -1;
579 }
580
581 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
582 otw->wizard->create(sorcery, otw->wizard_data, obj);
583 }
584 ao2_ref(obj, -1);
585
586 return 0;
587}
588
589static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
590 struct ast_category *wiz)
591{
592 int rc;
593
594 if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
595 return rc;
596 }
597
598 return handle_auth(sorcery, otw, wiz, "inbound");
599}
600
601static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
602 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
603{
604 struct ast_variable *wizvars = ast_category_first(wiz);
605 struct ast_sorcery_object *obj = NULL;
606 const char *id = ast_category_get_name(wiz);
607 const char *contact_pattern;
608 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
609 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
610 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
611
612 variable_list_append(&vars, "@pjsip_wizard", id);
613
614 if (!ast_strlen_zero(outbound_proxy)) {
615 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
616 }
617
618 /* If the user explicitly specified an aor/contact, don't use remote hosts. */
619 if (!ast_variable_find_last_in_list(vars, "contact")) {
620 if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
621 contact_pattern = "sip:${REMOTE_HOST}";
622 }
623
624 if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
625 int host_counter;
626
627 /* ast_str_substitute_variables operate on a varshead list so we have
628 * to create one to hold the REPORT_HOST substitution, do the substitution,
629 * then append the result to the ast_variable list.
630 */
631 for (host_counter = 0; host_counter < host_count; host_counter++) {
632 RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
634 struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
635 AST_VECTOR_GET(remote_hosts_vector, host_counter));
636
637 AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
638 ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
639 contact_pattern);
640
641 variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
642 }
643 }
644 }
645
646 obj = create_object(sorcery, id, "aor", vars);
647 if (!obj) {
648 return -1;
649 }
650
651 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
652 otw->wizard->create(sorcery, otw->wizard_data, obj);
653 }
654 ao2_ref(obj, -1);
655
656 return 0;
657}
658
659static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
660 struct ast_category *wiz)
661{
662 struct ast_variable *wizvars = ast_category_first(wiz);
663 struct ast_sorcery_object *obj = NULL;
664 const char *id = ast_category_get_name(wiz);
665 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
666 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
667 const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
668 const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
669 const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
670 char new_id[strlen(id) + MAX_ID_SUFFIX];
671 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
672
673 variable_list_append_return(&vars, "@pjsip_wizard", id);
674 variable_list_append_return(&vars, "aors", id);
675
676 if (!ast_strlen_zero(outbound_proxy)) {
677 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
678 }
679
680 if (ast_strlen_zero(hint_context)) {
681 hint_context = ast_variable_find_last_in_list(vars, "context");
682 }
683
684 if (ast_strlen_zero(hint_context)) {
685 hint_context = "default";
686 }
687
688 if (!ast_strlen_zero(hint_exten)) {
689 /* These are added so we can find and delete the hints when the endpoint gets deleted */
690 variable_list_append_return(&vars, "@hint_context", hint_context);
691 variable_list_append_return(&vars, "@hint_exten", hint_exten);
692 }
693
694 if (!ast_strlen_zero(transport)) {
695 variable_list_append_return(&vars, "transport", transport);
696 }
697
698 if (is_variable_true(wizvars, "sends_auth")) {
699 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
700 variable_list_append_return(&vars, "outbound_auth", new_id);
701 }
702
703 if (is_variable_true(wizvars, "accepts_auth")) {
704 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
705 variable_list_append_return(&vars, "auth", new_id);
706 }
707
708 obj = create_object(sorcery, id, "endpoint", vars);
709 if (!obj) {
710 return -1;
711 }
712
713 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
714 otw->wizard->create(sorcery, otw->wizard_data, obj);
715 }
716 ao2_ref(obj, -1);
717
718 if (!ast_strlen_zero(hint_exten)) {
719 if (is_variable_true(wizvars, "has_hint")) {
720 add_hints(hint_context, hint_exten, hint_application, id);
721 } else {
722 delete_extens(hint_context, hint_exten);
723 }
724 }
725
726 return 0;
727}
728
729static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
730 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
731{
732 struct ast_variable *wizvars = ast_category_first(wiz);
733 struct ast_sorcery_object *obj = NULL;
734 const char *id = ast_category_get_name(wiz);
735 char new_id[strlen(id) + MAX_ID_SUFFIX];
736 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
737 int host_counter;
738 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
739
740 snprintf(new_id, sizeof(new_id), "%s-identify", id);
741
742 /* If accepting registrations or we're sending line, we don't need an identify. */
743 if (is_variable_true(wizvars, "accepts_registrations")
744 || is_variable_true(wizvars, "sends_line_with_registrations")) {
745 /* If one exists, delete it. */
746 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
747 if (obj) {
748 otw->wizard->delete(sorcery, otw->wizard_data, obj);
749 ao2_ref(obj, -1);
750 }
751 return 0;
752 }
753
754 if (!host_count) {
756 "Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
757 return -1;
758 }
759
760 variable_list_append_return(&vars, "endpoint", id);
761 variable_list_append_return(&vars, "@pjsip_wizard", id);
762
763 if (!ast_variable_find_last_in_list(vars, "match")) {
764 for (host_counter = 0; host_counter < host_count; host_counter++) {
765 variable_list_append_return(&vars, "match",
766 AST_VECTOR_GET(remote_hosts_vector, host_counter));
767 }
768 }
769
770 obj = create_object(sorcery, new_id, "identify", vars);
771 if (!obj) {
772 return -1;
773 }
774
775 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
776 otw->wizard->create(sorcery, otw->wizard_data, obj);
777 }
778 ao2_ref(obj, -1);
779
780 return 0;
781}
782
783static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
784 struct ast_category *wiz)
785{
786 struct ast_variable *wizvars = ast_category_first(wiz);
787 struct ast_sorcery_object *obj = NULL;
788 const char *id = ast_category_get_name(wiz);
789 char new_id[strlen(id) + MAX_ID_SUFFIX];
790 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
791
792 snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
793
794 if (!is_variable_true(wizvars, "has_phoneprov")) {
795 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
796 if (obj) {
797 otw->wizard->delete(sorcery, otw->wizard_data, obj);
798 ao2_ref(obj, -1);
799 }
800 return 0;
801 }
802
803 if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
805 "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
806 return -1;
807 }
808
809 variable_list_append_return(&vars, "endpoint", id);
810 variable_list_append_return(&vars, "@pjsip_wizard", id);
811
812 obj = create_object(sorcery, new_id, "phoneprov", vars);
813 if (!obj) {
814 return -1;
815 }
816
817 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
818 otw->wizard->create(sorcery, otw->wizard_data, obj);
819 }
820 ao2_ref(obj, -1);
821
822 return 0;
823}
824
825static int delete_existing_cb(void *obj, void *arg, int flags)
826{
827 struct object_type_wizard *otw = arg;
828
829 if (!strcmp(otw->object_type, "endpoint")) {
830 const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
831 const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
832 if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) {
833 delete_extens(context, exten);
834 }
835 }
836
837 otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
838
839 return CMP_MATCH;
840}
841
842static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
843 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
844{
845 struct ast_variable *search;
846 struct ast_variable *wizvars = ast_category_first(wiz);
847 const char *id = ast_category_get_name(wiz);
848 const char *server_uri_pattern;
849 const char *client_uri_pattern;
850 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
851 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
852 const char *username;
853 char new_id[strlen(id) + MAX_ID_SUFFIX];
854 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
855 int host_counter;
856 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
857 RAII_VAR(struct ao2_container *, existing,
859
860 if (!existing) {
861 return -1;
862 }
863
864 /* Find any existing registrations. */
865 search = ast_variable_new("@pjsip_wizard", id, "");
866 if (!search) {
867 return -1;
868 }
869
870 if (!ast_strlen_zero(outbound_proxy)) {
871 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
872 }
873
874 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
875 ast_variables_destroy(search);
876
877 /* If not sending registrations, delete ALL existing registrations for this wizard. */
878 if (!is_variable_true(wizvars, "sends_registrations")) {
879 if (ao2_container_count(existing) > 0) {
881 }
882 return 0;
883 }
884
885 if (!host_count) {
886 ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
887 return -1;
888 }
889
890 variable_list_append_return(&vars, "@pjsip_wizard", id);
891
892 if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
893 server_uri_pattern = "sip:${REMOTE_HOST}";
894 }
895
896 if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
897 client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
898 }
899
900 if (is_variable_true(wizvars, "sends_auth")) {
901 if (!(username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username"))) {
902 ast_log(LOG_ERROR, "Wizard '%s' must have 'outbound_auth/username' if it sends"
903 " authentication.\n", id);
904 return -1;
905 }
906 } else {
907 username = id;
908 }
909
910
911 /* Unlike aor and identify, we need to create a separate registration object
912 * for each remote host.
913 */
914 for (host_counter = 0; host_counter < host_count; host_counter++) {
915 struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
916 AST_VECTOR_GET(remote_hosts_vector, host_counter));
917 struct ast_var_t *un = ast_var_assign("USERNAME", username);
918 struct ast_sorcery_object *obj;
919 RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
921 RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
922
923 AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
924 AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
925
926 if (!ast_strlen_zero(server_uri_pattern)) {
927 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
928 server_uri_pattern);
929 variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
930 }
931
932 if (!ast_strlen_zero(client_uri_pattern)) {
933 ast_str_reset(uri);
934 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
935 client_uri_pattern);
936 variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
937 }
938
939 if (is_variable_true(wizvars, "sends_auth")) {
940 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
941 variable_list_append_return(&registration_vars, "outbound_auth", new_id);
942 }
943
944 if (!ast_strlen_zero(transport)) {
945 variable_list_append_return(&registration_vars, "transport", transport);
946 }
947
948 if (is_variable_true(wizvars, "sends_line_with_registrations")) {
949 variable_list_append_return(&registration_vars, "line", "yes");
950 variable_list_append_return(&registration_vars, "endpoint", id);
951 }
952
953 snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
954
955 obj = create_object(sorcery, new_id, "registration", registration_vars);
956 if (!obj) {
957 return -1;
958 }
959
960 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
961 otw->wizard->create(sorcery, otw->wizard_data, obj);
962 }
963 ao2_ref(obj, -1);
964
965 /* Unlink it from the 'existing' container. Any left will be deleted from
966 * sorcery. If it wasn't in the existing container, no harm.
967 */
969 }
970
971 /* If there are any excess registrations, delete them. */
972 if (ao2_container_count(existing) > 0) {
974 }
975
976 return 0;
977}
978
979static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
980 struct ast_category *wiz)
981{
982 struct ast_variable *wizvars = ast_category_first(wiz);
983 struct string_vector remote_hosts_vector;
984 const char *remote_hosts;
985 int rc = -1;
986
987 AST_VECTOR_INIT(&remote_hosts_vector, 16);
988 remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
989
990 if (!ast_strlen_zero(remote_hosts)) {
991 char *host;
992 char *hosts = ast_strdupa(remote_hosts);
993
994 while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
995 host = ast_strdup(host);
996 if (host && AST_VECTOR_APPEND(&remote_hosts_vector, host)) {
997 ast_free(host);
998 }
999 }
1000 }
1001
1002 ast_debug(4, "%s handler starting.\n", otw->object_type);
1003
1004 if (!strcmp(otw->object_type, "auth")) {
1005 rc = handle_auths(sorcery, otw, wiz);
1006 } else if (!strcmp(otw->object_type, "aor")) {
1007 rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
1008 } else if (!strcmp(otw->object_type, "endpoint")) {
1009 rc = handle_endpoint(sorcery, otw, wiz);
1010 } else if (!strcmp(otw->object_type, "identify")) {
1011 rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
1012 } else if (!strcmp(otw->object_type, "phoneprov")) {
1013 rc = handle_phoneprov(sorcery, otw, wiz);
1014 } else if (!strcmp(otw->object_type, "registration")) {
1015 rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
1016 }
1017
1019 AST_VECTOR_FREE(&remote_hosts_vector);
1020
1021 ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
1022
1023 return rc;
1024}
1025
1026/*
1027 * Everything below are the sorcery observers.
1028 */
1029static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
1030static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
1031static void object_type_loaded_observer(const char *name,
1032 const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
1033static void object_type_registered_observer(const char *name,
1034 struct ast_sorcery *sorcery, const char *object_type);
1035
1038 .instance_destroying = instance_destroying_observer,
1039};
1040
1043 .object_type_loaded = object_type_loaded_observer,
1044};
1045
1046/*! \brief Called after an object type is loaded/reloaded */
1047static void object_type_loaded_observer(const char *name,
1048 const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1049{
1050 struct ast_category *category = NULL;
1051 struct object_type_wizard *otw = NULL;
1052 char *filename = "pjsip_wizard.conf";
1053 struct ast_flags flags = { 0 };
1054 struct ast_config *cfg;
1055
1056 if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
1057 /* Not interested. */
1058 return;
1059 }
1060
1061 otw = find_wizard(object_type);
1062 if (!otw) {
1063 ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
1064 return;
1065 }
1066
1067 if (reloaded && otw->last_config) {
1069 }
1070
1071 cfg = ast_config_load2(filename, object_type, flags);
1072
1073 if (!cfg) {
1074 ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
1075 return;
1076 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1077 ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
1078 return;
1079 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1080 ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
1081 return;
1082 }
1083
1084 while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
1085 const char *id = ast_category_get_name(category);
1086 struct ast_category *last_cat = NULL;
1087 int changes = 0;
1088
1089 if (otw->last_config) {
1090 last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
1091 changes = !ast_variable_lists_match(ast_category_first(category), ast_category_first(last_cat), 1);
1092 if (last_cat) {
1093 ast_category_delete(otw->last_config, last_cat);
1094 }
1095 }
1096
1097 if (!last_cat || changes) {
1098 ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
1099 if (wizard_apply_handler(sorcery, otw, category)) {
1100 ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
1101 }
1102 }
1103 }
1104
1105 if (!otw->last_config) {
1106 otw->last_config = cfg;
1107 return;
1108 }
1109
1110 /* Only wizards that weren't in the new config are left in last_config now so we need to delete
1111 * all objects belonging to them.
1112 */
1113 category = NULL;
1114 while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
1115 const char *id = ast_category_get_name(category);
1116 struct ast_variable *search;
1117 RAII_VAR(struct ao2_container *, existing,
1119
1120 if (!existing) {
1121 ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
1122 break;
1123 }
1124
1125 search = ast_variable_new("@pjsip_wizard", id, "");
1126 if (!search) {
1127 ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
1128 break;
1129 }
1130 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
1131 ast_variables_destroy(search);
1132
1133 if (ao2_container_count(existing) > 0) {
1134 ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
1135 reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
1137 delete_existing_cb, otw);
1138 }
1139 }
1140
1142 otw->last_config = cfg;
1143}
1144
1145/*! \brief When each object type is registered, map a memory wizard to it. */
1147 struct ast_sorcery *sorcery, const char *object_type)
1148{
1149 struct ast_sorcery_wizard *wizard;
1150 void *wizard_data;
1151 struct object_type_wizard *otw;
1152
1153 if (is_one_of(object_type, object_types)) {
1157 ast_log(LOG_ERROR, "Unable to apply sangoma wizard to object type '%s'\n", object_type);
1158 return;
1159 }
1160
1161 otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
1162 if (!otw) {
1163 return;
1164 }
1165
1166 otw->sorcery = sorcery;
1167 otw->wizard = wizard;
1168 otw->wizard_data = wizard_data;
1169 otw->last_config = NULL;
1170 strcpy(otw->object_type, object_type); /* Safe */
1171 AST_VECTOR_RW_WRLOCK(&object_type_wizards);
1172 if (AST_VECTOR_APPEND(&object_type_wizards, otw)) {
1173 ast_free(otw);
1174 } else {
1175 ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
1176 }
1177 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1178
1179 }
1180}
1181
1182/*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
1183 * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
1184 * destroyed.
1185 */
1186static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
1187{
1188 if (strcmp(name, "res_pjsip")) {
1189 return;
1190 }
1193}
1194
1195/*! \brief When the res_pjsip instance is destroyed, remove the observer
1196 * and unref the module. This should then allow this module to unload cleanly.
1197 */
1198static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
1199{
1200 if (strcmp(name, "res_pjsip")) {
1201 return;
1202 }
1203
1206}
1207
1208static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1209{
1210 struct ast_sorcery *sorcery;
1211 int idx;
1212 FILE *f = NULL;
1213 const char *fn = NULL;
1214
1215 switch (cmd) {
1216 case CLI_INIT:
1217 e->command = "pjsip export config_wizard primitives [to]";
1218 e->usage =
1219 "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
1220 " Export the config_wizard objects as pjsip primitives to\n"
1221 " the console or to <filename>\n";
1222 return NULL;
1223 case CLI_GENERATE:
1224 return NULL;
1225 }
1226
1227 if (a->argc > 5) {
1228 char date[256]="";
1229 time_t t;
1230 fn = a->argv[5];
1231
1232 time(&t);
1233 ast_copy_string(date, ctime(&t), sizeof(date));
1234 f = fopen(fn, "w");
1235 if (!f) {
1236 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
1237 return CLI_FAILURE;
1238 }
1239
1240 fprintf(f, ";!\n");
1241 fprintf(f, ";! Automatically generated configuration file\n");
1242 fprintf(f, ";! Filename: %s\n", fn);
1243 fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
1244 fprintf(f, ";! Creation Date: %s", date);
1245 fprintf(f, ";!\n");
1246 }
1247
1249
1250 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
1251 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
1252 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
1253 struct ao2_container *container;
1254 struct ao2_iterator i;
1255 void *o;
1256
1258 if (!container) {
1259 continue;
1260 }
1261
1263 while ((o = ao2_iterator_next(&i))) {
1264 struct ast_variable *vars;
1265 struct ast_variable *v;
1266
1268 if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
1269 if (f) {
1270 fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1271 } else {
1272 ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1273 }
1274 for (v = vars; v; v = v->next) {
1275 if (!ast_strlen_zero(v->value)) {
1276 if (f) {
1277 fprintf(f, "%s = %s\n", v->name, v->value);
1278 } else {
1279 ast_cli(a->fd, "%s = %s\n", v->name, v->value);
1280 }
1281 }
1282 }
1283 }
1285 ao2_ref(o, -1);
1286 }
1289 }
1290 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1291
1292 if (f) {
1293 fclose(f);
1294 ast_cli(a->fd, "Wrote configuration to %s\n", fn);
1295 }
1296
1297
1298 return CLI_SUCCESS;
1299}
1300
1302 AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
1303};
1304
1305static int reload_module(void)
1306{
1308 return 0;
1309}
1310
1311static int load_module(void)
1312{
1313 AST_VECTOR_RW_INIT(&object_type_wizards, 12);
1316
1317 /* If the PJSIP sorcery instance exists it means that we have been explicitly
1318 * loaded and things are potentially already set up. Since we won't receive any
1319 * observer callbacks informing us of this we add ourselves to the instance
1320 * as an observer in case it goes away, and determine which object types have
1321 * been registered and synthesize the observer callbacks for them. Once done
1322 * we force a reload so we are aware of the state of things.
1323 */
1324 if (ast_sip_get_sorcery()) {
1325 int i;
1326
1329
1330 /* See which object types already exist */
1331 for (i = 0; i < ARRAY_LEN(object_types); ++i) {
1332 if (!object_types[i]) {
1333 break;
1334 } else if (!ast_sorcery_get_object_type(ast_sip_get_sorcery(), object_types[i])) {
1335 continue;
1336 }
1337
1339 object_types[i]);
1340 }
1341
1343 }
1344
1346}
1347
1348static int unload_module(void)
1349{
1353 AST_VECTOR_RW_FREE(&object_type_wizards);
1354
1355 return 0;
1356}
1357
1359 .support_level = AST_MODULE_SUPPORT_CORE,
1360 .load = load_module,
1362 .unload = unload_module,
1363 .load_pri = AST_MODPRI_REALTIME_DRIVER,
#define paren
Definition: ael_lex.c:962
static const char app[]
Definition: app_adsiprog.c:56
enum queue_result id
Definition: app_queue.c:1638
#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[]
Definition: chan_ooh323.c:109
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
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#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:144
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
Definition: main/config.c:1118
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3322
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *cat)
Delete a category.
Definition: main/config.c:1568
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.
Definition: main/config.c:932
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.
Definition: main/config.c:920
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:544
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
Definition: main/config.c:1247
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.
Definition: main/config.c:862
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1112
@ CONFIG_FLAG_FILEUNCHANGED
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.
Definition: main/config.c:1425
#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:8562
int ast_wrlock_contexts(void)
Write locks the context list.
Definition: pbx.c:8463
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:4978
int ast_wrlock_context(struct ast_context *con)
Write locks a given context.
Definition: pbx.c:8481
const char * ast_get_extension_app(struct ast_exten *e)
Definition: pbx.c:8557
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:6149
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition: pbx.c:4948
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
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:7266
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:8473
static int reload(void)
struct ao2_container * container
Definition: res_fax.c:501
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 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 AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *)
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 handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static struct ast_sorcery * sorcery
#define NULL
Definition: resample.c:96
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:2335
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
#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:537
@ 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:2494
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition: sorcery.c:1408
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2464
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
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:1632
void ast_sorcery_global_observer_remove(const struct ast_sorcery_global_observer *callbacks)
Remove a global observer from sorcery.
Definition: sorcery.c:514
int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer *callbacks)
Add a global observer to sorcery.
Definition: sorcery.c:498
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:520
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:1897
@ 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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
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:2199
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
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:1835
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:284
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition: pbx.c:237
char * exten
Definition: pbx.c:238
void * data
Definition: pbx.c:248
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
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:127
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:230
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
int stacklen
Definition: extconf.h:237
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:941
#define ast_assert(a)
Definition: utils.h:739
#define ARRAY_LEN(a)
Definition: utils.h:666
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#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:461
#define AST_VECTOR_RW_WRLOCK(vec)
Obtain write lock on vector.
Definition: vector.h:887
#define AST_VECTOR_RW_UNLOCK(vec)
Unlock vector.
Definition: vector.h:897
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_RW_FREE(vec)
Deallocates this locked vector.
Definition: vector.h:202
#define AST_VECTOR_RW_RDLOCK(vec)
Obtain read lock on vector.
Definition: vector.h:877
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#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:680
#define AST_VECTOR_RW_INIT(vec, size)
Initialize a vector with a read/write lock.
Definition: vector.h:158