res_pjsip_config_wizard: Allow streamlined config of common pjsip scenarios
[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>res_pjsip</depend>
36         <support_level>core</support_level>
37  ***/
38
39 #include "asterisk.h"
40
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42
43 #include <regex.h>
44 #include <pjsip.h>
45
46 #include "asterisk/astobj2.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.</para>
67                         <para> </para>
68
69                         <para>[myphone]</para>
70                         <para>type = wizard</para>
71                         <para>sends_auth = no</para>
72                         <para>accepts_auth = yes</para>
73                         <para>sends_registrations = no</para>
74                         <para>accepts_registrations = yes</para>
75                         <para>has_phoneprov = yes</para>
76                         <para>transport = ipv4</para>
77                         <para>inbound_auth/username = testname</para>
78                         <para>inbound_auth/password = test password</para>
79                         <para>endpoint/allow = ulaw</para>
80                         <para>endpoint/context = default</para>
81                         <para>phoneprov/MAC = 001122aa4455</para>
82                         <para>phoneprov/PROFILE = profile1</para>
83                         <para> </para>
84
85                         <para>The first 7 items are specific to the wizard.  The rest of the items
86                         are passed verbatim to the underlying objects.</para>
87                         <para> </para>
88
89                         <para>The following configuration snippet would create the
90                         endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
91                         to another pbx or ITSP that requires registration.</para>
92                         <para> </para>
93
94                         <para>[mytrunk]</para>
95                         <para>type = wizard</para>
96                         <para>sends_auth = yes</para>
97                         <para>accepts_auth = no</para>
98                         <para>sends_registrations = yes</para>
99                         <para>accepts_registrations = no</para>
100                         <para>transport = ipv4</para>
101                         <para>remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060</para>
102                         <para>outbound_auth/username = testname</para>
103                         <para>outbound_auth/password = test password</para>
104                         <para>endpoint/allow = ulaw</para>
105                         <para>endpoint/context = default</para>
106                         <para> </para>
107
108                         <para>Of course, any of the items in either example could be placed into
109                         templates and shared among wizard objects.</para>
110                 </description>
111
112                 <configFile name="pjsip_wizard.conf">
113                         <configObject name="wizard">
114                                 <synopsis>Provides config wizard.</synopsis>
115                                 <configOption name="type">
116                                         <synopsis>Must be 'wizard'.</synopsis>
117                                 </configOption>
118                                 <configOption name="transport">
119                                         <synopsis>The name of a transport to use for this object.</synopsis>
120                                         <description><para>If not specified,
121                                         the default will be used.</para></description>
122                                 </configOption>
123                                 <configOption name="remote_hosts">
124                                         <synopsis>List of remote hosts.</synopsis>
125                                         <description><para>A comma-separated list of remote hosts in the form of
126                                         <replaceable>host</replaceable>[:<replaceable>port</replaceable>].
127                                         If set, an aor static contact and an identify match will be created for each
128                                         entry in the list.  If send_registrations is also set, a registration will
129                                         also be created for each.</para></description>
130                                 </configOption>
131                                 <configOption name="sends_auth" default="no">
132                                         <synopsis>Send outbound authentication to remote hosts.</synopsis>
133                                         <description><para>At least outbound_auth/username is required.</para></description>
134                                 </configOption>
135                                 <configOption name="accepts_auth" default="no">
136                                         <synopsis>Accept incoming authentication from remote hosts.</synopsis>
137                                         <description><para>At least inbound_auth/username is required.</para></description>
138                                 </configOption>
139                                 <configOption name="sends_registrations" default="no">
140                                         <synopsis>Send outbound registrations to remote hosts.</synopsis>
141                                         <description><para>remote_hosts is required and a registration object will
142                                         be created for each host in the remote _hosts string.  If authentication is required,
143                                         sends_auth and an outbound_auth/username must also be supplied.</para></description>
144                                 </configOption>
145                                 <configOption name="accepts_registrations" default="no">
146                                         <synopsis>Accept inbound registration from remote hosts.</synopsis>
147                                         <description><para>An AOR with dynamic contacts will be created.  If
148                                         the number of contacts nneds to be limited, set aor/max_contacts.</para></description>
149                                 </configOption>
150                                 <configOption name="has_phoneprov" default="no">
151                                         <synopsis>Create a phoneprov object for this endpoint.</synopsis>
152                                         <description><para>A phoneprov object will be created.  phoneprov/MAC
153                                         must be specified.</para></description>
154                                 </configOption>
155                                 <configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
156                                         <synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
157                                         <description><para>
158                                         The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
159                                         appropriate remote_host for each registration.</para></description>
160                                 </configOption>
161                                 <configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
162                                         <synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
163                                         <description><para>
164                                         The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
165                                         will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
166                                 </configOption>
167                                 <configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
168                                         <synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
169                                         <description><para>
170                                         The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
171                                         appropriate remote_host for each contact.</para></description>
172                                 </configOption>
173                                 <configOption name="endpoint&#47;*">
174                                         <synopsis>Variables to be passed directly to the endpoint.</synopsis>
175                                 </configOption>
176                                 <configOption name="aor&#47;*">
177                                         <synopsis>Variables to be passed directly to the aor.</synopsis>
178                                         <description><para>If an aor/contact is explicitly defined then remote_hosts
179                                         will not be used to create contacts automatically.</para></description>
180                                 </configOption>
181                                 <configOption name="inbound_auth&#47;*">
182                                         <synopsis>Variables to be passed directly to the inbound auth.</synopsis>
183                                 </configOption>
184                                 <configOption name="outbound_auth&#47;*">
185                                         <synopsis>Variables to be passed directly to the outbound auth.</synopsis>
186                                 </configOption>
187                                 <configOption name="identify&#47;*">
188                                         <synopsis>Variables to be passed directly to the identify.</synopsis>
189                                         <description><para>If an identify/match is explicitly defined then remote_hosts
190                                         will not be used to create matches automatically.</para></description>
191                                 </configOption>
192                                 <configOption name="registration&#47;*">
193                                         <synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
194                                 </configOption>
195                                 <configOption name="phoneprov&#47;*">
196                                         <synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
197                                         <description><para>
198                                         To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
199                                 </configOption>
200                         </configObject>
201                 </configFile>
202         </configInfo>
203  ***/
204
205  /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
206 #define MAX_ID_SUFFIX 20
207
208 /*! \brief A generic char * vector definition. */
209 AST_VECTOR(string_vector, char *);
210
211 /*! \brief Keeps track of the sorcery wizard and last config for each object type */
212 struct object_type_wizard {
213         struct ast_sorcery *sorcery;
214         struct ast_sorcery_wizard *wizard;
215         void *wizard_data;
216         struct ast_config *last_config;
217         char object_type[];
218 };
219 static AST_VECTOR(object_type_wizards, struct object_type_wizard *) object_type_wizards;
220
221 /*! \brief Callbacks for vector deletes */
222 #define NOT_EQUALS(a, b) (a != b)
223 #define OTW_DELETE_CB(otw) ({ \
224         ast_config_destroy(otw->last_config); \
225         ast_free(otw); \
226 })
227
228 const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
229
230 static int is_one_of(const char *needle, const char *haystack[])
231 {
232         int i;
233         for (i = 0; haystack[i]; i++) {
234                 if (!strcmp(needle, haystack[i])) {
235                         return 1;
236                 }
237         }
238
239         return 0;
240 }
241
242 /*! \brief Finds the otw for the object type */
243 static struct object_type_wizard *find_wizard(const char *object_type)
244 {
245         int idx;
246
247         for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
248                 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
249                 if (!strcmp(otw->object_type, object_type)) {
250                         return otw;
251                 }
252         }
253
254         return NULL;
255 }
256
257 /*! \brief Creates a sorcery object and applies a variable list */
258 static void *create_object(const struct ast_sorcery *sorcery,
259         const char *id, const char *type, struct ast_variable *vars)
260 {
261         struct ast_sorcery_object *obj = ast_sorcery_alloc(sorcery, type, id);
262
263         if (!obj) {
264                 ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
265                 return NULL;
266         }
267
268         if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
269                 ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'.  Check preceeding errors.\n", type, id);
270                 ao2_ref(obj, -1);
271                 return NULL;
272         }
273
274         return obj;
275 }
276
277 /*! \brief Finds a variable in a list and tests it */
278 static int is_variable_true(struct ast_variable *vars, const char *name)
279 {
280         return ast_true(ast_variable_find_in_list(vars, name));
281 }
282
283 /*! \brief Appends a variable to the end of an existing list */
284 static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
285 {
286         struct ast_variable *new = ast_variable_new(name, value, "");
287
288         if (!new) {
289                 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
290                 return -1;
291         }
292
293         ast_variable_list_append(existing, new);
294
295         return 0;
296 }
297
298 /*! \brief Appends a variable to the end of an existing list.  On failure, cause the calling
299  * function to return -1 */
300 #define variable_list_append_return(existing, name, value) ({ \
301         struct ast_variable *new = ast_variable_new(name, value, ""); \
302         if (!new) { \
303                 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
304                 return -1; \
305         } \
306         ast_variable_list_append(existing, new); \
307 })
308
309 /*! \brief We need to strip off the prefix from the name of each variable
310  * so they're suitable for objectset_apply.
311  * I.E.  will transform outbound_auth/username to username.
312  */
313 static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
314 {
315         struct ast_variable *return_vars = NULL;
316         struct ast_variable *v = vars;
317         int plen = strlen(prefix);
318
319         for(; v; v = v->next) {
320                 if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
321                         if (variable_list_append(&return_vars, v->name + plen, v->value)) {
322                                 ast_variables_destroy(return_vars);
323                                 return NULL;
324                         }
325                 }
326         }
327
328         return return_vars;
329 }
330
331 static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
332         struct ast_category *wiz, char *direction)
333 {
334         struct ast_variable *wizvars = ast_category_first(wiz);
335         struct ast_sorcery_object *obj = NULL;
336         const char *id = ast_category_get_name(wiz);
337         char new_id[strlen(id) + MAX_ID_SUFFIX];
338         char prefix[strlen(direction) + strlen("_auth/") + 1];
339         char *test_variable = NULL;
340         RAII_VAR(struct ast_variable *, vars, NULL, ast_variables_destroy);
341
342         snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
343         vars = get_object_variables(wizvars, prefix);
344
345         if (!strcmp(direction, "outbound")) {
346                 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
347                 test_variable = "sends_auth";
348         } else {
349                 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
350                 test_variable = "accepts_auth";
351         }
352
353         if (is_variable_true(wizvars, test_variable)) {
354                 if (!ast_variable_find_in_list(vars, "username")) {
355                         ast_log(LOG_ERROR,
356                                 "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
357                         return -1;
358                 }
359         } else {
360                 /* Delete auth if sends or accepts is now false. */
361                 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
362                 if (obj) {
363                         otw->wizard->delete(sorcery, otw->wizard_data, obj);
364                         ao2_ref(obj, -1);
365                 }
366                 return 0;
367         }
368
369         variable_list_append_return(&vars, "@pjsip_wizard", id);
370
371         /* If the user set auth_type, don't override it. */
372         if (!ast_variable_find_in_list(vars, "auth_type")) {
373                 variable_list_append_return(&vars, "auth_type", "userpass");
374         }
375
376         obj = create_object(sorcery, new_id, "auth", vars);
377         if (!obj) {
378                 return -1;
379         }
380
381         if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
382                 otw->wizard->create(sorcery, otw->wizard_data, obj);
383         }
384         ao2_ref(obj, -1);
385
386         return 0;
387 }
388
389 static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
390         struct ast_category *wiz)
391 {
392         int rc;
393
394         if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
395                 return rc;
396         }
397
398         return handle_auth(sorcery, otw, wiz, "inbound");
399 }
400
401 static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
402         struct ast_category *wiz, struct string_vector *remote_hosts_vector)
403 {
404         struct ast_variable *wizvars = ast_category_first(wiz);
405         struct ast_sorcery_object *obj = NULL;
406         const char *id = ast_category_get_name(wiz);
407         const char *contact_pattern;
408         int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
409         RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
410
411         variable_list_append(&vars, "@pjsip_wizard", id);
412
413         /* If the user explicitly specified an aor/contact, don't use remote hosts. */
414         if (!ast_variable_find_in_list(vars, "contact")) {
415                 if (!(contact_pattern = ast_variable_find_in_list(wizvars, "contact_pattern"))) {
416                         contact_pattern = "sip:${REMOTE_HOST}";
417                 }
418
419                 if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
420                         int host_counter;
421
422                         /* ast_str_substitute_variables operate on a varshead list so we have
423                          * to create one to hold the REPORT_HOST substitution, do the substitution,
424                          * then append the result to the ast_variable list.
425                          */
426                         for (host_counter = 0; host_counter < host_count; host_counter++) {
427                                 RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
428                                 RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
429                                 struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
430                                         AST_VECTOR_GET(remote_hosts_vector, host_counter));
431
432                                 AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
433                                 ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
434                                         contact_pattern);
435
436                                 variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
437                         }
438                 }
439         }
440
441         obj = create_object(sorcery, id, "aor", vars);
442         if (!obj) {
443                 return -1;
444         }
445
446         if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
447                 otw->wizard->create(sorcery, otw->wizard_data, obj);
448         }
449         ao2_ref(obj, -1);
450
451         return 0;
452 }
453
454 static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
455         struct ast_category *wiz)
456 {
457         struct ast_variable *wizvars = ast_category_first(wiz);
458         struct ast_sorcery_object *obj = NULL;
459         const char *id = ast_category_get_name(wiz);
460         const char *transport = ast_variable_find_in_list(wizvars, "transport");
461         char new_id[strlen(id) + MAX_ID_SUFFIX];
462         RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
463
464         variable_list_append_return(&vars, "@pjsip_wizard", id);
465         variable_list_append_return(&vars, "aors", id);
466
467         if (!ast_strlen_zero(transport)) {
468                 variable_list_append_return(&vars, "transport", transport);
469         }
470
471         if (is_variable_true(wizvars, "sends_auth")) {
472                 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
473                 variable_list_append_return(&vars, "outbound_auth", new_id);
474         }
475
476         if (is_variable_true(wizvars, "accepts_auth")) {
477                 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
478                 variable_list_append_return(&vars, "auth", new_id);
479         }
480
481         obj = create_object(sorcery, id, "endpoint", vars);
482         if (!obj) {
483                 return -1;
484         }
485
486         if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
487                 otw->wizard->create(sorcery, otw->wizard_data, obj);
488         }
489         ao2_ref(obj, -1);
490
491         return 0;
492 }
493
494 static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
495         struct ast_category *wiz, struct string_vector *remote_hosts_vector)
496 {
497         struct ast_variable *wizvars = ast_category_first(wiz);
498         struct ast_sorcery_object *obj = NULL;
499         const char *id = ast_category_get_name(wiz);
500         char new_id[strlen(id) + MAX_ID_SUFFIX];
501         int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
502         int host_counter;
503         RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
504
505         snprintf(new_id, sizeof(new_id), "%s-identify", id);
506
507         /* If accepting registrations, we don't need an identify. */
508         if (is_variable_true(wizvars, "accepts_registrations")) {
509                 /* If one exists, delete it. */
510                 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
511                 if (obj) {
512                         otw->wizard->delete(sorcery, otw->wizard_data, obj);
513                         ao2_ref(obj, -1);
514                 }
515                 return 0;
516         }
517
518         if (!host_count) {
519                 ast_log(LOG_ERROR,
520                         "Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
521                 return -1;
522         }
523
524         variable_list_append_return(&vars, "endpoint", id);
525         variable_list_append_return(&vars, "@pjsip_wizard", id);
526
527         if (!ast_variable_find_in_list(vars, "match")) {
528                 for (host_counter = 0; host_counter < host_count; host_counter++) {
529                         char *rhost = AST_VECTOR_GET(remote_hosts_vector, host_counter);
530                         char host[strlen(rhost) + 1];
531                         char *colon;
532
533                         /* If there's a :port specified, we have to remove it. */
534                         strcpy(host, rhost); /* Safe */
535                         colon = strchr(host, ':');
536                         if (colon) {
537                                 *colon = '\0';
538                         }
539
540                         variable_list_append_return(&vars, "match", host);
541                 }
542         }
543
544         obj = create_object(sorcery, new_id, "identify", vars);
545         if (!obj) {
546                 return -1;
547         }
548
549         if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
550                 otw->wizard->create(sorcery, otw->wizard_data, obj);
551         }
552         ao2_ref(obj, -1);
553
554         return 0;
555 }
556
557 static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
558         struct ast_category *wiz)
559 {
560         struct ast_variable *wizvars = ast_category_first(wiz);
561         struct ast_sorcery_object *obj = NULL;
562         const char *id = ast_category_get_name(wiz);
563         char new_id[strlen(id) + MAX_ID_SUFFIX];
564         RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
565
566         snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
567
568         if (!is_variable_true(wizvars, "has_phoneprov")) {
569                 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
570                 if (obj) {
571                         otw->wizard->delete(sorcery, otw->wizard_data, obj);
572                         ao2_ref(obj, -1);
573                 }
574                 return 0;
575         }
576
577         if (!ast_variable_find_in_list(wizvars, "phoneprov/MAC")) {
578                 ast_log(LOG_ERROR,
579                         "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
580                 return -1;
581         }
582
583         variable_list_append_return(&vars, "endpoint", id);
584         variable_list_append_return(&vars, "@pjsip_wizard", id);
585
586         obj = create_object(sorcery, new_id, "phoneprov", vars);
587         if (!obj) {
588                 return -1;
589         }
590
591         if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
592                 otw->wizard->create(sorcery, otw->wizard_data, obj);
593         }
594         ao2_ref(obj, -1);
595
596         return 0;
597 }
598
599 static int delete_existing_cb(void *obj, void *arg, int flags)
600 {
601         struct object_type_wizard *otw = arg;
602
603         otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
604
605         return CMP_MATCH;
606 }
607
608 static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
609         struct ast_category *wiz, struct string_vector *remote_hosts_vector)
610 {
611         struct ast_variable *search;
612         struct ast_variable *wizvars = ast_category_first(wiz);
613         const char *id = ast_category_get_name(wiz);
614         const char *server_uri_pattern;
615         const char *client_uri_pattern;
616         const char *transport = ast_variable_find_in_list(wizvars, "transport");
617         const char *username;
618         char new_id[strlen(id) + MAX_ID_SUFFIX];
619         int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
620         int host_counter;
621         RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
622         RAII_VAR(struct ao2_container *, existing,
623                 ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
624
625         if (!existing) {
626                 return -1;
627         }
628
629         /* Find any existing registrations. */
630         search = ast_variable_new("@pjsip_wizard", id, "");
631         if (!search) {
632                 return -1;
633         }
634
635         otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
636         ast_variables_destroy(search);
637
638         /* If not sending registrations, delete ALL existing registrations for this wizard. */
639         if (!is_variable_true(wizvars, "sends_registrations")) {
640                 if (ao2_container_count(existing) > 0) {
641                         ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
642                 }
643                 return 0;
644         }
645
646         if (!host_count) {
647                 ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
648                 return -1;
649         }
650
651         variable_list_append_return(&vars, "@pjsip_wizard", id);
652
653         if (!(server_uri_pattern = ast_variable_find_in_list(wizvars, "server_uri_pattern"))) {
654                 server_uri_pattern = "sip:${REMOTE_HOST}";
655         }
656
657         if (!(client_uri_pattern = ast_variable_find_in_list(wizvars, "client_uri_pattern"))) {
658                 client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
659         }
660
661         if(is_variable_true(wizvars, "sends_auth")) {
662                 username = ast_variable_find_in_list(wizvars, "outbound_auth/username");
663         } else {
664                 username = id;
665         }
666
667
668         /* Unlike aor and identify, we need to create a separate registration object
669          * for each remote host.
670          */
671         for (host_counter = 0; host_counter < host_count; host_counter++) {
672                 struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
673                         AST_VECTOR_GET(remote_hosts_vector, host_counter));
674                 struct ast_var_t *un = ast_var_assign("USERNAME", username);
675                 struct ast_sorcery_object *obj;
676                 RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
677                 RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
678                 RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
679
680                 AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
681                 AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
682
683                 if (!ast_strlen_zero(server_uri_pattern)) {
684                         ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
685                                 server_uri_pattern);
686                         variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
687                 }
688
689                 if (!ast_strlen_zero(client_uri_pattern)) {
690                         ast_str_reset(uri);
691                         ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
692                                 client_uri_pattern);
693                         variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
694                 }
695
696                 if (is_variable_true(wizvars, "sends_auth")) {
697                         snprintf(new_id, sizeof(new_id), "%s-oauth", id);
698                         variable_list_append_return(&registration_vars, "outbound_auth", new_id);
699                 }
700
701                 if (!ast_strlen_zero(transport)) {
702                         variable_list_append_return(&registration_vars, "transport", transport);
703                 }
704
705                 snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
706
707                 obj = create_object(sorcery, new_id, "registration", registration_vars);
708                 if (!obj) {
709                         return -1;
710                 }
711
712                 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
713                         otw->wizard->create(sorcery, otw->wizard_data, obj);
714                 }
715                 ao2_ref(obj, -1);
716
717                 /* Unlink it from the 'existing' container.  Any left will be deleted from
718                  * sorcery.  If it wasn't in the existing container, no harm.
719                  */
720                 ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY, ast_sorcery_object_id_compare, new_id);
721         }
722
723         /* If there are any excess registrations, delete them. */
724         if (ao2_container_count(existing) > 0) {
725                 ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
726         }
727
728         return 0;
729 }
730
731 static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
732         struct ast_category *wiz)
733 {
734         struct ast_variable *wizvars = ast_category_first(wiz);
735         struct string_vector remote_hosts_vector;
736         const char *remote_hosts;
737         int rc = -1;
738
739         AST_VECTOR_INIT(&remote_hosts_vector, 16);
740         remote_hosts = ast_variable_find_in_list(wizvars, "remote_hosts");
741
742         if (!ast_strlen_zero(remote_hosts)) {
743                 char *host;
744                 char *hosts = ast_strdupa(remote_hosts);
745
746                 while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
747                         AST_VECTOR_APPEND(&remote_hosts_vector, ast_strdup(host));
748                 }
749         }
750
751         ast_debug(4, "%s handler starting.\n", otw->object_type);
752
753         if (!strcmp(otw->object_type, "auth")) {
754                 rc = handle_auths(sorcery, otw, wiz);
755         } else if (!strcmp(otw->object_type, "aor")) {
756                 rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
757         } else if (!strcmp(otw->object_type, "endpoint")) {
758                 rc = handle_endpoint(sorcery, otw, wiz);
759         } else if (!strcmp(otw->object_type, "identify")) {
760                 rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
761         } else if (!strcmp(otw->object_type, "phoneprov")) {
762                 rc = handle_phoneprov(sorcery, otw, wiz);
763         } else if (!strcmp(otw->object_type, "registration")) {
764                 rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
765         }
766
767         AST_VECTOR_REMOVE_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
768         AST_VECTOR_FREE(&remote_hosts_vector);
769
770         ast_debug(4, "%s handler complete.  rc: %d\n", otw->object_type, rc);
771
772         return rc;
773 }
774
775 /*
776  * Everything below are the sorcery observers.
777  */
778 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
779 static void object_type_loaded_observer(const char *name,
780         const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
781 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
782         const char *object_type, struct ast_sorcery_wizard *wizard,
783         const char *wizard_args, void *wizard_data);
784 static void object_type_registered_observer(const char *name,
785         struct ast_sorcery *sorcery, const char *object_type);
786
787 const static struct ast_sorcery_global_observer global_observer = {
788         .instance_created = instance_created_observer,
789 };
790
791 struct ast_sorcery_instance_observer observer = {
792         .wizard_mapped = wizard_mapped_observer,
793         .object_type_registered = object_type_registered_observer,
794         .object_type_loaded = object_type_loaded_observer,
795 };
796
797 /*! \brief Called after an object type is loaded/reloaded */
798 static void object_type_loaded_observer(const char *name,
799         const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
800 {
801         struct ast_category *category = NULL;
802         struct object_type_wizard *otw = NULL;
803         char *filename = "pjsip_wizard.conf";
804         struct ast_flags flags = { reloaded ? CONFIG_FLAG_FILEUNCHANGED : 0 };
805         struct ast_config *cfg;
806
807         if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
808                 /* Not interested. */
809                 return;
810         }
811
812         otw = find_wizard(object_type);
813         if (!otw) {
814                 ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
815                 return;
816         }
817
818         cfg = ast_config_load2(filename, object_type, flags);
819
820         if (!cfg) {
821                 ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
822                 return;
823         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
824                 ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
825                 return;
826         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
827                 ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
828                 return;
829         }
830
831         while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
832                 const char *id = ast_category_get_name(category);
833                 struct ast_category *last_cat = NULL;
834                 struct ast_variable *change_set = NULL;
835
836                 if (otw->last_config) {
837                         last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
838                         ast_sorcery_changeset_create(ast_category_first(category), ast_category_first(last_cat), &change_set);
839                         if (last_cat) {
840                                 ast_category_delete(otw->last_config, last_cat);
841                         }
842                 }
843
844                 if (!last_cat || change_set) {
845                         ast_variables_destroy(change_set);
846                         ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
847                         if (wizard_apply_handler(sorcery, otw, category)) {
848                                 ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
849                         }
850                 }
851         }
852
853         if (!otw->last_config) {
854                 otw->last_config = cfg;
855                 return;
856         }
857
858         /* Only wizards that weren't in the new config are left in last_config now so we need to delete
859          * all objects belonging to them.
860          */
861         category = NULL;
862         while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
863                 const char *id = ast_category_get_name(category);
864                 struct ast_variable *search;
865                 RAII_VAR(struct ao2_container *, existing,
866                         ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
867
868                 if (!existing) {
869                         ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
870                         break;
871                 }
872
873                 search = ast_variable_new("@pjsip_wizard", id, "");
874                 if (!search) {
875                         ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
876                         break;
877                 }
878                 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
879                 ast_variables_destroy(search);
880
881                 if (ao2_container_count(existing) > 0) {
882                         ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
883                                 reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
884                         ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
885                                 delete_existing_cb, otw);
886                 }
887         }
888
889         ast_config_destroy(otw->last_config);
890         otw->last_config = cfg;
891 }
892
893 /*! \brief When each wizard is mapped, save it off to the vector. */
894 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
895         const char *object_type, struct ast_sorcery_wizard *wizard,
896         const char *wizard_args, void *wizard_data)
897 {
898         struct object_type_wizard *otw;
899
900         if (!is_one_of(object_type, object_types)) {
901                 /* Not interested. */
902                 return;
903         }
904
905         /* We're only interested in memory wizards with the pjsip_wizard tag. */
906         if (wizard_args && !strcmp(wizard_args, "pjsip_wizard")) {
907                 otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
908                 otw->sorcery = sorcery;
909                 otw->wizard = wizard;
910                 otw->wizard_data = wizard_data;
911                 otw->last_config = NULL;
912                 strcpy(otw->object_type, object_type); /* Safe */
913                 AST_VECTOR_APPEND(&object_type_wizards, otw);
914                 ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
915         }
916 }
917
918 /*! \brief When each object type is registered, map a memory wizard to it. */
919 static void object_type_registered_observer(const char *name,
920         struct ast_sorcery *sorcery, const char *object_type)
921 {
922         if (is_one_of(object_type, object_types)) {
923                 ast_sorcery_apply_wizard_mapping(sorcery, object_type, "memory", "pjsip_wizard", 0);
924         }
925 }
926
927 /*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
928  * Since you can't unload res_pjsip, this will only ever be called once.
929  */
930 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
931 {
932         if (strcmp(name, "res_pjsip")) {
933                 return;
934         }
935
936         ast_sorcery_instance_observer_add(sorcery, &observer);
937 }
938
939 static int load_module(void)
940 {
941         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
942         int i;
943
944         AST_VECTOR_INIT(&object_type_wizards, 12);
945         ast_sorcery_global_observer_add(&global_observer);
946
947         /* If this module is loading AFTER res_pjsip, we need to manually add the instance observer
948          * and map the wizards because the observers will never get triggered.
949          * The we neeed to schedule a reload.
950          */
951         if (sorcery) {
952                 /* CLean up and add the observer. */
953                 ast_sorcery_instance_observer_remove(sorcery, &observer);
954                 ast_sorcery_instance_observer_add(sorcery, &observer);
955
956                 for (i = 0; object_types[i]; i++) {
957                         ast_sorcery_apply_wizard_mapping(sorcery, object_types[i], "memory", "pjsip_wizard", 0);
958                 }
959
960                 ast_module_reload("res_pjsip");
961         }
962
963         return AST_MODULE_LOAD_SUCCESS;
964 }
965
966 static int unload_module(void)
967 {
968         struct object_type_wizard *otw;
969         int i;
970
971         ast_sorcery_global_observer_remove(&global_observer);
972
973         for (i = 0; object_types[i]; i++) {
974                 RAII_VAR(struct ao2_container *, existing,
975                         ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
976
977                 otw = find_wizard(object_types[i]);
978                 if (otw->sorcery) {
979                         ast_sorcery_instance_observer_remove(otw->sorcery, &observer);
980                 }
981
982                 otw->wizard->retrieve_multiple(otw->sorcery, otw->wizard_data, object_types[i], existing, NULL);
983                 ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
984         }
985
986         AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
987         AST_VECTOR_FREE(&object_type_wizards);
988
989         return 0;
990 }
991
992 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Config Wizard",
993                 .load = load_module,
994                 .unload = unload_module,
995                 .load_pri = AST_MODPRI_REALTIME_DRIVER,
996                 );