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