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