Asterisk - The Open Source Telephony Project  21.4.1
pbx_config.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.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 Populate and remember extensions from static config file
22  *
23  *
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 /*** DOCUMENTATION
31  <manager name="DialplanExtensionAdd" language="en_US">
32  <synopsis>
33  Add an extension to the dialplan
34  </synopsis>
35  <syntax>
36  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
37  <parameter name="Context" required="true">
38  <para>Context where the extension will be created. The context will
39  be created if it does not already exist.</para>
40  </parameter>
41  <parameter name="Extension" required="true">
42  <para>Name of the extension that will be created (may include callerid match by separating
43  with '/')</para>
44  </parameter>
45  <parameter name="Priority" required="true">
46  <para>Priority being added to this extension. Must be either <literal>hint</literal> or a
47  numerical value.</para>
48  </parameter>
49  <parameter name="Application" required="true">
50  <para>The application to use for this extension at the requested priority</para>
51  </parameter>
52  <parameter name="ApplicationData" required="false">
53  <para>Arguments to the application.</para>
54  </parameter>
55  <parameter name="Replace" required="false">
56  <para>If set to 'yes', '1', 'true' or any of the other values we evaluate as true, then
57  if an extension already exists at the requested context, extension, and priority it will
58  be overwritten. Otherwise, the existing extension will remain and the action will fail.
59  </para>
60  </parameter>
61  </syntax>
62  </manager>
63  <manager name="DialplanExtensionRemove" language="en_US">
64  <synopsis>
65  Remove an extension from the dialplan
66  </synopsis>
67  <syntax>
68  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
69  <parameter name="Context" required="true">
70  <para>Context of the extension being removed</para>
71  </parameter>
72  <parameter name="Extension" required="true">
73  <para>Name of the extension being removed (may include callerid match by separating with '/')</para>
74  </parameter>
75  <parameter name="Priority" required="false">
76  <para>If provided, only remove this priority from the extension instead of all
77  priorities in the extension.</para>
78  </parameter>
79  </syntax>
80  </manager>
81  ***/
82 
83 #include "asterisk.h"
84 
85 #include <ctype.h>
86 
87 #include "asterisk/paths.h" /* ast_config_AST_CONFIG_DIR */
88 #include "asterisk/pbx.h"
89 #include "asterisk/config.h"
90 #include "asterisk/module.h"
91 #include "asterisk/logger.h"
92 #include "asterisk/cli.h"
93 #include "asterisk/channel.h" /* AST_MAX_EXTENSION */
94 #include "asterisk/callerid.h"
95 
96 static const char config[] = "extensions.conf";
97 static const char registrar[] = "pbx_config";
98 static char userscontext[AST_MAX_EXTENSION] = "default";
99 
100 static int static_config = 0;
101 static int write_protect_config = 1;
102 static int autofallthrough_config = 1;
103 static int clearglobalvars_config = 0;
104 static int extenpatternmatchnew_config = 0;
105 static char *overrideswitch_config = NULL;
106 
107 static struct stasis_subscription *fully_booted_subscription;
108 
109 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
110 
111 AST_MUTEX_DEFINE_STATIC(reload_lock);
112 
113 static struct ast_context *local_contexts = NULL;
114 static struct ast_hashtab *local_table = NULL;
115 /*
116  * Prototypes for our completion functions
117  */
118 static char *complete_dialplan_remove_include(struct ast_cli_args *);
119 static char *complete_dialplan_add_include(struct ast_cli_args *);
120 static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *);
121 static char *complete_dialplan_add_ignorepat(struct ast_cli_args *);
122 static char *complete_dialplan_remove_extension(struct ast_cli_args *);
123 static char *complete_dialplan_add_extension(struct ast_cli_args *);
124 static char *complete_dialplan_remove_context(struct ast_cli_args *);
125 
126 /*
127  * Implementation of functions provided by this module
128  */
129 
130 /*!
131  * * REMOVE context command stuff
132  */
133 
134 static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
135 {
136  switch (cmd) {
137  case CLI_INIT:
138  e->command = "dialplan remove context";
139  e->usage =
140  "Usage: dialplan remove context <context>\n"
141  " Removes all extensions from a specified context.\n";
142  return NULL;
143  case CLI_GENERATE:
144  return complete_dialplan_remove_context(a);
145  }
146 
147  if (a->argc != 4) {
148  return CLI_SHOWUSAGE;
149  }
150 
151  if (ast_context_destroy_by_name(a->argv[3], NULL)) {
152  ast_cli(a->fd, "There is no such context as '%s'\n", a->argv[3]);
153  return CLI_SUCCESS;
154  } else {
155  ast_cli(a->fd, "Removed context '%s'\n", a->argv[3]);
156  return CLI_SUCCESS;
157  }
158 }
159 /*!
160  * REMOVE INCLUDE command stuff
161  */
162 static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
163 {
164  switch (cmd) {
165  case CLI_INIT:
166  e->command = "dialplan remove include";
167  e->usage =
168  "Usage: dialplan remove include <context> from <context>\n"
169  " Remove an included context from another context.\n";
170  return NULL;
171  case CLI_GENERATE:
172  return complete_dialplan_remove_include(a);
173  }
174 
175  if (a->argc != 6 || strcmp(a->argv[4], "from"))
176  return CLI_SHOWUSAGE;
177 
178  if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) {
179  ast_cli(a->fd, "We are not including '%s' into '%s' now\n",
180  a->argv[3], a->argv[5]);
181  return CLI_SUCCESS;
182  }
183 
184  ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n",
185  a->argv[3], a->argv[5]);
186  return CLI_FAILURE;
187 }
188 
189 /*! \brief return true if 'name' is included by context c */
190 static int lookup_ci(struct ast_context *c, const char *name)
191 {
192  int idx;
193  int ret = 0;
194 
195  if (ast_rdlock_context(c)) {
196  /* error, skip */
197  return 0;
198  }
199 
200  for (idx = 0; idx < ast_context_includes_count(c); idx++) {
201  const struct ast_include *i = ast_context_includes_get(c, idx);
202 
203  if (!strcmp(name, ast_get_include_name(i))) {
204  ret = -1;
205  break;
206  }
207  }
209 
210  return ret;
211 }
212 
213 /*! \brief return true if 'name' is in the ignorepats for context c */
214 static int lookup_c_ip(struct ast_context *c, const char *name)
215 {
216  int idx;
217  int ret = 0;
218 
219  if (ast_rdlock_context(c)) {
220  /* error, skip */
221  return 0;
222  }
223 
224  for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
225  const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
226 
227  if (!strcmp(name, ast_get_ignorepat_name(ip))) {
228  ret = -1;
229  break;
230  }
231  }
233 
234  return ret;
235 }
236 
237 /*! \brief moves to the n-th word in the string, or empty string if none */
238 static const char *skip_words(const char *p, int n)
239 {
240  int in_blank = 0;
241  for (;n && *p; p++) {
242  if (isblank(*p) /* XXX order is important */ && !in_blank) {
243  n--; /* one word is gone */
244  in_blank = 1;
245  } else if (/* !is_blank(*p), we know already, && */ in_blank) {
246  in_blank = 0;
247  }
248  }
249  return p;
250 }
251 
252 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
253 static int partial_match(const char *s, const char *word, int len)
254 {
255  return (len == 0 || !strncmp(s, word, len));
256 }
257 
258 /*! \brief split extension\@context in two parts, return -1 on error.
259  * The return string is malloc'ed and pointed by *ext
260  */
261 static int split_ec(const char *src, char **ext, char ** const ctx, char ** const cid)
262 {
263  char *i, *c, *e = ast_strdup(src); /* now src is not used anymore */
264 
265  if (e == NULL)
266  return -1; /* malloc error */
267  /* now, parse values from 'exten@context' */
268  *ext = e;
269  c = strchr(e, '@');
270  if (c == NULL) /* no context part */
271  *ctx = ""; /* it is not overwritten, anyways */
272  else { /* found context, check for duplicity ... */
273  *c++ = '\0';
274  *ctx = c;
275  if (strchr(c, '@')) { /* two @, not allowed */
276  ast_free(e);
277  return -1;
278  }
279  }
280  if (cid && (i = strchr(e, '/'))) {
281  *i++ = '\0';
282  *cid = i;
283  } else if (cid) {
284  /* Signal none detected */
285  *cid = NULL;
286  }
287  return 0;
288 }
289 
290 /* _X_ is the string we need to complete */
291 static char *complete_dialplan_remove_include(struct ast_cli_args *a)
292 {
293  int which = 0;
294  char *res = NULL;
295  int len = strlen(a->word); /* how many bytes to match */
296  struct ast_context *c = NULL;
297 
298  if (a->pos == 3) { /* "dialplan remove include _X_" */
299  if (ast_wrlock_contexts()) {
300  ast_log(LOG_ERROR, "Failed to lock context list\n");
301  return NULL;
302  }
303  /* walk contexts and their includes, return the n-th match */
304  while (!res && (c = ast_walk_contexts(c))) {
305  int idx;
306 
307  if (ast_rdlock_context(c)) /* error ? skip this one */
308  continue;
309 
310  for (idx = 0; idx < ast_context_includes_count(c); idx++) {
311  const struct ast_include *i = ast_context_includes_get(c, idx);
312  const char *i_name = ast_get_include_name(i);
313  struct ast_context *nc = NULL;
314  int already_served = 0;
315 
316  if (!partial_match(i_name, a->word, len))
317  continue; /* not matched */
318 
319  /* check if this include is already served or not */
320 
321  /* go through all contexts again till we reach actual
322  * context or already_served = 1
323  */
324  while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
325  already_served = lookup_ci(nc, i_name);
326 
327  if (!already_served && ++which > a->n) {
328  res = ast_strdup(i_name);
329  break;
330  }
331  }
333  }
334 
336  return res;
337  } else if (a->pos == 4) { /* "dialplan remove include CTX _X_" */
338  /*
339  * complete as 'from', but only if previous context is really
340  * included somewhere
341  */
342  char *context, *dupline;
343  const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
344 
345  if (a->n > 0)
346  return NULL;
347  context = dupline = ast_strdup(s);
348  if (!dupline) {
349  ast_log(LOG_ERROR, "Out of free memory\n");
350  return NULL;
351  }
352  strsep(&dupline, " ");
353 
354  if (ast_rdlock_contexts()) {
355  ast_log(LOG_ERROR, "Failed to lock contexts list\n");
356  ast_free(context);
357  return NULL;
358  }
359 
360  /* go through all contexts and check if is included ... */
361  while (!res && (c = ast_walk_contexts(c)))
362  if (lookup_ci(c, context)) /* context is really included, complete "from" command */
363  res = ast_strdup("from");
365  if (!res)
366  ast_log(LOG_WARNING, "%s not included anywhere\n", context);
367  ast_free(context);
368  return res;
369  } else if (a->pos == 5) { /* "dialplan remove include CTX from _X_" */
370  /*
371  * Context from which we removing include ...
372  */
373  char *context, *dupline, *from;
374  const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
375  context = dupline = ast_strdup(s);
376  if (!dupline) {
377  ast_log(LOG_ERROR, "Out of free memory\n");
378  return NULL;
379  }
380 
381  strsep(&dupline, " "); /* skip context */
382 
383  /* fourth word must be 'from' */
384  from = strsep(&dupline, " ");
385  if (!from || strcmp(from, "from")) {
386  ast_free(context);
387  return NULL;
388  }
389 
390  if (ast_rdlock_contexts()) {
391  ast_log(LOG_ERROR, "Failed to lock context list\n");
392  ast_free(context);
393  return NULL;
394  }
395 
396  /* walk through all contexts ... */
397  c = NULL;
398  while ( !res && (c = ast_walk_contexts(c))) {
399  const char *c_name = ast_get_context_name(c);
400  if (!partial_match(c_name, a->word, len)) /* not a good target */
401  continue;
402  /* walk through all includes and check if it is our context */
403  if (lookup_ci(c, context) && ++which > a->n)
404  res = ast_strdup(c_name);
405  }
407  ast_free(context);
408  return res;
409  }
410 
411  return NULL;
412 }
413 
414 /*!
415  * REMOVE EXTENSION command stuff
416  */
417 static char *handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
418 {
419  int removing_priority = 0;
420  char *exten, *context, *cid;
421  char *ret = CLI_FAILURE;
422 
423  switch (cmd) {
424  case CLI_INIT:
425  e->command = "dialplan remove extension";
426  e->usage =
427  "Usage: dialplan remove extension exten[/cid]@context [priority]\n"
428  " Remove an extension from a given context. If a priority\n"
429  " is given, only that specific priority from the given extension\n"
430  " will be removed.\n";
431  return NULL;
432  case CLI_GENERATE:
433  return complete_dialplan_remove_extension(a);
434  }
435 
436  if (a->argc != 5 && a->argc != 4)
437  return CLI_SHOWUSAGE;
438 
439  /*
440  * Priority input checking ...
441  */
442  if (a->argc == 5) {
443  const char *c = a->argv[4];
444 
445  /* check for digits in whole parameter for right priority ...
446  * why? because atoi (strtol) returns 0 if any characters in
447  * string and whole extension will be removed, it's not good
448  */
449  if (!strcmp("hint", c))
450  removing_priority = PRIORITY_HINT;
451  else {
452  while (*c && isdigit(*c))
453  c++;
454  if (*c) { /* non-digit in string */
455  ast_cli(a->fd, "Invalid priority '%s'\n", a->argv[4]);
456  return CLI_FAILURE;
457  }
458  removing_priority = atoi(a->argv[4]);
459  }
460 
461  if (removing_priority == 0) {
462  ast_cli(a->fd, "If you want to remove whole extension, please " \
463  "omit priority argument\n");
464  return CLI_FAILURE;
465  }
466  }
467 
468  /* XXX original overwrote argv[3] */
469  /*
470  * Format exten@context checking ...
471  */
472  if (split_ec(a->argv[3], &exten, &context, &cid))
473  return CLI_FAILURE; /* XXX malloc failure */
474  if ((!strlen(exten)) || (!(strlen(context)))) {
475  ast_cli(a->fd, "Missing extension or context name in third argument '%s'\n",
476  a->argv[3]);
477  ast_free(exten);
478  return CLI_FAILURE;
479  }
480 
481  if (!ast_context_remove_extension_callerid(context, exten, removing_priority,
482  /* Do NOT substitute S_OR; it is NOT the same thing */
483  cid ? cid : (removing_priority ? "" : NULL), cid ? 1 : 0, registrar)) {
484  if (!removing_priority)
485  ast_cli(a->fd, "Whole extension %s@%s removed\n",
486  exten, context);
487  else
488  ast_cli(a->fd, "Extension %s@%s with priority %d removed\n",
489  exten, context, removing_priority);
490 
491  ret = CLI_SUCCESS;
492  } else {
493  if (cid) {
494  ast_cli(a->fd, "Failed to remove extension %s/%s@%s\n", exten, cid, context);
495  } else {
496  ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context);
497  }
498  ret = CLI_FAILURE;
499  }
500  ast_free(exten);
501  return ret;
502 }
503 
504 static int manager_dialplan_extension_remove(struct mansession *s, const struct message *m)
505 {
506  const char *context = astman_get_header(m, "Context");
507  const char *extension = astman_get_header(m, "Extension");
508  const char *priority = astman_get_header(m, "Priority");
509 
510  int ipriority;
511  char *exten;
512  char *cidmatch = NULL;
513 
514  if (ast_strlen_zero(context) || ast_strlen_zero(extension)) {
515  astman_send_error(s, m, "Context and Extension must be provided "
516  "for DialplanExtensionRemove");
517  return 0;
518  }
519 
520  exten = ast_strdupa(extension);
521 
522  if (strchr(exten, '/')) {
523  cidmatch = exten;
524  strsep(&cidmatch, "/");
525  }
526 
527  if (ast_strlen_zero(priority)) {
528  ipriority = 0;
529  } else if (!strcmp("hint", priority)) {
530  ipriority = PRIORITY_HINT;
531  } else if ((sscanf(priority, "%30d", &ipriority) != 1) || ipriority <= 0) {
532  astman_send_error(s, m, "The priority specified was invalid.");
533  return 0;
534  }
535 
536  if (!ast_context_remove_extension_callerid(context, exten, ipriority,
537  /* Do not substitute S_OR; it is not the same thing */
538  !ast_strlen_zero(cidmatch) ? cidmatch : (ipriority ? "" : NULL),
539  !ast_strlen_zero(cidmatch) ? 1 : 0, registrar)) {
540  if (ipriority) {
541  astman_send_ack(s, m, "Removed the requested priority from the extension");
542  } else {
543  astman_send_ack(s, m, "Removed the requested extension");
544  }
545  } else {
546  astman_send_error(s, m, "Failed to remove requested extension");
547  }
548 
549  return 0;
550 }
551 
552 static char *complete_dialplan_remove_extension(struct ast_cli_args *a)
553 {
554  char *ret = NULL;
555  int which = 0;
556 
557  if (a->pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
558  struct ast_context *c = NULL;
559  char *context = NULL, *exten = NULL, *cid = NULL;
560  int le = 0; /* length of extension */
561  int lc = 0; /* length of context */
562  int lcid = 0; /* length of cid */
563 
564  lc = split_ec(a->word, &exten, &context, &cid);
565  if (lc) { /* error */
566  return NULL;
567  }
568  le = strlen(exten);
569  lc = strlen(context);
570  lcid = cid ? strlen(cid) : -1;
571 
572  if (ast_rdlock_contexts()) {
573  ast_log(LOG_ERROR, "Failed to lock context list\n");
574  goto error2;
575  }
576 
577  /* find our context ... */
578  while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
579  struct ast_exten *e = NULL;
580  /* XXX locking ? */
581  if (!partial_match(ast_get_context_name(c), context, lc))
582  continue; /* context not matched */
583  while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
584  if ( !strchr(a->word, '/') ||
585  (!strchr(a->word, '@') && partial_match(ast_get_extension_cidmatch(e), cid, lcid)) ||
586  (strchr(a->word, '@') && !strcmp(ast_get_extension_cidmatch(e), cid))) {
587  if ( ((strchr(a->word, '/') || strchr(a->word, '@')) && !strcmp(ast_get_extension_name(e), exten)) ||
588  (!strchr(a->word, '/') && !strchr(a->word, '@') && partial_match(ast_get_extension_name(e), exten, le))) { /* n-th match */
589  if (++which > a->n) {
590  /* If there is an extension then return exten@context. */
591  if (ast_get_extension_matchcid(e) && (!strchr(a->word, '@') || strchr(a->word, '/'))) {
592  if (ast_asprintf(&ret, "%s/%s@%s", ast_get_extension_name(e), ast_get_extension_cidmatch(e), ast_get_context_name(c)) < 0) {
593  ret = NULL;
594  }
595  break;
596  } else if (!ast_get_extension_matchcid(e) && !strchr(a->word, '/')) {
597  if (ast_asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)) < 0) {
598  ret = NULL;
599  }
600  break;
601  }
602  }
603  }
604  }
605  }
606  if (e) /* got a match */
607  break;
608  }
609 
611  error2:
612  ast_free(exten);
613  } else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
614  char *exten = NULL, *context, *cid, *p;
615  struct ast_context *c;
616  int le, lc, len;
617  const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'extension' */
618  int i = split_ec(s, &exten, &context, &cid); /* parse ext@context */
619 
620  if (i) /* error */
621  goto error3;
622  if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
623  *p = '\0';
624  if ( (p = strchr(context, ' ')) ) /* remove space after context */
625  *p = '\0';
626  le = strlen(exten);
627  lc = strlen(context);
628  len = strlen(a->word);
629  if (le == 0 || lc == 0)
630  goto error3;
631 
632  if (ast_rdlock_contexts()) {
633  ast_log(LOG_ERROR, "Failed to lock context list\n");
634  goto error3;
635  }
636 
637  /* walk contexts */
638  c = NULL;
639  while ( (c = ast_walk_contexts(c)) ) {
640  /* XXX locking on c ? */
641  struct ast_exten *e;
642  if (strcmp(ast_get_context_name(c), context) != 0)
643  continue;
644  /* got it, we must match here */
645  e = NULL;
646  while ( (e = ast_walk_context_extensions(c, e)) ) {
647  struct ast_exten *priority;
648  char buffer[10];
649 
650  if (cid && strcmp(ast_get_extension_cidmatch(e), cid) != 0) {
651  continue;
652  }
653  if (strcmp(ast_get_extension_name(e), exten) != 0)
654  continue;
655  /* XXX lock e ? */
656  priority = NULL;
657  while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
658  snprintf(buffer, sizeof(buffer), "%d", ast_get_extension_priority(priority));
659  if (partial_match(buffer, a->word, len) && ++which > a->n) /* n-th match */
660  ret = ast_strdup(buffer);
661  }
662  break;
663  }
664  break;
665  }
667  error3:
668  ast_free(exten);
669  }
670  return ret;
671 }
672 
673 /*!
674  * Include context ...
675  */
676 static char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
677 {
678  const char *into_context;
679 
680  switch (cmd) {
681  case CLI_INIT:
682  e->command = "dialplan add include";
683  e->usage =
684  "Usage: dialplan add include <context> into <context>\n"
685  " Include a context in another context.\n";
686  return NULL;
687  case CLI_GENERATE:
688  return complete_dialplan_add_include(a);
689  }
690 
691  if (a->argc != 6) /* dialplan add include CTX in CTX */
692  return CLI_SHOWUSAGE;
693 
694  /* fifth arg must be 'into' ... */
695  if (strcmp(a->argv[4], "into"))
696  return CLI_SHOWUSAGE;
697 
698  into_context = a->argv[5];
699 
700  if (!ast_context_find(into_context)) {
701  ast_cli(a->fd, "Context '%s' did not exist prior to add include - the context will be created.\n", into_context);
702  }
703 
704  if (!ast_context_find_or_create(NULL, NULL, into_context, registrar)) {
705  ast_cli(a->fd, "ast_context_find_or_create() failed\n");
706  ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",a->argv[3], a->argv[5]);
707  return CLI_FAILURE;
708  }
709 
710  if (ast_context_add_include(a->argv[5], a->argv[3], registrar)) {
711  switch (errno) {
712  case ENOMEM:
713  ast_cli(a->fd, "Out of memory for context addition\n");
714  break;
715 
716  case EBUSY:
717  ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
718  break;
719 
720  case EEXIST:
721  ast_cli(a->fd, "Context '%s' already included in '%s' context\n",
722  a->argv[3], a->argv[5]);
723  break;
724 
725  case ENOENT:
726  case EINVAL:
727  ast_cli(a->fd, "There is no existence of context '%s'\n",
728  errno == ENOENT ? a->argv[5] : a->argv[3]);
729  break;
730 
731  default:
732  ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",
733  a->argv[3], a->argv[5]);
734  break;
735  }
736  return CLI_FAILURE;
737  }
738 
739  /* show some info ... */
740  ast_cli(a->fd, "Context '%s' included in '%s' context\n",
741  a->argv[3], a->argv[5]);
742 
743  return CLI_SUCCESS;
744 }
745 
746 static char *complete_dialplan_add_include(struct ast_cli_args *a)
747 {
748  struct ast_context *c;
749  int which = 0;
750  char *ret = NULL;
751  int len = strlen(a->word);
752 
753  if (a->pos == 3) { /* 'dialplan add include _X_' (context) ... */
754  if (ast_rdlock_contexts()) {
755  ast_log(LOG_ERROR, "Failed to lock context list\n");
756  return NULL;
757  }
758  for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
759  if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n)
760  ret = ast_strdup(ast_get_context_name(c));
762  return ret;
763  } else if (a->pos == 4) { /* dialplan add include CTX _X_ */
764  /* always complete as 'into' */
765  return (a->n == 0) ? ast_strdup("into") : NULL;
766  } else if (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
767  char *context, *dupline, *into;
768  const char *s = skip_words(a->line, 3); /* should not fail */
769  context = dupline = ast_strdup(s);
770 
771  if (!dupline) {
772  ast_log(LOG_ERROR, "Out of free memory\n");
773  return NULL;
774  }
775 
776  strsep(&dupline, " "); /* skip context */
777  into = strsep(&dupline, " ");
778  /* error if missing context or fifth word is not 'into' */
779  if (!strlen(context) || strcmp(into, "into")) {
780  ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
781  context, into);
782  goto error3;
783  }
784 
785  if (ast_rdlock_contexts()) {
786  ast_log(LOG_ERROR, "Failed to lock context list\n");
787  goto error3;
788  }
789 
790  for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
791  if (!strcmp(context, ast_get_context_name(c)))
792  continue; /* skip ourselves */
793  if (partial_match(ast_get_context_name(c), a->word, len) &&
794  !lookup_ci(c, context) /* not included yet */ &&
795  ++which > a->n) {
796  ret = ast_strdup(ast_get_context_name(c));
797  }
798  }
800  error3:
801  ast_free(context);
802  return ret;
803  }
804 
805  return NULL;
806 }
807 
808 /*!
809  * \brief 'save dialplan' CLI command implementation functions ...
810  */
811 static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
812 {
813  char filename[256], overrideswitch[256] = "";
814  struct ast_context *c;
815  struct ast_config *cfg;
816  struct ast_variable *v;
817  int incomplete = 0; /* incomplete config write? */
818  FILE *output;
819  struct ast_flags config_flags = { 0 };
820  const char *base, *slash;
821 
822  switch (cmd) {
823  case CLI_INIT:
824  e->command = "dialplan save";
825  e->usage =
826  "Usage: dialplan save [/path/to/extension/file]\n"
827  " Save dialplan created by pbx_config module.\n"
828  "\n"
829  "Example: dialplan save (/etc/asterisk/extensions.conf)\n"
830  " dialplan save /home/markster (/home/markster/extensions.conf)\n";
831  return NULL;
832  case CLI_GENERATE:
833  return NULL;
834  }
835 
836  if (! (static_config && !write_protect_config)) {
837  ast_cli(a->fd,
838  "I can't save dialplan now, see '%s' example file.\n",
839  config);
840  return CLI_FAILURE;
841  }
842 
843  if (a->argc != 2 && a->argc != 3)
844  return CLI_SHOWUSAGE;
845 
846  if (ast_mutex_lock(&save_dialplan_lock)) {
847  ast_cli(a->fd,
848  "Failed to lock dialplan saving (another process saving?)\n");
849  return CLI_FAILURE;
850  }
851  /* XXX the code here is quite loose, a pathname with .conf in it
852  * is assumed to be a complete pathname
853  */
854  if (a->argc == 3) { /* have config path. Look for *.conf */
855  base = a->argv[2];
856  if (!strstr(a->argv[2], ".conf")) { /*no, this is assumed to be a pathname */
857  /* if filename ends with '/', do not add one */
858  slash = (*(a->argv[2] + strlen(a->argv[2]) -1) == '/') ? "/" : "";
859  } else { /* yes, complete file name */
860  slash = "";
861  }
862  } else {
863  /* no config file, default one */
864  base = ast_config_AST_CONFIG_DIR;
865  slash = "/";
866  }
867  snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
868 
869  cfg = ast_config_load("extensions.conf", config_flags);
870  if (!cfg) {
871  ast_cli(a->fd, "Failed to load extensions.conf\n");
872  ast_mutex_unlock(&save_dialplan_lock);
873  return CLI_FAILURE;
874  }
875 
876  /* try to lock contexts list */
877  if (ast_rdlock_contexts()) {
878  ast_cli(a->fd, "Failed to lock contexts list\n");
879  ast_mutex_unlock(&save_dialplan_lock);
880  ast_config_destroy(cfg);
881  return CLI_FAILURE;
882  }
883 
884  /* create new file ... */
885  if (!(output = fopen(filename, "wt"))) {
886  ast_cli(a->fd, "Failed to create file '%s'\n",
887  filename);
889  ast_mutex_unlock(&save_dialplan_lock);
890  ast_config_destroy(cfg);
891  return CLI_FAILURE;
892  }
893 
894  /* fireout general info */
895  if (overrideswitch_config) {
896  snprintf(overrideswitch, sizeof(overrideswitch), "overrideswitch=%s\n", overrideswitch_config);
897  }
898  fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\n%sextenpatternmatchnew=%s\n\n",
899  static_config ? "yes" : "no",
900  write_protect_config ? "yes" : "no",
901  autofallthrough_config ? "yes" : "no",
902  clearglobalvars_config ? "yes" : "no",
903  overrideswitch_config ? overrideswitch : "",
904  extenpatternmatchnew_config ? "yes" : "no");
905 
906  if ((v = ast_variable_browse(cfg, "globals"))) {
907  fprintf(output, "[globals]\n");
908  while(v) {
909  int escaped_len = 2 * strlen(v->value) + 1;
910  char escaped[escaped_len];
911 
912  ast_escape_semicolons(v->value, escaped, escaped_len);
913  fprintf(output, "%s => %s\n", v->name, escaped);
914  v = v->next;
915  }
916  fprintf(output, "\n");
917  }
918 
919  ast_config_destroy(cfg);
920 
921 #define PUT_CTX_HDR do { \
922  if (!context_header_written) { \
923  fprintf(output, "[%s]\n", ast_get_context_name(c)); \
924  context_header_written = 1; \
925  } \
926  } while (0)
927 
928  /* walk all contexts */
929  for (c = NULL; (c = ast_walk_contexts(c)); ) {
930  int context_header_written = 0;
931  struct ast_exten *ext, *last_written_e = NULL;
932  int idx;
933 
934  /* try to lock context and fireout all info */
935  if (ast_rdlock_context(c)) { /* lock failure */
936  incomplete = 1;
937  continue;
938  }
939  /* registered by this module? */
940  /* XXX do we need this ? */
941  if (!strcmp(ast_get_context_registrar(c), registrar)) {
942  fprintf(output, "[%s]\n", ast_get_context_name(c));
943  context_header_written = 1;
944  }
945 
946  /* walk extensions ... */
947  for (ext = NULL; (ext = ast_walk_context_extensions(c, ext)); ) {
948  struct ast_exten *p = NULL;
949 
950  /* fireout priorities */
951  while ( (p = ast_walk_extension_priorities(ext, p)) ) {
952  if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
953  continue;
954 
955  /* make empty line between different extensions */
956  if (last_written_e != NULL &&
957  strcmp(ast_get_extension_name(last_written_e),
958  ast_get_extension_name(p)))
959  fprintf(output, "\n");
960  last_written_e = p;
961 
962  PUT_CTX_HDR;
963 
964  if (ast_get_extension_priority(p) == PRIORITY_HINT) { /* easy */
965  fprintf(output, "exten => %s,hint,%s\n",
966  ast_get_extension_name(p),
967  ast_get_extension_app(p));
968  } else {
969  const char *sep, *cid;
970  const char *el = ast_get_extension_label(p);
971  char label[128] = "";
972  char *appdata = ast_get_extension_app_data(p);
973 
974  int escaped_len = (!ast_strlen_zero(appdata)) ? 2 * strlen(appdata) + 1 : 1;
975  char escaped[escaped_len];
976 
977  if (ast_get_extension_matchcid(p)) {
978  sep = "/";
979  cid = ast_get_extension_cidmatch(p);
980  } else {
981  sep = cid = "";
982  }
983 
984  if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) {
985  incomplete = 1; /* error encountered or label > 125 chars */
986  }
987 
988  if (!ast_strlen_zero(appdata)) {
989  ast_escape_semicolons(appdata, escaped, escaped_len);
990  } else {
991  escaped[0] = '\0';
992  }
993 
994  fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
995  ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
996  ast_get_extension_priority(p), label,
997  ast_get_extension_app(p), escaped);
998  }
999  }
1000  }
1001 
1002  /* written any extensions? ok, write space between exten & inc */
1003  if (last_written_e)
1004  fprintf(output, "\n");
1005 
1006  /* walk through includes */
1007  for (idx = 0; idx < ast_context_includes_count(c); idx++) {
1008  const struct ast_include *i = ast_context_includes_get(c, idx);
1009 
1010  if (strcmp(ast_get_include_registrar(i), registrar) != 0)
1011  continue; /* not mine */
1012  PUT_CTX_HDR;
1013  fprintf(output, "include => %s\n", ast_get_include_name(i));
1014  }
1015  if (ast_context_includes_count(c)) {
1016  fprintf(output, "\n");
1017  }
1018 
1019  /* walk through switches */
1020  for (idx = 0; idx < ast_context_switches_count(c); idx++) {
1021  const struct ast_sw *sw = ast_context_switches_get(c, idx);
1022 
1023  if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
1024  continue; /* not mine */
1025  PUT_CTX_HDR;
1026  fprintf(output, "switch => %s/%s\n",
1027  ast_get_switch_name(sw), ast_get_switch_data(sw));
1028  }
1029 
1030  if (ast_context_switches_count(c)) {
1031  fprintf(output, "\n");
1032  }
1033 
1034  /* fireout ignorepats ... */
1035  for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
1036  const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
1037 
1038  if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
1039  continue; /* not mine */
1040  PUT_CTX_HDR;
1041  fprintf(output, "ignorepat => %s\n",
1042  ast_get_ignorepat_name(ip));
1043  }
1044 
1045  ast_unlock_context(c);
1046  }
1047 
1049  ast_mutex_unlock(&save_dialplan_lock);
1050  fclose(output);
1051 
1052  if (incomplete) {
1053  ast_cli(a->fd, "Saved dialplan is incomplete\n");
1054  return CLI_FAILURE;
1055  }
1056 
1057  ast_cli(a->fd, "Dialplan successfully saved into '%s'\n",
1058  filename);
1059  return CLI_SUCCESS;
1060 }
1061 
1062 /*!
1063  * \brief ADD EXTENSION command stuff
1064  */
1065 static char *handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1066 {
1067  char *whole_exten;
1068  char *exten, *prior;
1069  int iprior = -2;
1070  char *cidmatch, *app, *app_data;
1071  char *start, *end;
1072  const char *into_context;
1073 
1074  switch (cmd) {
1075  case CLI_INIT:
1076  e->command = "dialplan add extension";
1077  e->usage =
1078  "Usage: dialplan add extension <exten>,<priority>,<app> into <context> [replace]\n"
1079  "\n"
1080  " app can be either:\n"
1081  " app-name\n"
1082  " app-name(app-data)\n"
1083  " app-name,<app-data>\n"
1084  "\n"
1085  " This command will add the new extension into <context>. If\n"
1086  " an extension with the same priority already exists and the\n"
1087  " 'replace' option is given we will replace the extension.\n"
1088  "\n"
1089  "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
1090  " Now, you can dial 6123 and talk to Markster :)\n";
1091  return NULL;
1092  case CLI_GENERATE:
1094  }
1095 
1096  /* check for arguments at first */
1097  if (a->argc != 6 && a->argc != 7)
1098  return CLI_SHOWUSAGE;
1099  if (strcmp(a->argv[4], "into"))
1100  return CLI_SHOWUSAGE;
1101  if (a->argc == 7)
1102  if (strcmp(a->argv[6], "replace"))
1103  return CLI_SHOWUSAGE;
1104 
1105  whole_exten = ast_strdupa(a->argv[3]);
1106  exten = strsep(&whole_exten,",");
1107  if (strchr(exten, '/')) {
1108  cidmatch = exten;
1109  strsep(&cidmatch,"/");
1110  } else {
1111  cidmatch = NULL;
1112  }
1113  prior = strsep(&whole_exten,",");
1114  if (prior) {
1115  if (!strcmp(prior, "hint")) {
1116  iprior = PRIORITY_HINT;
1117  } else {
1118  if (sscanf(prior, "%30d", &iprior) != 1) {
1119  ast_cli(a->fd, "'%s' is not a valid priority\n", prior);
1120  prior = NULL;
1121  }
1122  }
1123  }
1124  app = whole_exten;
1125  if (app) {
1126  if ((start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
1127  *start = *end = '\0';
1128  app_data = start + 1;
1129  } else {
1130  app_data = strchr(app, ',');
1131  if (app_data) {
1132  *app_data++ = '\0';
1133  }
1134  }
1135  } else {
1136  app_data = NULL;
1137  }
1138 
1139  if (!exten || !prior || !app) {
1140  return CLI_SHOWUSAGE;
1141  }
1142 
1143  if (!app_data) {
1144  app_data = "";
1145  }
1146  into_context = a->argv[5];
1147 
1148  if (!ast_context_find(into_context)) {
1149  ast_cli(a->fd, "Context '%s' did not exist prior to add extension - the context will be created.\n", into_context);
1150  }
1151 
1152  if (!ast_context_find_or_create(NULL, NULL, into_context, registrar)) {
1153  ast_cli(a->fd, "Failed to add '%s,%s,%s(%s)' extension into '%s' context\n",
1154  exten, prior, app, app_data, into_context);
1155  return CLI_FAILURE;
1156  }
1157 
1158  if (ast_add_extension(into_context, a->argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1159  ast_strdup(app_data), ast_free_ptr, registrar)) {
1160  switch (errno) {
1161  case ENOMEM:
1162  ast_cli(a->fd, "Out of free memory\n");
1163  break;
1164 
1165  case EBUSY:
1166  ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
1167  break;
1168 
1169  case ENOENT:
1170  ast_cli(a->fd, "No existence of '%s' context\n", into_context);
1171  break;
1172 
1173  case EEXIST:
1174  ast_cli(a->fd, "Extension %s@%s with priority %s already exists\n",
1175  exten, into_context, prior);
1176  break;
1177 
1178  default:
1179  ast_cli(a->fd, "Failed to add '%s,%s,%s(%s)' extension into '%s' context\n",
1180  exten, prior, app, app_data, into_context);
1181  break;
1182  }
1183  return CLI_FAILURE;
1184  }
1185 
1186  if (a->argc == 7) {
1187  ast_cli(a->fd, "Extension %s@%s (%s) replace by '%s,%s,%s(%s)'\n",
1188  exten, into_context, prior, exten, prior, app, app_data);
1189  } else {
1190  ast_cli(a->fd, "Extension '%s,%s,%s(%s)' added into '%s' context\n",
1191  exten, prior, app, app_data, into_context);
1192  }
1193 
1194  return CLI_SUCCESS;
1195 }
1196 
1197 static int manager_dialplan_extension_add(struct mansession *s, const struct message *m)
1198 {
1199  const char *context = astman_get_header(m, "Context");
1200  const char *extension = astman_get_header(m, "Extension");
1201  const char *priority = astman_get_header(m, "Priority");
1202  const char *application = astman_get_header(m, "Application");
1203  const char *application_data = astman_get_header(m, "ApplicationData");
1204  int replace = ast_true(astman_get_header(m, "Replace"));
1205  int ipriority;
1206  char *exten;
1207  char *cidmatch = NULL;
1208  struct ast_context *add_context;
1209 
1210  if (ast_strlen_zero(context) || ast_strlen_zero(extension) ||
1211  ast_strlen_zero(priority) || ast_strlen_zero(application)) {
1212  astman_send_error(s, m, "Context, Extension, Priority, and "
1213  "Application must be defined for DialplanExtensionAdd.");
1214  return 0;
1215  }
1216 
1217  /* Priority conversion/validation */
1218  if (!strcmp(priority, "hint")) {
1219  ipriority = PRIORITY_HINT;
1220  } else if ((sscanf(priority, "%30d", &ipriority) != 1) || (ipriority < 0)) {
1221  astman_send_error(s, m, "The priority specified was invalid.");
1222  return 0;
1223  }
1224 
1225  /* Split extension from cidmatch */
1226  exten = ast_strdupa(extension);
1227 
1228  if (strchr(exten, '/')) {
1229  cidmatch = exten;
1230  strsep(&cidmatch, "/");
1231  }
1232 
1233  if (ast_wrlock_contexts()) {
1234  astman_send_error(s, m, "Failed to lock contexts list. Try again later.");
1235  return 0;
1236  }
1237 
1238  add_context = ast_context_find_or_create(NULL, NULL, context, registrar);
1239  if (!add_context) {
1240  astman_send_error(s, m, "Could not find or create context specified "
1241  "for the extension.");
1243  return 0;
1244  }
1245 
1246  if (ast_add_extension2(add_context, replace, exten, ipriority, NULL, cidmatch,
1247  application, ast_strdup(application_data), ast_free_ptr, registrar, NULL, 0)) {
1249  switch (errno) {
1250  case ENOMEM:
1251  astman_send_error(s, m, "Out of Memory");
1252  break;
1253 
1254  case EBUSY:
1255  astman_send_error(s, m, "Failed to lock context(s) list");
1256  break;
1257 
1258  case ENOENT:
1259  astman_send_error(s, m, "Context does not exist");
1260  break;
1261 
1262  case EEXIST:
1263  astman_send_error(s, m, "That extension and priority already exist at that context");
1264  break;
1265 
1266  default:
1267  astman_send_error(s, m, "Failed to add extension");
1268  break;
1269  }
1270  return 0;
1271  }
1273 
1274  astman_send_ack(s, m, "Added requested extension");
1275 
1276  return 0;
1277 }
1278 
1279 static char *complete_dialplan_remove_context(struct ast_cli_args *a)
1280 {
1281  struct ast_context *c = NULL;
1282  int len = strlen(a->word);
1283  char *res = NULL;
1284  int which = 0;
1285 
1286  if (a->pos != 3) {
1287  return NULL;
1288  }
1289 
1290 
1291  /* try to lock contexts list ... */
1292  if (ast_rdlock_contexts()) {
1293  ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1294  return NULL;
1295  }
1296 
1297  /* walk through all contexts */
1298  while ( !res && (c = ast_walk_contexts(c)) ) {
1299  if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n) {
1300  res = ast_strdup(ast_get_context_name(c));
1301  }
1302  }
1304  return res;
1305 }
1306 
1307 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1309 {
1310  int which = 0;
1311 
1312  if (a->pos == 4) { /* complete 'into' word ... */
1313  return (a->n == 0) ? ast_strdup("into") : NULL;
1314  } else if (a->pos == 5) { /* complete context */
1315  struct ast_context *c = NULL;
1316  int len = strlen(a->word);
1317  char *res = NULL;
1318 
1319  /* try to lock contexts list ... */
1320  if (ast_rdlock_contexts()) {
1321  ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1322  return NULL;
1323  }
1324 
1325  /* walk through all contexts */
1326  while ( !res && (c = ast_walk_contexts(c)) )
1327  if (partial_match(ast_get_context_name(c), a->word, len) && ++which > a->n)
1328  res = ast_strdup(ast_get_context_name(c));
1330  return res;
1331  } else if (a->pos == 6) {
1332  return a->n == 0 ? ast_strdup("replace") : NULL;
1333  }
1334  return NULL;
1335 }
1336 
1337 /*!
1338  * IGNOREPAT CLI stuff
1339  */
1340 static char *handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1341 {
1342  switch (cmd) {
1343  case CLI_INIT:
1344  e->command = "dialplan add ignorepat";
1345  e->usage =
1346  "Usage: dialplan add ignorepat <pattern> into <context>\n"
1347  " This command adds a new ignore pattern into context <context>\n"
1348  "\n"
1349  "Example: dialplan add ignorepat _3XX into local\n";
1350  return NULL;
1351  case CLI_GENERATE:
1352  return complete_dialplan_add_ignorepat(a);
1353  }
1354 
1355  if (a->argc != 6)
1356  return CLI_SHOWUSAGE;
1357 
1358  if (strcmp(a->argv[4], "into"))
1359  return CLI_SHOWUSAGE;
1360 
1361  if (ast_context_add_ignorepat(a->argv[5], a->argv[3], registrar)) {
1362  switch (errno) {
1363  case ENOMEM:
1364  ast_cli(a->fd, "Out of free memory\n");
1365  break;
1366 
1367  case ENOENT:
1368  ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
1369  break;
1370 
1371  case EEXIST:
1372  ast_cli(a->fd, "Ignore pattern '%s' already included in '%s' context\n",
1373  a->argv[3], a->argv[5]);
1374  break;
1375 
1376  case EBUSY:
1377  ast_cli(a->fd, "Failed to lock context(s) list, please, try again later\n");
1378  break;
1379 
1380  default:
1381  ast_cli(a->fd, "Failed to add ignore pattern '%s' into '%s' context\n",
1382  a->argv[3], a->argv[5]);
1383  break;
1384  }
1385  return CLI_FAILURE;
1386  }
1387 
1388  ast_cli(a->fd, "Ignore pattern '%s' added into '%s' context\n",
1389  a->argv[3], a->argv[5]);
1390 
1391  return CLI_SUCCESS;
1392 }
1393 
1394 static char *complete_dialplan_add_ignorepat(struct ast_cli_args *a)
1395 {
1396  if (a->pos == 4)
1397  return a->n == 0 ? ast_strdup("into") : NULL;
1398  else if (a->pos == 5) {
1399  struct ast_context *c;
1400  int which = 0;
1401  char *dupline, *ignorepat = NULL;
1402  const char *s;
1403  char *ret = NULL;
1404  int len = strlen(a->word);
1405 
1406  /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1407  s = skip_words(a->line, 3);
1408  if (s == NULL)
1409  return NULL;
1410  dupline = ast_strdup(s);
1411  if (!dupline) {
1412  ast_log(LOG_ERROR, "Malloc failure\n");
1413  return NULL;
1414  }
1415  ignorepat = strsep(&dupline, " ");
1416 
1417  if (ast_rdlock_contexts()) {
1418  ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1419  return NULL;
1420  }
1421 
1422  for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1423  int found = 0;
1424 
1425  if (!partial_match(ast_get_context_name(c), a->word, len))
1426  continue; /* not mine */
1427  if (ignorepat) /* there must be one, right ? */
1428  found = lookup_c_ip(c, ignorepat);
1429  if (!found && ++which > a->n)
1430  ret = ast_strdup(ast_get_context_name(c));
1431  }
1432 
1433  ast_free(ignorepat);
1435  return ret;
1436  }
1437 
1438  return NULL;
1439 }
1440 
1441 static char *handle_cli_dialplan_remove_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1442 {
1443  switch (cmd) {
1444  case CLI_INIT:
1445  e->command = "dialplan remove ignorepat";
1446  e->usage =
1447  "Usage: dialplan remove ignorepat <pattern> from <context>\n"
1448  " This command removes an ignore pattern from context <context>\n"
1449  "\n"
1450  "Example: dialplan remove ignorepat _3XX from local\n";
1451  return NULL;
1452  case CLI_GENERATE:
1453  return complete_dialplan_remove_ignorepat(a);
1454  }
1455 
1456  if (a->argc != 6)
1457  return CLI_SHOWUSAGE;
1458 
1459  if (strcmp(a->argv[4], "from"))
1460  return CLI_SHOWUSAGE;
1461 
1462  if (ast_context_remove_ignorepat(a->argv[5], a->argv[3], registrar)) {
1463  switch (errno) {
1464  case EBUSY:
1465  ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
1466  break;
1467 
1468  case ENOENT:
1469  ast_cli(a->fd, "There is no existence of '%s' context\n", a->argv[5]);
1470  break;
1471 
1472  case EINVAL:
1473  ast_cli(a->fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1474  a->argv[3], a->argv[5]);
1475  break;
1476 
1477  default:
1478  ast_cli(a->fd, "Failed to remove ignore pattern '%s' from '%s' context\n",
1479  a->argv[3], a->argv[5]);
1480  break;
1481  }
1482  return CLI_FAILURE;
1483  }
1484 
1485  ast_cli(a->fd, "Ignore pattern '%s' removed from '%s' context\n",
1486  a->argv[3], a->argv[5]);
1487  return CLI_SUCCESS;
1488 }
1489 
1490 static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *a)
1491 {
1492  struct ast_context *c;
1493  int which = 0;
1494  char *ret = NULL;
1495 
1496  if (a->pos == 3) {
1497  int len = strlen(a->word);
1498  if (ast_rdlock_contexts()) {
1499  ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1500  return NULL;
1501  }
1502 
1503  for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1504  int idx;
1505 
1506  if (ast_rdlock_context(c)) /* error, skip it */
1507  continue;
1508  for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
1509  const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
1510 
1511  if (partial_match(ast_get_ignorepat_name(ip), a->word, len) && ++which > a->n) {
1512  /* n-th match */
1513  struct ast_context *cw = NULL;
1514  int found = 0;
1515  while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1516  /* XXX do i stop on c, or skip it ? */
1517  found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1518  }
1519  if (!found)
1520  ret = ast_strdup(ast_get_ignorepat_name(ip));
1521  }
1522  }
1523  ast_unlock_context(c);
1524  }
1526  return ret;
1527  } else if (a->pos == 4) {
1528  return a->n == 0 ? ast_strdup("from") : NULL;
1529  } else if (a->pos == 5) { /* XXX check this */
1530  char *dupline, *duplinet, *ignorepat;
1531  int len = strlen(a->word);
1532 
1533  dupline = ast_strdup(a->line);
1534  if (!dupline) {
1535  ast_log(LOG_WARNING, "Out of free memory\n");
1536  return NULL;
1537  }
1538 
1539  duplinet = dupline;
1540  strsep(&duplinet, " ");
1541  strsep(&duplinet, " ");
1542  ignorepat = strsep(&duplinet, " ");
1543 
1544  if (!ignorepat) {
1545  ast_free(dupline);
1546  return NULL;
1547  }
1548 
1549  if (ast_rdlock_contexts()) {
1550  ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1551  ast_free(dupline);
1552  return NULL;
1553  }
1554 
1555  for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1556  if (ast_rdlock_context(c)) {
1557  /* fail, skip it */
1558  continue;
1559  }
1560  if (!partial_match(ast_get_context_name(c), a->word, len)) {
1561  ast_unlock_context(c);
1562  continue;
1563  }
1564  if (lookup_c_ip(c, ignorepat) && ++which > a->n)
1565  ret = ast_strdup(ast_get_context_name(c));
1566  ast_unlock_context(c);
1567  }
1569  ast_free(dupline);
1570  return ret;
1571  }
1572 
1573  return NULL;
1574 }
1575 
1576 static int pbx_load_module(void);
1577 
1578 static char *handle_cli_dialplan_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1579 {
1580  switch (cmd) {
1581  case CLI_INIT:
1582  e->command = "dialplan reload";
1583  e->usage =
1584  "Usage: dialplan reload\n"
1585  " Reload extensions.conf without reloading any other\n"
1586  " modules. This command does not delete global variables\n"
1587  " unless clearglobalvars is set to yes in extensions.conf\n";
1588  return NULL;
1589  case CLI_GENERATE:
1590  return NULL;
1591  }
1592 
1593  if (a->argc != 2)
1594  return CLI_SHOWUSAGE;
1595 
1596  if (clearglobalvars_config)
1597  pbx_builtin_clear_globals();
1598 
1599  pbx_load_module();
1600  ast_cli(a->fd, "Dialplan reloaded.\n");
1601  return CLI_SUCCESS;
1602 }
1603 
1604 /*!
1605  * CLI entries for commands provided by this module
1606  */
1607 static struct ast_cli_entry cli_pbx_config[] = {
1608  AST_CLI_DEFINE(handle_cli_dialplan_add_extension, "Add new extension into context"),
1609  AST_CLI_DEFINE(handle_cli_dialplan_remove_extension, "Remove a specified extension"),
1610  AST_CLI_DEFINE(handle_cli_dialplan_remove_context, "Remove a specified context"),
1611  AST_CLI_DEFINE(handle_cli_dialplan_add_ignorepat, "Add new ignore pattern"),
1612  AST_CLI_DEFINE(handle_cli_dialplan_remove_ignorepat, "Remove ignore pattern from context"),
1613  AST_CLI_DEFINE(handle_cli_dialplan_add_include, "Include context in other context"),
1614  AST_CLI_DEFINE(handle_cli_dialplan_remove_include, "Remove a specified include from context"),
1615  AST_CLI_DEFINE(handle_cli_dialplan_reload, "Reload extensions and *only* extensions")
1616 };
1617 
1618 static struct ast_cli_entry cli_dialplan_save =
1619  AST_CLI_DEFINE(handle_cli_dialplan_save, "Save current dialplan into a file");
1620 
1621 #define AMI_EXTENSION_ADD "DialplanExtensionAdd"
1622 #define AMI_EXTENSION_REMOVE "DialplanExtensionRemove"
1623 
1624 /*!
1625  * Standard module functions ...
1626  */
1627 static int unload_module(void)
1628 {
1629  ast_cli_unregister(&cli_dialplan_save);
1630  ast_free(overrideswitch_config);
1631  overrideswitch_config = NULL;
1632 
1633  ast_cli_unregister_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
1634  ast_manager_unregister(AMI_EXTENSION_ADD);
1635  ast_manager_unregister(AMI_EXTENSION_REMOVE);
1636  ast_context_destroy(NULL, registrar);
1637 
1638  stasis_unsubscribe_and_join(fully_booted_subscription);
1639 
1640  return 0;
1641 }
1642 
1643 /*!\note Protect against misparsing based upon commas in the middle of fields
1644  * like character classes. We've taken steps to permit pretty much every other
1645  * printable character in a character class, so properly handling a comma at
1646  * this level is a natural extension. This is almost like the standard
1647  * application parser in app.c, except that it handles square brackets. */
1648 static char *pbx_strsep(char **destructible, const char *delim)
1649 {
1650  int square = 0;
1651  char *res;
1652 
1653  if (!destructible || !*destructible) {
1654  return NULL;
1655  }
1656  res = *destructible;
1657  for (; **destructible; (*destructible)++) {
1658  if (**destructible == '[' && !strchr(delim, '[')) {
1659  square++;
1660  } else if (**destructible == ']' && !strchr(delim, ']')) {
1661  if (square) {
1662  square--;
1663  }
1664  } else if (**destructible == '\\' && !strchr(delim, '\\')) {
1665  (*destructible)++;
1666  } else if (strchr(delim, **destructible) && !square) {
1667  **destructible = '\0';
1668  (*destructible)++;
1669  break;
1670  }
1671  }
1672  if (**destructible == '\0') {
1673  *destructible = NULL;
1674  }
1675  return res;
1676 }
1677 
1678 static int pbx_load_config(const char *config_file)
1679 {
1680  struct ast_config *cfg;
1681  char *end;
1682  char *label;
1683 #ifdef LOW_MEMORY
1684  char realvalue[256];
1685 #else
1686  char realvalue[8192];
1687 #endif
1688  int lastpri = -2;
1689  struct ast_context *con;
1690  struct ast_variable *v;
1691  const char *cxt;
1692  const char *aft;
1693  const char *newpm, *ovsw;
1694  struct ast_flags config_flags = { 0 };
1695  char lastextension[256];
1696  cfg = ast_config_load(config_file, config_flags);
1697  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID)
1698  return 0;
1699 
1700  /* Use existing config to populate the PBX table */
1701  static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
1702  write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
1703  if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
1704  autofallthrough_config = ast_true(aft);
1705  if ((newpm = ast_variable_retrieve(cfg, "general", "extenpatternmatchnew")))
1706  extenpatternmatchnew_config = ast_true(newpm);
1707  clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
1708  if ((ovsw = ast_variable_retrieve(cfg, "general", "overrideswitch"))) {
1709  if (overrideswitch_config) {
1710  ast_free(overrideswitch_config);
1711  }
1712  if (!ast_strlen_zero(ovsw)) {
1713  overrideswitch_config = ast_strdup(ovsw);
1714  } else {
1715  overrideswitch_config = NULL;
1716  }
1717  }
1718 
1719  ast_copy_string(userscontext, ast_variable_retrieve(cfg, "general", "userscontext") ?: "default", sizeof(userscontext));
1720 
1721  /* ast_variable_browse does not merge multiple [globals] sections */
1722  for (cxt = ast_category_browse(cfg, NULL);
1723  cxt;
1724  cxt = ast_category_browse(cfg, cxt)) {
1725  if (strcasecmp(cxt, "globals")) {
1726  continue;
1727  }
1728 
1729  for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1730  pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1731  pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1732  }
1733  }
1734 
1735  for (cxt = ast_category_browse(cfg, NULL);
1736  cxt;
1737  cxt = ast_category_browse(cfg, cxt)) {
1738  /* All categories but "general" or "globals" are considered contexts */
1739  if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) {
1740  continue;
1741  }
1742  if (!(con = ast_context_find_or_create(&local_contexts, local_table, cxt, registrar))) {
1743  continue;
1744  }
1745 
1746  /* Reset continuation items at the beginning of each context */
1747  lastextension[0] = '\0';
1748  lastpri = -2;
1749 
1750  for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1751  char *tc = NULL;
1752  char realext[256] = "";
1753  char *stringp, *ext;
1754  const char *vfile;
1755 
1756  /* get filename for error reporting from top level or an #include */
1757  vfile = !*v->file ? config_file : v->file;
1758 
1759  if (!strncasecmp(v->name, "same", 4)) {
1760  if (ast_strlen_zero(lastextension)) {
1761  ast_log(LOG_ERROR,
1762  "No previous pattern in the first entry of context '%s' to match '%s' at line %d of %s!\n",
1763  cxt, v->name, v->lineno, vfile);
1764  continue;
1765  }
1766  if ((stringp = tc = ast_strdup(v->value))) {
1767  ast_copy_string(realext, lastextension, sizeof(realext));
1768  goto process_extension;
1769  }
1770  } else if (!strcasecmp(v->name, "exten")) {
1771  int ipri;
1772  char *plus;
1773  char *pri, *appl, *data, *cidmatch;
1774 
1775  if (!(stringp = tc = ast_strdup(v->value))) {
1776  continue;
1777  }
1778 
1779  ext = S_OR(pbx_strsep(&stringp, ","), "");
1780  pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1781  ast_copy_string(lastextension, realext, sizeof(lastextension));
1782 process_extension:
1783  ipri = -2;
1784  if ((cidmatch = strchr(realext, '/'))) {
1785  *cidmatch++ = '\0';
1786  ast_shrink_phone_number(cidmatch);
1787  }
1788  pri = ast_strip(S_OR(strsep(&stringp, ","), ""));
1789  if ((label = strchr(pri, '('))) {
1790  *label++ = '\0';
1791  if ((end = strchr(label, ')'))) {
1792  *end = '\0';
1793  } else {
1794  ast_log(LOG_WARNING,
1795  "Label missing trailing ')' at line %d of %s\n",
1796  v->lineno, vfile);
1797  ast_free(tc);
1798  continue;
1799  }
1800  }
1801  if ((plus = strchr(pri, '+'))) {
1802  *plus++ = '\0';
1803  }
1804  if (!strcmp(pri,"hint")) {
1805  ipri = PRIORITY_HINT;
1806  } else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1807  if (lastpri > -2) {
1808  ipri = lastpri + 1;
1809  } else {
1810  ast_log(LOG_WARNING,
1811  "Can't use 'next' priority on the first entry at line %d of %s!\n",
1812  v->lineno, vfile);
1813  ast_free(tc);
1814  continue;
1815  }
1816  } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1817  if (lastpri > -2) {
1818  ipri = lastpri;
1819  } else {
1820  ast_log(LOG_WARNING,
1821  "Can't use 'same' priority on the first entry at line %d of %s!\n",
1822  v->lineno, vfile);
1823  ast_free(tc);
1824  continue;
1825  }
1826  } else if (sscanf(pri, "%30d", &ipri) != 1 &&
1827  (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1828  ast_log(LOG_WARNING,
1829  "Invalid priority/label '%s' at line %d of %s\n",
1830  pri, v->lineno, vfile);
1831  ipri = 0;
1832  ast_free(tc);
1833  continue;
1834  } else if (ipri < 1) {
1835  ast_log(LOG_WARNING, "Invalid priority '%s' at line %d of %s\n",
1836  pri, v->lineno, vfile);
1837  ast_free(tc);
1838  continue;
1839  }
1840  appl = S_OR(stringp, "");
1841  /* Find the first occurrence of '(' */
1842  if (!strchr(appl, '(')) {
1843  /* No arguments */
1844  data = "";
1845  } else {
1846  char *orig_appl = ast_strdup(appl);
1847 
1848  if (!orig_appl) {
1849  ast_free(tc);
1850  continue;
1851  }
1852 
1853  appl = strsep(&stringp, "(");
1854 
1855  /* check if there are variables or expressions without an application, like: exten => 100,hint,DAHDI/g0/${GLOBAL(var)} */
1856  if (strstr(appl, "${") || strstr(appl, "$[")){
1857  /* set appl to original one */
1858  strcpy(appl, orig_appl);
1859  /* set no data */
1860  data = "";
1861  /* no variable before application found -> go ahead */
1862  } else {
1863  data = S_OR(stringp, "");
1864  if ((end = strrchr(data, ')'))) {
1865  *end = '\0';
1866  } else {
1867  ast_log(LOG_WARNING,
1868  "No closing parenthesis found? '%s(%s' at line %d of %s\n",
1869  appl, data, v->lineno, vfile);
1870  }
1871  }
1872  ast_free(orig_appl);
1873  }
1874 
1875  appl = ast_skip_blanks(appl);
1876  if (ipri) {
1877  const char *registrar_file;
1878  if (plus) {
1879  ipri += atoi(plus);
1880  }
1881  lastpri = ipri;
1882  if (!ast_opt_dont_warn && (!strcmp(realext, "_.") || !strcmp(realext, "_!"))) {
1883  ast_log(LOG_WARNING,
1884  "The use of '%s' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X%c' instead at line %d of %s\n",
1885  realext, realext[1], v->lineno, vfile);
1886  }
1887  /* Don't include full path if the configuration file includes slashes */
1888  registrar_file = strrchr(vfile, '/');
1889  if (!registrar_file) {
1890  registrar_file = vfile;
1891  } else {
1892  registrar_file++; /* Skip past the end slash */
1893  }
1894  if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, ast_strdup(data), ast_free_ptr, registrar, registrar_file, v->lineno)) {
1895  ast_log(LOG_WARNING,
1896  "Unable to register extension at line %d of %s\n",
1897  v->lineno, vfile);
1898  }
1899  }
1900  ast_free(tc);
1901  } else if (!strcasecmp(v->name, "include")) {
1902  pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1903  if (ast_context_add_include2(con, realvalue, registrar)) {
1904  switch (errno) {
1905  case ENOMEM:
1906  ast_log(LOG_WARNING, "Out of memory for context addition\n");
1907  break;
1908 
1909  case EBUSY:
1910  ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
1911  break;
1912 
1913  case EEXIST:
1914  ast_log(LOG_WARNING,
1915  "Context '%s' already included in '%s' context on include at line %d of %s\n",
1916  v->value, cxt, v->lineno, vfile);
1917  break;
1918 
1919  case ENOENT:
1920  case EINVAL:
1921  ast_log(LOG_WARNING,
1922  "There is no existence of context '%s' included at line %d of %s\n",
1923  errno == ENOENT ? v->value : cxt, v->lineno, vfile);
1924  break;
1925 
1926  default:
1927  ast_log(LOG_WARNING,
1928  "Failed to include '%s' in '%s' context at line %d of %s\n",
1929  v->value, cxt, v->lineno, vfile);
1930  break;
1931  }
1932  }
1933  } else if (!strcasecmp(v->name, "ignorepat")) {
1934  pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1935  if (ast_context_add_ignorepat2(con, realvalue, registrar)) {
1936  ast_log(LOG_WARNING,
1937  "Unable to include ignorepat '%s' in context '%s' at line %d of %s\n",
1938  v->value, cxt, v->lineno, vfile);
1939  }
1940  } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1941  char *appl, *data;
1942  stringp = realvalue;
1943 
1944  if (!strcasecmp(v->name, "switch")) {
1945  pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1946  } else {
1947  ast_copy_string(realvalue, v->value, sizeof(realvalue));
1948  }
1949  appl = strsep(&stringp, "/");
1950  data = S_OR(stringp, "");
1951  if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) {
1952  ast_log(LOG_WARNING,
1953  "Unable to include switch '%s' in context '%s' at line %d of %s\n",
1954  v->value, cxt, v->lineno, vfile);
1955  }
1956  } else if (!strcasecmp(v->name, "autohints")) {
1958  } else {
1959  ast_log(LOG_WARNING,
1960  "==!!== Unknown directive: %s at line %d of %s -- IGNORING!!!\n",
1961  v->name, v->lineno, vfile);
1962  }
1963  }
1964  }
1965  ast_config_destroy(cfg);
1966  return 1;
1967 }
1968 
1969 static void append_interface(char *iface, int maxlen, char *add)
1970 {
1971  int len = strlen(iface);
1972  if (strlen(add) + len < maxlen - 2) {
1973  if (strlen(iface)) {
1974  iface[len] = '&';
1975  strcpy(iface + len + 1, add);
1976  } else
1977  strcpy(iface, add);
1978  }
1979 }
1980 
1981 static void startup_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
1982 {
1983  struct ast_json_payload *payload;
1984  const char *type;
1985 
1987  return;
1988  }
1989 
1990  payload = stasis_message_data(message);
1991  type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1992 
1993  if (strcmp(type, "FullyBooted")) {
1994  return;
1995  }
1996 
1997  ast_log(LOG_WARNING, "users.conf is deprecated and will be removed in a future version of Asterisk\n");
1998 
1999  fully_booted_subscription = stasis_unsubscribe(fully_booted_subscription);
2000 }
2001 
2002 static void pbx_load_users(void)
2003 {
2004  struct ast_config *cfg;
2005  char *cat, *chan;
2006  const char *dahdichan;
2007  const char *hasexten, *altexts;
2008  char tmp[256];
2009  char iface[256];
2010  char dahdicopy[256];
2011  char *ext, altcopy[256];
2012  char *c;
2013  int hasvoicemail;
2014  int start, finish, x;
2015  struct ast_context *con = NULL;
2016  struct ast_flags config_flags = { 0 };
2017 
2018  cfg = ast_config_load("users.conf", config_flags);
2019  if (!cfg)
2020  return;
2021 
2022  /*! \todo Remove users.conf support in Asterisk 23 */
2023  fully_booted_subscription =
2024  stasis_subscribe_pool(ast_manager_get_topic(), startup_event_cb, NULL);
2025 
2026  for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
2027  if (!strcasecmp(cat, "general"))
2028  continue;
2029  iface[0] = '\0';
2030  if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
2031  snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
2032  append_interface(iface, sizeof(iface), tmp);
2033  }
2034  if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
2035  snprintf(tmp, sizeof(tmp), "H323/%s", cat);
2036  append_interface(iface, sizeof(iface), tmp);
2037  }
2038  hasexten = ast_config_option(cfg, cat, "hasexten");
2039  if (hasexten && !ast_true(hasexten))
2040  continue;
2041  hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
2042  dahdichan = ast_variable_retrieve(cfg, cat, "dahdichan");
2043  if (!dahdichan)
2044  dahdichan = ast_variable_retrieve(cfg, "general", "dahdichan");
2045  if (!ast_strlen_zero(dahdichan)) {
2046  ast_copy_string(dahdicopy, dahdichan, sizeof(dahdicopy));
2047  c = dahdicopy;
2048  chan = strsep(&c, ",");
2049  while (chan) {
2050  if (sscanf(chan, "%30d-%30d", &start, &finish) == 2) {
2051  /* Range */
2052  } else if (sscanf(chan, "%30d", &start)) {
2053  /* Just one */
2054  finish = start;
2055  } else {
2056  start = 0; finish = 0;
2057  }
2058  if (finish < start) {
2059  x = finish;
2060  finish = start;
2061  start = x;
2062  }
2063  for (x = start; x <= finish; x++) {
2064  snprintf(tmp, sizeof(tmp), "DAHDI/%d", x);
2065  append_interface(iface, sizeof(iface), tmp);
2066  }
2067  chan = strsep(&c, ",");
2068  }
2069  }
2070  if (!ast_strlen_zero(iface)) {
2071  /* Only create a context here when it is really needed. Otherwise default empty context
2072  created by pbx_config may conflict with the one explicitly created by pbx_ael */
2073  if (!con)
2074  con = ast_context_find_or_create(&local_contexts, local_table, userscontext, registrar);
2075 
2076  if (!con) {
2077  ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext);
2078  return;
2079  }
2080 
2081  /* Add hint */
2082  ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar, NULL, 0);
2083  /* If voicemail, use "stdexten" else use plain old dial */
2084  if (hasvoicemail) {
2085  snprintf(tmp, sizeof(tmp), "%s,stdexten(${HINT})", cat);
2086  ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Gosub", ast_strdup(tmp), ast_free_ptr, registrar, NULL, 0);
2087  } else {
2088  ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", ast_strdup("${HINT}"), ast_free_ptr, registrar, NULL, 0);
2089  }
2090  altexts = ast_variable_retrieve(cfg, cat, "alternateexts");
2091  if (!ast_strlen_zero(altexts)) {
2092  snprintf(tmp, sizeof(tmp), "%s,1", cat);
2093  ast_copy_string(altcopy, altexts, sizeof(altcopy));
2094  c = altcopy;
2095  ext = strsep(&c, ",");
2096  while (ext) {
2097  ast_add_extension2(con, 0, ext, 1, NULL, NULL, "Goto", ast_strdup(tmp), ast_free_ptr, registrar, NULL, 0);
2098  ext = strsep(&c, ",");
2099  }
2100  }
2101  }
2102  }
2103  ast_config_destroy(cfg);
2104 }
2105 
2106 static int pbx_load_module(void)
2107 {
2108  struct ast_context *con;
2109 
2110  ast_mutex_lock(&reload_lock);
2111 
2112  if (!local_table) {
2113  local_table = ast_hashtab_create(17, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
2114  if (!local_table) {
2115  ast_mutex_unlock(&reload_lock);
2116  return AST_MODULE_LOAD_DECLINE;
2117  }
2118  }
2119 
2120  if (!pbx_load_config(config)) {
2121  ast_hashtab_destroy(local_table, NULL);
2122  local_table = NULL;
2123  ast_mutex_unlock(&reload_lock);
2124  return AST_MODULE_LOAD_DECLINE;
2125  }
2126 
2127  pbx_load_users();
2128 
2129  ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
2130  local_table = NULL; /* the local table has been moved into the global one. */
2131  local_contexts = NULL;
2132 
2133  ast_mutex_unlock(&reload_lock);
2134 
2135  for (con = NULL; (con = ast_walk_contexts(con));)
2137 
2138  pbx_set_overrideswitch(overrideswitch_config);
2139  pbx_set_autofallthrough(autofallthrough_config);
2140  pbx_set_extenpatternmatchnew(extenpatternmatchnew_config);
2141 
2142  return AST_MODULE_LOAD_SUCCESS;
2143 }
2144 
2145 static int load_module(void)
2146 {
2147  int res;
2148 
2149  if (pbx_load_module()) {
2150  unload_module();
2151  return AST_MODULE_LOAD_DECLINE;
2152  }
2153 
2154  if (static_config && !write_protect_config)
2155  ast_cli_register(&cli_dialplan_save);
2156  ast_cli_register_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
2157 
2158  res = ast_manager_register_xml(AMI_EXTENSION_ADD,
2159  EVENT_FLAG_SYSTEM, manager_dialplan_extension_add);
2160  res |= ast_manager_register_xml(AMI_EXTENSION_REMOVE,
2161  EVENT_FLAG_SYSTEM, manager_dialplan_extension_remove);
2162 
2163  if (res) {
2164  unload_module();
2165  return AST_MODULE_LOAD_DECLINE;
2166  }
2167 
2168  return AST_MODULE_LOAD_SUCCESS;
2169 }
2170 
2171 static int reload(void)
2172 {
2173  if (clearglobalvars_config)
2174  pbx_builtin_clear_globals();
2175  return pbx_load_module();
2176 }
2177 
2178 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
2179  .support_level = AST_MODULE_SUPPORT_CORE,
2180  .load = load_module,
2181  .unload = unload_module,
2182  .reload = reload,
2183 );
const char * label
Definition: pbx.c:244
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
struct ast_variable * next
int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b)
hashtable functions for contexts
Definition: ael_main.c:589
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
static char * handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_config.c:162
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 ast_hashtab_newsize_java(struct ast_hashtab *tab)
Create a prime number roughly 2x the current table size.
Definition: main/hashtab.c:127
int ast_context_remove_include(const char *context, const char *include, const char *registrar)
Remove a context include.
Definition: pbx.c:4836
int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
Remove an ignorepat.
Definition: pbx.c:6795
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
Find the priority of an extension that has the specified label.
Definition: extconf.c:4975
int ast_cli_unregister(struct ast_cli_entry *e)
Unregisters a command or an array of commands.
Definition: main/cli.c:2432
descriptor for a cli entry.
Definition: cli.h:171
Structure for variables, used for configurations and for channel variables.
int ast_context_add_switch2(struct ast_context *con, const char *sw, const char *data, int eval, const char *registrar)
Adds a switch (first param is a ast_context)
Definition: ael_main.c:370
static char * handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_config.c:134
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
static char * complete_dialplan_add_extension(struct ast_cli_args *)
Definition: pbx_config.c:1308
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8468
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
int ast_wrlock_contexts(void)
Write locks the context list.
Definition: pbx.c:8463
char * ast_escape_semicolons(const char *string, char *outbuf, int buflen)
Escape semicolons found in a string.
Definition: utils.c:811
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
static int partial_match(const char *s, const char *word, int len)
match the first 'len' chars of word. len==0 always succeeds
Definition: pbx_config.c:253
void ast_free_ptr(void *ptr)
free() wrapper
Definition: main/astmm.c:1739
const char * registrar
Definition: pbx.c:253
int ast_context_add_include(const char *context, const char *include, const char *registrar)
Add a context include.
Definition: pbx.c:6664
int ast_hashtab_resize_java(struct ast_hashtab *tab)
Determines if a table resize should occur using the Java algorithm (if the table load factor is 75% o...
Definition: main/hashtab.c:84
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
int ast_context_destroy_by_name(const char *context, const char *registrar)
Destroy a context by name.
Definition: pbx.c:8205
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
#define ast_cli_register(e)
Registers a command or an array of commands.
Definition: cli.h:256
const char * registrar
Definition: pbx_sw.c:40
static char * handle_cli_dialplan_add_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
ADD EXTENSION command stuff.
Definition: pbx_config.c:1065
static int lookup_ci(struct ast_context *c, const char *name)
return true if 'name' is included by context c
Definition: pbx_config.c:190
Configuration File Parser.
static const char config_file[]
static void pbx_load_users(void)
Definition: pbx_config.c:2002
int priority
Definition: pbx.c:243
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
struct ast_context * ast_context_find(const char *name)
Find a context.
Definition: extconf.c:4172
#define ast_config_load(filename, flags)
Load a config file.
ast_sw: Switch statement in extensions.conf
Definition: pbx_sw.c:37
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
int ast_context_add_ignorepat(const char *context, const char *ignorepat, const char *registrar)
Add an ignorepat.
Definition: pbx.c:6835
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static char * handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
'save dialplan' CLI command implementation functions ...
Definition: pbx_config.c:811
const char * registrar
Definition: pbx.c:286
#define AST_MAX_EXTENSION
Definition: channel.h:134
int pbx_set_extenpatternmatchnew(int newval)
Definition: pbx.c:4777
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
structure to hold extensions
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1785
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
int pbx_set_autofallthrough(int newval)
Definition: pbx.c:4770
#define PRIORITY_HINT
Definition: pbx.h:54
Core PBX routines and definitions.
static char * handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_config.c:417
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
static char * handle_cli_dialplan_add_ignorepat(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_config.c:1340
static int lookup_c_ip(struct ast_context *c, const char *name)
return true if 'name' is in the ignorepats for context c
Definition: pbx_config.c:214
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
void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *registrar)
Merge the temporary contexts into a global contexts list and delete from the global list the ones tha...
Definition: pbx.c:6426
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
ast_ignorepat: Ignore patterns in dial plan
Definition: pbx_ignorepat.c:37
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8473
static int split_ec(const char *src, char **ext, char **const ctx, char **const cid)
split extension@context in two parts, return -1 on error. The return string is malloc'ed and pointed ...
Definition: pbx_config.c:261
static int unload_module(void)
Definition: pbx_config.c:1627
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
static char * pbx_strsep(char **destructible, const char *delim)
Definition: pbx_config.c:1648
int ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar)
Add a context include.
Definition: ael_main.c:359
char * command
Definition: cli.h:186
Support for logging to various files, console and syslog Configuration in file logger.conf.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1134
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_cli_entry cli_pbx_config[]
Definition: pbx_config.c:1607
Structure used to handle boolean flags.
Definition: utils.h:199
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1880
const char * usage
Definition: cli.h:177
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_add_extension2(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)
Add an extension to an extension context, this time with an ast_context *.
Definition: pbx.c:7257
int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar)
Add and extension to an extension context.
Definition: pbx.c:6928
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
void pbx_set_overrideswitch(const char *newval)
Definition: pbx.c:4784
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:773
void ast_context_destroy(struct ast_context *con, const char *registrar)
Destroy a context (matches the specified context or ANY context if NULL)
Definition: pbx.c:8221
static char * handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_config.c:676
#define ast_hashtab_create(initial_buckets, compare, resize, newsize, hash, do_locking)
Create the hashtable list.
Definition: hashtab.h:254
int ast_context_verify_includes(struct ast_context *con)
Verifies includes in an ast_contect structure.
Definition: pbx.c:8732
static const char * skip_words(const char *p, int n)
moves to the n-th word in the string, or empty string if none
Definition: pbx_config.c:238
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_context_set_autohints(struct ast_context *con, int enabled)
Enable or disable autohints support on a context.
Definition: pbx.c:6230
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
void ast_shrink_phone_number(char *n)
Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s...
Definition: callerid.c:1101
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
ast_context: An extension context
Definition: pbx.c:284
const char * registrar
Definition: pbx_include.c:42
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8486
void ast_hashtab_destroy(struct ast_hashtab *tab, void(*objdestroyfunc)(void *obj))
This func will free the hash table and all its memory.
Definition: main/hashtab.c:363