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