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