Merge "rtp_engine/res_rtp_asterisk: Fix RTP struct reentrancy crashes."
[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                 username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username");
915         } else {
916                 username = id;
917         }
918
919
920         /* Unlike aor and identify, we need to create a separate registration object
921          * for each remote host.
922          */
923         for (host_counter = 0; host_counter < host_count; host_counter++) {
924                 struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
925                         AST_VECTOR_GET(remote_hosts_vector, host_counter));
926                 struct ast_var_t *un = ast_var_assign("USERNAME", username);
927                 struct ast_sorcery_object *obj;
928                 RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
929                 RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
930                 RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
931
932                 AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
933                 AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
934
935                 if (!ast_strlen_zero(server_uri_pattern)) {
936                         ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
937                                 server_uri_pattern);
938                         variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
939                 }
940
941                 if (!ast_strlen_zero(client_uri_pattern)) {
942                         ast_str_reset(uri);
943                         ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
944                                 client_uri_pattern);
945                         variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
946                 }
947
948                 if (is_variable_true(wizvars, "sends_auth")) {
949                         snprintf(new_id, sizeof(new_id), "%s-oauth", id);
950                         variable_list_append_return(&registration_vars, "outbound_auth", new_id);
951                 }
952
953                 if (!ast_strlen_zero(transport)) {
954                         variable_list_append_return(&registration_vars, "transport", transport);
955                 }
956
957                 if (is_variable_true(wizvars, "sends_line_with_registrations")) {
958                         variable_list_append_return(&registration_vars, "line", "yes");
959                         variable_list_append_return(&registration_vars, "endpoint", id);
960                 }
961
962                 snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
963
964                 obj = create_object(sorcery, new_id, "registration", registration_vars);
965                 if (!obj) {
966                         return -1;
967                 }
968
969                 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
970                         otw->wizard->create(sorcery, otw->wizard_data, obj);
971                 }
972                 ao2_ref(obj, -1);
973
974                 /* Unlink it from the 'existing' container.  Any left will be deleted from
975                  * sorcery.  If it wasn't in the existing container, no harm.
976                  */
977                 ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY, ast_sorcery_object_id_compare, new_id);
978         }
979
980         /* If there are any excess registrations, delete them. */
981         if (ao2_container_count(existing) > 0) {
982                 ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
983         }
984
985         return 0;
986 }
987
988 static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
989         struct ast_category *wiz)
990 {
991         struct ast_variable *wizvars = ast_category_first(wiz);
992         struct string_vector remote_hosts_vector;
993         const char *remote_hosts;
994         int rc = -1;
995
996         AST_VECTOR_INIT(&remote_hosts_vector, 16);
997         remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
998
999         if (!ast_strlen_zero(remote_hosts)) {
1000                 char *host;
1001                 char *hosts = ast_strdupa(remote_hosts);
1002
1003                 while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
1004                         AST_VECTOR_APPEND(&remote_hosts_vector, ast_strdup(host));
1005                 }
1006         }
1007
1008         ast_debug(4, "%s handler starting.\n", otw->object_type);
1009
1010         if (!strcmp(otw->object_type, "auth")) {
1011                 rc = handle_auths(sorcery, otw, wiz);
1012         } else if (!strcmp(otw->object_type, "aor")) {
1013                 rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
1014         } else if (!strcmp(otw->object_type, "endpoint")) {
1015                 rc = handle_endpoint(sorcery, otw, wiz);
1016         } else if (!strcmp(otw->object_type, "identify")) {
1017                 rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
1018         } else if (!strcmp(otw->object_type, "phoneprov")) {
1019                 rc = handle_phoneprov(sorcery, otw, wiz);
1020         } else if (!strcmp(otw->object_type, "registration")) {
1021                 rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
1022         }
1023
1024         AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
1025         AST_VECTOR_FREE(&remote_hosts_vector);
1026
1027         ast_debug(4, "%s handler complete.  rc: %d\n", otw->object_type, rc);
1028
1029         return rc;
1030 }
1031
1032 /*
1033  * Everything below are the sorcery observers.
1034  */
1035 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
1036 static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
1037 static void object_type_loaded_observer(const char *name,
1038         const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
1039 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
1040         const char *object_type, struct ast_sorcery_wizard *wizard,
1041         const char *wizard_args, void *wizard_data);
1042 static void object_type_registered_observer(const char *name,
1043         struct ast_sorcery *sorcery, const char *object_type);
1044
1045 const static struct ast_sorcery_global_observer global_observer = {
1046         .instance_created = instance_created_observer,
1047         .instance_destroying = instance_destroying_observer,
1048 };
1049
1050 struct ast_sorcery_instance_observer observer = {
1051         .wizard_mapped = wizard_mapped_observer,
1052         .object_type_registered = object_type_registered_observer,
1053         .object_type_loaded = object_type_loaded_observer,
1054 };
1055
1056 /*! \brief Called after an object type is loaded/reloaded */
1057 static void object_type_loaded_observer(const char *name,
1058         const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1059 {
1060         struct ast_category *category = NULL;
1061         struct object_type_wizard *otw = NULL;
1062         char *filename = "pjsip_wizard.conf";
1063         struct ast_flags flags = { 0 };
1064         struct ast_config *cfg;
1065
1066         if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
1067                 /* Not interested. */
1068                 return;
1069         }
1070
1071         otw = find_wizard(object_type);
1072         if (!otw) {
1073                 ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
1074                 return;
1075         }
1076
1077         if (reloaded && otw->last_config) {
1078                 flags.flags = CONFIG_FLAG_FILEUNCHANGED;
1079         }
1080
1081         cfg = ast_config_load2(filename, object_type, flags);
1082
1083         if (!cfg) {
1084                 ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
1085                 return;
1086         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1087                 ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
1088                 return;
1089         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1090                 ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
1091                 return;
1092         }
1093
1094         while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
1095                 const char *id = ast_category_get_name(category);
1096                 struct ast_category *last_cat = NULL;
1097                 struct ast_variable *change_set = NULL;
1098
1099                 if (otw->last_config) {
1100                         last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
1101                         ast_sorcery_changeset_create(ast_category_first(category), ast_category_first(last_cat), &change_set);
1102                         if (last_cat) {
1103                                 ast_category_delete(otw->last_config, last_cat);
1104                         }
1105                 }
1106
1107                 if (!last_cat || change_set) {
1108                         ast_variables_destroy(change_set);
1109                         ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
1110                         if (wizard_apply_handler(sorcery, otw, category)) {
1111                                 ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
1112                         }
1113                 }
1114         }
1115
1116         if (!otw->last_config) {
1117                 otw->last_config = cfg;
1118                 return;
1119         }
1120
1121         /* Only wizards that weren't in the new config are left in last_config now so we need to delete
1122          * all objects belonging to them.
1123          */
1124         category = NULL;
1125         while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
1126                 const char *id = ast_category_get_name(category);
1127                 struct ast_variable *search;
1128                 RAII_VAR(struct ao2_container *, existing,
1129                         ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
1130
1131                 if (!existing) {
1132                         ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
1133                         break;
1134                 }
1135
1136                 search = ast_variable_new("@pjsip_wizard", id, "");
1137                 if (!search) {
1138                         ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
1139                         break;
1140                 }
1141                 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
1142                 ast_variables_destroy(search);
1143
1144                 if (ao2_container_count(existing) > 0) {
1145                         ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
1146                                 reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
1147                         ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
1148                                 delete_existing_cb, otw);
1149                 }
1150         }
1151
1152         ast_config_destroy(otw->last_config);
1153         otw->last_config = cfg;
1154 }
1155
1156 /*! \brief When each wizard is mapped, save it off to the vector. */
1157 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
1158         const char *object_type, struct ast_sorcery_wizard *wizard,
1159         const char *wizard_args, void *wizard_data)
1160 {
1161         struct object_type_wizard *otw;
1162
1163         if (!is_one_of(object_type, object_types)) {
1164                 /* Not interested. */
1165                 return;
1166         }
1167
1168         /* We're only interested in memory wizards with the pjsip_wizard tag. */
1169         if (wizard_args && !strcmp(wizard_args, "pjsip_wizard")) {
1170                 otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
1171                 otw->sorcery = sorcery;
1172                 otw->wizard = wizard;
1173                 otw->wizard_data = wizard_data;
1174                 otw->last_config = NULL;
1175                 strcpy(otw->object_type, object_type); /* Safe */
1176                 AST_VECTOR_RW_WRLOCK(&object_type_wizards);
1177                 AST_VECTOR_APPEND(&object_type_wizards, otw);
1178                 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1179                 ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
1180         }
1181 }
1182
1183 /*! \brief When each object type is registered, map a memory wizard to it. */
1184 static void object_type_registered_observer(const char *name,
1185         struct ast_sorcery *sorcery, const char *object_type)
1186 {
1187         if (is_one_of(object_type, object_types)) {
1188                 ast_sorcery_apply_wizard_mapping(sorcery, object_type, "memory", "pjsip_wizard", 0);
1189         }
1190 }
1191
1192 /*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
1193  * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
1194  * destroyed.
1195  */
1196 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
1197 {
1198         if (strcmp(name, "res_pjsip")) {
1199                 return;
1200         }
1201         ast_module_ref(ast_module_info->self);
1202         ast_sorcery_instance_observer_add(sorcery, &observer);
1203 }
1204
1205 /*! \brief When the res_pjsip instance is destroyed, remove the observer
1206  * and unref the module.  This should then allow this module to unload cleanly.
1207  */
1208 static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
1209 {
1210         if (strcmp(name, "res_pjsip")) {
1211                 return;
1212         }
1213
1214         ast_sorcery_instance_observer_remove(sorcery, &observer);
1215         ast_module_unref(ast_module_info->self);
1216 }
1217
1218 static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1219 {
1220         struct ast_sorcery *sorcery;
1221         int idx;
1222         FILE *f = NULL;
1223         const char *fn = NULL;
1224
1225         switch (cmd) {
1226         case CLI_INIT:
1227                 e->command = "pjsip export config_wizard primitives [to]";
1228                 e->usage =
1229                         "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
1230                         "       Export the config_wizard objects as pjsip primitives to\n"
1231                         "       the console or to <filename>\n";
1232                 return NULL;
1233         case CLI_GENERATE:
1234                 return NULL;
1235         }
1236
1237         if (a->argc > 5) {
1238                 char date[256]="";
1239                 time_t t;
1240                 fn = a->argv[5];
1241
1242                 time(&t);
1243                 ast_copy_string(date, ctime(&t), sizeof(date));
1244                 f = fopen(fn, "w");
1245                 if (!f) {
1246                         ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
1247                         return CLI_FAILURE;
1248                 }
1249
1250                 fprintf(f, ";!\n");
1251                 fprintf(f, ";! Automatically generated configuration file\n");
1252                 fprintf(f, ";! Filename: %s\n", fn);
1253                 fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
1254                 fprintf(f, ";! Creation Date: %s", date);
1255                 fprintf(f, ";!\n");
1256         }
1257
1258         sorcery = ast_sip_get_sorcery();
1259
1260         AST_VECTOR_RW_RDLOCK(&object_type_wizards);
1261         for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
1262                 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
1263                 struct ao2_container *container;
1264                 struct ao2_iterator i;
1265                 void *o;
1266
1267                 container = ast_sorcery_retrieve_by_fields(sorcery, otw->object_type, AST_RETRIEVE_FLAG_MULTIPLE, NULL);
1268                 if (!container) {
1269                         continue;
1270                 }
1271
1272                 i = ao2_iterator_init(container, 0);
1273                 while ((o = ao2_iterator_next(&i))) {
1274                         struct ast_variable *vars;
1275                         struct ast_variable *v;
1276
1277                         vars = ast_sorcery_objectset_create(sorcery, o);
1278                         if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
1279                                 if (f) {
1280                                         fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1281                                 } else {
1282                                         ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1283                                 }
1284                                 for (v = vars; v; v = v->next) {
1285                                         if (!ast_strlen_zero(v->value)) {
1286                                                 if (f) {
1287                                                         fprintf(f, "%s = %s\n", v->name, v->value);
1288                                                 } else {
1289                                                         ast_cli(a->fd, "%s = %s\n", v->name, v->value);
1290                                                 }
1291                                         }
1292                                 }
1293                         }
1294                         ast_variables_destroy(vars);
1295                         ao2_ref(o, -1);
1296                 }
1297                 ao2_iterator_destroy(&i);
1298                 ao2_cleanup(container);
1299         }
1300         AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1301
1302         if (f) {
1303                 fclose(f);
1304                 ast_cli(a->fd, "Wrote configuration to %s\n", fn);
1305         }
1306
1307
1308         return CLI_SUCCESS;
1309 }
1310
1311 static struct ast_cli_entry config_wizard_cli[] = {
1312         AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
1313 };
1314
1315 static int load_module(void)
1316 {
1317         AST_VECTOR_RW_INIT(&object_type_wizards, 12);
1318         ast_sorcery_global_observer_add(&global_observer);
1319         ast_cli_register_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
1320
1321         return AST_MODULE_LOAD_SUCCESS;
1322 }
1323
1324 static int unload_module(void)
1325 {
1326         ast_cli_unregister_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
1327         ast_sorcery_global_observer_remove(&global_observer);
1328         AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
1329         AST_VECTOR_RW_FREE(&object_type_wizards);
1330
1331         return 0;
1332 }
1333
1334 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Config Wizard",
1335         .support_level = AST_MODULE_SUPPORT_CORE,
1336         .load = load_module,
1337         .unload = unload_module,
1338         .load_pri = AST_MODPRI_REALTIME_DRIVER,
1339 );