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