Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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  <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. */
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 {
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 
413  }
414 
415  if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
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;
456  ast_free(data);
457  return -1;
458  }
459  }
460 
461  /* Don't disturb existing, exact-match, entries. */
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 
474  }
475 
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 '%s@%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 '%s@%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  variable_list_append_return(&vars, "match",
768  AST_VECTOR_GET(remote_hosts_vector, host_counter));
769  }
770  }
771 
772  obj = create_object(sorcery, new_id, "identify", vars);
773  if (!obj) {
774  return -1;
775  }
776 
777  if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
778  otw->wizard->create(sorcery, otw->wizard_data, obj);
779  }
780  ao2_ref(obj, -1);
781 
782  return 0;
783 }
784 
785 static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
786  struct ast_category *wiz)
787 {
788  struct ast_variable *wizvars = ast_category_first(wiz);
789  struct ast_sorcery_object *obj = NULL;
790  const char *id = ast_category_get_name(wiz);
791  char new_id[strlen(id) + MAX_ID_SUFFIX];
792  RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
793 
794  snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
795 
796  if (!is_variable_true(wizvars, "has_phoneprov")) {
797  obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
798  if (obj) {
799  otw->wizard->delete(sorcery, otw->wizard_data, obj);
800  ao2_ref(obj, -1);
801  }
802  return 0;
803  }
804 
805  if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
807  "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
808  return -1;
809  }
810 
811  variable_list_append_return(&vars, "endpoint", id);
812  variable_list_append_return(&vars, "@pjsip_wizard", id);
813 
814  obj = create_object(sorcery, new_id, "phoneprov", vars);
815  if (!obj) {
816  return -1;
817  }
818 
819  if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
820  otw->wizard->create(sorcery, otw->wizard_data, obj);
821  }
822  ao2_ref(obj, -1);
823 
824  return 0;
825 }
826 
827 static int delete_existing_cb(void *obj, void *arg, int flags)
828 {
829  struct object_type_wizard *otw = arg;
830 
831  if (!strcmp(otw->object_type, "endpoint")) {
832  const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
833  const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
836  }
837  }
838 
839  otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
840 
841  return CMP_MATCH;
842 }
843 
844 static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
845  struct ast_category *wiz, struct string_vector *remote_hosts_vector)
846 {
847  struct ast_variable *search;
848  struct ast_variable *wizvars = ast_category_first(wiz);
849  const char *id = ast_category_get_name(wiz);
850  const char *server_uri_pattern;
851  const char *client_uri_pattern;
852  const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
853  const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
854  const char *username;
855  char new_id[strlen(id) + MAX_ID_SUFFIX];
856  int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
857  int host_counter;
858  RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
859  RAII_VAR(struct ao2_container *, existing,
861 
862  if (!existing) {
863  return -1;
864  }
865 
866  /* Find any existing registrations. */
867  search = ast_variable_new("@pjsip_wizard", id, "");
868  if (!search) {
869  return -1;
870  }
871 
872  if (!ast_strlen_zero(outbound_proxy)) {
873  variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
874  }
875 
876  otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
877  ast_variables_destroy(search);
878 
879  /* If not sending registrations, delete ALL existing registrations for this wizard. */
880  if (!is_variable_true(wizvars, "sends_registrations")) {
881  if (ao2_container_count(existing) > 0) {
883  }
884  return 0;
885  }
886 
887  if (!host_count) {
888  ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
889  return -1;
890  }
891 
892  variable_list_append_return(&vars, "@pjsip_wizard", id);
893 
894  if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
895  server_uri_pattern = "sip:${REMOTE_HOST}";
896  }
897 
898  if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
899  client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
900  }
901 
902  if (is_variable_true(wizvars, "sends_auth")) {
903  if (!(username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username"))) {
904  ast_log(LOG_ERROR, "Wizard '%s' must have 'outbound_auth/username' if it sends"
905  " authentication.\n", id);
906  return -1;
907  }
908  } else {
909  username = id;
910  }
911 
912 
913  /* Unlike aor and identify, we need to create a separate registration object
914  * for each remote host.
915  */
916  for (host_counter = 0; host_counter < host_count; host_counter++) {
917  struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
918  AST_VECTOR_GET(remote_hosts_vector, host_counter));
919  struct ast_var_t *un = ast_var_assign("USERNAME", username);
920  struct ast_sorcery_object *obj;
921  RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
922  RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
923  RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
924 
925  AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
926  AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
927 
928  if (!ast_strlen_zero(server_uri_pattern)) {
929  ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
930  server_uri_pattern);
931  variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
932  }
933 
934  if (!ast_strlen_zero(client_uri_pattern)) {
935  ast_str_reset(uri);
936  ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
937  client_uri_pattern);
938  variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
939  }
940 
941  if (is_variable_true(wizvars, "sends_auth")) {
942  snprintf(new_id, sizeof(new_id), "%s-oauth", id);
943  variable_list_append_return(&registration_vars, "outbound_auth", new_id);
944  }
945 
946  if (!ast_strlen_zero(transport)) {
947  variable_list_append_return(&registration_vars, "transport", transport);
948  }
949 
950  if (is_variable_true(wizvars, "sends_line_with_registrations")) {
951  variable_list_append_return(&registration_vars, "line", "yes");
952  variable_list_append_return(&registration_vars, "endpoint", id);
953  }
954 
955  snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
956 
957  obj = create_object(sorcery, new_id, "registration", registration_vars);
958  if (!obj) {
959  return -1;
960  }
961 
962  if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
963  otw->wizard->create(sorcery, otw->wizard_data, obj);
964  }
965  ao2_ref(obj, -1);
966 
967  /* Unlink it from the 'existing' container. Any left will be deleted from
968  * sorcery. If it wasn't in the existing container, no harm.
969  */
971  }
972 
973  /* If there are any excess registrations, delete them. */
974  if (ao2_container_count(existing) > 0) {
976  }
977 
978  return 0;
979 }
980 
981 static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
982  struct ast_category *wiz)
983 {
984  struct ast_variable *wizvars = ast_category_first(wiz);
985  struct string_vector remote_hosts_vector;
986  const char *remote_hosts;
987  int rc = -1;
988 
989  AST_VECTOR_INIT(&remote_hosts_vector, 16);
990  remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
991 
992  if (!ast_strlen_zero(remote_hosts)) {
993  char *host;
994  char *hosts = ast_strdupa(remote_hosts);
995 
996  while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
997  host = ast_strdup(host);
998  if (host && AST_VECTOR_APPEND(&remote_hosts_vector, host)) {
999  ast_free(host);
1000  }
1001  }
1002  }
1003 
1004  ast_debug(4, "%s handler starting.\n", otw->object_type);
1005 
1006  if (!strcmp(otw->object_type, "auth")) {
1007  rc = handle_auths(sorcery, otw, wiz);
1008  } else if (!strcmp(otw->object_type, "aor")) {
1009  rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
1010  } else if (!strcmp(otw->object_type, "endpoint")) {
1011  rc = handle_endpoint(sorcery, otw, wiz);
1012  } else if (!strcmp(otw->object_type, "identify")) {
1013  rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
1014  } else if (!strcmp(otw->object_type, "phoneprov")) {
1015  rc = handle_phoneprov(sorcery, otw, wiz);
1016  } else if (!strcmp(otw->object_type, "registration")) {
1017  rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
1018  }
1019 
1021  AST_VECTOR_FREE(&remote_hosts_vector);
1022 
1023  ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
1024 
1025  return rc;
1026 }
1027 
1028 /*
1029  * Everything below are the sorcery observers.
1030  */
1031 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
1032 static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
1033 static void object_type_loaded_observer(const char *name,
1034  const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
1035 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
1036  const char *object_type, struct ast_sorcery_wizard *wizard,
1037  const char *wizard_args, void *wizard_data);
1038 static void object_type_registered_observer(const char *name,
1039  struct ast_sorcery *sorcery, const char *object_type);
1040 
1043  .instance_destroying = instance_destroying_observer,
1044 };
1045 
1048  .object_type_registered = object_type_registered_observer,
1049  .object_type_loaded = object_type_loaded_observer,
1050 };
1051 
1052 /*! \brief Called after an object type is loaded/reloaded */
1053 static void object_type_loaded_observer(const char *name,
1054  const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1055 {
1056  struct ast_category *category = NULL;
1057  struct object_type_wizard *otw = NULL;
1058  char *filename = "pjsip_wizard.conf";
1059  struct ast_flags flags = { 0 };
1060  struct ast_config *cfg;
1061 
1062  if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
1063  /* Not interested. */
1064  return;
1065  }
1066 
1067  otw = find_wizard(object_type);
1068  if (!otw) {
1069  ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
1070  return;
1071  }
1072 
1073  if (reloaded && otw->last_config) {
1075  }
1076 
1077  cfg = ast_config_load2(filename, object_type, flags);
1078 
1079  if (!cfg) {
1080  ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
1081  return;
1082  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1083  ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
1084  return;
1085  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1086  ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
1087  return;
1088  }
1089 
1090  while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
1091  const char *id = ast_category_get_name(category);
1092  struct ast_category *last_cat = NULL;
1093  int changes = 0;
1094 
1095  if (otw->last_config) {
1096  last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
1097  changes = !ast_variable_lists_match(ast_category_first(category), ast_category_first(last_cat), 1);
1098  if (last_cat) {
1099  ast_category_delete(otw->last_config, last_cat);
1100  }
1101  }
1102 
1103  if (!last_cat || changes) {
1104  ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
1105  if (wizard_apply_handler(sorcery, otw, category)) {
1106  ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
1107  }
1108  }
1109  }
1110 
1111  if (!otw->last_config) {
1112  otw->last_config = cfg;
1113  return;
1114  }
1115 
1116  /* Only wizards that weren't in the new config are left in last_config now so we need to delete
1117  * all objects belonging to them.
1118  */
1119  category = NULL;
1120  while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
1121  const char *id = ast_category_get_name(category);
1122  struct ast_variable *search;
1123  RAII_VAR(struct ao2_container *, existing,
1125 
1126  if (!existing) {
1127  ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
1128  break;
1129  }
1130 
1131  search = ast_variable_new("@pjsip_wizard", id, "");
1132  if (!search) {
1133  ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
1134  break;
1135  }
1136  otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
1137  ast_variables_destroy(search);
1138 
1139  if (ao2_container_count(existing) > 0) {
1140  ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
1141  reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
1143  delete_existing_cb, otw);
1144  }
1145  }
1146 
1148  otw->last_config = cfg;
1149 }
1150 
1151 /*! \brief When each wizard is mapped, save it off to the vector. */
1152 static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
1153  const char *object_type, struct ast_sorcery_wizard *wizard,
1154  const char *wizard_args, void *wizard_data)
1155 {
1156  struct object_type_wizard *otw;
1157 
1158  if (!is_one_of(object_type, object_types)) {
1159  /* Not interested. */
1160  return;
1161  }
1162 
1163  /* We're only interested in memory wizards with the pjsip_wizard tag. */
1164  if (wizard_args && !strcmp(wizard_args, "pjsip_wizard")) {
1165  otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
1166  if (!otw) {
1167  return;
1168  }
1169 
1170  otw->sorcery = sorcery;
1171  otw->wizard = wizard;
1172  otw->wizard_data = wizard_data;
1173  otw->last_config = NULL;
1174  strcpy(otw->object_type, object_type); /* Safe */
1175  AST_VECTOR_RW_WRLOCK(&object_type_wizards);
1176  if (AST_VECTOR_APPEND(&object_type_wizards, otw)) {
1177  ast_free(otw);
1178  } else {
1179  ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
1180  }
1181  AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1182  }
1183 }
1184 
1185 /*! \brief When each object type is registered, map a memory wizard to it. */
1186 static void object_type_registered_observer(const char *name,
1187  struct ast_sorcery *sorcery, const char *object_type)
1188 {
1189  if (is_one_of(object_type, object_types)) {
1190  ast_sorcery_apply_wizard_mapping(sorcery, object_type, "memory", "pjsip_wizard", 0);
1191  }
1192 }
1193 
1194 /*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
1195  * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
1196  * destroyed.
1197  */
1198 static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
1199 {
1200  if (strcmp(name, "res_pjsip")) {
1201  return;
1202  }
1205 }
1206 
1207 /*! \brief When the res_pjsip instance is destroyed, remove the observer
1208  * and unref the module. This should then allow this module to unload cleanly.
1209  */
1210 static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
1211 {
1212  if (strcmp(name, "res_pjsip")) {
1213  return;
1214  }
1215 
1218 }
1219 
1220 static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1221 {
1222  struct ast_sorcery *sorcery;
1223  int idx;
1224  FILE *f = NULL;
1225  const char *fn = NULL;
1226 
1227  switch (cmd) {
1228  case CLI_INIT:
1229  e->command = "pjsip export config_wizard primitives [to]";
1230  e->usage =
1231  "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
1232  " Export the config_wizard objects as pjsip primitives to\n"
1233  " the console or to <filename>\n";
1234  return NULL;
1235  case CLI_GENERATE:
1236  return NULL;
1237  }
1238 
1239  if (a->argc > 5) {
1240  char date[256]="";
1241  time_t t;
1242  fn = a->argv[5];
1243 
1244  time(&t);
1245  ast_copy_string(date, ctime(&t), sizeof(date));
1246  f = fopen(fn, "w");
1247  if (!f) {
1248  ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
1249  return CLI_FAILURE;
1250  }
1251 
1252  fprintf(f, ";!\n");
1253  fprintf(f, ";! Automatically generated configuration file\n");
1254  fprintf(f, ";! Filename: %s\n", fn);
1255  fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
1256  fprintf(f, ";! Creation Date: %s", date);
1257  fprintf(f, ";!\n");
1258  }
1259 
1261 
1262  AST_VECTOR_RW_RDLOCK(&object_type_wizards);
1263  for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
1264  struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
1265  struct ao2_container *container;
1266  struct ao2_iterator i;
1267  void *o;
1268 
1270  if (!container) {
1271  continue;
1272  }
1273 
1274  i = ao2_iterator_init(container, 0);
1275  while ((o = ao2_iterator_next(&i))) {
1276  struct ast_variable *vars;
1277  struct ast_variable *v;
1278 
1280  if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
1281  if (f) {
1282  fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1283  } else {
1284  ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1285  }
1286  for (v = vars; v; v = v->next) {
1287  if (!ast_strlen_zero(v->value)) {
1288  if (f) {
1289  fprintf(f, "%s = %s\n", v->name, v->value);
1290  } else {
1291  ast_cli(a->fd, "%s = %s\n", v->name, v->value);
1292  }
1293  }
1294  }
1295  }
1296  ast_variables_destroy(vars);
1297  ao2_ref(o, -1);
1298  }
1301  }
1302  AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1303 
1304  if (f) {
1305  fclose(f);
1306  ast_cli(a->fd, "Wrote configuration to %s\n", fn);
1307  }
1308 
1309 
1310  return CLI_SUCCESS;
1311 }
1312 
1313 static struct ast_cli_entry config_wizard_cli[] = {
1314  AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
1315 };
1316 
1317 static int load_module(void)
1318 {
1319  AST_VECTOR_RW_INIT(&object_type_wizards, 12);
1322 
1323  return AST_MODULE_LOAD_SUCCESS;
1324 }
1325 
1326 static int unload_module(void)
1327 {
1331  AST_VECTOR_RW_FREE(&object_type_wizards);
1332 
1333  return 0;
1334 }
1335 
1337  .support_level = AST_MODULE_SUPPORT_CORE,
1338  .load = load_module,
1339  .unload = unload_module,
1340  .load_pri = AST_MODPRI_REALTIME_DRIVER,
1341 );
#define paren
Definition: ael_lex.c:973
static const char app[]
Definition: app_adsiprog.c:56
enum queue_result id
Definition: app_queue.c:1640
#define var
Definition: ast_expr2f.c:614
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
void ast_free_ptr(void *ptr)
free() wrapper
Definition: astmm.c:1739
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
static int priority
static rc_handle * rh
Definition: cdr_radius.c:96
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:122
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:120
static const char type[]
Definition: chan_ooh323.c:109
void ast_var_list_destroy(struct varshead *head)
Definition: chanvars.c:109
struct varshead * ast_var_list_create(void)
Definition: chanvars.c:97
static void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var)
Definition: chanvars.h:51
#define ast_var_assign(name, value)
Definition: chanvars.h:40
Standard Command Line Interface.
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
@ E_MATCH
Definition: extconf.h:218
static const char name[]
Definition: format_mp3.c:68
direction
static const char context_name[]
static char prefix[MAX_PREFIX]
Definition: http.c:144
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:904
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
Definition: main/config.c:1102
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:1409
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
Definition: main/config.c:1231
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:543
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:916
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *cat)
Delete a category.
Definition: main/config.c:1552
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
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:1096
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
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:3220
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:846
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_REALTIME_DRIVER
Definition: module.h:323
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
Core PBX routines and definitions.
int ast_wrlock_contexts(void)
Write locks the context list.
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:4985
int ast_wrlock_context(struct ast_context *con)
Write locks a given context.
const char * ast_get_extension_app(struct ast_exten *e)
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition: pbx.c:4955
int ast_unlock_context(struct ast_context *con)
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
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.
const char * ast_get_context_name(struct ast_context *con)
Definition: ael_main.c:421
void * ast_get_extension_app_data(struct ast_exten *e)
#define PRIORITY_HINT
Definition: pbx.h:54
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.
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
int ast_unlock_contexts(void)
Unlocks contexts.
struct ao2_container * container
Definition: res_fax.c:501
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
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....
static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
#define BASE_REGISTRAR
#define NOT_EQUALS(a, b)
static int add_hints(const char *context, const char *exten, const char *application, const char *id)
static int is_variable_true(struct ast_variable *vars, const char *name)
Finds the last variable in a list and tests it.
static int delete_existing_cb(void *obj, void *arg, int flags)
#define OTW_DELETE_CB(otw)
static int add_extension(struct ast_context *context, const char *exten, int priority, const char *application)
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.
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...
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.
static struct object_type_wizard * find_wizard(const char *object_type)
Finds the otw for the object type.
static const struct ast_sorcery_global_observer global_observer
static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, char *direction)
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....
static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
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.
#define MAX_ID_SUFFIX
Defines the maximum number of characters that can be added to a wizard id.
static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int is_one_of(const char *needle, const char *haystack[])
static char * handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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.
#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 -...
static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *)
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.
static struct ast_cli_entry config_wizard_cli[]
struct ast_sorcery_instance_observer observer
static int delete_extens(const char *context, const char *exten)
static int load_module(void)
static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int unload_module(void)
static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static struct ast_sorcery * sorcery
#define NULL
Definition: resample.c:96
Sorcery Data Access Layer API.
#define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching)
Apply additional object wizard mappings.
Definition: sorcery.h:510
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition: sorcery.h:1137
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
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition: sorcery.h:120
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
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
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
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
void ast_sorcery_global_observer_remove(const struct ast_sorcery_global_observer *callbacks)
Remove a global observer from sorcery.
Definition: sorcery.c:514
int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer *callbacks)
Add a global observer to sorcery.
Definition: sorcery.c:498
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
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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
@ AST_STRSEP_TRIM
Definition: strings.h:256
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:674
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:640
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
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
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: main/utils.c:1802
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
ast_context: An extension context - must remain in sync with fake_context
Definition: pbx.c:284
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition: pbx.c:237
void * data
Definition: pbx.c:248
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
struct ast_module * self
Definition: module.h:342
Interface for the global sorcery observer.
Definition: sorcery.h:220
void(* instance_created)(const char *name, struct ast_sorcery *sorcery)
Callback after an instance is created.
Definition: sorcery.h:222
Interface for the sorcery instance observer.
Definition: sorcery.h:237
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
Structure for internal sorcery object information.
Definition: sorcery.c:127
Interface for a sorcery wizard.
Definition: sorcery.h:276
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
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
int(* create)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for creating an object.
Definition: sorcery.h:293
int(* delete)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for deleting an object.
Definition: sorcery.h:319
int(* update)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for updating an object.
Definition: sorcery.h:316
Full structure for sorcery.
Definition: sorcery.c:230
Support for dynamic strings.
Definition: strings.h:604
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Keeps track of the sorcery wizard and last config for each object type.
struct ast_config * last_config
struct ast_sorcery * sorcery
struct ast_sorcery_wizard * wizard
int stacklen
Definition: extconf.h:238
A generic char * vector definition.
int value
Definition: syslog.c:37
static struct test_val a
#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:936
#define ast_assert(a)
Definition: utils.h:734
#define ARRAY_LEN(a)
Definition: utils.h:661
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#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
#define AST_VECTOR_RW_WRLOCK(vec)
Obtain write lock on vector.
Definition: vector.h:887
#define AST_VECTOR_RW_UNLOCK(vec)
Unlock vector.
Definition: vector.h:897
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_RW_FREE(vec)
Deallocates this locked vector.
Definition: vector.h:202
#define AST_VECTOR_RW_RDLOCK(vec)
Obtain read lock on vector.
Definition: vector.h:877
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
#define AST_VECTOR_RW_INIT(vec, size)
Initialize a vector with a read/write lock.
Definition: vector.h:158