Merged revisions 140563 via svnmerge from
[asterisk-bristuff.git] / main / pbx.c
blob76d6ddb5f72ff9359edd5e946687894b45ae811d
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2008, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
19 /*! \file
21 * \brief Core PBX routines.
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include "asterisk/_private.h"
31 #include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
32 #include <ctype.h>
33 #include <time.h>
34 #include <sys/time.h>
35 #if defined(HAVE_SYSINFO)
36 #include <sys/sysinfo.h>
37 #endif
38 #if defined(SOLARIS)
39 #include <sys/loadavg.h>
40 #endif
42 #include "asterisk/lock.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/file.h"
47 #include "asterisk/callerid.h"
48 #include "asterisk/cdr.h"
49 #include "asterisk/config.h"
50 #include "asterisk/term.h"
51 #include "asterisk/time.h"
52 #include "asterisk/manager.h"
53 #include "asterisk/ast_expr.h"
54 #include "asterisk/linkedlists.h"
55 #define SAY_STUBS /* generate declarations and stubs for say methods */
56 #include "asterisk/say.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/causes.h"
59 #include "asterisk/musiconhold.h"
60 #include "asterisk/app.h"
61 #include "asterisk/devicestate.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/event.h"
64 #include "asterisk/hashtab.h"
65 #include "asterisk/module.h"
66 #include "asterisk/indications.h"
67 #include "asterisk/taskprocessor.h"
69 /*!
70 * \note I M P O R T A N T :
72 * The speed of extension handling will likely be among the most important
73 * aspects of this PBX. The switching scheme as it exists right now isn't
74 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
75 * of priorities, but a constant search time here would be great ;-)
77 * A new algorithm to do searching based on a 'compiled' pattern tree is introduced
78 * here, and shows a fairly flat (constant) search time, even for over
79 * 10000 patterns.
81 * Also, using a hash table for context/priority name lookup can help prevent
82 * the find_extension routines from absorbing exponential cpu cycles as the number
83 * of contexts/priorities grow. I've previously tested find_extension with red-black trees,
84 * which have O(log2(n)) speed. Right now, I'm using hash tables, which do
85 * searches (ideally) in O(1) time. While these techniques do not yield much
86 * speed in small dialplans, they are worth the trouble in large dialplans.
90 #ifdef LOW_MEMORY
91 #define EXT_DATA_SIZE 256
92 #else
93 #define EXT_DATA_SIZE 8192
94 #endif
96 #define SWITCH_DATA_LENGTH 256
98 #define VAR_BUF_SIZE 4096
100 #define VAR_NORMAL 1
101 #define VAR_SOFTTRAN 2
102 #define VAR_HARDTRAN 3
104 #define BACKGROUND_SKIP (1 << 0)
105 #define BACKGROUND_NOANSWER (1 << 1)
106 #define BACKGROUND_MATCHEXTEN (1 << 2)
107 #define BACKGROUND_PLAYBACK (1 << 3)
109 AST_APP_OPTIONS(background_opts, {
110 AST_APP_OPTION('s', BACKGROUND_SKIP),
111 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
112 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
113 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
116 #define WAITEXTEN_MOH (1 << 0)
117 #define WAITEXTEN_DIALTONE (1 << 1)
119 AST_APP_OPTIONS(waitexten_opts, {
120 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
121 AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
124 struct ast_context;
125 struct ast_app;
127 static struct ast_taskprocessor *device_state_tps;
129 AST_THREADSTORAGE(switch_data);
132 \brief ast_exten: An extension
133 The dialplan is saved as a linked list with each context
134 having it's own linked list of extensions - one item per
135 priority.
137 struct ast_exten {
138 char *exten; /*!< Extension name */
139 int matchcid; /*!< Match caller id ? */
140 const char *cidmatch; /*!< Caller id to match for this extension */
141 int priority; /*!< Priority */
142 const char *label; /*!< Label */
143 struct ast_context *parent; /*!< The context this extension belongs to */
144 const char *app; /*!< Application to execute */
145 struct ast_app *cached_app; /*!< Cached location of application */
146 void *data; /*!< Data to use (arguments) */
147 void (*datad)(void *); /*!< Data destructor */
148 struct ast_exten *peer; /*!< Next higher priority with our extension */
149 struct ast_hashtab *peer_table; /*!< Priorities list in hashtab form -- only on the head of the peer list */
150 struct ast_hashtab *peer_label_table; /*!< labeled priorities in the peers -- only on the head of the peer list */
151 const char *registrar; /*!< Registrar */
152 struct ast_exten *next; /*!< Extension with a greater ID */
153 char stuff[0];
156 /*! \brief ast_include: include= support in extensions.conf */
157 struct ast_include {
158 const char *name;
159 const char *rname; /*!< Context to include */
160 const char *registrar; /*!< Registrar */
161 int hastime; /*!< If time construct exists */
162 struct ast_timing timing; /*!< time construct */
163 struct ast_include *next; /*!< Link them together */
164 char stuff[0];
167 /*! \brief ast_sw: Switch statement in extensions.conf */
168 struct ast_sw {
169 char *name;
170 const char *registrar; /*!< Registrar */
171 char *data; /*!< Data load */
172 int eval;
173 AST_LIST_ENTRY(ast_sw) list;
174 char stuff[0];
177 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
178 struct ast_ignorepat {
179 const char *registrar;
180 struct ast_ignorepat *next;
181 const char pattern[0];
184 /*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
185 struct match_char
187 int is_pattern; /* the pattern started with '_' */
188 int deleted; /* if this is set, then... don't return it */
189 char *x; /* the pattern itself-- matches a single char */
190 int specificity; /* simply the strlen of x, or 10 for X, 9 for Z, and 8 for N; and '.' and '!' will add 11 ? */
191 struct match_char *alt_char;
192 struct match_char *next_char;
193 struct ast_exten *exten; /* attached to last char of a pattern for exten */
196 struct scoreboard /* make sure all fields are 0 before calling new_find_extension */
198 int total_specificity;
199 int total_length;
200 char last_char; /* set to ! or . if they are the end of the pattern */
201 int canmatch; /* if the string to match was just too short */
202 struct match_char *node;
203 struct ast_exten *canmatch_exten;
204 struct ast_exten *exten;
207 /*! \brief ast_context: An extension context */
208 struct ast_context {
209 ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
210 struct ast_exten *root; /*!< The root of the list of extensions */
211 struct ast_hashtab *root_table; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */
212 struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */
213 struct ast_context *next; /*!< Link them together */
214 struct ast_include *includes; /*!< Include other contexts */
215 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
216 char *registrar; /*!< Registrar -- make sure you malloc this, as the registrar may have to survive module unloads */
217 int refcount; /*!< each module that would have created this context should inc/dec this as appropriate */
218 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
219 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
220 char name[0]; /*!< Name of the context */
224 /*! \brief ast_app: A registered application */
225 struct ast_app {
226 int (*execute)(struct ast_channel *chan, void *data);
227 const char *synopsis; /*!< Synopsis text for 'show applications' */
228 const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
229 AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
230 struct ast_module *module; /*!< Module this app belongs to */
231 char name[0]; /*!< Name of the application */
234 /*! \brief ast_state_cb: An extension state notify register item */
235 struct ast_state_cb {
236 int id;
237 void *data;
238 ast_state_cb_type callback;
239 AST_LIST_ENTRY(ast_state_cb) entry;
242 /*! \brief Structure for dial plan hints
244 \note Hints are pointers from an extension in the dialplan to one or
245 more devices (tech/name)
246 - See \ref AstExtState
248 struct ast_hint {
249 struct ast_exten *exten; /*!< Extension */
250 int laststate; /*!< Last known state */
251 AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks; /*!< Callback list for this extension */
252 AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
255 static const struct cfextension_states {
256 int extension_state;
257 const char * const text;
258 } extension_states[] = {
259 { AST_EXTENSION_NOT_INUSE, "Idle" },
260 { AST_EXTENSION_INUSE, "InUse" },
261 { AST_EXTENSION_BUSY, "Busy" },
262 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
263 { AST_EXTENSION_RINGING, "Ringing" },
264 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
265 { AST_EXTENSION_ONHOLD, "Hold" },
266 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
269 struct statechange {
270 AST_LIST_ENTRY(statechange) entry;
271 char dev[0];
274 struct pbx_exception {
275 AST_DECLARE_STRING_FIELDS(
276 AST_STRING_FIELD(context); /*!< Context associated with this exception */
277 AST_STRING_FIELD(exten); /*!< Exten associated with this exception */
278 AST_STRING_FIELD(reason); /*!< The exception reason */
281 int priority; /*!< Priority associated with this exception */
284 static int pbx_builtin_answer(struct ast_channel *, void *);
285 static int pbx_builtin_goto(struct ast_channel *, void *);
286 static int pbx_builtin_hangup(struct ast_channel *, void *);
287 static int pbx_builtin_background(struct ast_channel *, void *);
288 static int pbx_builtin_wait(struct ast_channel *, void *);
289 static int pbx_builtin_waitexten(struct ast_channel *, void *);
290 static int pbx_builtin_incomplete(struct ast_channel *, void *);
291 static int pbx_builtin_keepalive(struct ast_channel *, void *);
292 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
293 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
294 static int pbx_builtin_ringing(struct ast_channel *, void *);
295 static int pbx_builtin_proceeding(struct ast_channel *, void *);
296 static int pbx_builtin_progress(struct ast_channel *, void *);
297 static int pbx_builtin_congestion(struct ast_channel *, void *);
298 static int pbx_builtin_busy(struct ast_channel *, void *);
299 static int pbx_builtin_noop(struct ast_channel *, void *);
300 static int pbx_builtin_gotoif(struct ast_channel *, void *);
301 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
302 static int pbx_builtin_execiftime(struct ast_channel *, void *);
303 static int pbx_builtin_saynumber(struct ast_channel *, void *);
304 static int pbx_builtin_saydigits(struct ast_channel *, void *);
305 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
306 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
307 static int matchcid(const char *cidpattern, const char *callerid);
308 int pbx_builtin_setvar(struct ast_channel *, void *);
309 void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
310 int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
311 static int pbx_builtin_importvar(struct ast_channel *, void *);
312 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
313 static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid, const char *label, enum ext_match_t action);
314 static struct match_char *already_in_tree(struct match_char *current, char *pat);
315 static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly);
316 static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity, struct match_char **parent);
317 static void create_match_char_tree(struct ast_context *con);
318 static struct ast_exten *get_canmatch_exten(struct match_char *node);
319 static void destroy_pattern_tree(struct match_char *pattern_tree);
320 int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b);
321 static int hashtab_compare_extens(const void *ha_a, const void *ah_b);
322 static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b);
323 static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b);
324 unsigned int ast_hashtab_hash_contexts(const void *obj);
325 static unsigned int hashtab_hash_extens(const void *obj);
326 static unsigned int hashtab_hash_priority(const void *obj);
327 static unsigned int hashtab_hash_labels(const void *obj);
328 static void __ast_internal_context_destroy( struct ast_context *con);
330 /* a func for qsort to use to sort a char array */
331 static int compare_char(const void *a, const void *b)
333 const char *ac = a;
334 const char *bc = b;
335 if ((*ac) < (*bc))
336 return -1;
337 else if ((*ac) == (*bc))
338 return 0;
339 else
340 return 1;
343 /* labels, contexts are case sensitive priority numbers are ints */
344 int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b)
346 const struct ast_context *ac = ah_a;
347 const struct ast_context *bc = ah_b;
348 if (!ac || !bc) /* safety valve, but it might prevent a crash you'd rather have happen */
349 return 1;
350 /* assume context names are registered in a string table! */
351 return strcmp(ac->name, bc->name);
354 static int hashtab_compare_extens(const void *ah_a, const void *ah_b)
356 const struct ast_exten *ac = ah_a;
357 const struct ast_exten *bc = ah_b;
358 int x = strcmp(ac->exten, bc->exten);
359 if (x) { /* if exten names are diff, then return */
360 return x;
363 /* but if they are the same, do the cidmatch values match? */
364 if (ac->matchcid && bc->matchcid) {
365 return strcmp(ac->cidmatch,bc->cidmatch);
366 } else if (!ac->matchcid && !bc->matchcid) {
367 return 0; /* if there's no matchcid on either side, then this is a match */
368 } else {
369 return 1; /* if there's matchcid on one but not the other, they are different */
373 static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b)
375 const struct ast_exten *ac = ah_a;
376 const struct ast_exten *bc = ah_b;
377 return ac->priority != bc->priority;
380 static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b)
382 const struct ast_exten *ac = ah_a;
383 const struct ast_exten *bc = ah_b;
384 return strcmp(ac->label, bc->label);
387 unsigned int ast_hashtab_hash_contexts(const void *obj)
389 const struct ast_context *ac = obj;
390 return ast_hashtab_hash_string(ac->name);
393 static unsigned int hashtab_hash_extens(const void *obj)
395 const struct ast_exten *ac = obj;
396 unsigned int x = ast_hashtab_hash_string(ac->exten);
397 unsigned int y = 0;
398 if (ac->matchcid)
399 y = ast_hashtab_hash_string(ac->cidmatch);
400 return x+y;
403 static unsigned int hashtab_hash_priority(const void *obj)
405 const struct ast_exten *ac = obj;
406 return ast_hashtab_hash_int(ac->priority);
409 static unsigned int hashtab_hash_labels(const void *obj)
411 const struct ast_exten *ac = obj;
412 return ast_hashtab_hash_string(ac->label);
416 AST_RWLOCK_DEFINE_STATIC(globalslock);
417 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
419 static int autofallthrough = 1;
420 static int extenpatternmatchnew = 0;
421 static char *overrideswitch = NULL;
423 /*! \brief Subscription for device state change events */
424 static struct ast_event_sub *device_state_sub;
426 AST_MUTEX_DEFINE_STATIC(maxcalllock);
427 static int countcalls;
428 static int totalcalls;
430 static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
432 /*! \brief Declaration of builtin applications */
433 static struct pbx_builtin {
434 char name[AST_MAX_APP];
435 int (*execute)(struct ast_channel *chan, void *data);
436 char *synopsis;
437 char *description;
438 } builtins[] =
440 /* These applications are built into the PBX core and do not
441 need separate modules */
443 { "Answer", pbx_builtin_answer,
444 "Answer a channel if ringing",
445 " Answer([delay]): If the call has not been answered, this application will\n"
446 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
447 "Asterisk will wait this number of milliseconds before returning to\n"
448 "the dialplan after answering the call.\n"
451 { "BackGround", pbx_builtin_background,
452 "Play an audio file while waiting for digits of an extension to go to.",
453 " Background(filename1[&filename2...][,options[,langoverride][,context]]):\n"
454 "This application will play the given list of files (do not put extension)\n"
455 "while waiting for an extension to be dialed by the calling channel. To\n"
456 "continue waiting for digits after this application has finished playing\n"
457 "files, the WaitExten application should be used. The 'langoverride' option\n"
458 "explicitly specifies which language to attempt to use for the requested sound\n"
459 "files. If a 'context' is specified, this is the dialplan context that this\n"
460 "application will use when exiting to a dialed extension."
461 " If one of the requested sound files does not exist, call processing will be\n"
462 "terminated.\n"
463 " Options:\n"
464 " s - Causes the playback of the message to be skipped\n"
465 " if the channel is not in the 'up' state (i.e. it\n"
466 " hasn't been answered yet). If this happens, the\n"
467 " application will return immediately.\n"
468 " n - Don't answer the channel before playing the files.\n"
469 " m - Only break if a digit hit matches a one digit\n"
470 " extension in the destination context.\n"
471 "This application sets the following channel variable upon completion:\n"
472 " BACKGROUNDSTATUS The status of the background attempt as a text string, one of\n"
473 " SUCCESS | FAILED\n"
474 "See Also: Playback (application) -- Play sound file(s) to the channel,\n"
475 " that cannot be interrupted\n"
478 { "Busy", pbx_builtin_busy,
479 "Indicate the Busy condition",
480 " Busy([timeout]): This application will indicate the busy condition to\n"
481 "the calling channel. If the optional timeout is specified, the calling channel\n"
482 "will be hung up after the specified number of seconds. Otherwise, this\n"
483 "application will wait until the calling channel hangs up.\n"
486 { "Congestion", pbx_builtin_congestion,
487 "Indicate the Congestion condition",
488 " Congestion([timeout]): This application will indicate the congestion\n"
489 "condition to the calling channel. If the optional timeout is specified, the\n"
490 "calling channel will be hung up after the specified number of seconds.\n"
491 "Otherwise, this application will wait until the calling channel hangs up.\n"
494 { "ExecIfTime", pbx_builtin_execiftime,
495 "Conditional application execution based on the current time",
496 " ExecIfTime(<times>,<weekdays>,<mdays>,<months>?appname[(appargs)]):\n"
497 "This application will execute the specified dialplan application, with optional\n"
498 "arguments, if the current time matches the given time specification.\n"
501 { "Goto", pbx_builtin_goto,
502 "Jump to a particular priority, extension, or context",
503 " Goto([[context,]extension,]priority): This application will set the current\n"
504 "context, extension, and priority in the channel structure. After it completes, the\n"
505 "pbx engine will continue dialplan execution at the specified location.\n"
506 "If no specific extension, or extension and context, are specified, then this\n"
507 "application will just set the specified priority of the current extension.\n"
508 " At least a priority is required as an argument, or the goto will return a -1,\n"
509 "and the channel and call will be terminated.\n"
510 " If the location that is put into the channel information is bogus, and asterisk cannot\n"
511 "find that location in the dialplan,\n"
512 "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
513 "extension in the current context. If that does not exist, it will try to execute the\n"
514 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
515 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
516 "What this means is that, for example, you specify a context that does not exist, then\n"
517 "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
520 { "GotoIf", pbx_builtin_gotoif,
521 "Conditional goto",
522 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
523 "context, extension, and priority in the channel structure based on the evaluation of\n"
524 "the given condition. After this application completes, the\n"
525 "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
526 "The channel will continue at\n"
527 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
528 "false. The labels are specified with the same syntax as used within the Goto\n"
529 "application. If the label chosen by the condition is omitted, no jump is\n"
530 "performed, and the execution passes to the next instruction.\n"
531 "If the target location is bogus, and does not exist, the execution engine will try \n"
532 "to find and execute the code in the 'i' (invalid)\n"
533 "extension in the current context. If that does not exist, it will try to execute the\n"
534 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
535 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
536 "Remember that this command can set the current context, and if the context specified\n"
537 "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
538 "the channel and call will both be terminated!\n"
541 { "GotoIfTime", pbx_builtin_gotoiftime,
542 "Conditional Goto based on the current time",
543 " GotoIfTime(<times>,<weekdays>,<mdays>,<months>?[labeliftrue]:[labeliffalse]):\n"
544 "This application will set the context, extension, and priority in the channel structure\n"
545 "based on the evaluation of the given time specification. After this application completes,\n"
546 "the pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
547 "If the current time is within the given time specification, the channel will continue at\n"
548 "'labeliftrue'. Otherwise the channel will continue at 'labeliffalse'. If the label chosen\n"
549 "by the condition is omitted, no jump is performed, and execution passes to the next\n"
550 "instruction. If the target jump location is bogus, the same actions would be taken as for\n"
551 "Goto.\n"
552 "Further information on the time specification can be found in examples\n"
553 "illustrating how to do time-based context includes in the dialplan.\n"
556 { "ImportVar", pbx_builtin_importvar,
557 "Import a variable from a channel into a new variable",
558 " ImportVar(newvar=channelname,variable): This application imports a variable\n"
559 "from the specified channel (as opposed to the current one) and stores it as\n"
560 "a variable in the current channel (the channel that is calling this\n"
561 "application). Variables created by this application have the same inheritance\n"
562 "properties as those created with the Set application. See the documentation for\n"
563 "Set for more information.\n"
566 { "Hangup", pbx_builtin_hangup,
567 "Hang up the calling channel",
568 " Hangup([causecode]): This application will hang up the calling channel.\n"
569 "If a causecode is given the channel's hangup cause will be set to the given\n"
570 "value.\n"
573 { "Incomplete", pbx_builtin_incomplete,
574 "returns AST_PBX_INCOMPLETE value",
575 " Incomplete([n]): Signals the PBX routines that the previous matched extension\n"
576 "is incomplete and that further input should be allowed before matching can\n"
577 "be considered to be complete. Can be used within a pattern match when\n"
578 "certain criteria warrants a longer match.\n"
579 " If the 'n' option is specified, then Incomplete will not attempt to answer\n"
580 "the channel first. Note that most channel types need to be in Answer state\n"
581 "in order to receive DTMF.\n"
584 { "KeepAlive", pbx_builtin_keepalive,
585 "returns AST_PBX_KEEPALIVE value",
586 " KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
587 "Please do not run it alone from the dialplan!\n"
590 { "NoOp", pbx_builtin_noop,
591 "Do Nothing (No Operation)",
592 " NoOp(): This application does nothing. However, it is useful for debugging\n"
593 "purposes. Any text that is provided as arguments to this application can be\n"
594 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
595 "variables or functions without having any effect. Alternatively, see the\n"
596 "Verbose() application for finer grain control of output at custom verbose levels.\n"
599 { "Proceeding", pbx_builtin_proceeding,
600 "Indicate proceeding",
601 " Proceeding(): This application will request that a proceeding message\n"
602 "be provided to the calling channel.\n"
605 { "Progress", pbx_builtin_progress,
606 "Indicate progress",
607 " Progress(): This application will request that in-band progress information\n"
608 "be provided to the calling channel.\n"
611 { "RaiseException", pbx_builtin_raise_exception,
612 "Handle an exceptional condition",
613 " RaiseException(<reason>): This application will jump to the \"e\" extension\n"
614 "in the current context, setting the dialplan function EXCEPTION(). If the \"e\"\n"
615 "extension does not exist, the call will hangup.\n"
618 { "ResetCDR", pbx_builtin_resetcdr,
619 "Resets the Call Data Record",
620 " ResetCDR([options]): This application causes the Call Data Record to be\n"
621 "reset.\n"
622 " Options:\n"
623 " w -- Store the current CDR record before resetting it.\n"
624 " a -- Store any stacked records.\n"
625 " v -- Save CDR variables.\n"
626 " e -- Enable CDR only (negate effects of NoCDR).\n"
629 { "Ringing", pbx_builtin_ringing,
630 "Indicate ringing tone",
631 " Ringing(): This application will request that the channel indicate a ringing\n"
632 "tone to the user.\n"
635 { "SayAlpha", pbx_builtin_saycharacters,
636 "Say Alpha",
637 " SayAlpha(string): This application will play the sounds that correspond to\n"
638 "the letters of the given string.\n"
641 { "SayDigits", pbx_builtin_saydigits,
642 "Say Digits",
643 " SayDigits(digits): This application will play the sounds that correspond\n"
644 "to the digits of the given number. This will use the language that is currently\n"
645 "set for the channel. See the LANGUAGE function for more information on setting\n"
646 "the language for the channel.\n"
649 { "SayNumber", pbx_builtin_saynumber,
650 "Say Number",
651 " SayNumber(digits[,gender]): This application will play the sounds that\n"
652 "correspond to the given number. Optionally, a gender may be specified.\n"
653 "This will use the language that is currently set for the channel. See the\n"
654 "LANGUAGE function for more information on setting the language for the channel.\n"
657 { "SayPhonetic", pbx_builtin_sayphonetic,
658 "Say Phonetic",
659 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
660 "alphabet that correspond to the letters in the given string.\n"
663 { "Set", pbx_builtin_setvar,
664 "Set channel variable or function value",
665 " Set(name=value)\n"
666 "This function can be used to set the value of channel variables or dialplan\n"
667 "functions. When setting variables, if the variable name is prefixed with _,\n"
668 "the variable will be inherited into channels created from the current\n"
669 "channel. If the variable name is prefixed with __, the variable will be\n"
670 "inherited into channels created from the current channel and all children\n"
671 "channels.\n"
672 "Compatibility note: If (and only if), in /etc/asterisk/asterisk.conf, you have a [compat]\n"
673 "category, and you have app_set = 1.6 under that, then the behavior of this\n"
674 "app changes, and does not strip surrounding quotes from the right hand side\n"
675 "as it did previously in 1.4. The app_set = 1.6 is only inserted if 'make samples'\n"
676 "is executed, or if the users inserts this by hand into the asterisk.conf file.\n"
679 { "MSet", pbx_builtin_setvar_multiple,
680 "Set channel variable(s) or function value(s)",
681 " MSet(name1=value1,name2=value2,...)\n"
682 "This function can be used to set the value of channel variables or dialplan\n"
683 "functions. When setting variables, if the variable name is prefixed with _,\n"
684 "the variable will be inherited into channels created from the current\n"
685 "channel. If the variable name is prefixed with __, the variable will be\n"
686 "inherited into channels created from the current channel and all children\n"
687 "channels.\n\n"
688 "MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus\n"
689 "prone to doing things that you may not expect. For example, it strips surrounding\n"
690 "double-quotes from the right-hand side (value). Avoid its use if possible.\n"
693 { "SetAMAFlags", pbx_builtin_setamaflags,
694 "Set the AMA Flags",
695 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
696 " billing purposes.\n"
699 { "Wait", pbx_builtin_wait,
700 "Waits for some time",
701 " Wait(seconds): This application waits for a specified number of seconds.\n"
702 "Then, dialplan execution will continue at the next priority.\n"
703 " Note that the seconds can be passed with fractions of a second. For example,\n"
704 "'1.5' will ask the application to wait for 1.5 seconds.\n"
707 { "WaitExten", pbx_builtin_waitexten,
708 "Waits for an extension to be entered",
709 " WaitExten([seconds][,options]): This application waits for the user to enter\n"
710 "a new extension for a specified number of seconds.\n"
711 " Note that the seconds can be passed with fractions of a second. For example,\n"
712 "'1.5' will ask the application to wait for 1.5 seconds.\n"
713 " Options:\n"
714 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
715 " Optionally, specify the class for music on hold within parenthesis.\n"
716 "See Also: Playback(application), Background(application).\n"
721 static struct ast_context *contexts;
722 static struct ast_hashtab *contexts_table = NULL;
724 AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
726 static AST_RWLIST_HEAD_STATIC(apps, ast_app);
728 static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
730 static int stateid = 1;
731 /* WARNING:
732 When holding this list's lock, do _not_ do anything that will cause conlock
733 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
734 function will take the locks in conlock/hints order, so any other
735 paths that require both locks must also take them in that order.
737 static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
739 static AST_LIST_HEAD_NOLOCK_STATIC(statecbs, ast_state_cb);
741 #ifdef CONTEXT_DEBUG
743 /* these routines are provided for doing run-time checks
744 on the extension structures, in case you are having
745 problems, this routine might help you localize where
746 the problem is occurring. It's kinda like a debug memory
747 allocator's arena checker... It'll eat up your cpu cycles!
748 but you'll see, if you call it in the right places,
749 right where your problems began...
752 /* you can break on the check_contexts_trouble()
753 routine in your debugger to stop at the moment
754 there's a problem */
755 void check_contexts_trouble(void);
757 void check_contexts_trouble(void)
759 int x = 1;
760 x = 2;
763 static struct ast_context *find_context_locked(const char *context);
764 int check_contexts(char *, int);
766 int check_contexts(char *file, int line )
768 struct ast_hashtab_iter *t1;
769 struct ast_context *c1, *c2;
770 int found = 0;
771 struct ast_exten *e1, *e2, *e3;
772 struct ast_exten ex;
774 /* try to find inconsistencies */
775 /* is every context in the context table in the context list and vice-versa ? */
777 if (!contexts_table) {
778 ast_log(LOG_NOTICE,"Called from: %s:%d: No contexts_table!\n", file, line);
779 usleep(500000);
782 t1 = ast_hashtab_start_traversal(contexts_table);
783 while( (c1 = ast_hashtab_next(t1))) {
784 for(c2=contexts;c2;c2=c2->next) {
785 if (!strcmp(c1->name, c2->name)) {
786 found = 1;
787 break;
790 if (!found) {
791 ast_log(LOG_NOTICE,"Called from: %s:%d: Could not find the %s context in the linked list\n", file, line, c1->name);
792 check_contexts_trouble();
795 ast_hashtab_end_traversal(t1);
796 for(c2=contexts;c2;c2=c2->next) {
797 c1 = find_context_locked(c2->name);
798 if (!c1) {
799 ast_log(LOG_NOTICE,"Called from: %s:%d: Could not find the %s context in the hashtab\n", file, line, c2->name);
800 check_contexts_trouble();
801 } else
802 ast_unlock_contexts();
805 /* loop thru all contexts, and verify the exten structure compares to the
806 hashtab structure */
807 for(c2=contexts;c2;c2=c2->next) {
808 c1 = find_context_locked(c2->name);
809 if (c1)
812 ast_unlock_contexts();
814 /* is every entry in the root list also in the root_table? */
815 for(e1 = c1->root; e1; e1=e1->next)
817 char dummy_name[1024];
818 ex.exten = dummy_name;
819 ex.matchcid = e1->matchcid;
820 ex.cidmatch = e1->cidmatch;
821 ast_copy_string(dummy_name, e1->exten, sizeof(dummy_name));
822 e2 = ast_hashtab_lookup(c1->root_table, &ex);
823 if (!e2) {
824 if (e1->matchcid) {
825 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch );
826 } else {
827 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name );
829 check_contexts_trouble();
833 /* is every entry in the root_table also in the root list? */
834 if (!c2->root_table) {
835 if (c2->root) {
836 ast_log(LOG_NOTICE,"Called from: %s:%d: No c2->root_table for context %s!\n", file, line, c2->name);
837 usleep(500000);
839 } else {
840 t1 = ast_hashtab_start_traversal(c2->root_table);
841 while( (e2 = ast_hashtab_next(t1)) ) {
842 for(e1=c2->root;e1;e1=e1->next) {
843 if (!strcmp(e1->exten, e2->exten)) {
844 found = 1;
845 break;
848 if (!found) {
849 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, e2->exten);
850 check_contexts_trouble();
854 ast_hashtab_end_traversal(t1);
857 /* is every priority reflected in the peer_table at the head of the list? */
859 /* is every entry in the root list also in the root_table? */
860 /* are the per-extension peer_tables in the right place? */
862 for(e1 = c2->root; e1; e1 = e1->next) {
864 for(e2=e1;e2;e2=e2->peer) {
865 ex.priority = e2->priority;
866 if (e2 != e1 && e2->peer_table) {
867 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority );
868 check_contexts_trouble();
871 if (e2 != e1 && e2->peer_label_table) {
872 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority has a peer_label_table entry, and shouldn't!\n", file, line, c2->name, e1->exten, e2->priority );
873 check_contexts_trouble();
876 if (e2 == e1 && !e2->peer_table){
877 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_table!\n", file, line, c2->name, e1->exten, e2->priority );
878 check_contexts_trouble();
881 if (e2 == e1 && !e2->peer_label_table) {
882 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority doesn't have a peer_label_table!\n", file, line, c2->name, e1->exten, e2->priority );
883 check_contexts_trouble();
887 e3 = ast_hashtab_lookup(e1->peer_table, &ex);
888 if (!e3) {
889 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer_table\n", file, line, c2->name, e1->exten, e2->priority );
890 check_contexts_trouble();
894 if (!e1->peer_table){
895 ast_log(LOG_NOTICE,"Called from: %s:%d: No e1->peer_table!\n", file, line);
896 usleep(500000);
899 /* is every entry in the peer_table also in the peer list? */
900 t1 = ast_hashtab_start_traversal(e1->peer_table);
901 while( (e2 = ast_hashtab_next(t1)) ) {
902 for(e3=e1;e3;e3=e3->peer) {
903 if (e3->priority == e2->priority) {
904 found = 1;
905 break;
908 if (!found) {
909 ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context, %s exten, %d priority is not reflected in the peer list\n", file, line, c2->name, e1->exten, e2->priority );
910 check_contexts_trouble();
913 ast_hashtab_end_traversal(t1);
916 return 0;
918 #endif
921 \note This function is special. It saves the stack so that no matter
922 how many times it is called, it returns to the same place */
923 int pbx_exec(struct ast_channel *c, /*!< Channel */
924 struct ast_app *app, /*!< Application */
925 void *data) /*!< Data for execution */
927 int res;
928 struct ast_module_user *u = NULL;
929 const char *saved_c_appl;
930 const char *saved_c_data;
932 if (c->cdr && !ast_check_hangup(c))
933 ast_cdr_setapp(c->cdr, app->name, data);
935 /* save channel values */
936 saved_c_appl= c->appl;
937 saved_c_data= c->data;
939 c->appl = app->name;
940 c->data = data;
941 if (app->module)
942 u = __ast_module_user_add(app->module, c);
943 res = app->execute(c, S_OR(data, ""));
944 if (app->module && u)
945 __ast_module_user_remove(app->module, u);
946 /* restore channel values */
947 c->appl = saved_c_appl;
948 c->data = saved_c_data;
949 return res;
953 /*! Go no deeper than this through includes (not counting loops) */
954 #define AST_PBX_MAX_STACK 128
956 /*! \brief Find application handle in linked list
958 struct ast_app *pbx_findapp(const char *app)
960 struct ast_app *tmp;
962 AST_RWLIST_RDLOCK(&apps);
963 AST_RWLIST_TRAVERSE(&apps, tmp, list) {
964 if (!strcasecmp(tmp->name, app))
965 break;
967 AST_RWLIST_UNLOCK(&apps);
969 return tmp;
972 static struct ast_switch *pbx_findswitch(const char *sw)
974 struct ast_switch *asw;
976 AST_RWLIST_RDLOCK(&switches);
977 AST_RWLIST_TRAVERSE(&switches, asw, list) {
978 if (!strcasecmp(asw->name, sw))
979 break;
981 AST_RWLIST_UNLOCK(&switches);
983 return asw;
986 static inline int include_valid(struct ast_include *i)
988 if (!i->hastime)
989 return 1;
991 return ast_check_timing(&(i->timing));
994 static void pbx_destroy(struct ast_pbx *p)
996 ast_free(p);
999 /* form a tree that fully describes all the patterns in a context's extensions
1000 * in this tree, a "node" represents an individual character or character set
1001 * meant to match the corresponding character in a dial string. The tree
1002 * consists of a series of match_char structs linked in a chain
1003 * via the alt_char pointers. More than one pattern can share the same parts of the
1004 * tree as other extensions with the same pattern to that point.
1005 * My first attempt to duplicate the finding of the 'best' pattern was flawed in that
1006 * I misunderstood the general algorithm. I thought that the 'best' pattern
1007 * was the one with lowest total score. This was not true. Thus, if you have
1008 * patterns "1XXXXX" and "X11111", you would be tempted to say that "X11111" is
1009 * the "best" match because it has fewer X's, and is therefore more specific,
1010 * but this is not how the old algorithm works. It sorts matching patterns
1011 * in a similar collating sequence as sorting alphabetic strings, from left to
1012 * right. Thus, "1XXXXX" comes before "X11111", and would be the "better" match,
1013 * because "1" is more specific than "X".
1014 * So, to accomodate this philosophy, I sort the tree branches along the alt_char
1015 * line so they are lowest to highest in specificity numbers. This way, as soon
1016 * as we encounter our first complete match, we automatically have the "best"
1017 * match and can stop the traversal immediately. Same for CANMATCH/MATCHMORE.
1018 * If anyone would like to resurrect the "wrong" pattern trie searching algorithm,
1019 * they are welcome to revert pbx to before 1 Apr 2008.
1020 * As an example, consider these 4 extensions:
1021 * (a) NXXNXXXXXX
1022 * (b) 307754XXXX
1023 * (c) fax
1024 * (d) NXXXXXXXXX
1026 * In the above, between (a) and (d), (a) is a more specific pattern than (d), and would win over
1027 * most numbers. For all numbers beginning with 307754, (b) should always win.
1029 * These pattern should form a (sorted) tree that looks like this:
1030 * { "3" } --next--> { "0" } --next--> { "7" } --next--> { "7" } --next--> { "5" } ... blah ... --> { "X" exten_match: (b) }
1032 * |alt
1034 * { "f" } --next--> { "a" } --next--> { "x" exten_match: (c) }
1035 * { "N" } --next--> { "X" } --next--> { "X" } --next--> { "N" } --next--> { "X" } ... blah ... --> { "X" exten_match: (a) }
1036 * | |
1037 * | |alt
1038 * |alt |
1039 * | { "X" } --next--> { "X" } ... blah ... --> { "X" exten_match: (d) }
1041 * NULL
1043 * In the above, I could easily turn "N" into "23456789", but I think that a quick "if( *z >= '2' && *z <= '9' )" might take
1044 * fewer CPU cycles than a call to index("23456789",*z), where *z is the char to match...
1046 * traversal is pretty simple: one routine merely traverses the alt list, and for each matching char in the pattern, it calls itself
1047 * on the corresponding next pointer, incrementing also the pointer of the string to be matched, and passing the total specificity and length.
1048 * We pass a pointer to a scoreboard down through, also.
1049 * The scoreboard isn't as necessary to the revised algorithm, but I kept it as a handy way to return the matched extension.
1050 * The first complete match ends the traversal, which should make this version of the pattern matcher faster
1051 * the previous. The same goes for "CANMATCH" or "MATCHMORE"; the first such match ends the traversal. In both
1052 * these cases, the reason we can stop immediately, is because the first pattern match found will be the "best"
1053 * according to the sort criteria.
1054 * Hope the limit on stack depth won't be a problem... this routine should
1055 * be pretty lean as far a stack usage goes. Any non-match terminates the recursion down a branch.
1057 * In the above example, with the number "3077549999" as the pattern, the traversor could match extensions a, b and d. All are
1058 * of length 10; they have total specificities of 24580, 10246, and 25090, respectively, not that this matters
1059 * at all. (b) wins purely because the first character "3" is much more specific (lower specificity) than "N". I have
1060 * left the specificity totals in the code as an artifact; at some point, I will strip it out.
1062 * Just how much time this algorithm might save over a plain linear traversal over all possible patterns is unknown,
1063 * because it's a function of how many extensions are stored in a context. With thousands of extensions, the speedup
1064 * can be very noticeable. The new matching algorithm can run several hundreds of times faster, if not a thousand or
1065 * more times faster in extreme cases.
1067 * MatchCID patterns are also supported, and stored in the tree just as the extension pattern is. Thus, you
1068 * can have patterns in your CID field as well.
1070 * */
1073 static void update_scoreboard(struct scoreboard *board, int length, int spec, struct ast_exten *exten, char last, const char *callerid, int deleted, struct match_char *node)
1075 /* if this extension is marked as deleted, then skip this -- if it never shows
1076 on the scoreboard, it will never be found, nor will halt the traversal. */
1077 if (deleted)
1078 return;
1079 board->total_specificity = spec;
1080 board->total_length = length;
1081 board->exten = exten;
1082 board->last_char = last;
1083 board->node = node;
1084 #ifdef NEED_DEBUG_HERE
1085 ast_log(LOG_NOTICE,"Scoreboarding (LONGER) %s, len=%d, score=%d\n", exten->exten, length, spec);
1086 #endif
1089 void log_match_char_tree(struct match_char *node, char *prefix)
1091 char extenstr[40];
1092 struct ast_str *my_prefix = ast_str_alloca(1024);
1094 extenstr[0] = '\0';
1096 if (node && node->exten && node->exten)
1097 snprintf(extenstr, sizeof(extenstr), "(%p)", node->exten);
1099 if (strlen(node->x) > 1) {
1100 ast_debug(1, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N',
1101 node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"",
1102 node->exten ? node->exten->exten : "", extenstr);
1103 } else {
1104 ast_debug(1, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N',
1105 node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"",
1106 node->exten ? node->exten->exten : "", extenstr);
1109 ast_str_set(&my_prefix, 0, "%s+ ", prefix);
1111 if (node->next_char)
1112 log_match_char_tree(node->next_char, my_prefix->str);
1114 if (node->alt_char)
1115 log_match_char_tree(node->alt_char, prefix);
1118 static void cli_match_char_tree(struct match_char *node, char *prefix, int fd)
1120 char extenstr[40];
1121 struct ast_str *my_prefix = ast_str_alloca(1024);
1123 extenstr[0] = '\0';
1125 if (node && node->exten && node->exten)
1126 snprintf(extenstr, sizeof(extenstr), "(%p)", node->exten);
1128 if (strlen(node->x) > 1) {
1129 ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
1130 node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
1131 node->exten ? node->exten->exten : "", extenstr);
1132 } else {
1133 ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
1134 node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
1135 node->exten ? node->exten->exten : "", extenstr);
1138 ast_str_set(&my_prefix, 0, "%s+ ", prefix);
1140 if (node->next_char)
1141 cli_match_char_tree(node->next_char, my_prefix->str, fd);
1143 if (node->alt_char)
1144 cli_match_char_tree(node->alt_char, prefix, fd);
1147 static struct ast_exten *get_canmatch_exten(struct match_char *node)
1149 /* find the exten at the end of the rope */
1150 struct match_char *node2 = node;
1152 for (node2 = node; node2; node2 = node2->next_char) {
1153 if (node2->exten) {
1154 #ifdef NEED_DEBUG_HERE
1155 ast_log(LOG_NOTICE,"CanMatch_exten returns exten %s(%p)\n", node2->exten->exten, node2->exten);
1156 #endif
1157 return node2->exten;
1160 #ifdef NEED_DEBUG_HERE
1161 ast_log(LOG_NOTICE,"CanMatch_exten returns NULL, match_char=%s\n", node->x);
1162 #endif
1163 return 0;
1166 static struct ast_exten *trie_find_next_match(struct match_char *node)
1168 struct match_char *m3;
1169 struct match_char *m4;
1170 struct ast_exten *e3;
1172 if (node && node->x[0] == '.' && !node->x[1]) /* dot and ! will ALWAYS be next match in a matchmore */
1173 return node->exten;
1175 if (node && node->x[0] == '!' && !node->x[1])
1176 return node->exten;
1178 if (!node || !node->next_char)
1179 return NULL;
1181 m3 = node->next_char;
1183 if (m3->exten)
1184 return m3->exten;
1185 for(m4=m3->alt_char; m4; m4 = m4->alt_char) {
1186 if (m4->exten)
1187 return m4->exten;
1189 for(m4=m3; m4; m4 = m4->alt_char) {
1190 e3 = trie_find_next_match(m3);
1191 if (e3)
1192 return e3;
1194 return NULL;
1197 #ifdef DEBUG_THIS
1198 static char *action2str(enum ext_match_t action)
1200 switch(action)
1202 case E_MATCH:
1203 return "MATCH";
1204 case E_CANMATCH:
1205 return "CANMATCH";
1206 case E_MATCHMORE:
1207 return "MATCHMORE";
1208 case E_FINDLABEL:
1209 return "FINDLABEL";
1210 case E_SPAWN:
1211 return "SPAWN";
1212 default:
1213 return "?ACTION?";
1217 #endif
1219 static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *label, const char *callerid, enum ext_match_t action)
1221 struct match_char *p; /* note minimal stack storage requirements */
1222 struct ast_exten pattern = { .label = label };
1223 #ifdef DEBUG_THIS
1224 if (tree)
1225 ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree %s action=%s\n", str, tree->x, action2str(action));
1226 else
1227 ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree NULL action=%s\n", str, action2str(action));
1228 #endif
1229 for (p=tree; p; p=p->alt_char) {
1230 if (p->x[0] == 'N') {
1231 if (p->x[1] == 0 && *str >= '2' && *str <= '9' ) {
1232 #define NEW_MATCHER_CHK_MATCH \
1233 if (p->exten && !(*(str+1))) { /* if a shorter pattern matches along the way, might as well report it */ \
1234 if (action == E_MATCH || action == E_SPAWN || action == E_FINDLABEL) { /* if in CANMATCH/MATCHMORE, don't let matches get in the way */ \
1235 update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p); \
1236 if (!p->deleted) { \
1237 if (action == E_FINDLABEL) { \
1238 if (ast_hashtab_lookup(score->exten->peer_label_table, &pattern)) { \
1239 ast_debug(4, "Found label in preferred extension\n"); \
1240 return; \
1242 } else { \
1243 ast_debug(4,"returning an exact match-- first found-- %s\n", p->exten->exten); \
1244 return; /* the first match, by definition, will be the best, because of the sorted tree */ \
1250 #define NEW_MATCHER_RECURSE \
1251 if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0) \
1252 || p->next_char->x[0] == '!')) { \
1253 if (*(str+1) || p->next_char->x[0] == '!') { \
1254 new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid, label, action); \
1255 if (score->exten) { \
1256 ast_debug(4,"returning an exact match-- %s\n", score->exten->exten); \
1257 return; /* the first match is all we need */ \
1259 } else { \
1260 new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid, label, action); \
1261 if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) { \
1262 ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->exten : \
1263 "NULL"); \
1264 return; /* the first match is all we need */ \
1267 } else if (p->next_char && !*(str+1)) { \
1268 score->canmatch = 1; \
1269 score->canmatch_exten = get_canmatch_exten(p); \
1270 if (action == E_CANMATCH || action == E_MATCHMORE) { \
1271 ast_debug(4,"returning a canmatch/matchmore--- str=%s\n", str); \
1272 return; \
1276 NEW_MATCHER_CHK_MATCH;
1277 NEW_MATCHER_RECURSE;
1279 } else if (p->x[0] == 'Z') {
1280 if (p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
1281 NEW_MATCHER_CHK_MATCH;
1282 NEW_MATCHER_RECURSE;
1284 } else if (p->x[0] == 'X') {
1285 if (p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
1286 NEW_MATCHER_CHK_MATCH;
1287 NEW_MATCHER_RECURSE;
1289 } else if (p->x[0] == '.' && p->x[1] == 0) {
1290 /* how many chars will the . match against? */
1291 int i = 0;
1292 const char *str2 = str;
1293 while (*str2 && *str2 != '/') {
1294 str2++;
1295 i++;
1297 if (p->exten && *str2 != '/') {
1298 update_scoreboard(score, length+i, spec+(i*p->specificity), p->exten, '.', callerid, p->deleted, p);
1299 if (score->exten) {
1300 ast_debug(4,"return because scoreboard has a match with '/'--- %s\n", score->exten->exten);
1301 return; /* the first match is all we need */
1304 if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
1305 new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid, label, action);
1306 if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
1307 ast_debug(4,"return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set--- %s\n", score->exten ? score->exten->exten : "NULL");
1308 return; /* the first match is all we need */
1311 } else if (p->x[0] == '!' && p->x[1] == 0) {
1312 /* how many chars will the . match against? */
1313 int i = 1;
1314 const char *str2 = str;
1315 while (*str2 && *str2 != '/') {
1316 str2++;
1317 i++;
1319 if (p->exten && *str2 != '/') {
1320 update_scoreboard(score, length+1, spec+(p->specificity*i), p->exten, '!', callerid, p->deleted, p);
1321 if (score->exten) {
1322 ast_debug(4,"return because scoreboard has a '!' match--- %s\n", score->exten->exten);
1323 return; /* the first match is all we need */
1326 if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
1327 new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid, label, action);
1328 if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
1329 ast_debug(4,"return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n", score->exten ? score->exten->exten : "NULL");
1330 return; /* the first match is all we need */
1333 } else if (p->x[0] == '/' && p->x[1] == 0) {
1334 /* the pattern in the tree includes the cid match! */
1335 if (p->next_char && callerid && *callerid) {
1336 new_find_extension(callerid, score, p->next_char, length+1, spec, callerid, label, action);
1337 if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
1338 ast_debug(4,"return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n", score->exten ? score->exten->exten : "NULL");
1339 return; /* the first match is all we need */
1342 } else if (index(p->x, *str)) {
1343 ast_debug(4, "Nothing strange about this match\n");
1344 NEW_MATCHER_CHK_MATCH;
1345 NEW_MATCHER_RECURSE;
1348 ast_debug(4,"return at end of func\n");
1351 /* the algorithm for forming the extension pattern tree is also a bit simple; you
1352 * traverse all the extensions in a context, and for each char of the extension,
1353 * you see if it exists in the tree; if it doesn't, you add it at the appropriate
1354 * spot. What more can I say? At the end of each exten, you cap it off by adding the
1355 * address of the extension involved. Duplicate patterns will be complained about.
1357 * Ideally, this would be done for each context after it is created and fully
1358 * filled. It could be done as a finishing step after extensions.conf or .ael is
1359 * loaded, or it could be done when the first search is encountered. It should only
1360 * have to be done once, until the next unload or reload.
1362 * I guess forming this pattern tree would be analogous to compiling a regex. Except
1363 * that a regex only handles 1 pattern, really. This trie holds any number
1364 * of patterns. Well, really, it **could** be considered a single pattern,
1365 * where the "|" (or) operator is allowed, I guess, in a way, sort of...
1368 static struct match_char *already_in_tree(struct match_char *current, char *pat)
1370 struct match_char *t;
1372 if (!current)
1373 return 0;
1375 for (t = current; t; t = t->alt_char) {
1376 if (!strcmp(pat, t->x)) /* uh, we may want to sort exploded [] contents to make matching easy */
1377 return t;
1380 return 0;
1383 /* The first arg is the location of the tree ptr, or the
1384 address of the next_char ptr in the node, so we can mess
1385 with it, if we need to insert at the beginning of the list */
1387 static void insert_in_next_chars_alt_char_list(struct match_char **parent_ptr, struct match_char *node)
1389 struct match_char *curr, *lcurr;
1391 /* insert node into the tree at "current", so the alt_char list from current is
1392 sorted in increasing value as you go to the leaves */
1393 if (!(*parent_ptr)) {
1394 *parent_ptr = node;
1395 } else {
1396 if ((*parent_ptr)->specificity > node->specificity){
1397 /* insert at head */
1398 node->alt_char = (*parent_ptr);
1399 *parent_ptr = node;
1400 } else {
1401 lcurr = *parent_ptr;
1402 for (curr=(*parent_ptr)->alt_char; curr; curr = curr->alt_char) {
1403 if (curr->specificity > node->specificity) {
1404 node->alt_char = curr;
1405 lcurr->alt_char = node;
1406 break;
1408 lcurr = curr;
1410 if (!curr)
1412 lcurr->alt_char = node;
1420 static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity, struct match_char **nextcharptr)
1422 struct match_char *m;
1424 if (!(m = ast_calloc(1, sizeof(*m))))
1425 return NULL;
1427 if (!(m->x = ast_strdup(pattern))) {
1428 ast_free(m);
1429 return NULL;
1432 /* the specificity scores are the same as used in the old
1433 pattern matcher. */
1434 m->is_pattern = is_pattern;
1435 if (specificity == 1 && is_pattern && pattern[0] == 'N')
1436 m->specificity = 0x0802;
1437 else if (specificity == 1 && is_pattern && pattern[0] == 'Z')
1438 m->specificity = 0x0901;
1439 else if (specificity == 1 && is_pattern && pattern[0] == 'X')
1440 m->specificity = 0x0a00;
1441 else if (specificity == 1 && is_pattern && pattern[0] == '.')
1442 m->specificity = 0x10000;
1443 else if (specificity == 1 && is_pattern && pattern[0] == '!')
1444 m->specificity = 0x20000;
1445 else
1446 m->specificity = specificity;
1448 if (!con->pattern_tree) {
1449 insert_in_next_chars_alt_char_list(&con->pattern_tree, m);
1450 } else {
1451 if (already) { /* switch to the new regime (traversing vs appending)*/
1452 insert_in_next_chars_alt_char_list(nextcharptr, m);
1453 } else {
1454 insert_in_next_chars_alt_char_list(&current->next_char, m);
1458 return m;
1461 static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly)
1463 struct match_char *m1 = NULL, *m2 = NULL, **m0;
1464 int specif;
1465 int already;
1466 int pattern = 0;
1467 char buf[256];
1468 char extenbuf[512];
1469 char *s1 = extenbuf;
1470 int l1 = strlen(e1->exten) + strlen(e1->cidmatch) + 2;
1473 strncpy(extenbuf,e1->exten,sizeof(extenbuf));
1474 if (e1->matchcid && l1 <= sizeof(extenbuf)) {
1475 strcat(extenbuf,"/");
1476 strcat(extenbuf,e1->cidmatch);
1477 } else if (l1 > sizeof(extenbuf)) {
1478 ast_log(LOG_ERROR,"The pattern %s/%s is too big to deal with: it will be ignored! Disaster!\n", e1->exten, e1->cidmatch);
1479 return 0;
1481 #ifdef NEED_DEBUG
1482 ast_log(LOG_DEBUG,"Adding exten %s%c%s to tree\n", s1, e1->matchcid? '/':' ', e1->matchcid? e1->cidmatch : "");
1483 #endif
1484 m1 = con->pattern_tree; /* each pattern starts over at the root of the pattern tree */
1485 m0 = &con->pattern_tree;
1486 already = 1;
1488 if ( *s1 == '_') {
1489 pattern = 1;
1490 s1++;
1492 while( *s1 ) {
1493 if (pattern && *s1 == '[' && *(s1-1) != '\\') {
1494 char *s2 = buf;
1495 buf[0] = 0;
1496 s1++; /* get past the '[' */
1497 while (*s1 != ']' && *(s1-1) != '\\' ) {
1498 if (*s1 == '\\') {
1499 if (*(s1+1) == ']') {
1500 *s2++ = ']';
1501 s1++;s1++;
1502 } else if (*(s1+1) == '\\') {
1503 *s2++ = '\\';
1504 s1++;s1++;
1505 } else if (*(s1+1) == '-') {
1506 *s2++ = '-';
1507 s1++; s1++;
1508 } else if (*(s1+1) == '[') {
1509 *s2++ = '[';
1510 s1++; s1++;
1512 } else if (*s1 == '-') { /* remember to add some error checking to all this! */
1513 char s3 = *(s1-1);
1514 char s4 = *(s1+1);
1515 for (s3++; s3 <= s4; s3++) {
1516 *s2++ = s3;
1518 s1++; s1++;
1519 } else {
1520 *s2++ = *s1++;
1523 *s2 = 0; /* null terminate the exploded range */
1524 /* sort the characters */
1526 specif = strlen(buf);
1527 qsort(buf, specif, 1, compare_char);
1528 specif <<= 8;
1529 specif += buf[0];
1530 } else {
1532 if (*s1 == '\\') {
1533 s1++;
1534 buf[0] = *s1;
1535 } else {
1536 if (pattern) {
1537 if (*s1 == 'n') /* make sure n,x,z patterns are canonicalized to N,X,Z */
1538 *s1 = 'N';
1539 else if (*s1 == 'x')
1540 *s1 = 'X';
1541 else if (*s1 == 'z')
1542 *s1 = 'Z';
1544 buf[0] = *s1;
1546 buf[1] = 0;
1547 specif = 1;
1549 m2 = 0;
1550 if (already && (m2=already_in_tree(m1,buf)) && m2->next_char) {
1551 if (!(*(s1+1))) { /* if this is the end of the pattern, but not the end of the tree, then mark this node with the exten...
1552 a shorter pattern might win if the longer one doesn't match */
1553 m2->exten = e1;
1554 m2->deleted = 0;
1556 m1 = m2->next_char; /* m1 points to the node to compare against */
1557 m0 = &m2->next_char; /* m0 points to the ptr that points to m1 */
1558 } else { /* not already OR not m2 OR nor m2->next_char */
1559 if (m2) {
1560 if (findonly)
1561 return m2;
1562 m1 = m2; /* while m0 stays the same */
1563 } else {
1564 if (findonly)
1565 return m1;
1566 m1 = add_pattern_node(con, m1, buf, pattern, already,specif, m0); /* m1 is the node just added */
1567 m0 = &m1->next_char;
1570 if (!(*(s1+1))) {
1571 m1->deleted = 0;
1572 m1->exten = e1;
1575 already = 0;
1577 s1++; /* advance to next char */
1579 return m1;
1582 static void create_match_char_tree(struct ast_context *con)
1584 struct ast_hashtab_iter *t1;
1585 struct ast_exten *e1;
1586 #ifdef NEED_DEBUG
1587 int biggest_bucket, resizes, numobjs, numbucks;
1589 ast_log(LOG_DEBUG,"Creating Extension Trie for context %s\n", con->name);
1590 ast_hashtab_get_stats(con->root_table, &biggest_bucket, &resizes, &numobjs, &numbucks);
1591 ast_log(LOG_DEBUG,"This tree has %d objects in %d bucket lists, longest list=%d objects, and has resized %d times\n",
1592 numobjs, numbucks, biggest_bucket, resizes);
1593 #endif
1594 t1 = ast_hashtab_start_traversal(con->root_table);
1595 while( (e1 = ast_hashtab_next(t1)) ) {
1596 if (e1->exten)
1597 add_exten_to_pattern_tree(con, e1, 0);
1598 else
1599 ast_log(LOG_ERROR,"Attempt to create extension with no extension name.\n");
1601 ast_hashtab_end_traversal(t1);
1604 static void destroy_pattern_tree(struct match_char *pattern_tree) /* pattern tree is a simple binary tree, sort of, so the proper way to destroy it is... recursively! */
1606 /* destroy all the alternates */
1607 if (pattern_tree->alt_char) {
1608 destroy_pattern_tree(pattern_tree->alt_char);
1609 pattern_tree->alt_char = 0;
1611 /* destroy all the nexts */
1612 if (pattern_tree->next_char) {
1613 destroy_pattern_tree(pattern_tree->next_char);
1614 pattern_tree->next_char = 0;
1616 pattern_tree->exten = 0; /* never hurts to make sure there's no pointers laying around */
1617 if (pattern_tree->x)
1618 free(pattern_tree->x);
1619 free(pattern_tree);
1623 * Special characters used in patterns:
1624 * '_' underscore is the leading character of a pattern.
1625 * In other position it is treated as a regular char.
1626 * ' ' '-' space and '-' are separator and ignored.
1627 * . one or more of any character. Only allowed at the end of
1628 * a pattern.
1629 * ! zero or more of anything. Also impacts the result of CANMATCH
1630 * and MATCHMORE. Only allowed at the end of a pattern.
1631 * In the core routine, ! causes a match with a return code of 2.
1632 * In turn, depending on the search mode: (XXX check if it is implemented)
1633 * - E_MATCH retuns 1 (does match)
1634 * - E_MATCHMORE returns 0 (no match)
1635 * - E_CANMATCH returns 1 (does match)
1637 * / should not appear as it is considered the separator of the CID info.
1638 * XXX at the moment we may stop on this char.
1640 * X Z N match ranges 0-9, 1-9, 2-9 respectively.
1641 * [ denotes the start of a set of character. Everything inside
1642 * is considered literally. We can have ranges a-d and individual
1643 * characters. A '[' and '-' can be considered literally if they
1644 * are just before ']'.
1645 * XXX currently there is no way to specify ']' in a range, nor \ is
1646 * considered specially.
1648 * When we compare a pattern with a specific extension, all characters in the extension
1649 * itself are considered literally with the only exception of '-' which is considered
1650 * as a separator and thus ignored.
1651 * XXX do we want to consider space as a separator as well ?
1652 * XXX do we want to consider the separators in non-patterns as well ?
1656 * \brief helper functions to sort extensions and patterns in the desired way,
1657 * so that more specific patterns appear first.
1659 * ext_cmp1 compares individual characters (or sets of), returning
1660 * an int where bits 0-7 are the ASCII code of the first char in the set,
1661 * while bit 8-15 are the cardinality of the set minus 1.
1662 * This way more specific patterns (smaller cardinality) appear first.
1663 * Wildcards have a special value, so that we can directly compare them to
1664 * sets by subtracting the two values. In particular:
1665 * 0x000xx one character, xx
1666 * 0x0yyxx yy character set starting with xx
1667 * 0x10000 '.' (one or more of anything)
1668 * 0x20000 '!' (zero or more of anything)
1669 * 0x30000 NUL (end of string)
1670 * 0x40000 error in set.
1671 * The pointer to the string is advanced according to needs.
1672 * NOTES:
1673 * 1. the empty set is equivalent to NUL.
1674 * 2. given that a full set has always 0 as the first element,
1675 * we could encode the special cases as 0xffXX where XX
1676 * is 1, 2, 3, 4 as used above.
1678 static int ext_cmp1(const char **p)
1680 uint32_t chars[8];
1681 int c, cmin = 0xff, count = 0;
1682 const char *end;
1684 /* load, sign extend and advance pointer until we find
1685 * a valid character.
1687 while ( (c = *(*p)++) && (c == ' ' || c == '-') )
1688 ; /* ignore some characters */
1690 /* always return unless we have a set of chars */
1691 switch (toupper(c)) {
1692 default: /* ordinary character */
1693 return 0x0000 | (c & 0xff);
1695 case 'N': /* 2..9 */
1696 return 0x0700 | '2' ;
1698 case 'X': /* 0..9 */
1699 return 0x0900 | '0';
1701 case 'Z': /* 1..9 */
1702 return 0x0800 | '1';
1704 case '.': /* wildcard */
1705 return 0x10000;
1707 case '!': /* earlymatch */
1708 return 0x20000; /* less specific than NULL */
1710 case '\0': /* empty string */
1711 *p = NULL;
1712 return 0x30000;
1714 case '[': /* pattern */
1715 break;
1717 /* locate end of set */
1718 end = strchr(*p, ']');
1720 if (end == NULL) {
1721 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
1722 return 0x40000; /* XXX make this entry go last... */
1725 bzero(chars, sizeof(chars)); /* clear all chars in the set */
1726 for (; *p < end ; (*p)++) {
1727 unsigned char c1, c2; /* first-last char in range */
1728 c1 = (unsigned char)((*p)[0]);
1729 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
1730 c2 = (unsigned char)((*p)[2]);
1731 *p += 2; /* skip a total of 3 chars */
1732 } else /* individual character */
1733 c2 = c1;
1734 if (c1 < cmin)
1735 cmin = c1;
1736 for (; c1 <= c2; c1++) {
1737 uint32_t mask = 1 << (c1 % 32);
1738 if ( (chars[ c1 / 32 ] & mask) == 0)
1739 count += 0x100;
1740 chars[ c1 / 32 ] |= mask;
1743 (*p)++;
1744 return count == 0 ? 0x30000 : (count | cmin);
1748 * \brief the full routine to compare extensions in rules.
1750 static int ext_cmp(const char *a, const char *b)
1752 /* make sure non-patterns come first.
1753 * If a is not a pattern, it either comes first or
1754 * we use strcmp to compare the strings.
1756 int ret = 0;
1758 if (a[0] != '_')
1759 return (b[0] == '_') ? -1 : strcmp(a, b);
1761 /* Now we know a is a pattern; if b is not, a comes first */
1762 if (b[0] != '_')
1763 return 1;
1764 #if 0 /* old mode for ext matching */
1765 return strcmp(a, b);
1766 #endif
1767 /* ok we need full pattern sorting routine */
1768 while (!ret && a && b)
1769 ret = ext_cmp1(&a) - ext_cmp1(&b);
1770 if (ret == 0)
1771 return 0;
1772 else
1773 return (ret > 0) ? 1 : -1;
1776 int ast_extension_cmp(const char *a, const char *b)
1778 return ext_cmp(a, b);
1782 * \internal
1783 * \brief used ast_extension_{match|close}
1784 * mode is as follows:
1785 * E_MATCH success only on exact match
1786 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
1787 * E_CANMATCH either of the above.
1788 * \retval 0 on no-match
1789 * \retval 1 on match
1790 * \retval 2 on early match.
1793 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
1795 mode &= E_MATCH_MASK; /* only consider the relevant bits */
1797 #ifdef NEED_DEBUG_HERE
1798 ast_log(LOG_NOTICE,"match core: pat: '%s', dat: '%s', mode=%d\n", pattern, data, (int)mode);
1799 #endif
1801 if ( (mode == E_MATCH) && (pattern[0] == '_') && (!strcasecmp(pattern,data)) ) { /* note: if this test is left out, then _x. will not match _x. !!! */
1802 #ifdef NEED_DEBUG_HERE
1803 ast_log(LOG_NOTICE,"return (1) - pattern matches pattern\n");
1804 #endif
1805 return 1;
1808 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
1809 int ld = strlen(data), lp = strlen(pattern);
1811 if (lp < ld) { /* pattern too short, cannot match */
1812 #ifdef NEED_DEBUG_HERE
1813 ast_log(LOG_NOTICE,"return (0) - pattern too short, cannot match\n");
1814 #endif
1815 return 0;
1817 /* depending on the mode, accept full or partial match or both */
1818 if (mode == E_MATCH) {
1819 #ifdef NEED_DEBUG_HERE
1820 ast_log(LOG_NOTICE,"return (!strcmp(%s,%s) when mode== E_MATCH)\n", pattern, data);
1821 #endif
1822 return !strcmp(pattern, data); /* 1 on match, 0 on fail */
1824 if (ld == 0 || !strncasecmp(pattern, data, ld)) { /* partial or full match */
1825 #ifdef NEED_DEBUG_HERE
1826 ast_log(LOG_NOTICE,"return (mode(%d) == E_MATCHMORE ? lp(%d) > ld(%d) : 1)\n", mode, lp, ld);
1827 #endif
1828 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
1829 } else {
1830 #ifdef NEED_DEBUG_HERE
1831 ast_log(LOG_NOTICE,"return (0) when ld(%d) > 0 && pattern(%s) != data(%s)\n", ld, pattern, data);
1832 #endif
1833 return 0;
1836 pattern++; /* skip leading _ */
1838 * XXX below we stop at '/' which is a separator for the CID info. However we should
1839 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
1841 while (*data && *pattern && *pattern != '/') {
1842 const char *end;
1844 if (*data == '-') { /* skip '-' in data (just a separator) */
1845 data++;
1846 continue;
1848 switch (toupper(*pattern)) {
1849 case '[': /* a range */
1850 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
1851 if (end == NULL) {
1852 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
1853 return 0; /* unconditional failure */
1855 for (pattern++; pattern != end; pattern++) {
1856 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
1857 if (*data >= pattern[0] && *data <= pattern[2])
1858 break; /* match found */
1859 else {
1860 pattern += 2; /* skip a total of 3 chars */
1861 continue;
1863 } else if (*data == pattern[0])
1864 break; /* match found */
1866 if (pattern == end) {
1867 #ifdef NEED_DEBUG_HERE
1868 ast_log(LOG_NOTICE,"return (0) when pattern==end\n");
1869 #endif
1870 return 0;
1872 pattern = end; /* skip and continue */
1873 break;
1874 case 'N':
1875 if (*data < '2' || *data > '9') {
1876 #ifdef NEED_DEBUG_HERE
1877 ast_log(LOG_NOTICE,"return (0) N is matched\n");
1878 #endif
1879 return 0;
1881 break;
1882 case 'X':
1883 if (*data < '0' || *data > '9') {
1884 #ifdef NEED_DEBUG_HERE
1885 ast_log(LOG_NOTICE,"return (0) X is matched\n");
1886 #endif
1887 return 0;
1889 break;
1890 case 'Z':
1891 if (*data < '1' || *data > '9') {
1892 #ifdef NEED_DEBUG_HERE
1893 ast_log(LOG_NOTICE,"return (0) Z is matched\n");
1894 #endif
1895 return 0;
1897 break;
1898 case '.': /* Must match, even with more digits */
1899 #ifdef NEED_DEBUG_HERE
1900 ast_log(LOG_NOTICE,"return (1) when '.' is matched\n");
1901 #endif
1902 return 1;
1903 case '!': /* Early match */
1904 #ifdef NEED_DEBUG_HERE
1905 ast_log(LOG_NOTICE,"return (2) when '!' is matched\n");
1906 #endif
1907 return 2;
1908 case ' ':
1909 case '-': /* Ignore these in patterns */
1910 data--; /* compensate the final data++ */
1911 break;
1912 default:
1913 if (*data != *pattern) {
1914 #ifdef NEED_DEBUG_HERE
1915 ast_log(LOG_NOTICE,"return (0) when *data(%c) != *pattern(%c)\n", *data, *pattern);
1916 #endif
1917 return 0;
1921 data++;
1922 pattern++;
1924 if (*data) /* data longer than pattern, no match */ {
1925 #ifdef NEED_DEBUG_HERE
1926 ast_log(LOG_NOTICE,"return (0) when data longer than pattern\n");
1927 #endif
1928 return 0;
1932 * match so far, but ran off the end of the data.
1933 * Depending on what is next, determine match or not.
1935 if (*pattern == '\0' || *pattern == '/') { /* exact match */
1936 #ifdef NEED_DEBUG_HERE
1937 ast_log(LOG_NOTICE,"at end, return (%d) in 'exact match'\n", (mode==E_MATCHMORE) ? 0 : 1);
1938 #endif
1939 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
1940 } else if (*pattern == '!') { /* early match */
1941 #ifdef NEED_DEBUG_HERE
1942 ast_log(LOG_NOTICE,"at end, return (2) when '!' is matched\n");
1943 #endif
1944 return 2;
1945 } else { /* partial match */
1946 #ifdef NEED_DEBUG_HERE
1947 ast_log(LOG_NOTICE,"at end, return (%d) which deps on E_MATCH\n", (mode == E_MATCH) ? 0 : 1);
1948 #endif
1949 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
1954 * Wrapper around _extension_match_core() to do performance measurement
1955 * using the profiling code.
1957 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
1959 int i;
1960 static int prof_id = -2; /* marker for 'unallocated' id */
1961 if (prof_id == -2)
1962 prof_id = ast_add_profile("ext_match", 0);
1963 ast_mark(prof_id, 1);
1964 i = _extension_match_core(pattern, data, mode);
1965 ast_mark(prof_id, 0);
1966 return i;
1969 int ast_extension_match(const char *pattern, const char *data)
1971 return extension_match_core(pattern, data, E_MATCH);
1974 int ast_extension_close(const char *pattern, const char *data, int needmore)
1976 if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
1977 ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
1978 return extension_match_core(pattern, data, needmore);
1981 struct fake_context /* this struct is purely for matching in the hashtab */
1983 ast_rwlock_t lock;
1984 struct ast_exten *root;
1985 struct ast_hashtab *root_table;
1986 struct match_char *pattern_tree;
1987 struct ast_context *next;
1988 struct ast_include *includes;
1989 struct ast_ignorepat *ignorepats;
1990 const char *registrar;
1991 int refcount;
1992 AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
1993 ast_mutex_t macrolock;
1994 char name[256];
1997 struct ast_context *ast_context_find(const char *name)
1999 struct ast_context *tmp = NULL;
2000 struct fake_context item;
2001 strncpy(item.name,name,256);
2002 ast_rdlock_contexts();
2003 if( contexts_table ) {
2004 tmp = ast_hashtab_lookup(contexts_table,&item);
2005 } else {
2006 while ( (tmp = ast_walk_contexts(tmp)) ) {
2007 if (!name || !strcasecmp(name, tmp->name))
2008 break;
2011 ast_unlock_contexts();
2012 return tmp;
2015 #define STATUS_NO_CONTEXT 1
2016 #define STATUS_NO_EXTENSION 2
2017 #define STATUS_NO_PRIORITY 3
2018 #define STATUS_NO_LABEL 4
2019 #define STATUS_SUCCESS 5
2021 static int matchcid(const char *cidpattern, const char *callerid)
2023 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
2024 failing to get a number should count as a match, otherwise not */
2026 if (ast_strlen_zero(callerid))
2027 return ast_strlen_zero(cidpattern) ? 1 : 0;
2029 return ast_extension_match(cidpattern, callerid);
2032 struct ast_exten *pbx_find_extension(struct ast_channel *chan,
2033 struct ast_context *bypass, struct pbx_find_info *q,
2034 const char *context, const char *exten, int priority,
2035 const char *label, const char *callerid, enum ext_match_t action)
2037 int x, res;
2038 struct ast_context *tmp = NULL;
2039 struct ast_exten *e = NULL, *eroot = NULL;
2040 struct ast_include *i = NULL;
2041 struct ast_sw *sw = NULL;
2042 struct ast_exten pattern = {NULL, };
2043 struct scoreboard score = {0, };
2044 struct ast_str *tmpdata = NULL;
2046 pattern.label = label;
2047 pattern.priority = priority;
2048 #ifdef NEED_DEBUG_HERE
2049 ast_log(LOG_NOTICE,"Looking for cont/ext/prio/label/action = %s/%s/%d/%s/%d\n", context, exten, priority, label, (int)action);
2050 #endif
2052 if (ast_strlen_zero(exten))
2053 return NULL;
2055 /* Initialize status if appropriate */
2056 if (q->stacklen == 0) {
2057 q->status = STATUS_NO_CONTEXT;
2058 q->swo = NULL;
2059 q->data = NULL;
2060 q->foundcontext = NULL;
2061 } else if (q->stacklen >= AST_PBX_MAX_STACK) {
2062 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
2063 return NULL;
2066 /* Check first to see if we've already been checked */
2067 for (x = 0; x < q->stacklen; x++) {
2068 if (!strcasecmp(q->incstack[x], context))
2069 return NULL;
2072 if (bypass) /* bypass means we only look there */
2073 tmp = bypass;
2074 else { /* look in contexts */
2075 struct fake_context item;
2076 strncpy(item.name,context,256);
2077 tmp = ast_hashtab_lookup(contexts_table,&item);
2078 #ifdef NOTNOW
2079 tmp = NULL;
2080 while ((tmp = ast_walk_contexts(tmp)) ) {
2081 if (!strcmp(tmp->name, context))
2082 break;
2084 #endif
2085 if (!tmp)
2086 return NULL;
2090 if (q->status < STATUS_NO_EXTENSION)
2091 q->status = STATUS_NO_EXTENSION;
2093 /* Do a search for matching extension */
2095 eroot = NULL;
2096 score.total_specificity = 0;
2097 score.exten = 0;
2098 score.total_length = 0;
2099 if (!tmp->pattern_tree && tmp->root_table)
2101 create_match_char_tree(tmp);
2102 #ifdef NEED_DEBUG
2103 ast_log(LOG_DEBUG,"Tree Created in context %s:\n", context);
2104 log_match_char_tree(tmp->pattern_tree," ");
2105 #endif
2107 #ifdef NEED_DEBUG
2108 ast_log(LOG_NOTICE,"The Trie we are searching in:\n");
2109 log_match_char_tree(tmp->pattern_tree, ":: ");
2110 #endif
2112 do {
2113 if (!ast_strlen_zero(overrideswitch)) {
2114 char *osw = ast_strdupa(overrideswitch), *name;
2115 struct ast_switch *asw;
2116 ast_switch_f *aswf = NULL;
2117 char *datap;
2118 int eval = 0;
2120 name = strsep(&osw, "/");
2121 asw = pbx_findswitch(name);
2123 if (!asw) {
2124 ast_log(LOG_WARNING, "No such switch '%s'\n", name);
2125 break;
2128 if (osw && strchr(osw, '$')) {
2129 eval = 1;
2132 if (eval && !(tmpdata = ast_str_thread_get(&switch_data, 512))) {
2133 ast_log(LOG_WARNING, "Can't evaluate overrideswitch?!");
2134 break;
2135 } else if (eval) {
2136 /* Substitute variables now */
2137 pbx_substitute_variables_helper(chan, osw, tmpdata->str, tmpdata->len);
2138 datap = tmpdata->str;
2139 } else {
2140 datap = osw;
2143 /* equivalent of extension_match_core() at the switch level */
2144 if (action == E_CANMATCH)
2145 aswf = asw->canmatch;
2146 else if (action == E_MATCHMORE)
2147 aswf = asw->matchmore;
2148 else /* action == E_MATCH */
2149 aswf = asw->exists;
2150 if (!aswf) {
2151 res = 0;
2152 } else {
2153 if (chan) {
2154 ast_autoservice_start(chan);
2156 res = aswf(chan, context, exten, priority, callerid, datap);
2157 if (chan) {
2158 ast_autoservice_stop(chan);
2161 if (res) { /* Got a match */
2162 q->swo = asw;
2163 q->data = datap;
2164 q->foundcontext = context;
2165 /* XXX keep status = STATUS_NO_CONTEXT ? */
2166 return NULL;
2169 } while (0);
2171 if (extenpatternmatchnew) {
2172 new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid, label, action);
2173 eroot = score.exten;
2175 if (score.last_char == '!' && action == E_MATCHMORE) {
2176 /* We match an extension ending in '!'.
2177 * The decision in this case is final and is NULL (no match).
2179 #ifdef NEED_DEBUG_HERE
2180 ast_log(LOG_NOTICE,"Returning MATCHMORE NULL with exclamation point.\n");
2181 #endif
2182 return NULL;
2185 if (!eroot && (action == E_CANMATCH || action == E_MATCHMORE) && score.canmatch_exten) {
2186 q->status = STATUS_SUCCESS;
2187 #ifdef NEED_DEBUG_HERE
2188 ast_log(LOG_NOTICE,"Returning CANMATCH exten %s\n", score.canmatch_exten->exten);
2189 #endif
2190 return score.canmatch_exten;
2193 if ((action == E_MATCHMORE || action == E_CANMATCH) && eroot) {
2194 if (score.node) {
2195 struct ast_exten *z = trie_find_next_match(score.node);
2196 if (z) {
2197 #ifdef NEED_DEBUG_HERE
2198 ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten %s\n", z->exten);
2199 #endif
2200 } else {
2201 if (score.canmatch_exten) {
2202 #ifdef NEED_DEBUG_HERE
2203 ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE canmatchmatch exten %s(%p)\n", score.canmatch_exten->exten, score.canmatch_exten);
2204 #endif
2205 return score.canmatch_exten;
2206 } else {
2207 #ifdef NEED_DEBUG_HERE
2208 ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten NULL\n");
2209 #endif
2212 return z;
2214 #ifdef NEED_DEBUG_HERE
2215 ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE NULL (no next_match)\n");
2216 #endif
2217 return NULL; /* according to the code, complete matches are null matches in MATCHMORE mode */
2220 if (eroot) {
2221 /* found entry, now look for the right priority */
2222 if (q->status < STATUS_NO_PRIORITY)
2223 q->status = STATUS_NO_PRIORITY;
2224 e = NULL;
2225 if (action == E_FINDLABEL && label ) {
2226 if (q->status < STATUS_NO_LABEL)
2227 q->status = STATUS_NO_LABEL;
2228 e = ast_hashtab_lookup(eroot->peer_label_table, &pattern);
2229 } else {
2230 e = ast_hashtab_lookup(eroot->peer_table, &pattern);
2232 if (e) { /* found a valid match */
2233 q->status = STATUS_SUCCESS;
2234 q->foundcontext = context;
2235 #ifdef NEED_DEBUG_HERE
2236 ast_log(LOG_NOTICE,"Returning complete match of exten %s\n", e->exten);
2237 #endif
2238 return e;
2241 } else { /* the old/current default exten pattern match algorithm */
2243 /* scan the list trying to match extension and CID */
2244 eroot = NULL;
2245 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
2246 int match = extension_match_core(eroot->exten, exten, action);
2247 /* 0 on fail, 1 on match, 2 on earlymatch */
2249 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
2250 continue; /* keep trying */
2251 if (match == 2 && action == E_MATCHMORE) {
2252 /* We match an extension ending in '!'.
2253 * The decision in this case is final and is NULL (no match).
2255 return NULL;
2257 /* found entry, now look for the right priority */
2258 if (q->status < STATUS_NO_PRIORITY)
2259 q->status = STATUS_NO_PRIORITY;
2260 e = NULL;
2261 if (action == E_FINDLABEL && label ) {
2262 if (q->status < STATUS_NO_LABEL)
2263 q->status = STATUS_NO_LABEL;
2264 e = ast_hashtab_lookup(eroot->peer_label_table, &pattern);
2265 } else {
2266 e = ast_hashtab_lookup(eroot->peer_table, &pattern);
2268 #ifdef NOTNOW
2269 while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
2270 /* Match label or priority */
2271 if (action == E_FINDLABEL) {
2272 if (q->status < STATUS_NO_LABEL)
2273 q->status = STATUS_NO_LABEL;
2274 if (label && e->label && !strcmp(label, e->label))
2275 break; /* found it */
2276 } else if (e->priority == priority) {
2277 break; /* found it */
2278 } /* else keep searching */
2280 #endif
2281 if (e) { /* found a valid match */
2282 q->status = STATUS_SUCCESS;
2283 q->foundcontext = context;
2284 return e;
2290 /* Check alternative switches */
2291 AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
2292 struct ast_switch *asw = pbx_findswitch(sw->name);
2293 ast_switch_f *aswf = NULL;
2294 char *datap;
2296 if (!asw) {
2297 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
2298 continue;
2300 /* Substitute variables now */
2302 if (sw->eval) {
2303 if (!(tmpdata = ast_str_thread_get(&switch_data, 512))) {
2304 ast_log(LOG_WARNING, "Can't evaluate switch?!");
2305 continue;
2307 pbx_substitute_variables_helper(chan, sw->data, tmpdata->str, tmpdata->len);
2310 /* equivalent of extension_match_core() at the switch level */
2311 if (action == E_CANMATCH)
2312 aswf = asw->canmatch;
2313 else if (action == E_MATCHMORE)
2314 aswf = asw->matchmore;
2315 else /* action == E_MATCH */
2316 aswf = asw->exists;
2317 datap = sw->eval ? tmpdata->str : sw->data;
2318 if (!aswf)
2319 res = 0;
2320 else {
2321 if (chan)
2322 ast_autoservice_start(chan);
2323 res = aswf(chan, context, exten, priority, callerid, datap);
2324 if (chan)
2325 ast_autoservice_stop(chan);
2327 if (res) { /* Got a match */
2328 q->swo = asw;
2329 q->data = datap;
2330 q->foundcontext = context;
2331 /* XXX keep status = STATUS_NO_CONTEXT ? */
2332 return NULL;
2335 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
2336 /* Now try any includes we have in this context */
2337 for (i = tmp->includes; i; i = i->next) {
2338 if (include_valid(i)) {
2339 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
2340 #ifdef NEED_DEBUG_HERE
2341 ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
2342 #endif
2343 return e;
2345 if (q->swo)
2346 return NULL;
2349 return NULL;
2352 /*!
2353 * \brief extract offset:length from variable name.
2354 * \return 1 if there is a offset:length part, which is
2355 * trimmed off (values go into variables)
2357 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
2359 int parens = 0;
2361 *offset = 0;
2362 *length = INT_MAX;
2363 *isfunc = 0;
2364 for (; *var; var++) {
2365 if (*var == '(') {
2366 (*isfunc)++;
2367 parens++;
2368 } else if (*var == ')') {
2369 parens--;
2370 } else if (*var == ':' && parens == 0) {
2371 *var++ = '\0';
2372 sscanf(var, "%d:%d", offset, length);
2373 return 1; /* offset:length valid */
2376 return 0;
2379 /*!
2380 *\brief takes a substring. It is ok to call with value == workspace.
2381 * \param value
2382 * \param offset < 0 means start from the end of the string and set the beginning
2383 * to be that many characters back.
2384 * \param length is the length of the substring, a value less than 0 means to leave
2385 * that many off the end.
2386 * \param workspace
2387 * \param workspace_len
2388 * Always return a copy in workspace.
2390 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
2392 char *ret = workspace;
2393 int lr; /* length of the input string after the copy */
2395 ast_copy_string(workspace, value, workspace_len); /* always make a copy */
2397 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
2399 /* Quick check if no need to do anything */
2400 if (offset == 0 && length >= lr) /* take the whole string */
2401 return ret;
2403 if (offset < 0) { /* translate negative offset into positive ones */
2404 offset = lr + offset;
2405 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
2406 offset = 0;
2409 /* too large offset result in empty string so we know what to return */
2410 if (offset >= lr)
2411 return ret + lr; /* the final '\0' */
2413 ret += offset; /* move to the start position */
2414 if (length >= 0 && length < lr - offset) /* truncate if necessary */
2415 ret[length] = '\0';
2416 else if (length < 0) {
2417 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
2418 ret[lr + length - offset] = '\0';
2419 else
2420 ret[0] = '\0';
2423 return ret;
2426 /*! \brief Support for Asterisk built-in variables in the dialplan
2428 \note See also
2429 - \ref AstVar Channel variables
2430 - \ref AstCauses The HANGUPCAUSE variable
2432 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
2434 const char not_found = '\0';
2435 char *tmpvar;
2436 const char *s; /* the result */
2437 int offset, length;
2438 int i, need_substring;
2439 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
2441 if (c) {
2442 ast_channel_lock(c);
2443 places[0] = &c->varshead;
2446 * Make a copy of var because parse_variable_name() modifies the string.
2447 * Then if called directly, we might need to run substring() on the result;
2448 * remember this for later in 'need_substring', 'offset' and 'length'
2450 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
2451 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
2454 * Look first into predefined variables, then into variable lists.
2455 * Variable 's' points to the result, according to the following rules:
2456 * s == &not_found (set at the beginning) means that we did not find a
2457 * matching variable and need to look into more places.
2458 * If s != &not_found, s is a valid result string as follows:
2459 * s = NULL if the variable does not have a value;
2460 * you typically do this when looking for an unset predefined variable.
2461 * s = workspace if the result has been assembled there;
2462 * typically done when the result is built e.g. with an snprintf(),
2463 * so we don't need to do an additional copy.
2464 * s != workspace in case we have a string, that needs to be copied
2465 * (the ast_copy_string is done once for all at the end).
2466 * Typically done when the result is already available in some string.
2468 s = &not_found; /* default value */
2469 if (c) { /* This group requires a valid channel */
2470 /* Names with common parts are looked up a piece at a time using strncmp. */
2471 if (!strncmp(var, "CALL", 4)) {
2472 if (!strncmp(var + 4, "ING", 3)) {
2473 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
2474 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
2475 s = workspace;
2476 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
2477 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
2478 s = workspace;
2479 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
2480 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
2481 s = workspace;
2482 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
2483 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
2484 s = workspace;
2487 } else if (!strcmp(var, "HINT")) {
2488 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
2489 } else if (!strcmp(var, "HINTNAME")) {
2490 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
2491 } else if (!strcmp(var, "EXTEN")) {
2492 s = c->exten;
2493 } else if (!strcmp(var, "CONTEXT")) {
2494 s = c->context;
2495 } else if (!strcmp(var, "PRIORITY")) {
2496 snprintf(workspace, workspacelen, "%d", c->priority);
2497 s = workspace;
2498 } else if (!strcmp(var, "CHANNEL")) {
2499 s = c->name;
2500 } else if (!strcmp(var, "UNIQUEID")) {
2501 s = c->uniqueid;
2502 } else if (!strcmp(var, "HANGUPCAUSE")) {
2503 snprintf(workspace, workspacelen, "%d", c->hangupcause);
2504 s = workspace;
2507 if (s == &not_found) { /* look for more */
2508 if (!strcmp(var, "EPOCH")) {
2509 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
2510 s = workspace;
2511 } else if (!strcmp(var, "SYSTEMNAME")) {
2512 s = ast_config_AST_SYSTEM_NAME;
2513 } else if (!strcmp(var, "ENTITYID")) {
2514 ast_eid_to_str(workspace, workspacelen, &g_eid);
2515 s = workspace;
2518 /* if not found, look into chanvars or global vars */
2519 for (i = 0; s == &not_found && i < ARRAY_LEN(places); i++) {
2520 struct ast_var_t *variables;
2521 if (!places[i])
2522 continue;
2523 if (places[i] == &globals)
2524 ast_rwlock_rdlock(&globalslock);
2525 AST_LIST_TRAVERSE(places[i], variables, entries) {
2526 if (!strcasecmp(ast_var_name(variables), var)) {
2527 s = ast_var_value(variables);
2528 break;
2531 if (places[i] == &globals)
2532 ast_rwlock_unlock(&globalslock);
2534 if (s == &not_found || s == NULL)
2535 *ret = NULL;
2536 else {
2537 if (s != workspace)
2538 ast_copy_string(workspace, s, workspacelen);
2539 *ret = workspace;
2540 if (need_substring)
2541 *ret = substring(*ret, offset, length, workspace, workspacelen);
2544 if (c)
2545 ast_channel_unlock(c);
2548 static void exception_store_free(void *data)
2550 struct pbx_exception *exception = data;
2551 ast_string_field_free_memory(exception);
2552 ast_free(exception);
2555 static struct ast_datastore_info exception_store_info = {
2556 .type = "EXCEPTION",
2557 .destroy = exception_store_free,
2560 int pbx_builtin_raise_exception(struct ast_channel *chan, void *vreason)
2562 const char *reason = vreason;
2563 struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
2564 struct pbx_exception *exception = NULL;
2566 if (!ds) {
2567 ds = ast_datastore_alloc(&exception_store_info, NULL);
2568 if (!ds)
2569 return -1;
2570 exception = ast_calloc(1, sizeof(struct pbx_exception));
2571 if (!exception) {
2572 ast_datastore_free(ds);
2573 return -1;
2575 if (ast_string_field_init(exception, 128)) {
2576 ast_free(exception);
2577 ast_datastore_free(ds);
2578 return -1;
2580 ds->data = exception;
2581 ast_channel_datastore_add(chan, ds);
2582 } else
2583 exception = ds->data;
2585 ast_string_field_set(exception, reason, reason);
2586 ast_string_field_set(exception, context, chan->context);
2587 ast_string_field_set(exception, exten, chan->exten);
2588 exception->priority = chan->priority;
2589 set_ext_pri(chan, "e", 0);
2590 return 0;
2593 static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
2595 struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
2596 struct pbx_exception *exception = NULL;
2597 if (!ds || !ds->data)
2598 return -1;
2599 exception = ds->data;
2600 if (!strcasecmp(data, "REASON"))
2601 ast_copy_string(buf, exception->reason, buflen);
2602 else if (!strcasecmp(data, "CONTEXT"))
2603 ast_copy_string(buf, exception->context, buflen);
2604 else if (!strncasecmp(data, "EXTEN", 5))
2605 ast_copy_string(buf, exception->exten, buflen);
2606 else if (!strcasecmp(data, "PRIORITY"))
2607 snprintf(buf, buflen, "%d", exception->priority);
2608 else
2609 return -1;
2610 return 0;
2613 static struct ast_custom_function exception_function = {
2614 .name = "EXCEPTION",
2615 .synopsis = "Retrieve the details of the current dialplan exception",
2616 .desc =
2617 "The following fields are available for retrieval:\n"
2618 " reason INVALID, ERROR, RESPONSETIMEOUT, ABSOLUTETIMEOUT, or custom\n"
2619 " value set by the RaiseException() application\n"
2620 " context The context executing when the exception occurred\n"
2621 " exten The extension executing when the exception occurred\n"
2622 " priority The numeric priority executing when the exception occurred\n",
2623 .syntax = "EXCEPTION(<field>)",
2624 .read = acf_exception_read,
2627 static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2629 struct ast_custom_function *acf;
2630 int count_acf = 0;
2631 int like = 0;
2633 switch (cmd) {
2634 case CLI_INIT:
2635 e->command = "core show functions [like]";
2636 e->usage =
2637 "Usage: core show functions [like <text>]\n"
2638 " List builtin functions, optionally only those matching a given string\n";
2639 return NULL;
2640 case CLI_GENERATE:
2641 return NULL;
2644 if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
2645 like = 1;
2646 } else if (a->argc != 3) {
2647 return CLI_SHOWUSAGE;
2650 ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
2652 AST_RWLIST_RDLOCK(&acf_root);
2653 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
2654 if (!like || strstr(acf->name, a->argv[4])) {
2655 count_acf++;
2656 ast_cli(a->fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
2659 AST_RWLIST_UNLOCK(&acf_root);
2661 ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
2663 return CLI_SUCCESS;
2666 static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2668 struct ast_custom_function *acf;
2669 /* Maximum number of characters added by terminal coloring is 22 */
2670 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
2671 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
2672 char stxtitle[40], *syntax = NULL;
2673 int synopsis_size, description_size, syntax_size;
2674 char *ret = NULL;
2675 int which = 0;
2676 int wordlen;
2678 switch (cmd) {
2679 case CLI_INIT:
2680 e->command = "core show function";
2681 e->usage =
2682 "Usage: core show function <function>\n"
2683 " Describe a particular dialplan function.\n";
2684 return NULL;
2685 case CLI_GENERATE:
2686 wordlen = strlen(a->word);
2687 /* case-insensitive for convenience in this 'complete' function */
2688 AST_RWLIST_RDLOCK(&acf_root);
2689 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
2690 if (!strncasecmp(a->word, acf->name, wordlen) && ++which > a->n) {
2691 ret = ast_strdup(acf->name);
2692 break;
2695 AST_RWLIST_UNLOCK(&acf_root);
2697 return ret;
2700 if (a->argc < 4)
2701 return CLI_SHOWUSAGE;
2703 if (!(acf = ast_custom_function_find(a->argv[3]))) {
2704 ast_cli(a->fd, "No function by that name registered.\n");
2705 return CLI_FAILURE;
2709 if (acf->synopsis)
2710 synopsis_size = strlen(acf->synopsis) + 23;
2711 else
2712 synopsis_size = strlen("Not available") + 23;
2713 synopsis = alloca(synopsis_size);
2715 if (acf->desc)
2716 description_size = strlen(acf->desc) + 23;
2717 else
2718 description_size = strlen("Not available") + 23;
2719 description = alloca(description_size);
2721 if (acf->syntax)
2722 syntax_size = strlen(acf->syntax) + 23;
2723 else
2724 syntax_size = strlen("Not available") + 23;
2725 syntax = alloca(syntax_size);
2727 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
2728 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
2729 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
2730 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
2731 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
2732 term_color(syntax,
2733 acf->syntax ? acf->syntax : "Not available",
2734 COLOR_CYAN, 0, syntax_size);
2735 term_color(synopsis,
2736 acf->synopsis ? acf->synopsis : "Not available",
2737 COLOR_CYAN, 0, synopsis_size);
2738 term_color(description,
2739 acf->desc ? acf->desc : "Not available",
2740 COLOR_CYAN, 0, description_size);
2742 ast_cli(a->fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
2744 return CLI_SUCCESS;
2747 struct ast_custom_function *ast_custom_function_find(const char *name)
2749 struct ast_custom_function *acf = NULL;
2751 AST_RWLIST_RDLOCK(&acf_root);
2752 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
2753 if (!strcmp(name, acf->name))
2754 break;
2756 AST_RWLIST_UNLOCK(&acf_root);
2758 return acf;
2761 int ast_custom_function_unregister(struct ast_custom_function *acf)
2763 struct ast_custom_function *cur;
2765 if (!acf)
2766 return -1;
2768 AST_RWLIST_WRLOCK(&acf_root);
2769 if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist)))
2770 ast_verb(2, "Unregistered custom function %s\n", cur->name);
2771 AST_RWLIST_UNLOCK(&acf_root);
2773 return cur ? 0 : -1;
2776 int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
2778 struct ast_custom_function *cur;
2779 char tmps[80];
2781 if (!acf)
2782 return -1;
2784 acf->mod = mod;
2786 AST_RWLIST_WRLOCK(&acf_root);
2788 AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
2789 if (!strcmp(acf->name, cur->name)) {
2790 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
2791 AST_RWLIST_UNLOCK(&acf_root);
2792 return -1;
2796 /* Store in alphabetical order */
2797 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
2798 if (strcasecmp(acf->name, cur->name) < 0) {
2799 AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
2800 break;
2803 AST_RWLIST_TRAVERSE_SAFE_END;
2804 if (!cur)
2805 AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
2807 AST_RWLIST_UNLOCK(&acf_root);
2809 ast_verb(2, "Registered custom function '%s'\n", term_color(tmps, acf->name, COLOR_BRCYAN, 0, sizeof(tmps)));
2811 return 0;
2814 /*! \brief return a pointer to the arguments of the function,
2815 * and terminates the function name with '\\0'
2817 static char *func_args(char *function)
2819 char *args = strchr(function, '(');
2821 if (!args)
2822 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
2823 else {
2824 char *p;
2825 *args++ = '\0';
2826 if ((p = strrchr(args, ')')) )
2827 *p = '\0';
2828 else
2829 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
2831 return args;
2834 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
2836 char *copy = ast_strdupa(function);
2837 char *args = func_args(copy);
2838 struct ast_custom_function *acfptr = ast_custom_function_find(copy);
2840 if (acfptr == NULL)
2841 ast_log(LOG_ERROR, "Function %s not registered\n", copy);
2842 else if (!acfptr->read)
2843 ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
2844 else {
2845 int res;
2846 struct ast_module_user *u = NULL;
2847 if (acfptr->mod)
2848 u = __ast_module_user_add(acfptr->mod, chan);
2849 res = acfptr->read(chan, copy, args, workspace, len);
2850 if (acfptr->mod && u)
2851 __ast_module_user_remove(acfptr->mod, u);
2852 return res;
2854 return -1;
2857 int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
2859 char *copy = ast_strdupa(function);
2860 char *args = func_args(copy);
2861 struct ast_custom_function *acfptr = ast_custom_function_find(copy);
2863 if (acfptr == NULL)
2864 ast_log(LOG_ERROR, "Function %s not registered\n", copy);
2865 else if (!acfptr->write)
2866 ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
2867 else {
2868 int res;
2869 struct ast_module_user *u = NULL;
2870 if (acfptr->mod)
2871 u = __ast_module_user_add(acfptr->mod, chan);
2872 res = acfptr->write(chan, copy, args, value);
2873 if (acfptr->mod && u)
2874 __ast_module_user_remove(acfptr->mod, u);
2875 return res;
2878 return -1;
2881 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
2883 /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
2884 char *cp4;
2885 const char *tmp, *whereweare;
2886 int length, offset, offset2, isfunction;
2887 char *workspace = NULL;
2888 char *ltmp = NULL, *var = NULL;
2889 char *nextvar, *nextexp, *nextthing;
2890 char *vars, *vare;
2891 int pos, brackets, needsub, len;
2893 *cp2 = 0; /* just in case nothing ends up there */
2894 whereweare=tmp=cp1;
2895 while (!ast_strlen_zero(whereweare) && count) {
2896 /* Assume we're copying the whole remaining string */
2897 pos = strlen(whereweare);
2898 nextvar = NULL;
2899 nextexp = NULL;
2900 nextthing = strchr(whereweare, '$');
2901 if (nextthing) {
2902 switch (nextthing[1]) {
2903 case '{':
2904 nextvar = nextthing;
2905 pos = nextvar - whereweare;
2906 break;
2907 case '[':
2908 nextexp = nextthing;
2909 pos = nextexp - whereweare;
2910 break;
2911 default:
2912 pos = 1;
2916 if (pos) {
2917 /* Can't copy more than 'count' bytes */
2918 if (pos > count)
2919 pos = count;
2921 /* Copy that many bytes */
2922 memcpy(cp2, whereweare, pos);
2924 count -= pos;
2925 cp2 += pos;
2926 whereweare += pos;
2927 *cp2 = 0;
2930 if (nextvar) {
2931 /* We have a variable. Find the start and end, and determine
2932 if we are going to have to recursively call ourselves on the
2933 contents */
2934 vars = vare = nextvar + 2;
2935 brackets = 1;
2936 needsub = 0;
2938 /* Find the end of it */
2939 while (brackets && *vare) {
2940 if ((vare[0] == '$') && (vare[1] == '{')) {
2941 needsub++;
2942 } else if (vare[0] == '{') {
2943 brackets++;
2944 } else if (vare[0] == '}') {
2945 brackets--;
2946 } else if ((vare[0] == '$') && (vare[1] == '['))
2947 needsub++;
2948 vare++;
2950 if (brackets)
2951 ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
2952 len = vare - vars - 1;
2954 /* Skip totally over variable string */
2955 whereweare += (len + 3);
2957 if (!var)
2958 var = alloca(VAR_BUF_SIZE);
2960 /* Store variable name (and truncate) */
2961 ast_copy_string(var, vars, len + 1);
2963 /* Substitute if necessary */
2964 if (needsub) {
2965 if (!ltmp)
2966 ltmp = alloca(VAR_BUF_SIZE);
2968 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
2969 vars = ltmp;
2970 } else {
2971 vars = var;
2974 if (!workspace)
2975 workspace = alloca(VAR_BUF_SIZE);
2977 workspace[0] = '\0';
2979 parse_variable_name(vars, &offset, &offset2, &isfunction);
2980 if (isfunction) {
2981 /* Evaluate function */
2982 if (c || !headp)
2983 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
2984 else {
2985 struct varshead old;
2986 struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
2987 if (bogus) {
2988 memcpy(&old, &bogus->varshead, sizeof(old));
2989 memcpy(&bogus->varshead, headp, sizeof(bogus->varshead));
2990 cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
2991 /* Don't deallocate the varshead that was passed in */
2992 memcpy(&bogus->varshead, &old, sizeof(bogus->varshead));
2993 ast_channel_free(bogus);
2994 } else
2995 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
2997 ast_debug(2, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
2998 } else {
2999 /* Retrieve variable value */
3000 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
3002 if (cp4) {
3003 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
3005 length = strlen(cp4);
3006 if (length > count)
3007 length = count;
3008 memcpy(cp2, cp4, length);
3009 count -= length;
3010 cp2 += length;
3011 *cp2 = 0;
3013 } else if (nextexp) {
3014 /* We have an expression. Find the start and end, and determine
3015 if we are going to have to recursively call ourselves on the
3016 contents */
3017 vars = vare = nextexp + 2;
3018 brackets = 1;
3019 needsub = 0;
3021 /* Find the end of it */
3022 while (brackets && *vare) {
3023 if ((vare[0] == '$') && (vare[1] == '[')) {
3024 needsub++;
3025 brackets++;
3026 vare++;
3027 } else if (vare[0] == '[') {
3028 brackets++;
3029 } else if (vare[0] == ']') {
3030 brackets--;
3031 } else if ((vare[0] == '$') && (vare[1] == '{')) {
3032 needsub++;
3033 vare++;
3035 vare++;
3037 if (brackets)
3038 ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
3039 len = vare - vars - 1;
3041 /* Skip totally over expression */
3042 whereweare += (len + 3);
3044 if (!var)
3045 var = alloca(VAR_BUF_SIZE);
3047 /* Store variable name (and truncate) */
3048 ast_copy_string(var, vars, len + 1);
3050 /* Substitute if necessary */
3051 if (needsub) {
3052 if (!ltmp)
3053 ltmp = alloca(VAR_BUF_SIZE);
3055 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
3056 vars = ltmp;
3057 } else {
3058 vars = var;
3061 length = ast_expr(vars, cp2, count, c);
3063 if (length) {
3064 ast_debug(1, "Expression result is '%s'\n", cp2);
3065 count -= length;
3066 cp2 += length;
3067 *cp2 = 0;
3073 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
3075 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
3078 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
3080 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
3083 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
3085 const char *tmp;
3087 /* Nothing more to do */
3088 if (!e->data)
3089 return;
3091 /* No variables or expressions in e->data, so why scan it? */
3092 if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
3093 ast_copy_string(passdata, e->data, datalen);
3094 return;
3097 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
3100 /*!
3101 * \brief The return value depends on the action:
3103 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
3104 * and return 0 on failure, -1 on match;
3105 * E_FINDLABEL maps the label to a priority, and returns
3106 * the priority on success, ... XXX
3107 * E_SPAWN, spawn an application,
3109 * \retval 0 on success.
3110 * \retval -1 on failure.
3112 * \note The channel is auto-serviced in this function, because doing an extension
3113 * match may block for a long time. For example, if the lookup has to use a network
3114 * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
3115 * auto-service code will queue up any important signalling frames to be processed
3116 * after this is done.
3118 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
3119 const char *context, const char *exten, int priority,
3120 const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
3122 struct ast_exten *e;
3123 struct ast_app *app;
3124 int res;
3125 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
3126 char passdata[EXT_DATA_SIZE];
3128 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
3130 ast_rdlock_contexts();
3131 if (found)
3132 *found = 0;
3134 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
3135 if (e) {
3136 if (found)
3137 *found = 1;
3138 if (matching_action) {
3139 ast_unlock_contexts();
3140 return -1; /* success, we found it */
3141 } else if (action == E_FINDLABEL) { /* map the label to a priority */
3142 res = e->priority;
3143 ast_unlock_contexts();
3144 return res; /* the priority we were looking for */
3145 } else { /* spawn */
3146 if (!e->cached_app)
3147 e->cached_app = pbx_findapp(e->app);
3148 app = e->cached_app;
3149 ast_unlock_contexts();
3150 if (!app) {
3151 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
3152 return -1;
3154 if (c->context != context)
3155 ast_copy_string(c->context, context, sizeof(c->context));
3156 if (c->exten != exten)
3157 ast_copy_string(c->exten, exten, sizeof(c->exten));
3158 c->priority = priority;
3159 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
3160 #ifdef CHANNEL_TRACE
3161 ast_channel_trace_update(c);
3162 #endif
3163 ast_debug(1, "Launching '%s'\n", app->name);
3164 if (VERBOSITY_ATLEAST(3)) {
3165 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
3166 ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
3167 exten, context, priority,
3168 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
3169 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
3170 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
3171 "in new stack");
3173 manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
3174 "Channel: %s\r\n"
3175 "Context: %s\r\n"
3176 "Extension: %s\r\n"
3177 "Priority: %d\r\n"
3178 "Application: %s\r\n"
3179 "AppData: %s\r\n"
3180 "Uniqueid: %s\r\n",
3181 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
3182 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
3184 } else if (q.swo) { /* not found here, but in another switch */
3185 ast_unlock_contexts();
3186 if (matching_action) {
3187 return -1;
3188 } else {
3189 if (!q.swo->exec) {
3190 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
3191 res = -1;
3193 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
3195 } else { /* not found anywhere, see what happened */
3196 ast_unlock_contexts();
3197 switch (q.status) {
3198 case STATUS_NO_CONTEXT:
3199 if (!matching_action && !combined_find_spawn)
3200 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
3201 break;
3202 case STATUS_NO_EXTENSION:
3203 if (!matching_action && !combined_find_spawn)
3204 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
3205 break;
3206 case STATUS_NO_PRIORITY:
3207 if (!matching_action && !combined_find_spawn)
3208 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
3209 break;
3210 case STATUS_NO_LABEL:
3211 if (context && !combined_find_spawn)
3212 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
3213 break;
3214 default:
3215 ast_debug(1, "Shouldn't happen!\n");
3218 return (matching_action) ? 0 : -1;
3222 /*! \brief Find hint for given extension in context */
3223 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
3225 struct ast_exten *e;
3226 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
3228 ast_rdlock_contexts();
3229 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
3230 ast_unlock_contexts();
3232 return e;
3235 /*! \brief Check state of extension by using hints */
3236 static int ast_extension_state2(struct ast_exten *e)
3238 char hint[AST_MAX_EXTENSION] = "";
3239 char *cur, *rest;
3240 struct ast_devstate_aggregate agg;
3241 enum ast_device_state state;
3243 if (!e)
3244 return -1;
3246 ast_devstate_aggregate_init(&agg);
3248 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
3250 rest = hint; /* One or more devices separated with a & character */
3252 while ( (cur = strsep(&rest, "&")) )
3253 ast_devstate_aggregate_add(&agg, ast_device_state(cur));
3255 state = ast_devstate_aggregate_result(&agg);
3257 switch (state) {
3258 case AST_DEVICE_ONHOLD:
3259 return AST_EXTENSION_ONHOLD;
3260 case AST_DEVICE_BUSY:
3261 return AST_EXTENSION_BUSY;
3262 case AST_DEVICE_UNAVAILABLE:
3263 return AST_EXTENSION_UNAVAILABLE;
3264 case AST_DEVICE_RINGINUSE:
3265 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
3266 case AST_DEVICE_RINGING:
3267 return AST_EXTENSION_RINGING;
3268 case AST_DEVICE_INUSE:
3269 return AST_EXTENSION_INUSE;
3270 case AST_DEVICE_UNKNOWN:
3271 case AST_DEVICE_INVALID:
3272 case AST_DEVICE_NOT_INUSE:
3273 return AST_EXTENSION_NOT_INUSE;
3276 return AST_EXTENSION_NOT_INUSE;
3279 /*! \brief Return extension_state as string */
3280 const char *ast_extension_state2str(int extension_state)
3282 int i;
3284 for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
3285 if (extension_states[i].extension_state == extension_state)
3286 return extension_states[i].text;
3288 return "Unknown";
3291 /*! \brief Check extension state for an extension by using hint */
3292 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
3294 struct ast_exten *e;
3296 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
3297 if (!e)
3298 return -1; /* No hint, return -1 */
3300 return ast_extension_state2(e); /* Check all devices in the hint */
3303 static int handle_statechange(void *datap)
3305 struct ast_hint *hint;
3306 struct statechange *sc = datap;
3307 AST_RWLIST_RDLOCK(&hints);
3309 AST_RWLIST_TRAVERSE(&hints, hint, list) {
3310 struct ast_state_cb *cblist;
3311 char buf[AST_MAX_EXTENSION];
3312 char *parse = buf;
3313 char *cur;
3314 int state;
3316 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
3317 while ( (cur = strsep(&parse, "&")) ) {
3318 if (!strcasecmp(cur, sc->dev))
3319 break;
3321 if (!cur)
3322 continue;
3324 /* Get device state for this hint */
3325 state = ast_extension_state2(hint->exten);
3327 if ((state == -1) || (state == hint->laststate))
3328 continue;
3330 /* Device state changed since last check - notify the watchers */
3332 /* For general callbacks */
3333 AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
3334 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
3337 /* For extension callbacks */
3338 AST_LIST_TRAVERSE(&hint->callbacks, cblist, entry) {
3339 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
3342 hint->laststate = state; /* record we saw the change */
3344 AST_RWLIST_UNLOCK(&hints);
3345 ast_free(sc);
3346 return 0;
3349 /*! \brief Add watcher for extension states */
3350 int ast_extension_state_add(const char *context, const char *exten,
3351 ast_state_cb_type callback, void *data)
3353 struct ast_hint *hint;
3354 struct ast_state_cb *cblist;
3355 struct ast_exten *e;
3357 /* If there's no context and extension: add callback to statecbs list */
3358 if (!context && !exten) {
3359 AST_RWLIST_WRLOCK(&hints);
3361 AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
3362 if (cblist->callback == callback) {
3363 cblist->data = data;
3364 AST_RWLIST_UNLOCK(&hints);
3365 return 0;
3369 /* Now insert the callback */
3370 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
3371 AST_RWLIST_UNLOCK(&hints);
3372 return -1;
3374 cblist->id = 0;
3375 cblist->callback = callback;
3376 cblist->data = data;
3378 AST_LIST_INSERT_HEAD(&statecbs, cblist, entry);
3380 AST_RWLIST_UNLOCK(&hints);
3382 return 0;
3385 if (!context || !exten)
3386 return -1;
3388 /* This callback type is for only one hint, so get the hint */
3389 e = ast_hint_extension(NULL, context, exten);
3390 if (!e) {
3391 return -1;
3394 /* If this is a pattern, dynamically create a new extension for this
3395 * particular match. Note that this will only happen once for each
3396 * individual extension, because the pattern will no longer match first.
3398 if (e->exten[0] == '_') {
3399 ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
3400 e->cidmatch, e->app, ast_strdup(e->data), ast_free_ptr,
3401 e->registrar);
3402 e = ast_hint_extension(NULL, context, exten);
3403 if (!e || e->exten[0] == '_') {
3404 return -1;
3408 /* Find the hint in the list of hints */
3409 AST_RWLIST_WRLOCK(&hints);
3411 AST_RWLIST_TRAVERSE(&hints, hint, list) {
3412 if (hint->exten == e)
3413 break;
3416 if (!hint) {
3417 /* We have no hint, sorry */
3418 AST_RWLIST_UNLOCK(&hints);
3419 return -1;
3422 /* Now insert the callback in the callback list */
3423 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
3424 AST_RWLIST_UNLOCK(&hints);
3425 return -1;
3428 cblist->id = stateid++; /* Unique ID for this callback */
3429 cblist->callback = callback; /* Pointer to callback routine */
3430 cblist->data = data; /* Data for the callback */
3432 AST_LIST_INSERT_HEAD(&hint->callbacks, cblist, entry);
3434 AST_RWLIST_UNLOCK(&hints);
3436 return cblist->id;
3439 /*! \brief Remove a watcher from the callback list */
3440 int ast_extension_state_del(int id, ast_state_cb_type callback)
3442 struct ast_state_cb *p_cur = NULL;
3443 int ret = -1;
3445 if (!id && !callback)
3446 return -1;
3448 AST_RWLIST_WRLOCK(&hints);
3450 if (!id) { /* id == 0 is a callback without extension */
3451 AST_LIST_TRAVERSE_SAFE_BEGIN(&statecbs, p_cur, entry) {
3452 if (p_cur->callback == callback) {
3453 AST_LIST_REMOVE_CURRENT(entry);
3454 break;
3457 AST_LIST_TRAVERSE_SAFE_END
3458 } else { /* callback with extension, find the callback based on ID */
3459 struct ast_hint *hint;
3460 AST_RWLIST_TRAVERSE(&hints, hint, list) {
3461 AST_LIST_TRAVERSE_SAFE_BEGIN(&hint->callbacks, p_cur, entry) {
3462 if (p_cur->id == id) {
3463 AST_LIST_REMOVE_CURRENT(entry);
3464 break;
3467 AST_LIST_TRAVERSE_SAFE_END
3469 if (p_cur)
3470 break;
3474 if (p_cur) {
3475 ast_free(p_cur);
3478 AST_RWLIST_UNLOCK(&hints);
3480 return ret;
3483 /*! \brief Add hint to hint list, check initial extension state */
3484 static int ast_add_hint(struct ast_exten *e)
3486 struct ast_hint *hint;
3488 if (!e)
3489 return -1;
3491 AST_RWLIST_WRLOCK(&hints);
3493 /* Search if hint exists, do nothing */
3494 AST_RWLIST_TRAVERSE(&hints, hint, list) {
3495 if (hint->exten == e) {
3496 AST_RWLIST_UNLOCK(&hints);
3497 ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
3498 return -1;
3502 ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
3504 if (!(hint = ast_calloc(1, sizeof(*hint)))) {
3505 AST_RWLIST_UNLOCK(&hints);
3506 return -1;
3508 /* Initialize and insert new item at the top */
3509 hint->exten = e;
3510 hint->laststate = ast_extension_state2(e);
3511 AST_RWLIST_INSERT_HEAD(&hints, hint, list);
3513 AST_RWLIST_UNLOCK(&hints);
3514 return 0;
3517 /*! \brief Change hint for an extension */
3518 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
3520 struct ast_hint *hint;
3521 int res = -1;
3523 AST_RWLIST_WRLOCK(&hints);
3524 AST_RWLIST_TRAVERSE(&hints, hint, list) {
3525 if (hint->exten == oe) {
3526 hint->exten = ne;
3527 res = 0;
3528 break;
3531 AST_RWLIST_UNLOCK(&hints);
3533 return res;
3536 /*! \brief Remove hint from extension */
3537 static int ast_remove_hint(struct ast_exten *e)
3539 /* Cleanup the Notifys if hint is removed */
3540 struct ast_hint *hint;
3541 struct ast_state_cb *cblist;
3542 int res = -1;
3544 if (!e)
3545 return -1;
3547 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
3548 if (hint->exten != e)
3549 continue;
3551 while ((cblist = AST_LIST_REMOVE_HEAD(&hint->callbacks, entry))) {
3552 /* Notify with -1 and remove all callbacks */
3553 cblist->callback(hint->exten->parent->name, hint->exten->exten,
3554 AST_EXTENSION_DEACTIVATED, cblist->data);
3555 ast_free(cblist);
3558 AST_RWLIST_REMOVE_CURRENT(list);
3559 ast_free(hint);
3561 res = 0;
3563 break;
3565 AST_RWLIST_TRAVERSE_SAFE_END;
3567 return res;
3571 /*! \brief Get hint for channel */
3572 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
3574 struct ast_exten *e = ast_hint_extension(c, context, exten);
3576 if (e) {
3577 if (hint)
3578 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
3579 if (name) {
3580 const char *tmp = ast_get_extension_app_data(e);
3581 if (tmp)
3582 ast_copy_string(name, tmp, namesize);
3584 return -1;
3586 return 0;
3589 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
3591 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
3594 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
3596 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
3599 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
3601 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
3604 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
3606 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
3609 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
3611 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
3614 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
3616 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
3619 /*! helper function to set extension and priority */
3620 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
3622 ast_channel_lock(c);
3623 ast_copy_string(c->exten, exten, sizeof(c->exten));
3624 c->priority = pri;
3625 ast_channel_unlock(c);
3629 * \brief collect digits from the channel into the buffer.
3630 * \retval 0 on timeout or done.
3631 * \retval -1 on error.
3633 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
3635 int digit;
3637 buf[pos] = '\0'; /* make sure it is properly terminated */
3638 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
3639 /* As long as we're willing to wait, and as long as it's not defined,
3640 keep reading digits until we can't possibly get a right answer anymore. */
3641 digit = ast_waitfordigit(c, waittime * 1000);
3642 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
3643 c->_softhangup = 0;
3644 } else {
3645 if (!digit) /* No entry */
3646 break;
3647 if (digit < 0) /* Error, maybe a hangup */
3648 return -1;
3649 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
3650 buf[pos++] = digit;
3651 buf[pos] = '\0';
3653 waittime = c->pbx->dtimeoutms;
3656 return 0;
3659 static int __ast_pbx_run(struct ast_channel *c)
3661 int found = 0; /* set if we find at least one match */
3662 int res = 0;
3663 int autoloopflag;
3664 int error = 0; /* set an error conditions */
3666 /* A little initial setup here */
3667 if (c->pbx) {
3668 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
3669 /* XXX and now what ? */
3670 ast_free(c->pbx);
3672 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
3673 return -1;
3674 /* Set reasonable defaults */
3675 c->pbx->rtimeoutms = 10000;
3676 c->pbx->dtimeoutms = 5000;
3678 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
3679 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
3681 /* Start by trying whatever the channel is set to */
3682 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
3683 /* If not successful fall back to 's' */
3684 ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
3685 /* XXX the original code used the existing priority in the call to
3686 * ast_exists_extension(), and reset it to 1 afterwards.
3687 * I believe the correct thing is to set it to 1 immediately.
3689 set_ext_pri(c, "s", 1);
3690 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
3691 /* JK02: And finally back to default if everything else failed */
3692 ast_verb(2, "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
3693 ast_copy_string(c->context, "default", sizeof(c->context));
3696 for (;;) {
3697 char dst_exten[256]; /* buffer to accumulate digits */
3698 int pos = 0; /* XXX should check bounds */
3699 int digit = 0;
3700 int invalid = 0;
3701 int timeout = 0;
3703 /* loop on priorities in this context/exten */
3704 while ( !(res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found,1))) {
3705 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) {
3706 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
3707 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
3708 memset(&c->whentohangup, 0, sizeof(c->whentohangup));
3709 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
3710 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
3711 pbx_builtin_raise_exception(c, "ABSOLUTETIMEOUT");
3712 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
3713 memset(&c->whentohangup, 0, sizeof(c->whentohangup));
3714 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
3715 } else if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
3716 c->_softhangup = 0;
3717 continue;
3718 } else if (ast_check_hangup(c)) {
3719 ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
3720 c->exten, c->priority);
3721 error = 1;
3722 break;
3724 c->priority++;
3725 } /* end while - from here on we can use 'break' to go out */
3726 if (found && res) {
3727 /* Something bad happened, or a hangup has been requested. */
3728 if (strchr("0123456789ABCDEF*#", res)) {
3729 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
3730 pos = 0;
3731 dst_exten[pos++] = digit = res;
3732 dst_exten[pos] = '\0';
3733 } else if (res == AST_PBX_KEEPALIVE) {
3734 ast_debug(1, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
3735 ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
3736 error = 1;
3737 } else if (res == AST_PBX_INCOMPLETE) {
3738 ast_debug(1, "Spawn extension (%s,%s,%d) exited INCOMPLETE on '%s'\n", c->context, c->exten, c->priority, c->name);
3739 ast_verb(2, "Spawn extension (%s, %s, %d) exited INCOMPLETE on '%s'\n", c->context, c->exten, c->priority, c->name);
3741 /* Don't cycle on incomplete - this will happen if the only extension that matches is our "incomplete" extension */
3742 if (!ast_matchmore_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
3743 invalid = 1;
3744 } else {
3745 ast_copy_string(dst_exten, c->exten, sizeof(dst_exten));
3746 digit = 1;
3747 pos = strlen(dst_exten);
3749 } else {
3750 ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
3751 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
3753 if ((res == AST_PBX_ERROR) && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
3754 /* if we are already on the 'e' exten, don't jump to it again */
3755 if (!strcmp(c->exten, "e")) {
3756 ast_verb(2, "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", c->context, c->exten, c->priority, c->name);
3757 error = 1;
3758 } else {
3759 pbx_builtin_raise_exception(c, "ERROR");
3760 continue;
3764 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
3765 c->_softhangup = 0;
3766 continue;
3767 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) {
3768 set_ext_pri(c, "T", 1);
3769 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
3770 memset(&c->whentohangup, 0, sizeof(c->whentohangup));
3771 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
3772 continue;
3773 } else {
3774 if (c->cdr)
3775 ast_cdr_update(c);
3776 error = 1;
3777 break;
3781 if (error)
3782 break;
3784 /*!\note
3785 * We get here on a failure of some kind: non-existing extension or
3786 * hangup. We have options, here. We can either catch the failure
3787 * and continue, or we can drop out entirely. */
3789 if (invalid || !ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
3790 /*!\note
3791 * If there is no match at priority 1, it is not a valid extension anymore.
3792 * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
3793 * neither exist.
3795 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
3796 ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
3797 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
3798 set_ext_pri(c, "i", 1);
3799 } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
3800 pbx_builtin_raise_exception(c, "INVALID");
3801 } else {
3802 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
3803 c->name, c->exten, c->context);
3804 error = 1; /* we know what to do with it */
3805 break;
3807 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
3808 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
3809 c->_softhangup = 0;
3810 } else { /* keypress received, get more digits for a full extension */
3811 int waittime = 0;
3812 if (digit)
3813 waittime = c->pbx->dtimeoutms;
3814 else if (!autofallthrough)
3815 waittime = c->pbx->rtimeoutms;
3816 if (!waittime) {
3817 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
3818 if (!status)
3819 status = "UNKNOWN";
3820 ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
3821 if (!strcasecmp(status, "CONGESTION"))
3822 res = pbx_builtin_congestion(c, "10");
3823 else if (!strcasecmp(status, "CHANUNAVAIL"))
3824 res = pbx_builtin_congestion(c, "10");
3825 else if (!strcasecmp(status, "BUSY"))
3826 res = pbx_builtin_busy(c, "10");
3827 error = 1; /* XXX disable message */
3828 break; /* exit from the 'for' loop */
3831 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
3832 break;
3833 if (res == AST_PBX_INCOMPLETE && ast_strlen_zero(&dst_exten[pos]))
3834 timeout = 1;
3835 if (!timeout && ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
3836 set_ext_pri(c, dst_exten, 1);
3837 else {
3838 /* No such extension */
3839 if (!timeout && !ast_strlen_zero(dst_exten)) {
3840 /* An invalid extension */
3841 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
3842 ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
3843 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
3844 set_ext_pri(c, "i", 1);
3845 } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
3846 pbx_builtin_raise_exception(c, "INVALID");
3847 } else {
3848 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
3849 found = 1; /* XXX disable message */
3850 break;
3852 } else {
3853 /* A simple timeout */
3854 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
3855 ast_verb(3, "Timeout on %s\n", c->name);
3856 set_ext_pri(c, "t", 1);
3857 } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
3858 pbx_builtin_raise_exception(c, "RESPONSETIMEOUT");
3859 } else {
3860 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
3861 found = 1; /* XXX disable message */
3862 break;
3866 if (c->cdr) {
3867 ast_verb(2, "CDR updated on %s\n",c->name);
3868 ast_cdr_update(c);
3872 if (!found && !error)
3873 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
3874 if (res != AST_PBX_KEEPALIVE)
3875 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
3876 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
3877 set_ext_pri(c, "h", 1);
3878 while ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found, 1)) == 0) {
3879 c->priority++;
3881 if (found && res) {
3882 /* Something bad happened, or a hangup has been requested. */
3883 ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
3884 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
3887 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
3889 pbx_destroy(c->pbx);
3890 c->pbx = NULL;
3891 if (res != AST_PBX_KEEPALIVE)
3892 ast_hangup(c);
3893 return 0;
3896 /*!
3897 * \brief Increase call count for channel
3898 * \retval 0 on success
3899 * \retval non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached
3901 static int increase_call_count(const struct ast_channel *c)
3903 int failed = 0;
3904 double curloadavg;
3905 #if defined(HAVE_SYSINFO)
3906 long curfreemem;
3907 struct sysinfo sys_info;
3908 #endif
3910 ast_mutex_lock(&maxcalllock);
3911 if (option_maxcalls) {
3912 if (countcalls >= option_maxcalls) {
3913 ast_log(LOG_WARNING, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
3914 failed = -1;
3917 if (option_maxload) {
3918 getloadavg(&curloadavg, 1);
3919 if (curloadavg >= option_maxload) {
3920 ast_log(LOG_WARNING, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
3921 failed = -1;
3924 #if defined(HAVE_SYSINFO)
3925 if (option_minmemfree) {
3926 if (!sysinfo(&sys_info)) {
3927 /* make sure that the free system memory is above the configured low watermark
3928 * convert the amount of freeram from mem_units to MB */
3929 curfreemem = sys_info.freeram / sys_info.mem_unit;
3930 curfreemem /= 1024*1024;
3931 if (curfreemem < option_minmemfree) {
3932 ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
3933 failed = -1;
3937 #endif
3939 if (!failed) {
3940 countcalls++;
3941 totalcalls++;
3943 ast_mutex_unlock(&maxcalllock);
3945 return failed;
3948 static void decrease_call_count(void)
3950 ast_mutex_lock(&maxcalllock);
3951 if (countcalls > 0)
3952 countcalls--;
3953 ast_mutex_unlock(&maxcalllock);
3956 static void destroy_exten(struct ast_exten *e)
3958 if (e->priority == PRIORITY_HINT)
3959 ast_remove_hint(e);
3961 if (e->peer_table)
3962 ast_hashtab_destroy(e->peer_table,0);
3963 if (e->peer_label_table)
3964 ast_hashtab_destroy(e->peer_label_table, 0);
3965 if (e->datad)
3966 e->datad(e->data);
3967 ast_free(e);
3970 static void *pbx_thread(void *data)
3972 /* Oh joyeous kernel, we're a new thread, with nothing to do but
3973 answer this channel and get it going.
3975 /* NOTE:
3976 The launcher of this function _MUST_ increment 'countcalls'
3977 before invoking the function; it will be decremented when the
3978 PBX has finished running on the channel
3980 struct ast_channel *c = data;
3982 __ast_pbx_run(c);
3983 decrease_call_count();
3985 pthread_exit(NULL);
3987 return NULL;
3990 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
3992 pthread_t t;
3994 if (!c) {
3995 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
3996 return AST_PBX_FAILED;
3999 if (increase_call_count(c))
4000 return AST_PBX_CALL_LIMIT;
4002 /* Start a new thread, and get something handling this channel. */
4003 if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
4004 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
4005 return AST_PBX_FAILED;
4008 return AST_PBX_SUCCESS;
4011 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
4013 enum ast_pbx_result res = AST_PBX_SUCCESS;
4015 if (increase_call_count(c))
4016 return AST_PBX_CALL_LIMIT;
4018 res = __ast_pbx_run(c);
4019 decrease_call_count();
4021 return res;
4024 int ast_active_calls(void)
4026 return countcalls;
4029 int ast_processed_calls(void)
4031 return totalcalls;
4034 int pbx_set_autofallthrough(int newval)
4036 int oldval = autofallthrough;
4037 autofallthrough = newval;
4038 return oldval;
4041 int pbx_set_extenpatternmatchnew(int newval)
4043 int oldval = extenpatternmatchnew;
4044 extenpatternmatchnew = newval;
4045 return oldval;
4048 void pbx_set_overrideswitch(const char *newval)
4050 if (overrideswitch) {
4051 ast_free(overrideswitch);
4053 if (!ast_strlen_zero(newval)) {
4054 overrideswitch = ast_strdup(newval);
4055 } else {
4056 overrideswitch = NULL;
4061 * \brief lookup for a context with a given name,
4062 * \retval with conlock held if found.
4063 * \retval NULL if not found.
4065 static struct ast_context *find_context_locked(const char *context)
4067 struct ast_context *c = NULL;
4068 struct fake_context item;
4070 ast_copy_string(item.name, context, sizeof(item.name));
4072 ast_rdlock_contexts();
4073 c = ast_hashtab_lookup(contexts_table,&item);
4075 #ifdef NOTNOW
4077 while ( (c = ast_walk_contexts(c)) ) {
4078 if (!strcmp(ast_get_context_name(c), context))
4079 return c;
4081 #endif
4082 if (!c)
4083 ast_unlock_contexts();
4085 return c;
4089 * \brief Remove included contexts.
4090 * This function locks contexts list by &conlist, search for the right context
4091 * structure, leave context list locked and call ast_context_remove_include2
4092 * which removes include, unlock contexts list and return ...
4094 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
4096 int ret = -1;
4097 struct ast_context *c = find_context_locked(context);
4099 if (c) {
4100 /* found, remove include from this context ... */
4101 ret = ast_context_remove_include2(c, include, registrar);
4102 ast_unlock_contexts();
4104 return ret;
4108 * \brief Locks context, remove included contexts, unlocks context.
4109 * When we call this function, &conlock lock must be locked, because when
4110 * we giving *con argument, some process can remove/change this context
4111 * and after that there can be segfault.
4113 * \retval 0 on success.
4114 * \retval -1 on failure.
4116 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
4118 struct ast_include *i, *pi = NULL;
4119 int ret = -1;
4121 ast_wrlock_context(con);
4123 /* find our include */
4124 for (i = con->includes; i; pi = i, i = i->next) {
4125 if (!strcmp(i->name, include) &&
4126 (!registrar || !strcmp(i->registrar, registrar))) {
4127 /* remove from list */
4128 ast_verb(3, "Removing inclusion of context '%s' in context '%s; registrar=%s'\n", include, ast_get_context_name(con), registrar);
4129 if (pi)
4130 pi->next = i->next;
4131 else
4132 con->includes = i->next;
4133 /* free include and return */
4134 ast_free(i);
4135 ret = 0;
4136 break;
4140 ast_unlock_context(con);
4142 return ret;
4146 * \note This function locks contexts list by &conlist, search for the rigt context
4147 * structure, leave context list locked and call ast_context_remove_switch2
4148 * which removes switch, unlock contexts list and return ...
4150 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
4152 int ret = -1; /* default error return */
4153 struct ast_context *c = find_context_locked(context);
4155 if (c) {
4156 /* remove switch from this context ... */
4157 ret = ast_context_remove_switch2(c, sw, data, registrar);
4158 ast_unlock_contexts();
4160 return ret;
4164 * \brief This function locks given context, removes switch, unlock context and
4165 * return.
4166 * \note When we call this function, &conlock lock must be locked, because when
4167 * we giving *con argument, some process can remove/change this context
4168 * and after that there can be segfault.
4171 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
4173 struct ast_sw *i;
4174 int ret = -1;
4176 ast_wrlock_context(con);
4178 /* walk switches */
4179 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
4180 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
4181 (!registrar || !strcmp(i->registrar, registrar))) {
4182 /* found, remove from list */
4183 ast_verb(3, "Removing switch '%s' from context '%s; registrar=%s'\n", sw, ast_get_context_name(con), registrar);
4184 AST_LIST_REMOVE_CURRENT(list);
4185 ast_free(i); /* free switch and return */
4186 ret = 0;
4187 break;
4190 AST_LIST_TRAVERSE_SAFE_END;
4192 ast_unlock_context(con);
4194 return ret;
4198 * \note This functions lock contexts list, search for the right context,
4199 * call ast_context_remove_extension2, unlock contexts list and return.
4200 * In this function we are using
4202 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
4204 return ast_context_remove_extension_callerid(context, extension, priority, NULL, 0, registrar);
4207 int ast_context_remove_extension_callerid(const char *context, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar)
4209 int ret = -1; /* default error return */
4210 struct ast_context *c = find_context_locked(context);
4212 if (c) { /* ... remove extension ... */
4213 ret = ast_context_remove_extension_callerid2(c, extension, priority, callerid, matchcallerid, registrar, 1);
4214 ast_unlock_contexts();
4216 return ret;
4220 * \brief This functionc locks given context, search for the right extension and
4221 * fires out all peer in this extensions with given priority. If priority
4222 * is set to 0, all peers are removed. After that, unlock context and
4223 * return.
4224 * \note When do you want to call this function, make sure that &conlock is locked,
4225 * because some process can handle with your *con context before you lock
4226 * it.
4229 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar, int already_locked)
4231 return ast_context_remove_extension_callerid2(con, extension, priority, NULL, 0, registrar, already_locked);
4234 int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar, int already_locked)
4236 struct ast_exten *exten, *prev_exten = NULL;
4237 struct ast_exten *peer;
4238 struct ast_exten ex, *exten2, *exten3;
4239 char dummy_name[1024];
4240 struct ast_exten *previous_peer = NULL;
4241 struct ast_exten *next_peer = NULL;
4242 int found = 0;
4244 if (!already_locked)
4245 ast_wrlock_context(con);
4247 /* Handle this is in the new world */
4249 /* FIXME For backwards compatibility, if callerid==NULL, then remove ALL
4250 * peers, not just those matching the callerid. */
4251 #ifdef NEED_DEBUG
4252 ast_verb(3,"Removing %s/%s/%d%s%s from trees, registrar=%s\n", con->name, extension, priority, matchcallerid ? "/" : "", matchcallerid ? callerid : "", registrar);
4253 #endif
4254 #ifdef CONTEXT_DEBUG
4255 check_contexts(__FILE__, __LINE__);
4256 #endif
4257 /* find this particular extension */
4258 ex.exten = dummy_name;
4259 ex.matchcid = matchcallerid && !ast_strlen_zero(callerid); /* don't say match if there's no callerid */
4260 ex.cidmatch = callerid;
4261 ast_copy_string(dummy_name, extension, sizeof(dummy_name));
4262 exten = ast_hashtab_lookup(con->root_table, &ex);
4263 if (exten) {
4264 if (priority == 0) {
4265 exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
4266 if (!exten2)
4267 ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_table\n", extension, con->name);
4268 if (con->pattern_tree) {
4270 struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
4272 if (x->exten) { /* this test for safety purposes */
4273 x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
4274 x->exten = 0; /* get rid of what will become a bad pointer */
4275 } else {
4276 ast_log(LOG_WARNING,"Trying to delete an exten from a context, but the pattern tree node returned isn't a full extension\n");
4279 } else {
4280 ex.priority = priority;
4281 exten2 = ast_hashtab_lookup(exten->peer_table, &ex);
4282 if (exten2) {
4284 if (exten2->label) { /* if this exten has a label, remove that, too */
4285 exten3 = ast_hashtab_remove_this_object(exten->peer_label_table,exten2);
4286 if (!exten3)
4287 ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_table of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
4290 exten3 = ast_hashtab_remove_this_object(exten->peer_table, exten2);
4291 if (!exten3)
4292 ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_table of context %s, extension %s!\n", priority, con->name, exten2->exten);
4293 if (exten2 == exten && exten2->peer) {
4294 exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
4295 ast_hashtab_insert_immediate(con->root_table, exten2->peer);
4297 if (ast_hashtab_size(exten->peer_table) == 0) {
4298 /* well, if the last priority of an exten is to be removed,
4299 then, the extension is removed, too! */
4300 exten3 = ast_hashtab_remove_this_object(con->root_table, exten);
4301 if (!exten3)
4302 ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_table (%s) (priority %d)\n", exten->exten, con->name, priority);
4303 if (con->pattern_tree) {
4304 struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
4305 if (x->exten) { /* this test for safety purposes */
4306 x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
4307 x->exten = 0; /* get rid of what will become a bad pointer */
4311 } else {
4312 ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
4313 priority, exten->exten, con->name);
4316 } else {
4317 /* hmmm? this exten is not in this pattern tree? */
4318 ast_log(LOG_WARNING,"Cannot find extension %s in root_table in context %s\n",
4319 extension, con->name);
4321 #ifdef NEED_DEBUG
4322 if (con->pattern_tree) {
4323 ast_log(LOG_NOTICE,"match char tree after exten removal:\n");
4324 log_match_char_tree(con->pattern_tree, " ");
4326 #endif
4328 /* scan the extension list to find first matching extension-registrar */
4329 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
4330 if (!strcmp(exten->exten, extension) &&
4331 (!registrar || !strcmp(exten->registrar, registrar)) &&
4332 (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch))))
4333 break;
4335 if (!exten) {
4336 /* we can't find right extension */
4337 if (!already_locked)
4338 ast_unlock_context(con);
4339 return -1;
4342 /* scan the priority list to remove extension with exten->priority == priority */
4343 for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
4344 peer && !strcmp(peer->exten, extension) && (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(peer->cidmatch) && !strcmp(peer->cidmatch,callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(peer->cidmatch)));
4345 peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
4346 if ((priority == 0 || peer->priority == priority) &&
4347 (!callerid || !matchcallerid || (matchcallerid && !strcmp(peer->cidmatch, callerid))) &&
4348 (!registrar || !strcmp(peer->registrar, registrar) )) {
4349 found = 1;
4351 /* we are first priority extension? */
4352 if (!previous_peer) {
4354 * We are first in the priority chain, so must update the extension chain.
4355 * The next node is either the next priority or the next extension
4357 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
4358 if (peer->peer) {
4359 /* move the peer_table and peer_label_table down to the next peer, if
4360 it is there */
4361 peer->peer->peer_table = peer->peer_table;
4362 peer->peer->peer_label_table = peer->peer_label_table;
4363 peer->peer_table = NULL;
4364 peer->peer_label_table = NULL;
4366 if (!prev_exten) { /* change the root... */
4367 con->root = next_node;
4368 } else {
4369 prev_exten->next = next_node; /* unlink */
4371 if (peer->peer) { /* update the new head of the pri list */
4372 peer->peer->next = peer->next;
4374 } else { /* easy, we are not first priority in extension */
4375 previous_peer->peer = peer->peer;
4378 /* now, free whole priority extension */
4379 destroy_exten(peer);
4380 } else {
4381 previous_peer = peer;
4384 if (!already_locked)
4385 ast_unlock_context(con);
4386 return found ? 0 : -1;
4391 * \note This function locks contexts list by &conlist, searches for the right context
4392 * structure, and locks the macrolock mutex in that context.
4393 * macrolock is used to limit a macro to be executed by one call at a time.
4395 int ast_context_lockmacro(const char *context)
4397 struct ast_context *c = NULL;
4398 int ret = -1;
4399 struct fake_context item;
4401 ast_rdlock_contexts();
4403 strncpy(item.name,context,256);
4404 c = ast_hashtab_lookup(contexts_table,&item);
4405 if (c)
4406 ret = 0;
4409 #ifdef NOTNOW
4411 while ((c = ast_walk_contexts(c))) {
4412 if (!strcmp(ast_get_context_name(c), context)) {
4413 ret = 0;
4414 break;
4418 #endif
4419 ast_unlock_contexts();
4421 /* if we found context, lock macrolock */
4422 if (ret == 0)
4423 ret = ast_mutex_lock(&c->macrolock);
4425 return ret;
4429 * \note This function locks contexts list by &conlist, searches for the right context
4430 * structure, and unlocks the macrolock mutex in that context.
4431 * macrolock is used to limit a macro to be executed by one call at a time.
4433 int ast_context_unlockmacro(const char *context)
4435 struct ast_context *c = NULL;
4436 int ret = -1;
4437 struct fake_context item;
4439 ast_rdlock_contexts();
4441 strncpy(item.name, context, 256);
4442 c = ast_hashtab_lookup(contexts_table,&item);
4443 if (c)
4444 ret = 0;
4445 #ifdef NOTNOW
4447 while ((c = ast_walk_contexts(c))) {
4448 if (!strcmp(ast_get_context_name(c), context)) {
4449 ret = 0;
4450 break;
4454 #endif
4455 ast_unlock_contexts();
4457 /* if we found context, unlock macrolock */
4458 if (ret == 0)
4459 ret = ast_mutex_unlock(&c->macrolock);
4461 return ret;
4464 /*! \brief Dynamically register a new dial plan application */
4465 int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description, void *mod)
4467 struct ast_app *tmp, *cur = NULL;
4468 char tmps[80];
4469 int length, res;
4471 AST_RWLIST_WRLOCK(&apps);
4472 AST_RWLIST_TRAVERSE(&apps, tmp, list) {
4473 if (!(res = strcasecmp(app, tmp->name))) {
4474 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
4475 AST_RWLIST_UNLOCK(&apps);
4476 return -1;
4477 } else if (res < 0)
4478 break;
4481 length = sizeof(*tmp) + strlen(app) + 1;
4483 if (!(tmp = ast_calloc(1, length))) {
4484 AST_RWLIST_UNLOCK(&apps);
4485 return -1;
4488 strcpy(tmp->name, app);
4489 tmp->execute = execute;
4490 tmp->synopsis = synopsis;
4491 tmp->description = description;
4492 tmp->module = mod;
4494 /* Store in alphabetical order */
4495 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
4496 if (strcasecmp(tmp->name, cur->name) < 0) {
4497 AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
4498 break;
4501 AST_RWLIST_TRAVERSE_SAFE_END;
4502 if (!cur)
4503 AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
4505 ast_verb(2, "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
4507 AST_RWLIST_UNLOCK(&apps);
4509 return 0;
4513 * Append to the list. We don't have a tail pointer because we need
4514 * to scan the list anyways to check for duplicates during insertion.
4516 int ast_register_switch(struct ast_switch *sw)
4518 struct ast_switch *tmp;
4520 AST_RWLIST_WRLOCK(&switches);
4521 AST_RWLIST_TRAVERSE(&switches, tmp, list) {
4522 if (!strcasecmp(tmp->name, sw->name)) {
4523 AST_RWLIST_UNLOCK(&switches);
4524 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
4525 return -1;
4528 AST_RWLIST_INSERT_TAIL(&switches, sw, list);
4529 AST_RWLIST_UNLOCK(&switches);
4531 return 0;
4534 void ast_unregister_switch(struct ast_switch *sw)
4536 AST_RWLIST_WRLOCK(&switches);
4537 AST_RWLIST_REMOVE(&switches, sw, list);
4538 AST_RWLIST_UNLOCK(&switches);
4542 * Help for CLI commands ...
4546 * \brief 'show application' CLI command implementation function...
4548 static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4550 struct ast_app *aa;
4551 int app, no_registered_app = 1;
4552 char *ret = NULL;
4553 int which = 0;
4554 int wordlen;
4556 switch (cmd) {
4557 case CLI_INIT:
4558 e->command = "core show application";
4559 e->usage =
4560 "Usage: core show application <application> [<application> [<application> [...]]]\n"
4561 " Describes a particular application.\n";
4562 return NULL;
4563 case CLI_GENERATE:
4565 * There is a possibility to show informations about more than one
4566 * application at one time. You can type 'show application Dial Echo' and
4567 * you will see informations about these two applications ...
4569 wordlen = strlen(a->word);
4570 /* return the n-th [partial] matching entry */
4571 AST_RWLIST_RDLOCK(&apps);
4572 AST_RWLIST_TRAVERSE(&apps, aa, list) {
4573 if (!strncasecmp(a->word, aa->name, wordlen) && ++which > a->n) {
4574 ret = ast_strdup(aa->name);
4575 break;
4578 AST_RWLIST_UNLOCK(&apps);
4580 return ret;
4583 if (a->argc < 4)
4584 return CLI_SHOWUSAGE;
4586 /* ... go through all applications ... */
4587 AST_RWLIST_RDLOCK(&apps);
4588 AST_RWLIST_TRAVERSE(&apps, aa, list) {
4589 /* ... compare this application name with all arguments given
4590 * to 'show application' command ... */
4591 for (app = 3; app < a->argc; app++) {
4592 if (!strcasecmp(aa->name, a->argv[app])) {
4593 /* Maximum number of characters added by terminal coloring is 22 */
4594 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
4595 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
4596 int synopsis_size, description_size;
4598 no_registered_app = 0;
4600 if (aa->synopsis)
4601 synopsis_size = strlen(aa->synopsis) + 23;
4602 else
4603 synopsis_size = strlen("Not available") + 23;
4604 synopsis = alloca(synopsis_size);
4606 if (aa->description)
4607 description_size = strlen(aa->description) + 23;
4608 else
4609 description_size = strlen("Not available") + 23;
4610 description = alloca(description_size);
4612 if (synopsis && description) {
4613 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", aa->name);
4614 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
4615 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
4616 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
4617 term_color(synopsis,
4618 aa->synopsis ? aa->synopsis : "Not available",
4619 COLOR_CYAN, 0, synopsis_size);
4620 term_color(description,
4621 aa->description ? aa->description : "Not available",
4622 COLOR_CYAN, 0, description_size);
4624 ast_cli(a->fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
4625 } else {
4626 /* ... one of our applications, show info ...*/
4627 ast_cli(a->fd,"\n -= Info about application '%s' =- \n\n"
4628 "[Synopsis]\n %s\n\n"
4629 "[Description]\n%s\n",
4630 aa->name,
4631 aa->synopsis ? aa->synopsis : "Not available",
4632 aa->description ? aa->description : "Not available");
4637 AST_RWLIST_UNLOCK(&apps);
4639 /* we found at least one app? no? */
4640 if (no_registered_app) {
4641 ast_cli(a->fd, "Your application(s) is (are) not registered\n");
4642 return CLI_FAILURE;
4645 return CLI_SUCCESS;
4648 /*! \brief handle_show_hints: CLI support for listing registered dial plan hints */
4649 static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4651 struct ast_hint *hint;
4652 int num = 0;
4653 int watchers;
4654 struct ast_state_cb *watcher;
4656 switch (cmd) {
4657 case CLI_INIT:
4658 e->command = "core show hints";
4659 e->usage =
4660 "Usage: core show hints\n"
4661 " List registered hints\n";
4662 return NULL;
4663 case CLI_GENERATE:
4664 return NULL;
4667 AST_RWLIST_RDLOCK(&hints);
4668 if (AST_RWLIST_EMPTY(&hints)) {
4669 ast_cli(a->fd, "There are no registered dialplan hints\n");
4670 AST_RWLIST_UNLOCK(&hints);
4671 return CLI_SUCCESS;
4673 /* ... we have hints ... */
4674 ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
4675 AST_RWLIST_TRAVERSE(&hints, hint, list) {
4676 watchers = 0;
4677 AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
4678 watchers++;
4680 ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
4681 ast_get_extension_name(hint->exten),
4682 ast_get_context_name(ast_get_extension_context(hint->exten)),
4683 ast_get_extension_app(hint->exten),
4684 ast_extension_state2str(hint->laststate), watchers);
4685 num++;
4687 ast_cli(a->fd, "----------------\n");
4688 ast_cli(a->fd, "- %d hints registered\n", num);
4689 AST_RWLIST_UNLOCK(&hints);
4690 return CLI_SUCCESS;
4693 /*! \brief autocomplete for CLI command 'core show hint' */
4694 static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
4696 struct ast_hint *hint;
4697 char *ret = NULL;
4698 int which = 0;
4699 int wordlen;
4701 if (pos != 3)
4702 return NULL;
4704 wordlen = strlen(word);
4706 AST_RWLIST_RDLOCK(&hints);
4707 /* walk through all hints */
4708 AST_RWLIST_TRAVERSE(&hints, hint, list) {
4709 if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) {
4710 ret = ast_strdup(ast_get_extension_name(hint->exten));
4711 break;
4714 AST_RWLIST_UNLOCK(&hints);
4716 return ret;
4719 /*! \brief handle_show_hint: CLI support for listing registered dial plan hint */
4720 static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4722 struct ast_hint *hint;
4723 int watchers;
4724 int num = 0, extenlen;
4725 struct ast_state_cb *watcher;
4727 switch (cmd) {
4728 case CLI_INIT:
4729 e->command = "core show hint";
4730 e->usage =
4731 "Usage: core show hint <exten>\n"
4732 " List registered hint\n";
4733 return NULL;
4734 case CLI_GENERATE:
4735 return complete_core_show_hint(a->line, a->word, a->pos, a->n);
4738 if (a->argc < 4)
4739 return CLI_SHOWUSAGE;
4741 AST_RWLIST_RDLOCK(&hints);
4742 if (AST_RWLIST_EMPTY(&hints)) {
4743 ast_cli(a->fd, "There are no registered dialplan hints\n");
4744 AST_RWLIST_UNLOCK(&hints);
4745 return CLI_SUCCESS;
4747 extenlen = strlen(a->argv[3]);
4748 AST_RWLIST_TRAVERSE(&hints, hint, list) {
4749 if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
4750 watchers = 0;
4751 AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
4752 watchers++;
4754 ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
4755 ast_get_extension_name(hint->exten),
4756 ast_get_context_name(ast_get_extension_context(hint->exten)),
4757 ast_get_extension_app(hint->exten),
4758 ast_extension_state2str(hint->laststate), watchers);
4759 num++;
4762 AST_RWLIST_UNLOCK(&hints);
4763 if (!num)
4764 ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
4765 else
4766 ast_cli(a->fd, "%d hint%s matching extension %s\n", num, (num!=1 ? "s":""), a->argv[3]);
4767 return CLI_SUCCESS;
4771 /*! \brief handle_show_switches: CLI support for listing registered dial plan switches */
4772 static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4774 struct ast_switch *sw;
4776 switch (cmd) {
4777 case CLI_INIT:
4778 e->command = "core show switches";
4779 e->usage =
4780 "Usage: core show switches\n"
4781 " List registered switches\n";
4782 return NULL;
4783 case CLI_GENERATE:
4784 return NULL;
4787 AST_RWLIST_RDLOCK(&switches);
4789 if (AST_RWLIST_EMPTY(&switches)) {
4790 AST_RWLIST_UNLOCK(&switches);
4791 ast_cli(a->fd, "There are no registered alternative switches\n");
4792 return CLI_SUCCESS;
4795 ast_cli(a->fd, "\n -= Registered Asterisk Alternative Switches =-\n");
4796 AST_RWLIST_TRAVERSE(&switches, sw, list)
4797 ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
4799 AST_RWLIST_UNLOCK(&switches);
4801 return CLI_SUCCESS;
4804 static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4806 struct ast_app *aa;
4807 int like = 0, describing = 0;
4808 int total_match = 0; /* Number of matches in like clause */
4809 int total_apps = 0; /* Number of apps registered */
4810 static char* choices[] = { "like", "describing", NULL };
4812 switch (cmd) {
4813 case CLI_INIT:
4814 e->command = "core show applications [like|describing]";
4815 e->usage =
4816 "Usage: core show applications [{like|describing} <text>]\n"
4817 " List applications which are currently available.\n"
4818 " If 'like', <text> will be a substring of the app name\n"
4819 " If 'describing', <text> will be a substring of the description\n";
4820 return NULL;
4821 case CLI_GENERATE:
4822 return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
4825 AST_RWLIST_RDLOCK(&apps);
4827 if (AST_RWLIST_EMPTY(&apps)) {
4828 ast_cli(a->fd, "There are no registered applications\n");
4829 AST_RWLIST_UNLOCK(&apps);
4830 return CLI_SUCCESS;
4833 /* core list applications like <keyword> */
4834 if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
4835 like = 1;
4836 } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
4837 describing = 1;
4840 /* core list applications describing <keyword1> [<keyword2>] [...] */
4841 if ((!like) && (!describing)) {
4842 ast_cli(a->fd, " -= Registered Asterisk Applications =-\n");
4843 } else {
4844 ast_cli(a->fd, " -= Matching Asterisk Applications =-\n");
4847 AST_RWLIST_TRAVERSE(&apps, aa, list) {
4848 int printapp = 0;
4849 total_apps++;
4850 if (like) {
4851 if (strcasestr(aa->name, a->argv[4])) {
4852 printapp = 1;
4853 total_match++;
4855 } else if (describing) {
4856 if (aa->description) {
4857 /* Match all words on command line */
4858 int i;
4859 printapp = 1;
4860 for (i = 4; i < a->argc; i++) {
4861 if (!strcasestr(aa->description, a->argv[i])) {
4862 printapp = 0;
4863 } else {
4864 total_match++;
4868 } else {
4869 printapp = 1;
4872 if (printapp) {
4873 ast_cli(a->fd," %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
4876 if ((!like) && (!describing)) {
4877 ast_cli(a->fd, " -= %d Applications Registered =-\n",total_apps);
4878 } else {
4879 ast_cli(a->fd, " -= %d Applications Matching =-\n",total_match);
4882 AST_RWLIST_UNLOCK(&apps);
4884 return CLI_SUCCESS;
4888 * 'show dialplan' CLI command implementation functions ...
4890 static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
4891 int state)
4893 struct ast_context *c = NULL;
4894 char *ret = NULL;
4895 int which = 0;
4896 int wordlen;
4898 /* we are do completion of [exten@]context on second position only */
4899 if (pos != 2)
4900 return NULL;
4902 ast_rdlock_contexts();
4904 wordlen = strlen(word);
4906 /* walk through all contexts and return the n-th match */
4907 while ( (c = ast_walk_contexts(c)) ) {
4908 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
4909 ret = ast_strdup(ast_get_context_name(c));
4910 break;
4914 ast_unlock_contexts();
4916 return ret;
4919 /*! \brief Counters for the show dialplan manager command */
4920 struct dialplan_counters {
4921 int total_items;
4922 int total_context;
4923 int total_exten;
4924 int total_prio;
4925 int context_existence;
4926 int extension_existence;
4929 /*! \brief helper function to print an extension */
4930 static void print_ext(struct ast_exten *e, char * buf, int buflen)
4932 int prio = ast_get_extension_priority(e);
4933 if (prio == PRIORITY_HINT) {
4934 snprintf(buf, buflen, "hint: %s",
4935 ast_get_extension_app(e));
4936 } else {
4937 snprintf(buf, buflen, "%d. %s(%s)",
4938 prio, ast_get_extension_app(e),
4939 (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
4943 /* XXX not verified */
4944 static int show_dialplan_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, const char *includes[])
4946 struct ast_context *c = NULL;
4947 int res = 0, old_total_exten = dpc->total_exten;
4949 ast_rdlock_contexts();
4951 /* walk all contexts ... */
4952 while ( (c = ast_walk_contexts(c)) ) {
4953 struct ast_exten *e;
4954 struct ast_include *i;
4955 struct ast_ignorepat *ip;
4956 char buf[256], buf2[256];
4957 int context_info_printed = 0;
4959 if (context && strcmp(ast_get_context_name(c), context))
4960 continue; /* skip this one, name doesn't match */
4962 dpc->context_existence = 1;
4964 ast_rdlock_context(c);
4966 /* are we looking for exten too? if yes, we print context
4967 * only if we find our extension.
4968 * Otherwise print context even if empty ?
4969 * XXX i am not sure how the rinclude is handled.
4970 * I think it ought to go inside.
4972 if (!exten) {
4973 dpc->total_context++;
4974 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
4975 ast_get_context_name(c), ast_get_context_registrar(c));
4976 context_info_printed = 1;
4979 /* walk extensions ... */
4980 e = NULL;
4981 while ( (e = ast_walk_context_extensions(c, e)) ) {
4982 struct ast_exten *p;
4984 if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
4985 continue; /* skip, extension match failed */
4987 dpc->extension_existence = 1;
4989 /* may we print context info? */
4990 if (!context_info_printed) {
4991 dpc->total_context++;
4992 if (rinclude) { /* TODO Print more info about rinclude */
4993 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
4994 ast_get_context_name(c), ast_get_context_registrar(c));
4995 } else {
4996 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
4997 ast_get_context_name(c), ast_get_context_registrar(c));
4999 context_info_printed = 1;
5001 dpc->total_prio++;
5003 /* write extension name and first peer */
5004 if (e->matchcid)
5005 snprintf(buf, sizeof(buf), "'%s' (CID match '%s') => ", ast_get_extension_name(e), e->cidmatch);
5006 else
5007 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
5009 print_ext(e, buf2, sizeof(buf2));
5011 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
5012 ast_get_extension_registrar(e));
5014 dpc->total_exten++;
5015 /* walk next extension peers */
5016 p = e; /* skip the first one, we already got it */
5017 while ( (p = ast_walk_extension_priorities(e, p)) ) {
5018 const char *el = ast_get_extension_label(p);
5019 dpc->total_prio++;
5020 if (el)
5021 snprintf(buf, sizeof(buf), " [%s]", el);
5022 else
5023 buf[0] = '\0';
5024 print_ext(p, buf2, sizeof(buf2));
5026 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
5027 ast_get_extension_registrar(p));
5031 /* walk included and write info ... */
5032 i = NULL;
5033 while ( (i = ast_walk_context_includes(c, i)) ) {
5034 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
5035 if (exten) {
5036 /* Check all includes for the requested extension */
5037 if (includecount >= AST_PBX_MAX_STACK) {
5038 ast_log(LOG_WARNING, "Maximum include depth exceeded!\n");
5039 } else {
5040 int dupe = 0;
5041 int x;
5042 for (x = 0; x < includecount; x++) {
5043 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
5044 dupe++;
5045 break;
5048 if (!dupe) {
5049 includes[includecount] = ast_get_include_name(i);
5050 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
5051 } else {
5052 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
5055 } else {
5056 ast_cli(fd, " Include => %-45s [%s]\n",
5057 buf, ast_get_include_registrar(i));
5061 /* walk ignore patterns and write info ... */
5062 ip = NULL;
5063 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
5064 const char *ipname = ast_get_ignorepat_name(ip);
5065 char ignorepat[AST_MAX_EXTENSION];
5066 snprintf(buf, sizeof(buf), "'%s'", ipname);
5067 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
5068 if (!exten || ast_extension_match(ignorepat, exten)) {
5069 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
5070 buf, ast_get_ignorepat_registrar(ip));
5073 if (!rinclude) {
5074 struct ast_sw *sw = NULL;
5075 while ( (sw = ast_walk_context_switches(c, sw)) ) {
5076 snprintf(buf, sizeof(buf), "'%s/%s'",
5077 ast_get_switch_name(sw),
5078 ast_get_switch_data(sw));
5079 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
5080 buf, ast_get_switch_registrar(sw));
5084 if (option_debug && c->pattern_tree)
5086 ast_cli(fd,"\r\n In-mem exten Trie for Fast Extension Pattern Matching:\r\n\r\n");
5088 ast_cli(fd,"\r\n Explanation: Node Contents Format = <char(s) to match>:<pattern?>:<specif>:[matched extension]\r\n");
5089 ast_cli(fd, " Where <char(s) to match> is a set of chars, any one of which should match the current character\r\n");
5090 ast_cli(fd, " <pattern?>: Y if this a pattern match (eg. _XZN[5-7]), N otherwise\r\n");
5091 ast_cli(fd, " <specif>: an assigned 'exactness' number for this matching char. The lower the number, the more exact the match\r\n");
5092 ast_cli(fd, " [matched exten]: If all chars matched to this point, which extension this matches. In form: EXTEN:<exten string> \r\n");
5093 ast_cli(fd, " In general, you match a trie node to a string character, from left to right. All possible matching chars\r\n");
5094 ast_cli(fd, " are in a string vertically, separated by an unbroken string of '+' characters.\r\n\r\n");
5095 cli_match_char_tree(c->pattern_tree, " ", fd);
5098 ast_unlock_context(c);
5100 /* if we print something in context, make an empty line */
5101 if (context_info_printed)
5102 ast_cli(fd, "\r\n");
5104 ast_unlock_contexts();
5106 return (dpc->total_exten == old_total_exten) ? -1 : res;
5109 static char *handle_show_dialplan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5111 char *exten = NULL, *context = NULL;
5112 /* Variables used for different counters */
5113 struct dialplan_counters counters;
5114 const char *incstack[AST_PBX_MAX_STACK];
5116 switch (cmd) {
5117 case CLI_INIT:
5118 e->command = "dialplan show";
5119 e->usage =
5120 "Usage: dialplan show [[exten@]context]\n"
5121 " Show dialplan\n";
5122 return NULL;
5123 case CLI_GENERATE:
5124 return complete_show_dialplan_context(a->line, a->word, a->pos, a->n);
5127 memset(&counters, 0, sizeof(counters));
5129 if (a->argc != 2 && a->argc != 3)
5130 return CLI_SHOWUSAGE;
5132 /* we obtain [exten@]context? if yes, split them ... */
5133 if (a->argc == 3) {
5134 if (strchr(a->argv[2], '@')) { /* split into exten & context */
5135 context = ast_strdupa(a->argv[2]);
5136 exten = strsep(&context, "@");
5137 /* change empty strings to NULL */
5138 if (ast_strlen_zero(exten))
5139 exten = NULL;
5140 } else { /* no '@' char, only context given */
5141 context = a->argv[2];
5143 if (ast_strlen_zero(context))
5144 context = NULL;
5146 /* else Show complete dial plan, context and exten are NULL */
5147 show_dialplan_helper(a->fd, context, exten, &counters, NULL, 0, incstack);
5149 /* check for input failure and throw some error messages */
5150 if (context && !counters.context_existence) {
5151 ast_cli(a->fd, "There is no existence of '%s' context\n", context);
5152 return CLI_FAILURE;
5155 if (exten && !counters.extension_existence) {
5156 if (context)
5157 ast_cli(a->fd, "There is no existence of %s@%s extension\n",
5158 exten, context);
5159 else
5160 ast_cli(a->fd,
5161 "There is no existence of '%s' extension in all contexts\n",
5162 exten);
5163 return CLI_FAILURE;
5166 ast_cli(a->fd,"-= %d %s (%d %s) in %d %s. =-\n",
5167 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
5168 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
5169 counters.total_context, counters.total_context == 1 ? "context" : "contexts");
5171 /* everything ok */
5172 return CLI_SUCCESS;
5175 /*! \brief Send ack once */
5176 static void manager_dpsendack(struct mansession *s, const struct message *m)
5178 astman_send_listack(s, m, "DialPlan list will follow", "start");
5181 /*! \brief Show dialplan extensions
5182 * XXX this function is similar but not exactly the same as the CLI's
5183 * show dialplan. Must check whether the difference is intentional or not.
5185 static int manager_show_dialplan_helper(struct mansession *s, const struct message *m,
5186 const char *actionidtext, const char *context,
5187 const char *exten, struct dialplan_counters *dpc,
5188 struct ast_include *rinclude)
5190 struct ast_context *c;
5191 int res = 0, old_total_exten = dpc->total_exten;
5193 if (ast_strlen_zero(exten))
5194 exten = NULL;
5195 if (ast_strlen_zero(context))
5196 context = NULL;
5198 ast_debug(3, "manager_show_dialplan: Context: -%s- Extension: -%s-\n", context, exten);
5200 /* try to lock contexts */
5201 if (ast_rdlock_contexts()) {
5202 astman_send_error(s, m, "Failed to lock contexts");
5203 ast_log(LOG_WARNING, "Failed to lock contexts list for manager: listdialplan\n");
5204 return -1;
5207 c = NULL; /* walk all contexts ... */
5208 while ( (c = ast_walk_contexts(c)) ) {
5209 struct ast_exten *e;
5210 struct ast_include *i;
5211 struct ast_ignorepat *ip;
5213 if (context && strcmp(ast_get_context_name(c), context) != 0)
5214 continue; /* not the name we want */
5216 dpc->context_existence = 1;
5218 ast_debug(3, "manager_show_dialplan: Found Context: %s \n", ast_get_context_name(c));
5220 if (ast_rdlock_context(c)) { /* failed to lock */
5221 ast_debug(3, "manager_show_dialplan: Failed to lock context\n");
5222 continue;
5225 /* XXX note- an empty context is not printed */
5226 e = NULL; /* walk extensions in context */
5227 while ( (e = ast_walk_context_extensions(c, e)) ) {
5228 struct ast_exten *p;
5230 /* looking for extension? is this our extension? */
5231 if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) {
5232 /* not the one we are looking for, continue */
5233 ast_debug(3, "manager_show_dialplan: Skipping extension %s\n", ast_get_extension_name(e));
5234 continue;
5236 ast_debug(3, "manager_show_dialplan: Found Extension: %s \n", ast_get_extension_name(e));
5238 dpc->extension_existence = 1;
5240 /* may we print context info? */
5241 dpc->total_context++;
5242 dpc->total_exten++;
5244 p = NULL; /* walk next extension peers */
5245 while ( (p = ast_walk_extension_priorities(e, p)) ) {
5246 int prio = ast_get_extension_priority(p);
5248 dpc->total_prio++;
5249 if (!dpc->total_items++)
5250 manager_dpsendack(s, m);
5251 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
5252 astman_append(s, "Context: %s\r\nExtension: %s\r\n", ast_get_context_name(c), ast_get_extension_name(e) );
5254 /* XXX maybe make this conditional, if p != e ? */
5255 if (ast_get_extension_label(p))
5256 astman_append(s, "ExtensionLabel: %s\r\n", ast_get_extension_label(p));
5258 if (prio == PRIORITY_HINT) {
5259 astman_append(s, "Priority: hint\r\nApplication: %s\r\n", ast_get_extension_app(p));
5260 } else {
5261 astman_append(s, "Priority: %d\r\nApplication: %s\r\nAppData: %s\r\n", prio, ast_get_extension_app(p), (char *) ast_get_extension_app_data(p));
5263 astman_append(s, "Registrar: %s\r\n\r\n", ast_get_extension_registrar(e));
5267 i = NULL; /* walk included and write info ... */
5268 while ( (i = ast_walk_context_includes(c, i)) ) {
5269 if (exten) {
5270 /* Check all includes for the requested extension */
5271 manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i);
5272 } else {
5273 if (!dpc->total_items++)
5274 manager_dpsendack(s, m);
5275 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
5276 astman_append(s, "Context: %s\r\nIncludeContext: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_include_name(i), ast_get_include_registrar(i));
5277 astman_append(s, "\r\n");
5278 ast_debug(3, "manager_show_dialplan: Found Included context: %s \n", ast_get_include_name(i));
5282 ip = NULL; /* walk ignore patterns and write info ... */
5283 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
5284 const char *ipname = ast_get_ignorepat_name(ip);
5285 char ignorepat[AST_MAX_EXTENSION];
5287 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
5288 if (!exten || ast_extension_match(ignorepat, exten)) {
5289 if (!dpc->total_items++)
5290 manager_dpsendack(s, m);
5291 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
5292 astman_append(s, "Context: %s\r\nIgnorePattern: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ipname, ast_get_ignorepat_registrar(ip));
5293 astman_append(s, "\r\n");
5296 if (!rinclude) {
5297 struct ast_sw *sw = NULL;
5298 while ( (sw = ast_walk_context_switches(c, sw)) ) {
5299 if (!dpc->total_items++)
5300 manager_dpsendack(s, m);
5301 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
5302 astman_append(s, "Context: %s\r\nSwitch: %s/%s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_registrar(sw));
5303 astman_append(s, "\r\n");
5304 ast_debug(3, "manager_show_dialplan: Found Switch : %s \n", ast_get_switch_name(sw));
5308 ast_unlock_context(c);
5310 ast_unlock_contexts();
5312 if (dpc->total_exten == old_total_exten) {
5313 ast_debug(3, "manager_show_dialplan: Found nothing new\n");
5314 /* Nothing new under the sun */
5315 return -1;
5316 } else {
5317 return res;
5321 /*! \brief Manager listing of dial plan */
5322 static int manager_show_dialplan(struct mansession *s, const struct message *m)
5324 const char *exten, *context;
5325 const char *id = astman_get_header(m, "ActionID");
5326 char idtext[256];
5327 int res;
5329 /* Variables used for different counters */
5330 struct dialplan_counters counters;
5332 if (!ast_strlen_zero(id))
5333 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
5334 else
5335 idtext[0] = '\0';
5337 memset(&counters, 0, sizeof(counters));
5339 exten = astman_get_header(m, "Extension");
5340 context = astman_get_header(m, "Context");
5342 res = manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL);
5344 if (context && !counters.context_existence) {
5345 char errorbuf[BUFSIZ];
5347 snprintf(errorbuf, sizeof(errorbuf), "Did not find context %s", context);
5348 astman_send_error(s, m, errorbuf);
5349 return 0;
5351 if (exten && !counters.extension_existence) {
5352 char errorbuf[BUFSIZ];
5354 if (context)
5355 snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s@%s", exten, context);
5356 else
5357 snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s in any context", exten);
5358 astman_send_error(s, m, errorbuf);
5359 return 0;
5362 manager_event(EVENT_FLAG_CONFIG, "ShowDialPlanComplete",
5363 "EventList: Complete\r\n"
5364 "ListItems: %d\r\n"
5365 "ListExtensions: %d\r\n"
5366 "ListPriorities: %d\r\n"
5367 "ListContexts: %d\r\n"
5368 "%s"
5369 "\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext);
5371 /* everything ok */
5372 return 0;
5375 static char mandescr_show_dialplan[] =
5376 "Description: Show dialplan contexts and extensions.\n"
5377 "Be aware that showing the full dialplan may take a lot of capacity\n"
5378 "Variables: \n"
5379 " ActionID: <id> Action ID for this AMI transaction (optional)\n"
5380 " Extension: <extension> Extension (Optional)\n"
5381 " Context: <context> Context (Optional)\n"
5382 "\n";
5384 /*! \brief CLI support for listing global variables in a parseable way */
5385 static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5387 int i = 0;
5388 struct ast_var_t *newvariable;
5390 switch (cmd) {
5391 case CLI_INIT:
5392 e->command = "dialplan show globals";
5393 e->usage =
5394 "Usage: dialplan show globals\n"
5395 " List current global dialplan variables and their values\n";
5396 return NULL;
5397 case CLI_GENERATE:
5398 return NULL;
5401 ast_rwlock_rdlock(&globalslock);
5402 AST_LIST_TRAVERSE (&globals, newvariable, entries) {
5403 i++;
5404 ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
5406 ast_rwlock_unlock(&globalslock);
5407 ast_cli(a->fd, "\n -- %d variable(s)\n", i);
5409 return CLI_SUCCESS;
5412 static char *handle_show_globals_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5415 char *res = handle_show_globals(e, cmd, a);
5416 if (cmd == CLI_INIT)
5417 e->command = "core show globals";
5418 return res;
5421 /*! \brief CLI support for listing chanvar's variables in a parseable way */
5422 static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5424 struct ast_channel *chan = NULL;
5425 struct ast_str *vars = ast_str_alloca(BUFSIZ * 4); /* XXX large because we might have lots of channel vars */
5427 switch (cmd) {
5428 case CLI_INIT:
5429 e->command = "dialplan show chanvar";
5430 e->usage =
5431 "Usage: dialplan show chanvar <channel>\n"
5432 " List current channel variables and their values\n";
5433 return NULL;
5434 case CLI_GENERATE:
5435 return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
5438 if (a->argc != e->args + 1)
5439 return CLI_SHOWUSAGE;
5441 if (!(chan = ast_get_channel_by_name_locked(a->argv[e->args]))) {
5442 ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
5443 return CLI_FAILURE;
5446 pbx_builtin_serialize_variables(chan, &vars);
5447 if (vars->str) {
5448 ast_cli(a->fd, "\nVariables for channel %s:\n%s\n", a->argv[e->args], vars->str);
5450 ast_channel_unlock(chan);
5451 return CLI_SUCCESS;
5454 static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5456 switch (cmd) {
5457 case CLI_INIT:
5458 e->command = "dialplan set global";
5459 e->usage =
5460 "Usage: dialplan set global <name> <value>\n"
5461 " Set global dialplan variable <name> to <value>\n";
5462 return NULL;
5463 case CLI_GENERATE:
5464 return NULL;
5467 if (a->argc != e->args + 2)
5468 return CLI_SHOWUSAGE;
5470 pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
5471 ast_cli(a->fd, "\n -- Global variable '%s' set to '%s'\n", a->argv[3], a->argv[4]);
5473 return CLI_SUCCESS;
5476 static char *handle_set_global_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5478 char *res = handle_set_global(e, cmd, a);
5479 if (cmd == CLI_INIT)
5480 e->command = "core set global";
5481 return res;
5484 static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5486 struct ast_channel *chan;
5487 const char *chan_name, *var_name, *var_value;
5489 switch (cmd) {
5490 case CLI_INIT:
5491 e->command = "dialplan set chanvar";
5492 e->usage =
5493 "Usage: dialplan set chanvar <channel> <varname> <value>\n"
5494 " Set channel variable <varname> to <value>\n";
5495 return NULL;
5496 case CLI_GENERATE:
5497 return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
5500 if (a->argc != e->args + 3)
5501 return CLI_SHOWUSAGE;
5503 chan_name = a->argv[e->args];
5504 var_name = a->argv[e->args + 1];
5505 var_value = a->argv[e->args + 2];
5507 if (!(chan = ast_get_channel_by_name_locked(chan_name))) {
5508 ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
5509 return CLI_FAILURE;
5512 pbx_builtin_setvar_helper(chan, var_name, var_value);
5513 ast_channel_unlock(chan);
5514 ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n", var_name, var_value, chan_name);
5516 return CLI_SUCCESS;
5519 static char *handle_set_chanvar_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5521 char *res = handle_set_chanvar(e, cmd, a);
5522 if (cmd == CLI_INIT)
5523 e->command = "core set chanvar";
5524 return res;
5527 static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5529 int oldval = 0;
5531 switch (cmd) {
5532 case CLI_INIT:
5533 e->command = "dialplan set extenpatternmatchnew true";
5534 e->usage =
5535 "Usage: dialplan set extenpatternmatchnew true|false\n"
5536 " Use the NEW extension pattern matching algorithm, true or false.\n";
5537 return NULL;
5538 case CLI_GENERATE:
5539 return NULL;
5542 if (a->argc != 4)
5543 return CLI_SHOWUSAGE;
5545 oldval = pbx_set_extenpatternmatchnew(1);
5547 if (oldval)
5548 ast_cli(a->fd, "\n -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
5549 else
5550 ast_cli(a->fd, "\n -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
5552 return CLI_SUCCESS;
5555 static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5557 int oldval = 0;
5559 switch (cmd) {
5560 case CLI_INIT:
5561 e->command = "dialplan set extenpatternmatchnew false";
5562 e->usage =
5563 "Usage: dialplan set extenpatternmatchnew true|false\n"
5564 " Use the NEW extension pattern matching algorithm, true or false.\n";
5565 return NULL;
5566 case CLI_GENERATE:
5567 return NULL;
5570 if (a->argc != 4)
5571 return CLI_SHOWUSAGE;
5573 oldval = pbx_set_extenpatternmatchnew(0);
5575 if (!oldval)
5576 ast_cli(a->fd, "\n -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
5577 else
5578 ast_cli(a->fd, "\n -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
5580 return CLI_SUCCESS;
5584 * Deprecated CLI commands
5587 static struct ast_cli_entry cli_show_globals_deprecated = AST_CLI_DEFINE(handle_show_globals_deprecated, "Show global dialplan variables.");
5588 static struct ast_cli_entry cli_set_chanvar_deprecated = AST_CLI_DEFINE(handle_set_chanvar_deprecated, "Set a channel variable.");
5589 static struct ast_cli_entry cli_set_global_deprecated = AST_CLI_DEFINE(handle_set_global_deprecated, "Set global dialplan variable.");
5592 * CLI entries for upper commands ...
5594 static struct ast_cli_entry pbx_cli[] = {
5595 AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
5596 AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
5597 AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
5598 AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
5599 AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
5600 AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables", .deprecate_cmd = &cli_show_globals_deprecated),
5601 AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
5602 AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
5603 AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
5604 AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable", .deprecate_cmd = &cli_set_global_deprecated),
5605 AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable", .deprecate_cmd = &cli_set_chanvar_deprecated),
5606 AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
5607 AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
5608 AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
5611 static void unreference_cached_app(struct ast_app *app)
5613 struct ast_context *context = NULL;
5614 struct ast_exten *eroot = NULL, *e = NULL;
5616 ast_rdlock_contexts();
5617 while ((context = ast_walk_contexts(context))) {
5618 while ((eroot = ast_walk_context_extensions(context, eroot))) {
5619 while ((e = ast_walk_extension_priorities(eroot, e))) {
5620 if (e->cached_app == app)
5621 e->cached_app = NULL;
5625 ast_unlock_contexts();
5627 return;
5630 int ast_unregister_application(const char *app)
5632 struct ast_app *tmp;
5634 AST_RWLIST_WRLOCK(&apps);
5635 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
5636 if (!strcasecmp(app, tmp->name)) {
5637 unreference_cached_app(tmp);
5638 AST_RWLIST_REMOVE_CURRENT(list);
5639 ast_verb(2, "Unregistered application '%s'\n", tmp->name);
5640 ast_free(tmp);
5641 break;
5644 AST_RWLIST_TRAVERSE_SAFE_END;
5645 AST_RWLIST_UNLOCK(&apps);
5647 return tmp ? 0 : -1;
5650 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
5652 struct ast_context *tmp, **local_contexts;
5653 struct fake_context search;
5654 int length = sizeof(struct ast_context) + strlen(name) + 1;
5656 if (!contexts_table) {
5657 contexts_table = ast_hashtab_create(17,
5658 ast_hashtab_compare_contexts,
5659 ast_hashtab_resize_java,
5660 ast_hashtab_newsize_java,
5661 ast_hashtab_hash_contexts,
5665 strncpy(search.name,name,sizeof(search.name));
5666 if (!extcontexts) {
5667 ast_rdlock_contexts();
5668 local_contexts = &contexts;
5669 tmp = ast_hashtab_lookup(contexts_table, &search);
5670 ast_unlock_contexts();
5671 if (tmp) {
5672 tmp->refcount++;
5673 return tmp;
5675 } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
5676 local_contexts = extcontexts;
5677 tmp = ast_hashtab_lookup(exttable, &search);
5678 if (tmp) {
5679 tmp->refcount++;
5680 return tmp;
5684 if ((tmp = ast_calloc(1, length))) {
5685 ast_rwlock_init(&tmp->lock);
5686 ast_mutex_init(&tmp->macrolock);
5687 strcpy(tmp->name, name);
5688 tmp->root = NULL;
5689 tmp->root_table = NULL;
5690 tmp->registrar = ast_strdup(registrar);
5691 tmp->includes = NULL;
5692 tmp->ignorepats = NULL;
5693 tmp->refcount = 1;
5694 } else {
5695 ast_log(LOG_ERROR, "Danger! We failed to allocate a context for %s!\n", name);
5696 return NULL;
5699 if (!extcontexts) {
5700 ast_wrlock_contexts();
5701 tmp->next = *local_contexts;
5702 *local_contexts = tmp;
5703 ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */
5704 ast_unlock_contexts();
5705 ast_debug(1, "Registered context '%s'(%p) in table %p registrar: %s\n", tmp->name, tmp, contexts_table, registrar);
5706 ast_verb(3, "Registered extension context '%s' (%p) in table %p; registrar: %s\n", tmp->name, tmp, contexts_table, registrar);
5707 } else {
5708 tmp->next = *local_contexts;
5709 if (exttable)
5710 ast_hashtab_insert_immediate(exttable, tmp); /*put this context into the tree */
5712 *local_contexts = tmp;
5713 ast_debug(1, "Registered context '%s'(%p) in local table %p; registrar: %s\n", tmp->name, tmp, exttable, registrar);
5714 ast_verb(3, "Registered extension context '%s' (%p) in local table %p; registrar: %s\n", tmp->name, tmp, exttable, registrar);
5716 return tmp;
5719 void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar);
5721 struct store_hint {
5722 char *context;
5723 char *exten;
5724 AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks;
5725 int laststate;
5726 AST_LIST_ENTRY(store_hint) list;
5727 char data[1];
5730 AST_LIST_HEAD(store_hints, store_hint);
5732 static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar)
5734 struct ast_include *i;
5735 struct ast_ignorepat *ip;
5736 struct ast_sw *sw;
5738 ast_verb(3, "merging incls/swits/igpats from old(%s) to new(%s) context, registrar = %s\n", ast_get_context_name(old), ast_get_context_name(new), registrar);
5739 /* copy in the includes, switches, and ignorepats */
5740 /* walk through includes */
5741 for (i = NULL; (i = ast_walk_context_includes(old, i)) ; ) {
5742 if (strcmp(ast_get_include_registrar(i), registrar) == 0)
5743 continue; /* not mine */
5744 ast_context_add_include2(new, ast_get_include_name(i), ast_get_include_registrar(i));
5747 /* walk through switches */
5748 for (sw = NULL; (sw = ast_walk_context_switches(old, sw)) ; ) {
5749 if (strcmp(ast_get_switch_registrar(sw), registrar) == 0)
5750 continue; /* not mine */
5751 ast_context_add_switch2(new, ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_eval(sw), ast_get_switch_registrar(sw));
5754 /* walk thru ignorepats ... */
5755 for (ip = NULL; (ip = ast_walk_context_ignorepats(old, ip)); ) {
5756 if (strcmp(ast_get_ignorepat_registrar(ip), registrar) == 0)
5757 continue; /* not mine */
5758 ast_context_add_ignorepat2(new, ast_get_ignorepat_name(ip), ast_get_ignorepat_registrar(ip));
5763 /* the purpose of this routine is to duplicate a context, with all its substructure,
5764 except for any extens that have a matching registrar */
5765 static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *exttable, struct ast_context *context, const char *registrar)
5767 struct ast_context *new = ast_hashtab_lookup(exttable, context); /* is there a match in the new set? */
5768 struct ast_exten *exten_item, *prio_item, *new_exten_item, *new_prio_item;
5769 struct ast_hashtab_iter *exten_iter;
5770 struct ast_hashtab_iter *prio_iter;
5771 int insert_count = 0;
5772 int first = 1;
5774 /* We'll traverse all the extensions/prios, and see which are not registrar'd with
5775 the current registrar, and copy them to the new context. If the new context does not
5776 exist, we'll create it "on demand". If no items are in this context to copy, then we'll
5777 only create the empty matching context if the old one meets the criteria */
5779 if (context->root_table) {
5780 exten_iter = ast_hashtab_start_traversal(context->root_table);
5781 while ((exten_item=ast_hashtab_next(exten_iter))) {
5782 if (new) {
5783 new_exten_item = ast_hashtab_lookup(new->root_table, exten_item);
5784 } else {
5785 new_exten_item = NULL;
5787 prio_iter = ast_hashtab_start_traversal(exten_item->peer_table);
5788 while ((prio_item=ast_hashtab_next(prio_iter))) {
5789 int res1;
5790 char *dupdstr;
5792 if (new_exten_item) {
5793 new_prio_item = ast_hashtab_lookup(new_exten_item->peer_table, prio_item);
5794 } else {
5795 new_prio_item = NULL;
5797 if (strcmp(prio_item->registrar,registrar) == 0) {
5798 continue;
5800 /* make sure the new context exists, so we have somewhere to stick this exten/prio */
5801 if (!new) {
5802 new = ast_context_find_or_create(extcontexts, exttable, context->name, prio_item->registrar); /* a new context created via priority from a different context in the old dialplan, gets its registrar from the prio's registrar */
5805 /* copy in the includes, switches, and ignorepats */
5806 if (first) { /* but, only need to do this once */
5807 context_merge_incls_swits_igps_other_registrars(new, context, registrar);
5808 first = 0;
5811 if (!new) {
5812 ast_log(LOG_ERROR,"Could not allocate a new context for %s in merge_and_delete! Danger!\n", context->name);
5813 return; /* no sense continuing. */
5815 /* we will not replace existing entries in the new context with stuff from the old context.
5816 but, if this is because of some sort of registrar conflict, we ought to say something... */
5818 dupdstr = ast_strdup(prio_item->data);
5820 res1 = ast_add_extension2(new, 0, prio_item->exten, prio_item->priority, prio_item->label,
5821 prio_item->cidmatch, prio_item->app, dupdstr, prio_item->datad, prio_item->registrar);
5822 if (!res1 && new_exten_item && new_prio_item){
5823 ast_verb(3,"Dropping old dialplan item %s/%s/%d [%s(%s)] (registrar=%s) due to conflict with new dialplan\n",
5824 context->name, prio_item->exten, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar);
5825 } else {
5826 /* we do NOT pass the priority data from the old to the new -- we pass a copy of it, so no changes to the current dialplan take place,
5827 and no double frees take place, either! */
5828 insert_count++;
5831 ast_hashtab_end_traversal(prio_iter);
5833 ast_hashtab_end_traversal(exten_iter);
5836 if (!insert_count && !new && (strcmp(context->registrar, registrar) != 0 ||
5837 (strcmp(context->registrar, registrar) == 0 && context->refcount > 1))) {
5838 /* we could have given it the registrar of the other module who incremented the refcount,
5839 but that's not available, so we give it the registrar we know about */
5840 new = ast_context_find_or_create(extcontexts, exttable, context->name, context->registrar);
5842 /* copy in the includes, switches, and ignorepats */
5843 context_merge_incls_swits_igps_other_registrars(new, context, registrar);
5848 /* XXX this does not check that multiple contexts are merged */
5849 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *registrar)
5851 double ft;
5852 struct ast_context *tmp, *oldcontextslist;
5853 struct ast_hashtab *oldtable;
5854 struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
5855 struct store_hint *this;
5856 struct ast_hint *hint;
5857 struct ast_exten *exten;
5858 int length;
5859 struct ast_state_cb *thiscb;
5860 struct ast_hashtab_iter *iter;
5862 /* it is very important that this function hold the hint list lock _and_ the conlock
5863 during its operation; not only do we need to ensure that the list of contexts
5864 and extensions does not change, but also that no hint callbacks (watchers) are
5865 added or removed during the merge/delete process
5867 in addition, the locks _must_ be taken in this order, because there are already
5868 other code paths that use this order
5871 struct timeval begintime, writelocktime, endlocktime, enddeltime;
5872 int wrlock_ver;
5874 begintime = ast_tvnow();
5875 ast_rdlock_contexts();
5876 iter = ast_hashtab_start_traversal(contexts_table);
5877 while ((tmp = ast_hashtab_next(iter))) {
5878 context_merge(extcontexts, exttable, tmp, registrar);
5880 ast_hashtab_end_traversal(iter);
5881 wrlock_ver = ast_wrlock_contexts_version();
5883 ast_unlock_contexts(); /* this feels real retarded, but you must do
5884 what you must do If this isn't done, the following
5885 wrlock is a guraranteed deadlock */
5886 ast_wrlock_contexts();
5887 if (ast_wrlock_contexts_version() > wrlock_ver+1) {
5888 ast_log(LOG_WARNING,"Something changed the contexts in the middle of merging contexts!\n");
5891 AST_RWLIST_WRLOCK(&hints);
5892 writelocktime = ast_tvnow();
5894 /* preserve all watchers for hints associated with this registrar */
5895 AST_RWLIST_TRAVERSE(&hints, hint, list) {
5896 if (!AST_LIST_EMPTY(&hint->callbacks) && !strcmp(registrar, hint->exten->parent->registrar)) {
5897 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
5898 if (!(this = ast_calloc(1, length)))
5899 continue;
5900 AST_LIST_APPEND_LIST(&this->callbacks, &hint->callbacks, entry);
5901 this->laststate = hint->laststate;
5902 this->context = this->data;
5903 strcpy(this->data, hint->exten->parent->name);
5904 this->exten = this->data + strlen(this->context) + 1;
5905 strcpy(this->exten, hint->exten->exten);
5906 AST_LIST_INSERT_HEAD(&store, this, list);
5910 /* save the old table and list */
5911 oldtable = contexts_table;
5912 oldcontextslist = contexts;
5914 /* move in the new table and list */
5915 contexts_table = exttable;
5916 contexts = *extcontexts;
5918 /* restore the watchers for hints that can be found; notify those that
5919 cannot be restored
5921 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
5922 struct pbx_find_info q = { .stacklen = 0 };
5923 exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
5924 /* If this is a pattern, dynamically create a new extension for this
5925 * particular match. Note that this will only happen once for each
5926 * individual extension, because the pattern will no longer match first.
5928 if (exten && exten->exten[0] == '_') {
5929 ast_add_extension(exten->parent->name, 0, this->exten, PRIORITY_HINT, NULL,
5930 0, exten->app, ast_strdup(exten->data), ast_free_ptr, registrar);
5931 exten = ast_hint_extension(NULL, this->context, this->exten);
5934 /* Find the hint in the list of hints */
5935 AST_RWLIST_TRAVERSE(&hints, hint, list) {
5936 if (hint->exten == exten)
5937 break;
5939 if (!exten || !hint) {
5940 /* this hint has been removed, notify the watchers */
5941 while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) {
5942 thiscb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, thiscb->data);
5943 ast_free(thiscb);
5945 } else {
5946 AST_LIST_APPEND_LIST(&hint->callbacks, &this->callbacks, entry);
5947 hint->laststate = this->laststate;
5949 ast_free(this);
5952 AST_RWLIST_UNLOCK(&hints);
5953 ast_unlock_contexts();
5954 endlocktime = ast_tvnow();
5956 /* the old list and hashtab no longer are relevant, delete them while the rest of asterisk
5957 is now freely using the new stuff instead */
5959 ast_hashtab_destroy(oldtable, NULL);
5961 for (tmp = oldcontextslist; tmp; ) {
5962 struct ast_context *next; /* next starting point */
5963 next = tmp->next;
5964 __ast_internal_context_destroy(tmp);
5965 tmp = next;
5967 enddeltime = ast_tvnow();
5969 ft = ast_tvdiff_us(writelocktime, begintime);
5970 ft /= 1000000.0;
5971 ast_verb(3,"Time to scan old dialplan and merge leftovers back into the new: %8.6f sec\n", ft);
5973 ft = ast_tvdiff_us(endlocktime, writelocktime);
5974 ft /= 1000000.0;
5975 ast_verb(3,"Time to restore hints and swap in new dialplan: %8.6f sec\n", ft);
5977 ft = ast_tvdiff_us(enddeltime, endlocktime);
5978 ft /= 1000000.0;
5979 ast_verb(3,"Time to delete the old dialplan: %8.6f sec\n", ft);
5981 ft = ast_tvdiff_us(enddeltime, begintime);
5982 ft /= 1000000.0;
5983 ast_verb(3,"Total time merge_contexts_delete: %8.6f sec\n", ft);
5984 return;
5988 * errno values
5989 * EBUSY - can't lock
5990 * ENOENT - no existence of context
5992 int ast_context_add_include(const char *context, const char *include, const char *registrar)
5994 int ret = -1;
5995 struct ast_context *c = find_context_locked(context);
5997 if (c) {
5998 ret = ast_context_add_include2(c, include, registrar);
5999 ast_unlock_contexts();
6001 return ret;
6004 /*! \brief Helper for get_range.
6005 * return the index of the matching entry, starting from 1.
6006 * If names is not supplied, try numeric values.
6008 static int lookup_name(const char *s, char *const names[], int max)
6010 int i;
6012 if (names) {
6013 for (i = 0; names[i]; i++) {
6014 if (!strcasecmp(s, names[i]))
6015 return i+1;
6017 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
6018 return i;
6020 return 0; /* error return */
6023 /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
6024 * names, if supplied, is an array of names that should be mapped to numbers.
6026 static unsigned get_range(char *src, int max, char *const names[], const char *msg)
6028 int s, e; /* start and ending position */
6029 unsigned int mask = 0;
6031 /* Check for whole range */
6032 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
6033 s = 0;
6034 e = max - 1;
6035 } else {
6036 /* Get start and ending position */
6037 char *c = strchr(src, '-');
6038 if (c)
6039 *c++ = '\0';
6040 /* Find the start */
6041 s = lookup_name(src, names, max);
6042 if (!s) {
6043 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
6044 return 0;
6046 s--;
6047 if (c) { /* find end of range */
6048 e = lookup_name(c, names, max);
6049 if (!e) {
6050 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
6051 return 0;
6053 e--;
6054 } else
6055 e = s;
6057 /* Fill the mask. Remember that ranges are cyclic */
6058 mask = 1 << e; /* initialize with last element */
6059 while (s != e) {
6060 if (s >= max) {
6061 s = 0;
6062 mask |= (1 << s);
6063 } else {
6064 mask |= (1 << s);
6065 s++;
6068 return mask;
6071 /*! \brief store a bitmask of valid times, one bit each 2 minute */
6072 static void get_timerange(struct ast_timing *i, char *times)
6074 char *e;
6075 int x;
6076 int s1, s2;
6077 int e1, e2;
6078 /* int cth, ctm; */
6080 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
6081 memset(i->minmask, 0, sizeof(i->minmask));
6083 /* 2-minutes per bit, since the mask has only 32 bits :( */
6084 /* Star is all times */
6085 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
6086 for (x = 0; x < 24; x++)
6087 i->minmask[x] = 0x3fffffff; /* 30 bits */
6088 return;
6090 /* Otherwise expect a range */
6091 e = strchr(times, '-');
6092 if (!e) {
6093 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
6094 return;
6096 *e++ = '\0';
6097 /* XXX why skip non digits ? */
6098 while (*e && !isdigit(*e))
6099 e++;
6100 if (!*e) {
6101 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
6102 return;
6104 if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
6105 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
6106 return;
6108 if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
6109 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
6110 return;
6112 /* XXX this needs to be optimized */
6113 #if 1
6114 s1 = s1 * 30 + s2/2;
6115 if ((s1 < 0) || (s1 >= 24*30)) {
6116 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
6117 return;
6119 e1 = e1 * 30 + e2/2;
6120 if ((e1 < 0) || (e1 >= 24*30)) {
6121 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
6122 return;
6124 /* Go through the time and enable each appropriate bit */
6125 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
6126 i->minmask[x/30] |= (1 << (x % 30));
6128 /* Do the last one */
6129 i->minmask[x/30] |= (1 << (x % 30));
6130 #else
6131 for (cth = 0; cth < 24; cth++) {
6132 /* Initialize masks to blank */
6133 i->minmask[cth] = 0;
6134 for (ctm = 0; ctm < 30; ctm++) {
6135 if (
6136 /* First hour with more than one hour */
6137 (((cth == s1) && (ctm >= s2)) &&
6138 ((cth < e1)))
6139 /* Only one hour */
6140 || (((cth == s1) && (ctm >= s2)) &&
6141 ((cth == e1) && (ctm <= e2)))
6142 /* In between first and last hours (more than 2 hours) */
6143 || ((cth > s1) &&
6144 (cth < e1))
6145 /* Last hour with more than one hour */
6146 || ((cth > s1) &&
6147 ((cth == e1) && (ctm <= e2)))
6149 i->minmask[cth] |= (1 << (ctm / 2));
6152 #endif
6153 /* All done */
6154 return;
6157 static char *days[] =
6159 "sun",
6160 "mon",
6161 "tue",
6162 "wed",
6163 "thu",
6164 "fri",
6165 "sat",
6166 NULL,
6169 static char *months[] =
6171 "jan",
6172 "feb",
6173 "mar",
6174 "apr",
6175 "may",
6176 "jun",
6177 "jul",
6178 "aug",
6179 "sep",
6180 "oct",
6181 "nov",
6182 "dec",
6183 NULL,
6186 int ast_build_timing(struct ast_timing *i, const char *info_in)
6188 char info_save[256];
6189 char *info;
6191 /* Check for empty just in case */
6192 if (ast_strlen_zero(info_in))
6193 return 0;
6194 /* make a copy just in case we were passed a static string */
6195 ast_copy_string(info_save, info_in, sizeof(info_save));
6196 info = info_save;
6197 /* Assume everything except time */
6198 i->monthmask = 0xfff; /* 12 bits */
6199 i->daymask = 0x7fffffffU; /* 31 bits */
6200 i->dowmask = 0x7f; /* 7 bits */
6201 /* on each call, use strsep() to move info to the next argument */
6202 get_timerange(i, strsep(&info, "|,"));
6203 if (info)
6204 i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
6205 if (info)
6206 i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
6207 if (info)
6208 i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
6209 return 1;
6212 int ast_check_timing(const struct ast_timing *i)
6214 struct ast_tm tm;
6215 struct timeval now = ast_tvnow();
6217 ast_localtime(&now, &tm, NULL);
6219 /* If it's not the right month, return */
6220 if (!(i->monthmask & (1 << tm.tm_mon)))
6221 return 0;
6223 /* If it's not that time of the month.... */
6224 /* Warning, tm_mday has range 1..31! */
6225 if (!(i->daymask & (1 << (tm.tm_mday-1))))
6226 return 0;
6228 /* If it's not the right day of the week */
6229 if (!(i->dowmask & (1 << tm.tm_wday)))
6230 return 0;
6232 /* Sanity check the hour just to be safe */
6233 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
6234 ast_log(LOG_WARNING, "Insane time...\n");
6235 return 0;
6238 /* Now the tough part, we calculate if it fits
6239 in the right time based on min/hour */
6240 if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
6241 return 0;
6243 /* If we got this far, then we're good */
6244 return 1;
6248 * errno values
6249 * ENOMEM - out of memory
6250 * EBUSY - can't lock
6251 * EEXIST - already included
6252 * EINVAL - there is no existence of context for inclusion
6254 int ast_context_add_include2(struct ast_context *con, const char *value,
6255 const char *registrar)
6257 struct ast_include *new_include;
6258 char *c;
6259 struct ast_include *i, *il = NULL; /* include, include_last */
6260 int length;
6261 char *p;
6263 length = sizeof(struct ast_include);
6264 length += 2 * (strlen(value) + 1);
6266 /* allocate new include structure ... */
6267 if (!(new_include = ast_calloc(1, length)))
6268 return -1;
6269 /* Fill in this structure. Use 'p' for assignments, as the fields
6270 * in the structure are 'const char *'
6272 p = new_include->stuff;
6273 new_include->name = p;
6274 strcpy(p, value);
6275 p += strlen(value) + 1;
6276 new_include->rname = p;
6277 strcpy(p, value);
6278 /* Strip off timing info, and process if it is there */
6279 if ( (c = strchr(p, ',')) ) {
6280 *c++ = '\0';
6281 new_include->hastime = ast_build_timing(&(new_include->timing), c);
6283 new_include->next = NULL;
6284 new_include->registrar = registrar;
6286 ast_wrlock_context(con);
6288 /* ... go to last include and check if context is already included too... */
6289 for (i = con->includes; i; i = i->next) {
6290 if (!strcasecmp(i->name, new_include->name)) {
6291 ast_free(new_include);
6292 ast_unlock_context(con);
6293 errno = EEXIST;
6294 return -1;
6296 il = i;
6299 /* ... include new context into context list, unlock, return */
6300 if (il)
6301 il->next = new_include;
6302 else
6303 con->includes = new_include;
6304 ast_verb(3, "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
6306 ast_unlock_context(con);
6308 return 0;
6312 * errno values
6313 * EBUSY - can't lock
6314 * ENOENT - no existence of context
6316 int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
6318 int ret = -1;
6319 struct ast_context *c = find_context_locked(context);
6321 if (c) { /* found, add switch to this context */
6322 ret = ast_context_add_switch2(c, sw, data, eval, registrar);
6323 ast_unlock_contexts();
6325 return ret;
6329 * errno values
6330 * ENOMEM - out of memory
6331 * EBUSY - can't lock
6332 * EEXIST - already included
6333 * EINVAL - there is no existence of context for inclusion
6335 int ast_context_add_switch2(struct ast_context *con, const char *value,
6336 const char *data, int eval, const char *registrar)
6338 struct ast_sw *new_sw;
6339 struct ast_sw *i;
6340 int length;
6341 char *p;
6343 length = sizeof(struct ast_sw);
6344 length += strlen(value) + 1;
6345 if (data)
6346 length += strlen(data);
6347 length++;
6349 /* allocate new sw structure ... */
6350 if (!(new_sw = ast_calloc(1, length)))
6351 return -1;
6352 /* ... fill in this structure ... */
6353 p = new_sw->stuff;
6354 new_sw->name = p;
6355 strcpy(new_sw->name, value);
6356 p += strlen(value) + 1;
6357 new_sw->data = p;
6358 if (data) {
6359 strcpy(new_sw->data, data);
6360 p += strlen(data) + 1;
6361 } else {
6362 strcpy(new_sw->data, "");
6363 p++;
6365 new_sw->eval = eval;
6366 new_sw->registrar = registrar;
6368 /* ... try to lock this context ... */
6369 ast_wrlock_context(con);
6371 /* ... go to last sw and check if context is already swd too... */
6372 AST_LIST_TRAVERSE(&con->alts, i, list) {
6373 if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
6374 ast_free(new_sw);
6375 ast_unlock_context(con);
6376 errno = EEXIST;
6377 return -1;
6381 /* ... sw new context into context list, unlock, return */
6382 AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
6384 ast_verb(3, "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
6386 ast_unlock_context(con);
6388 return 0;
6392 * EBUSY - can't lock
6393 * ENOENT - there is not context existence
6395 int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
6397 int ret = -1;
6398 struct ast_context *c = find_context_locked(context);
6400 if (c) {
6401 ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
6402 ast_unlock_contexts();
6404 return ret;
6407 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
6409 struct ast_ignorepat *ip, *ipl = NULL;
6411 ast_wrlock_context(con);
6413 for (ip = con->ignorepats; ip; ip = ip->next) {
6414 if (!strcmp(ip->pattern, ignorepat) &&
6415 (!registrar || (registrar == ip->registrar))) {
6416 if (ipl) {
6417 ipl->next = ip->next;
6418 ast_free(ip);
6419 } else {
6420 con->ignorepats = ip->next;
6421 ast_free(ip);
6423 ast_unlock_context(con);
6424 return 0;
6426 ipl = ip;
6429 ast_unlock_context(con);
6430 errno = EINVAL;
6431 return -1;
6435 * EBUSY - can't lock
6436 * ENOENT - there is no existence of context
6438 int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
6440 int ret = -1;
6441 struct ast_context *c = find_context_locked(context);
6443 if (c) {
6444 ret = ast_context_add_ignorepat2(c, value, registrar);
6445 ast_unlock_contexts();
6447 return ret;
6450 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
6452 struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
6453 int length;
6454 length = sizeof(struct ast_ignorepat);
6455 length += strlen(value) + 1;
6456 if (!(ignorepat = ast_calloc(1, length)))
6457 return -1;
6458 /* The cast to char * is because we need to write the initial value.
6459 * The field is not supposed to be modified otherwise
6461 strcpy((char *)ignorepat->pattern, value);
6462 ignorepat->next = NULL;
6463 ignorepat->registrar = registrar;
6464 ast_wrlock_context(con);
6465 for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
6466 ignorepatl = ignorepatc;
6467 if (!strcasecmp(ignorepatc->pattern, value)) {
6468 /* Already there */
6469 ast_unlock_context(con);
6470 errno = EEXIST;
6471 return -1;
6474 if (ignorepatl)
6475 ignorepatl->next = ignorepat;
6476 else
6477 con->ignorepats = ignorepat;
6478 ast_unlock_context(con);
6479 return 0;
6483 int ast_ignore_pattern(const char *context, const char *pattern)
6485 struct ast_context *con = ast_context_find(context);
6486 if (con) {
6487 struct ast_ignorepat *pat;
6488 for (pat = con->ignorepats; pat; pat = pat->next) {
6489 if (ast_extension_match(pat->pattern, pattern))
6490 return 1;
6494 return 0;
6498 * EBUSY - can't lock
6499 * ENOENT - no existence of context
6502 int ast_add_extension(const char *context, int replace, const char *extension,
6503 int priority, const char *label, const char *callerid,
6504 const char *application, void *data, void (*datad)(void *), const char *registrar)
6506 int ret = -1;
6507 struct ast_context *c = find_context_locked(context);
6509 if (c) {
6510 ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
6511 application, data, datad, registrar);
6512 ast_unlock_contexts();
6515 return ret;
6518 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
6520 if (!chan)
6521 return -1;
6523 ast_channel_lock(chan);
6525 if (!ast_strlen_zero(context))
6526 ast_copy_string(chan->context, context, sizeof(chan->context));
6527 if (!ast_strlen_zero(exten))
6528 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
6529 if (priority > -1) {
6530 chan->priority = priority;
6531 /* see flag description in channel.h for explanation */
6532 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
6533 chan->priority--;
6536 ast_channel_unlock(chan);
6538 return 0;
6541 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
6543 int res = 0;
6545 ast_channel_lock(chan);
6547 if (chan->pbx) { /* This channel is currently in the PBX */
6548 ast_explicit_goto(chan, context, exten, priority + 1);
6549 ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
6550 } else {
6551 /* In order to do it when the channel doesn't really exist within
6552 the PBX, we have to make a new channel, masquerade, and start the PBX
6553 at the new location */
6554 struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
6555 if (!tmpchan) {
6556 res = -1;
6557 } else {
6558 if (chan->cdr) {
6559 ast_cdr_discard(tmpchan->cdr);
6560 tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
6562 /* Make formats okay */
6563 tmpchan->readformat = chan->readformat;
6564 tmpchan->writeformat = chan->writeformat;
6565 /* Setup proper location */
6566 ast_explicit_goto(tmpchan,
6567 S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
6569 /* Masquerade into temp channel */
6570 ast_channel_masquerade(tmpchan, chan);
6572 /* Grab the locks and get going */
6573 ast_channel_lock(tmpchan);
6574 ast_do_masquerade(tmpchan);
6575 ast_channel_unlock(tmpchan);
6576 /* Start the PBX going on our stolen channel */
6577 if (ast_pbx_start(tmpchan)) {
6578 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
6579 ast_hangup(tmpchan);
6580 res = -1;
6584 ast_channel_unlock(chan);
6585 return res;
6588 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
6590 struct ast_channel *chan;
6591 int res = -1;
6593 chan = ast_get_channel_by_name_locked(channame);
6594 if (chan) {
6595 res = ast_async_goto(chan, context, exten, priority);
6596 ast_channel_unlock(chan);
6598 return res;
6601 /*! \brief copy a string skipping whitespace */
6602 static int ext_strncpy(char *dst, const char *src, int len)
6604 int count = 0;
6606 while (*src && (count < len - 1)) {
6607 switch (*src) {
6608 case ' ':
6609 /* otherwise exten => [a-b],1,... doesn't work */
6610 /* case '-': */
6611 /* Ignore */
6612 break;
6613 default:
6614 *dst = *src;
6615 dst++;
6617 src++;
6618 count++;
6620 *dst = '\0';
6622 return count;
6625 /*!
6626 * \brief add the extension in the priority chain.
6627 * \retval 0 on success.
6628 * \retval -1 on failure.
6630 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
6631 struct ast_exten *el, struct ast_exten *e, int replace)
6633 struct ast_exten *ep;
6634 struct ast_exten *eh=e;
6636 for (ep = NULL; e ; ep = e, e = e->peer) {
6637 if (e->priority >= tmp->priority)
6638 break;
6640 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
6641 ast_hashtab_insert_safe(eh->peer_table, tmp);
6643 if (tmp->label) {
6644 ast_hashtab_insert_safe(eh->peer_label_table, tmp);
6646 ep->peer = tmp;
6647 return 0; /* success */
6649 if (e->priority == tmp->priority) {
6650 /* Can't have something exactly the same. Is this a
6651 replacement? If so, replace, otherwise, bonk. */
6652 if (!replace) {
6653 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
6654 if (tmp->datad) {
6655 tmp->datad(tmp->data);
6656 /* if you free this, null it out */
6657 tmp->data = NULL;
6660 ast_free(tmp);
6661 return -1;
6663 /* we are replacing e, so copy the link fields and then update
6664 * whoever pointed to e to point to us
6666 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
6667 tmp->peer = e->peer; /* always meaningful */
6668 if (ep) { /* We're in the peer list, just insert ourselves */
6669 ast_hashtab_remove_object_via_lookup(eh->peer_table,e);
6671 if (e->label) {
6672 ast_hashtab_remove_object_via_lookup(eh->peer_label_table,e);
6675 ast_hashtab_insert_safe(eh->peer_table,tmp);
6676 if (tmp->label) {
6677 ast_hashtab_insert_safe(eh->peer_label_table,tmp);
6680 ep->peer = tmp;
6681 } else if (el) { /* We're the first extension. Take over e's functions */
6682 struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
6683 tmp->peer_table = e->peer_table;
6684 tmp->peer_label_table = e->peer_label_table;
6685 ast_hashtab_remove_object_via_lookup(tmp->peer_table,e);
6686 ast_hashtab_insert_safe(tmp->peer_table,tmp);
6687 if (e->label) {
6688 ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e);
6690 if (tmp->label) {
6691 ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
6694 ast_hashtab_remove_object_via_lookup(con->root_table, e);
6695 ast_hashtab_insert_safe(con->root_table, tmp);
6696 el->next = tmp;
6697 /* The pattern trie points to this exten; replace the pointer,
6698 and all will be well */
6699 if (x) { /* if the trie isn't formed yet, don't sweat this */
6700 if (x->exten) { /* this test for safety purposes */
6701 x->exten = tmp; /* replace what would become a bad pointer */
6702 } else {
6703 ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
6706 } else { /* We're the very first extension. */
6707 struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
6708 ast_hashtab_remove_object_via_lookup(con->root_table, e);
6709 ast_hashtab_insert_safe(con->root_table, tmp);
6710 tmp->peer_table = e->peer_table;
6711 tmp->peer_label_table = e->peer_label_table;
6712 ast_hashtab_remove_object_via_lookup(tmp->peer_table, e);
6713 ast_hashtab_insert_safe(tmp->peer_table, tmp);
6714 if (e->label) {
6715 ast_hashtab_remove_object_via_lookup(tmp->peer_label_table, e);
6717 if (tmp->label) {
6718 ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
6721 ast_hashtab_remove_object_via_lookup(con->root_table, e);
6722 ast_hashtab_insert_safe(con->root_table, tmp);
6723 con->root = tmp;
6724 /* The pattern trie points to this exten; replace the pointer,
6725 and all will be well */
6726 if (x) { /* if the trie isn't formed yet; no problem */
6727 if (x->exten) { /* this test for safety purposes */
6728 x->exten = tmp; /* replace what would become a bad pointer */
6729 } else {
6730 ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
6734 if (tmp->priority == PRIORITY_HINT)
6735 ast_change_hint(e,tmp);
6736 /* Destroy the old one */
6737 if (e->datad)
6738 e->datad(e->data);
6739 ast_free(e);
6740 } else { /* Slip ourselves in just before e */
6741 tmp->peer = e;
6742 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
6743 if (ep) { /* Easy enough, we're just in the peer list */
6744 if (tmp->label) {
6745 ast_hashtab_insert_safe(eh->peer_label_table, tmp);
6747 ast_hashtab_insert_safe(eh->peer_table, tmp);
6748 ep->peer = tmp;
6749 } else { /* we are the first in some peer list, so link in the ext list */
6750 tmp->peer_table = e->peer_table;
6751 tmp->peer_label_table = e->peer_label_table;
6752 e->peer_table = 0;
6753 e->peer_label_table = 0;
6754 ast_hashtab_insert_safe(tmp->peer_table, tmp);
6755 if (tmp->label) {
6756 ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
6758 ast_hashtab_remove_object_via_lookup(con->root_table, e);
6759 ast_hashtab_insert_safe(con->root_table, tmp);
6760 if (el)
6761 el->next = tmp; /* in the middle... */
6762 else
6763 con->root = tmp; /* ... or at the head */
6764 e->next = NULL; /* e is no more at the head, so e->next must be reset */
6766 /* And immediately return success. */
6767 if (tmp->priority == PRIORITY_HINT)
6768 ast_add_hint(tmp);
6770 return 0;
6773 /*! \brief
6774 * Main interface to add extensions to the list for out context.
6776 * We sort extensions in order of matching preference, so that we can
6777 * stop the search as soon as we find a suitable match.
6778 * This ordering also takes care of wildcards such as '.' (meaning
6779 * "one or more of any character") and '!' (which is 'earlymatch',
6780 * meaning "zero or more of any character" but also impacts the
6781 * return value from CANMATCH and EARLYMATCH.
6783 * The extension match rules defined in the devmeeting 2006.05.05 are
6784 * quite simple: WE SELECT THE LONGEST MATCH.
6785 * In detail, "longest" means the number of matched characters in
6786 * the extension. In case of ties (e.g. _XXX and 333) in the length
6787 * of a pattern, we give priority to entries with the smallest cardinality
6788 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
6789 * while the latter has 7, etc.
6790 * In case of same cardinality, the first element in the range counts.
6791 * If we still have a tie, any final '!' will make this as a possibly
6792 * less specific pattern.
6794 * EBUSY - can't lock
6795 * EEXIST - extension with the same priority exist and no replace is set
6798 int ast_add_extension2(struct ast_context *con,
6799 int replace, const char *extension, int priority, const char *label, const char *callerid,
6800 const char *application, void *data, void (*datad)(void *),
6801 const char *registrar)
6804 * Sort extensions (or patterns) according to the rules indicated above.
6805 * These are implemented by the function ext_cmp()).
6806 * All priorities for the same ext/pattern/cid are kept in a list,
6807 * using the 'peer' field as a link field..
6809 struct ast_exten *tmp, *tmp2, *e, *el = NULL;
6810 int res;
6811 int length;
6812 char *p;
6813 char expand_buf[VAR_BUF_SIZE];
6814 struct ast_exten dummy_exten = {0};
6815 char dummy_name[1024];
6817 if (ast_strlen_zero(extension)) {
6818 ast_log(LOG_ERROR,"You have to be kidding-- add exten '' to context %s? Figure out a name and call me back. Action ignored.\n",
6819 con->name);
6820 return -1;
6823 /* If we are adding a hint evalulate in variables and global variables */
6824 if (priority == PRIORITY_HINT && strstr(application, "${") && !strstr(extension, "_")) {
6825 struct ast_channel c = {0, };
6827 /* Start out with regular variables */
6828 ast_copy_string(c.exten, extension, sizeof(c.exten));
6829 ast_copy_string(c.context, con->name, sizeof(c.context));
6830 pbx_substitute_variables_helper(&c, application, expand_buf, sizeof(expand_buf));
6832 /* Move on to global variables if they exist */
6833 ast_rwlock_rdlock(&globalslock);
6834 if (AST_LIST_FIRST(&globals)) {
6835 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
6836 application = expand_buf;
6838 ast_rwlock_unlock(&globalslock);
6841 length = sizeof(struct ast_exten);
6842 length += strlen(extension) + 1;
6843 length += strlen(application) + 1;
6844 if (label)
6845 length += strlen(label) + 1;
6846 if (callerid)
6847 length += strlen(callerid) + 1;
6848 else
6849 length ++; /* just the '\0' */
6851 /* Be optimistic: Build the extension structure first */
6852 if (!(tmp = ast_calloc(1, length)))
6853 return -1;
6855 if (ast_strlen_zero(label)) /* let's turn empty labels to a null ptr */
6856 label = 0;
6858 /* use p as dst in assignments, as the fields are const char * */
6859 p = tmp->stuff;
6860 if (label) {
6861 tmp->label = p;
6862 strcpy(p, label);
6863 p += strlen(label) + 1;
6865 tmp->exten = p;
6866 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
6867 tmp->priority = priority;
6868 tmp->cidmatch = p; /* but use p for assignments below */
6869 if (!ast_strlen_zero(callerid)) {
6870 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
6871 tmp->matchcid = 1;
6872 } else {
6873 *p++ = '\0';
6874 tmp->matchcid = 0;
6876 tmp->app = p;
6877 strcpy(p, application);
6878 tmp->parent = con;
6879 tmp->data = data;
6880 tmp->datad = datad;
6881 tmp->registrar = registrar;
6883 ast_wrlock_context(con);
6885 if (con->pattern_tree) { /* usually, on initial load, the pattern_tree isn't formed until the first find_exten; so if we are adding
6886 an extension, and the trie exists, then we need to incrementally add this pattern to it. */
6887 strncpy(dummy_name,extension,sizeof(dummy_name));
6888 dummy_exten.exten = dummy_name;
6889 dummy_exten.matchcid = 0;
6890 dummy_exten.cidmatch = 0;
6891 tmp2 = ast_hashtab_lookup(con->root_table, &dummy_exten);
6892 if (!tmp2) {
6893 /* hmmm, not in the trie; */
6894 add_exten_to_pattern_tree(con, tmp, 0);
6895 ast_hashtab_insert_safe(con->root_table, tmp); /* for the sake of completeness */
6898 res = 0; /* some compilers will think it is uninitialized otherwise */
6899 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
6900 res = ext_cmp(e->exten, extension);
6901 if (res == 0) { /* extension match, now look at cidmatch */
6902 if (!e->matchcid && !tmp->matchcid)
6903 res = 0;
6904 else if (tmp->matchcid && !e->matchcid)
6905 res = 1;
6906 else if (e->matchcid && !tmp->matchcid)
6907 res = -1;
6908 else
6909 res = strcasecmp(e->cidmatch, tmp->cidmatch);
6911 if (res >= 0)
6912 break;
6914 if (e && res == 0) { /* exact match, insert in the pri chain */
6915 res = add_pri(con, tmp, el, e, replace);
6916 ast_unlock_context(con);
6917 if (res < 0) {
6918 errno = EEXIST; /* XXX do we care ? */
6919 return 0; /* XXX should we return -1 maybe ? */
6921 } else {
6923 * not an exact match, this is the first entry with this pattern,
6924 * so insert in the main list right before 'e' (if any)
6926 tmp->next = e;
6927 if (el) { /* there is another exten already in this context */
6928 el->next = tmp;
6929 tmp->peer_table = ast_hashtab_create(13,
6930 hashtab_compare_exten_numbers,
6931 ast_hashtab_resize_java,
6932 ast_hashtab_newsize_java,
6933 hashtab_hash_priority,
6935 tmp->peer_label_table = ast_hashtab_create(7,
6936 hashtab_compare_exten_labels,
6937 ast_hashtab_resize_java,
6938 ast_hashtab_newsize_java,
6939 hashtab_hash_labels,
6941 if (label) {
6942 ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
6944 ast_hashtab_insert_safe(tmp->peer_table, tmp);
6945 } else { /* this is the first exten in this context */
6946 if (!con->root_table)
6947 con->root_table = ast_hashtab_create(27,
6948 hashtab_compare_extens,
6949 ast_hashtab_resize_java,
6950 ast_hashtab_newsize_java,
6951 hashtab_hash_extens,
6953 con->root = tmp;
6954 con->root->peer_table = ast_hashtab_create(13,
6955 hashtab_compare_exten_numbers,
6956 ast_hashtab_resize_java,
6957 ast_hashtab_newsize_java,
6958 hashtab_hash_priority,
6960 con->root->peer_label_table = ast_hashtab_create(7,
6961 hashtab_compare_exten_labels,
6962 ast_hashtab_resize_java,
6963 ast_hashtab_newsize_java,
6964 hashtab_hash_labels,
6966 if (label) {
6967 ast_hashtab_insert_safe(con->root->peer_label_table, tmp);
6969 ast_hashtab_insert_safe(con->root->peer_table, tmp);
6972 ast_hashtab_insert_safe(con->root_table, tmp);
6973 ast_unlock_context(con);
6974 if (tmp->priority == PRIORITY_HINT)
6975 ast_add_hint(tmp);
6977 if (option_debug) {
6978 if (tmp->matchcid) {
6979 ast_debug(1, "Added extension '%s' priority %d (CID match '%s') to %s (%p)\n",
6980 tmp->exten, tmp->priority, tmp->cidmatch, con->name, con);
6981 } else {
6982 ast_debug(1, "Added extension '%s' priority %d to %s (%p)\n",
6983 tmp->exten, tmp->priority, con->name, con);
6987 if (tmp->matchcid) {
6988 ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s (%p)\n",
6989 tmp->exten, tmp->priority, tmp->cidmatch, con->name, con);
6990 } else {
6991 ast_verb(3, "Added extension '%s' priority %d to %s (%p)\n",
6992 tmp->exten, tmp->priority, con->name, con);
6995 return 0;
6998 struct async_stat {
6999 pthread_t p;
7000 struct ast_channel *chan;
7001 char context[AST_MAX_CONTEXT];
7002 char exten[AST_MAX_EXTENSION];
7003 int priority;
7004 int timeout;
7005 char app[AST_MAX_EXTENSION];
7006 char appdata[1024];
7009 static void *async_wait(void *data)
7011 struct async_stat *as = data;
7012 struct ast_channel *chan = as->chan;
7013 int timeout = as->timeout;
7014 int res;
7015 struct ast_frame *f;
7016 struct ast_app *app;
7018 while (timeout && (chan->_state != AST_STATE_UP)) {
7019 res = ast_waitfor(chan, timeout);
7020 if (res < 1)
7021 break;
7022 if (timeout > -1)
7023 timeout = res;
7024 f = ast_read(chan);
7025 if (!f)
7026 break;
7027 if (f->frametype == AST_FRAME_CONTROL) {
7028 if ((f->subclass == AST_CONTROL_BUSY) ||
7029 (f->subclass == AST_CONTROL_CONGESTION) ) {
7030 ast_frfree(f);
7031 break;
7034 ast_frfree(f);
7036 if (chan->_state == AST_STATE_UP) {
7037 if (!ast_strlen_zero(as->app)) {
7038 app = pbx_findapp(as->app);
7039 if (app) {
7040 ast_verb(3, "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
7041 pbx_exec(chan, app, as->appdata);
7042 } else
7043 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
7044 } else {
7045 if (!ast_strlen_zero(as->context))
7046 ast_copy_string(chan->context, as->context, sizeof(chan->context));
7047 if (!ast_strlen_zero(as->exten))
7048 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
7049 if (as->priority > 0)
7050 chan->priority = as->priority;
7051 /* Run the PBX */
7052 if (ast_pbx_run(chan)) {
7053 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
7054 } else {
7055 /* PBX will have taken care of this */
7056 chan = NULL;
7060 ast_free(as);
7061 if (chan)
7062 ast_hangup(chan);
7063 return NULL;
7066 /*!
7067 * \brief Function to post an empty cdr after a spool call fails.
7068 * \note This function posts an empty cdr for a failed spool call
7070 static int ast_pbx_outgoing_cdr_failed(void)
7072 /* allocate a channel */
7073 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
7075 if (!chan)
7076 return -1; /* failure */
7078 if (!chan->cdr) {
7079 /* allocation of the cdr failed */
7080 ast_channel_free(chan); /* free the channel */
7081 return -1; /* return failure */
7084 /* allocation of the cdr was successful */
7085 ast_cdr_init(chan->cdr, chan); /* initialize our channel's cdr */
7086 ast_cdr_start(chan->cdr); /* record the start and stop time */
7087 ast_cdr_end(chan->cdr);
7088 ast_cdr_failed(chan->cdr); /* set the status to failed */
7089 ast_cdr_detach(chan->cdr); /* post and free the record */
7090 ast_channel_free(chan); /* free the channel */
7092 return 0; /* success */
7095 int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
7097 struct ast_channel *chan;
7098 struct async_stat *as;
7099 int res = -1, cdr_res = -1;
7100 struct outgoing_helper oh;
7102 if (synchronous) {
7103 oh.context = context;
7104 oh.exten = exten;
7105 oh.priority = priority;
7106 oh.cid_num = cid_num;
7107 oh.cid_name = cid_name;
7108 oh.account = account;
7109 oh.vars = vars;
7110 oh.parent_channel = NULL;
7112 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
7113 if (channel) {
7114 *channel = chan;
7115 if (chan)
7116 ast_channel_lock(chan);
7118 if (chan) {
7119 if (chan->_state == AST_STATE_UP) {
7120 res = 0;
7121 ast_verb(4, "Channel %s was answered.\n", chan->name);
7123 if (synchronous > 1) {
7124 if (channel)
7125 ast_channel_unlock(chan);
7126 if (ast_pbx_run(chan)) {
7127 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
7128 if (channel)
7129 *channel = NULL;
7130 ast_hangup(chan);
7131 chan = NULL;
7132 res = -1;
7134 } else {
7135 if (ast_pbx_start(chan)) {
7136 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
7137 if (channel) {
7138 *channel = NULL;
7139 ast_channel_unlock(chan);
7141 ast_hangup(chan);
7142 res = -1;
7144 chan = NULL;
7146 } else {
7147 ast_verb(4, "Channel %s was never answered.\n", chan->name);
7149 if (chan->cdr) { /* update the cdr */
7150 /* here we update the status of the call, which sould be busy.
7151 * if that fails then we set the status to failed */
7152 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
7153 ast_cdr_failed(chan->cdr);
7156 if (channel) {
7157 *channel = NULL;
7158 ast_channel_unlock(chan);
7160 ast_hangup(chan);
7161 chan = NULL;
7165 if (res < 0) { /* the call failed for some reason */
7166 if (*reason == 0) { /* if the call failed (not busy or no answer)
7167 * update the cdr with the failed message */
7168 cdr_res = ast_pbx_outgoing_cdr_failed();
7169 if (cdr_res != 0) {
7170 res = cdr_res;
7171 goto outgoing_exten_cleanup;
7175 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
7176 /* check if "failed" exists */
7177 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
7178 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
7179 if (chan) {
7180 char failed_reason[4] = "";
7181 if (!ast_strlen_zero(context))
7182 ast_copy_string(chan->context, context, sizeof(chan->context));
7183 set_ext_pri(chan, "failed", 1);
7184 ast_set_variables(chan, vars);
7185 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
7186 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
7187 if (account)
7188 ast_cdr_setaccount(chan, account);
7189 if (ast_pbx_run(chan)) {
7190 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
7191 ast_hangup(chan);
7193 chan = NULL;
7197 } else {
7198 if (!(as = ast_calloc(1, sizeof(*as)))) {
7199 res = -1;
7200 goto outgoing_exten_cleanup;
7202 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
7203 if (channel) {
7204 *channel = chan;
7205 if (chan)
7206 ast_channel_lock(chan);
7208 if (!chan) {
7209 ast_free(as);
7210 res = -1;
7211 goto outgoing_exten_cleanup;
7213 as->chan = chan;
7214 ast_copy_string(as->context, context, sizeof(as->context));
7215 set_ext_pri(as->chan, exten, priority);
7216 as->timeout = timeout;
7217 ast_set_variables(chan, vars);
7218 if (account)
7219 ast_cdr_setaccount(chan, account);
7220 if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
7221 ast_log(LOG_WARNING, "Failed to start async wait\n");
7222 ast_free(as);
7223 if (channel) {
7224 *channel = NULL;
7225 ast_channel_unlock(chan);
7227 ast_hangup(chan);
7228 res = -1;
7229 goto outgoing_exten_cleanup;
7231 res = 0;
7233 outgoing_exten_cleanup:
7234 ast_variables_destroy(vars);
7235 return res;
7238 struct app_tmp {
7239 char app[256];
7240 char data[256];
7241 struct ast_channel *chan;
7242 pthread_t t;
7245 /*! \brief run the application and free the descriptor once done */
7246 static void *ast_pbx_run_app(void *data)
7248 struct app_tmp *tmp = data;
7249 struct ast_app *app;
7250 app = pbx_findapp(tmp->app);
7251 if (app) {
7252 ast_verb(4, "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
7253 pbx_exec(tmp->chan, app, tmp->data);
7254 } else
7255 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
7256 ast_hangup(tmp->chan);
7257 ast_free(tmp);
7258 return NULL;
7261 int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
7263 struct ast_channel *chan;
7264 struct app_tmp *tmp;
7265 int res = -1, cdr_res = -1;
7266 struct outgoing_helper oh;
7268 memset(&oh, 0, sizeof(oh));
7269 oh.vars = vars;
7270 oh.account = account;
7272 if (locked_channel)
7273 *locked_channel = NULL;
7274 if (ast_strlen_zero(app)) {
7275 res = -1;
7276 goto outgoing_app_cleanup;
7278 if (synchronous) {
7279 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
7280 if (chan) {
7281 ast_set_variables(chan, vars);
7282 if (account)
7283 ast_cdr_setaccount(chan, account);
7284 if (chan->_state == AST_STATE_UP) {
7285 res = 0;
7286 ast_verb(4, "Channel %s was answered.\n", chan->name);
7287 tmp = ast_calloc(1, sizeof(*tmp));
7288 if (!tmp)
7289 res = -1;
7290 else {
7291 ast_copy_string(tmp->app, app, sizeof(tmp->app));
7292 if (appdata)
7293 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
7294 tmp->chan = chan;
7295 if (synchronous > 1) {
7296 if (locked_channel)
7297 ast_channel_unlock(chan);
7298 ast_pbx_run_app(tmp);
7299 } else {
7300 if (locked_channel)
7301 ast_channel_lock(chan);
7302 if (ast_pthread_create_detached(&tmp->t, NULL, ast_pbx_run_app, tmp)) {
7303 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
7304 ast_free(tmp);
7305 if (locked_channel)
7306 ast_channel_unlock(chan);
7307 ast_hangup(chan);
7308 res = -1;
7309 } else {
7310 if (locked_channel)
7311 *locked_channel = chan;
7315 } else {
7316 ast_verb(4, "Channel %s was never answered.\n", chan->name);
7317 if (chan->cdr) { /* update the cdr */
7318 /* here we update the status of the call, which sould be busy.
7319 * if that fails then we set the status to failed */
7320 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
7321 ast_cdr_failed(chan->cdr);
7323 ast_hangup(chan);
7327 if (res < 0) { /* the call failed for some reason */
7328 if (*reason == 0) { /* if the call failed (not busy or no answer)
7329 * update the cdr with the failed message */
7330 cdr_res = ast_pbx_outgoing_cdr_failed();
7331 if (cdr_res != 0) {
7332 res = cdr_res;
7333 goto outgoing_app_cleanup;
7338 } else {
7339 struct async_stat *as;
7340 if (!(as = ast_calloc(1, sizeof(*as)))) {
7341 res = -1;
7342 goto outgoing_app_cleanup;
7344 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
7345 if (!chan) {
7346 ast_free(as);
7347 res = -1;
7348 goto outgoing_app_cleanup;
7350 as->chan = chan;
7351 ast_copy_string(as->app, app, sizeof(as->app));
7352 if (appdata)
7353 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
7354 as->timeout = timeout;
7355 ast_set_variables(chan, vars);
7356 if (account)
7357 ast_cdr_setaccount(chan, account);
7358 /* Start a new thread, and get something handling this channel. */
7359 if (locked_channel)
7360 ast_channel_lock(chan);
7361 if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
7362 ast_log(LOG_WARNING, "Failed to start async wait\n");
7363 ast_free(as);
7364 if (locked_channel)
7365 ast_channel_unlock(chan);
7366 ast_hangup(chan);
7367 res = -1;
7368 goto outgoing_app_cleanup;
7369 } else {
7370 if (locked_channel)
7371 *locked_channel = chan;
7373 res = 0;
7375 outgoing_app_cleanup:
7376 ast_variables_destroy(vars);
7377 return res;
7380 /* this is the guts of destroying a context --
7381 freeing up the structure, traversing and destroying the
7382 extensions, switches, ignorepats, includes, etc. etc. */
7384 static void __ast_internal_context_destroy( struct ast_context *con)
7386 struct ast_include *tmpi;
7387 struct ast_sw *sw;
7388 struct ast_exten *e, *el, *en;
7389 struct ast_ignorepat *ipi;
7390 struct ast_context *tmp = con;
7392 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
7393 struct ast_include *tmpil = tmpi;
7394 tmpi = tmpi->next;
7395 ast_free(tmpil);
7397 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
7398 struct ast_ignorepat *ipl = ipi;
7399 ipi = ipi->next;
7400 ast_free(ipl);
7402 if (tmp->registrar)
7403 ast_free(tmp->registrar);
7405 /* destroy the hash tabs */
7406 if (tmp->root_table) {
7407 ast_hashtab_destroy(tmp->root_table, 0);
7409 /* and destroy the pattern tree */
7410 if (tmp->pattern_tree)
7411 destroy_pattern_tree(tmp->pattern_tree);
7413 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
7414 ast_free(sw);
7415 for (e = tmp->root; e;) {
7416 for (en = e->peer; en;) {
7417 el = en;
7418 en = en->peer;
7419 destroy_exten(el);
7421 el = e;
7422 e = e->next;
7423 destroy_exten(el);
7425 tmp->root = NULL;
7426 ast_rwlock_destroy(&tmp->lock);
7427 ast_free(tmp);
7431 void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar)
7433 struct ast_context *tmp, *tmpl=NULL;
7434 struct ast_exten *exten_item, *prio_item;
7436 for (tmp = list; tmp; ) {
7437 struct ast_context *next = NULL; /* next starting point */
7438 /* The following code used to skip forward to the next
7439 context with matching registrar, but this didn't
7440 make sense; individual priorities registrar'd to
7441 the matching registrar could occur in any context! */
7442 ast_debug(1, "Investigate ctx %s %s\n", tmp->name, tmp->registrar);
7443 if (con) {
7444 for (; tmp; tmpl = tmp, tmp = tmp->next) { /* skip to the matching context */
7445 ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar);
7446 if ( !strcasecmp(tmp->name, con->name) ) {
7447 break; /* found it */
7452 if (!tmp) /* not found, we are done */
7453 break;
7454 ast_wrlock_context(tmp);
7456 if (registrar) {
7457 /* then search thru and remove any extens that match registrar. */
7458 struct ast_hashtab_iter *exten_iter;
7459 struct ast_hashtab_iter *prio_iter;
7460 struct ast_ignorepat *ip, *ipl = NULL, *ipn = NULL;
7461 struct ast_include *i, *pi = NULL, *ni = NULL;
7462 struct ast_sw *sw = NULL;
7464 /* remove any ignorepats whose registrar matches */
7465 for (ip = tmp->ignorepats; ip; ip = ipn) {
7466 ipn = ip->next;
7467 if (!strcmp(ip->registrar, registrar)) {
7468 if (ipl) {
7469 ipl->next = ip->next;
7470 ast_free(ip);
7471 continue; /* don't change ipl */
7472 } else {
7473 tmp->ignorepats = ip->next;
7474 ast_free(ip);
7475 continue; /* don't change ipl */
7478 ipl = ip;
7480 /* remove any includes whose registrar matches */
7481 for (i = tmp->includes; i; i = ni) {
7482 ni = i->next;
7483 if (strcmp(i->registrar, registrar) == 0) {
7484 /* remove from list */
7485 if (pi) {
7486 pi->next = i->next;
7487 /* free include */
7488 ast_free(i);
7489 continue; /* don't change pi */
7490 } else {
7491 tmp->includes = i->next;
7492 /* free include */
7493 ast_free(i);
7494 continue; /* don't change pi */
7497 pi = i;
7499 /* remove any switches whose registrar matches */
7500 AST_LIST_TRAVERSE_SAFE_BEGIN(&tmp->alts, sw, list) {
7501 if (strcmp(sw->registrar,registrar) == 0) {
7502 AST_LIST_REMOVE_CURRENT(list);
7503 ast_free(sw);
7506 AST_LIST_TRAVERSE_SAFE_END
7508 if (tmp->root_table) { /* it is entirely possible that the context is EMPTY */
7509 exten_iter = ast_hashtab_start_traversal(tmp->root_table);
7510 while ((exten_item=ast_hashtab_next(exten_iter))) {
7511 prio_iter = ast_hashtab_start_traversal(exten_item->peer_table);
7512 while ((prio_item=ast_hashtab_next(prio_iter))) {
7513 if (!prio_item->registrar || strcmp(prio_item->registrar, registrar) != 0) {
7514 continue;
7516 ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n",
7517 tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
7518 /* set matchcid to 1 to insure we get a direct match, and NULL registrar to make sure no wildcarding is done */
7519 ast_context_remove_extension_callerid2(tmp, prio_item->exten, prio_item->priority, prio_item->cidmatch, 1, NULL, 1);
7521 ast_hashtab_end_traversal(prio_iter);
7523 ast_hashtab_end_traversal(exten_iter);
7526 /* delete the context if it's registrar matches, is empty, has refcount of 1, */
7527 /* it's not empty, if it has includes, ignorepats, or switches that are registered from
7528 another registrar. It's not empty if there are any extensions */
7529 if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root && !tmp->ignorepats && !tmp->includes && AST_LIST_EMPTY(&tmp->alts)) {
7530 ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
7531 ast_hashtab_remove_this_object(contexttab, tmp);
7533 next = tmp->next;
7534 if (tmpl)
7535 tmpl->next = next;
7536 else
7537 contexts = next;
7538 /* Okay, now we're safe to let it go -- in a sense, we were
7539 ready to let it go as soon as we locked it. */
7540 ast_unlock_context(tmp);
7541 __ast_internal_context_destroy(tmp);
7542 } else {
7543 ast_debug(1,"Couldn't delete ctx %s/%s; refc=%d; tmp.root=%p\n", tmp->name, tmp->registrar,
7544 tmp->refcount, tmp->root);
7545 ast_unlock_context(tmp);
7546 next = tmp->next;
7547 tmpl = tmp;
7549 } else if (con) {
7550 ast_verb(3, "Deleting context %s registrar=%s\n", tmp->name, tmp->registrar);
7551 ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
7552 ast_hashtab_remove_this_object(contexttab, tmp);
7554 next = tmp->next;
7555 if (tmpl)
7556 tmpl->next = next;
7557 else
7558 contexts = next;
7559 /* Okay, now we're safe to let it go -- in a sense, we were
7560 ready to let it go as soon as we locked it. */
7561 ast_unlock_context(tmp);
7562 __ast_internal_context_destroy(tmp);
7565 /* if we have a specific match, we are done, otherwise continue */
7566 tmp = con ? NULL : next;
7570 void ast_context_destroy(struct ast_context *con, const char *registrar)
7572 ast_wrlock_contexts();
7573 __ast_context_destroy(contexts, contexts_table, con,registrar);
7574 ast_unlock_contexts();
7577 static void wait_for_hangup(struct ast_channel *chan, void *data)
7579 int res;
7580 struct ast_frame *f;
7581 double waitsec;
7582 int waittime;
7584 if (ast_strlen_zero(data) || (sscanf(data, "%lg", &waitsec) != 1) || (waitsec < 0))
7585 waitsec = -1;
7586 if (waitsec > -1) {
7587 waittime = waitsec * 1000.0;
7588 ast_safe_sleep(chan, waittime);
7589 } else do {
7590 res = ast_waitfor(chan, -1);
7591 if (res < 0)
7592 return;
7593 f = ast_read(chan);
7594 if (f)
7595 ast_frfree(f);
7596 } while(f);
7600 * \ingroup applications
7602 static int pbx_builtin_proceeding(struct ast_channel *chan, void *data)
7604 ast_indicate(chan, AST_CONTROL_PROCEEDING);
7605 return 0;
7609 * \ingroup applications
7611 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
7613 ast_indicate(chan, AST_CONTROL_PROGRESS);
7614 return 0;
7618 * \ingroup applications
7620 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
7622 ast_indicate(chan, AST_CONTROL_RINGING);
7623 return 0;
7627 * \ingroup applications
7629 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
7631 ast_indicate(chan, AST_CONTROL_BUSY);
7632 /* Don't change state of an UP channel, just indicate
7633 busy in audio */
7634 if (chan->_state != AST_STATE_UP)
7635 ast_setstate(chan, AST_STATE_BUSY);
7636 wait_for_hangup(chan, data);
7637 return -1;
7641 * \ingroup applications
7643 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
7645 ast_indicate(chan, AST_CONTROL_CONGESTION);
7646 /* Don't change state of an UP channel, just indicate
7647 congestion in audio */
7648 if (chan->_state != AST_STATE_UP)
7649 ast_setstate(chan, AST_STATE_BUSY);
7650 wait_for_hangup(chan, data);
7651 return -1;
7655 * \ingroup applications
7657 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
7659 int delay = 0;
7661 if ((chan->_state != AST_STATE_UP) && !ast_strlen_zero(data))
7662 delay = atoi(data);
7664 if (delay < 0) {
7665 delay = 0;
7668 return __ast_answer(chan, delay);
7671 static int pbx_builtin_keepalive(struct ast_channel *chan, void *data)
7673 return AST_PBX_KEEPALIVE;
7676 static int pbx_builtin_incomplete(struct ast_channel *chan, void *data)
7678 char *options = data;
7679 int answer = 1;
7681 /* Some channels can receive DTMF in unanswered state; some cannot */
7682 if (!ast_strlen_zero(options) && strchr(options, 'n')) {
7683 answer = 0;
7686 /* If the channel is hungup, stop waiting */
7687 if (ast_check_hangup(chan)) {
7688 return -1;
7689 } else if (chan->_state != AST_STATE_UP && answer) {
7690 __ast_answer(chan, 0);
7693 return AST_PBX_INCOMPLETE;
7696 AST_APP_OPTIONS(resetcdr_opts, {
7697 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
7698 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
7699 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
7700 AST_APP_OPTION('e', AST_CDR_FLAG_POST_ENABLE),
7704 * \ingroup applications
7706 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
7708 char *args;
7709 struct ast_flags flags = { 0 };
7711 if (!ast_strlen_zero(data)) {
7712 args = ast_strdupa(data);
7713 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
7716 ast_cdr_reset(chan->cdr, &flags);
7718 return 0;
7722 * \ingroup applications
7724 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
7726 /* Copy the AMA Flags as specified */
7727 ast_cdr_setamaflags(chan, data ? data : "");
7728 return 0;
7732 * \ingroup applications
7734 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
7736 if (!ast_strlen_zero(data)) {
7737 int cause;
7738 char *endptr;
7740 if ((cause = ast_str2cause(data)) > -1) {
7741 chan->hangupcause = cause;
7742 return -1;
7745 cause = strtol((const char *) data, &endptr, 10);
7746 if (cause != 0 || (data != endptr)) {
7747 chan->hangupcause = cause;
7748 return -1;
7751 ast_log(LOG_WARNING, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
7754 if (!chan->hangupcause) {
7755 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
7758 return -1;
7762 * \ingroup applications
7764 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
7766 char *s, *ts, *branch1, *branch2, *branch;
7767 struct ast_timing timing;
7769 if (ast_strlen_zero(data)) {
7770 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?'labeliftrue':'labeliffalse'\n");
7771 return -1;
7774 ts = s = ast_strdupa(data);
7776 /* Separate the Goto path */
7777 strsep(&ts, "?");
7778 branch1 = strsep(&ts,":");
7779 branch2 = strsep(&ts,"");
7781 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
7782 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
7783 branch = branch1;
7784 else
7785 branch = branch2;
7787 if (ast_strlen_zero(branch)) {
7788 ast_debug(1, "Not taking any branch\n");
7789 return 0;
7792 return pbx_builtin_goto(chan, branch);
7796 * \ingroup applications
7798 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
7800 char *s, *appname;
7801 struct ast_timing timing;
7802 struct ast_app *app;
7803 static const char *usage = "ExecIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?<appname>[(<appargs>)]";
7805 if (ast_strlen_zero(data)) {
7806 ast_log(LOG_WARNING, "%s\n", usage);
7807 return -1;
7810 appname = ast_strdupa(data);
7812 s = strsep(&appname, "?"); /* Separate the timerange and application name/data */
7813 if (!appname) { /* missing application */
7814 ast_log(LOG_WARNING, "%s\n", usage);
7815 return -1;
7818 if (!ast_build_timing(&timing, s)) {
7819 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
7820 return -1;
7823 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
7824 return 0;
7826 /* now split appname(appargs) */
7827 if ((s = strchr(appname, '('))) {
7828 char *e;
7829 *s++ = '\0';
7830 if ((e = strrchr(s, ')')))
7831 *e = '\0';
7832 else
7833 ast_log(LOG_WARNING, "Failed to find closing parenthesis\n");
7837 if ((app = pbx_findapp(appname))) {
7838 return pbx_exec(chan, app, S_OR(s, ""));
7839 } else {
7840 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
7841 return -1;
7846 * \ingroup applications
7848 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
7850 double s;
7851 int ms;
7853 /* Wait for "n" seconds */
7854 if (data && (s = atof(data)) > 0.0) {
7855 ms = s * 1000.0;
7856 return ast_safe_sleep(chan, ms);
7858 return 0;
7862 * \ingroup applications
7864 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
7866 int ms, res;
7867 double s;
7868 struct ast_flags flags = {0};
7869 char *opts[1] = { NULL };
7870 char *parse;
7871 AST_DECLARE_APP_ARGS(args,
7872 AST_APP_ARG(timeout);
7873 AST_APP_ARG(options);
7876 if (!ast_strlen_zero(data)) {
7877 parse = ast_strdupa(data);
7878 AST_STANDARD_APP_ARGS(args, parse);
7879 } else
7880 memset(&args, 0, sizeof(args));
7882 if (args.options)
7883 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
7885 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
7886 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
7887 } else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
7888 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
7889 } else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
7890 const struct ind_tone_zone_sound *ts = ast_get_indication_tone(chan->zone, "dial");
7891 if (ts)
7892 ast_playtones_start(chan, 0, ts->data, 0);
7893 else
7894 ast_tonepair_start(chan, 350, 440, 0, 0);
7896 /* Wait for "n" seconds */
7897 if (args.timeout && (s = atof(args.timeout)) > 0)
7898 ms = s * 1000.0;
7899 else if (chan->pbx)
7900 ms = chan->pbx->rtimeoutms;
7901 else
7902 ms = 10000;
7904 res = ast_waitfordigit(chan, ms);
7905 if (!res) {
7906 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
7907 ast_verb(3, "Timeout on %s, continuing...\n", chan->name);
7908 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
7909 ast_verb(3, "Call timeout on %s, checking for 'T'\n", chan->name);
7910 res = -1;
7911 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
7912 ast_verb(3, "Timeout on %s, going to 't'\n", chan->name);
7913 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
7914 } else {
7915 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
7916 res = -1;
7920 if (ast_test_flag(&flags, WAITEXTEN_MOH))
7921 ast_indicate(chan, AST_CONTROL_UNHOLD);
7922 else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE))
7923 ast_playtones_stop(chan);
7925 return res;
7929 * \ingroup applications
7931 static int pbx_builtin_background(struct ast_channel *chan, void *data)
7933 int res = 0;
7934 int mres = 0;
7935 struct ast_flags flags = {0};
7936 char *parse;
7937 AST_DECLARE_APP_ARGS(args,
7938 AST_APP_ARG(filename);
7939 AST_APP_ARG(options);
7940 AST_APP_ARG(lang);
7941 AST_APP_ARG(context);
7944 if (ast_strlen_zero(data)) {
7945 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
7946 return -1;
7949 parse = ast_strdupa(data);
7951 AST_STANDARD_APP_ARGS(args, parse);
7953 if (ast_strlen_zero(args.lang))
7954 args.lang = (char *)chan->language; /* XXX this is const */
7956 if (ast_strlen_zero(args.context))
7957 args.context = chan->context;
7959 if (args.options) {
7960 if (!strcasecmp(args.options, "skip"))
7961 flags.flags = BACKGROUND_SKIP;
7962 else if (!strcasecmp(args.options, "noanswer"))
7963 flags.flags = BACKGROUND_NOANSWER;
7964 else
7965 ast_app_parse_options(background_opts, &flags, NULL, args.options);
7968 /* Answer if need be */
7969 if (chan->_state != AST_STATE_UP) {
7970 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
7971 goto done;
7972 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
7973 res = ast_answer(chan);
7977 if (!res) {
7978 char *back = args.filename;
7979 char *front;
7981 ast_stopstream(chan); /* Stop anything playing */
7982 /* Stream the list of files */
7983 while (!res && (front = strsep(&back, "&")) ) {
7984 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
7985 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
7986 res = 0;
7987 mres = 1;
7988 break;
7990 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
7991 res = ast_waitstream(chan, "");
7992 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
7993 res = ast_waitstream_exten(chan, args.context);
7994 } else {
7995 res = ast_waitstream(chan, AST_DIGIT_ANY);
7997 ast_stopstream(chan);
8000 if (args.context != chan->context && res) {
8001 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
8002 ast_copy_string(chan->context, args.context, sizeof(chan->context));
8003 chan->priority = 0;
8004 res = 0;
8006 done:
8007 pbx_builtin_setvar_helper(chan, "BACKGROUNDSTATUS", mres ? "FAILED" : "SUCCESS");
8008 return res;
8011 /*! Goto
8012 * \ingroup applications
8014 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
8016 int res = ast_parseable_goto(chan, data);
8017 if (!res)
8018 ast_verb(3, "Goto (%s,%s,%d)\n", chan->context, chan->exten, chan->priority + 1);
8019 return res;
8023 int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
8025 struct ast_var_t *variables;
8026 const char *var, *val;
8027 int total = 0;
8029 if (!chan)
8030 return 0;
8032 (*buf)->used = 0;
8033 (*buf)->str[0] = '\0';
8035 ast_channel_lock(chan);
8037 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
8038 if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
8039 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
8041 if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
8042 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
8043 break;
8044 } else
8045 total++;
8046 } else
8047 break;
8050 ast_channel_unlock(chan);
8052 return total;
8055 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
8057 struct ast_var_t *variables;
8058 const char *ret = NULL;
8059 int i;
8060 struct varshead *places[2] = { NULL, &globals };
8062 if (!name)
8063 return NULL;
8065 if (chan) {
8066 ast_channel_lock(chan);
8067 places[0] = &chan->varshead;
8070 for (i = 0; i < 2; i++) {
8071 if (!places[i])
8072 continue;
8073 if (places[i] == &globals)
8074 ast_rwlock_rdlock(&globalslock);
8075 AST_LIST_TRAVERSE(places[i], variables, entries) {
8076 if (!strcmp(name, ast_var_name(variables))) {
8077 ret = ast_var_value(variables);
8078 break;
8081 if (places[i] == &globals)
8082 ast_rwlock_unlock(&globalslock);
8083 if (ret)
8084 break;
8087 if (chan)
8088 ast_channel_unlock(chan);
8090 return ret;
8093 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
8095 struct ast_var_t *newvariable;
8096 struct varshead *headp;
8098 if (name[strlen(name)-1] == ')') {
8099 char *function = ast_strdupa(name);
8101 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
8102 ast_func_write(chan, function, value);
8103 return;
8106 if (chan) {
8107 ast_channel_lock(chan);
8108 headp = &chan->varshead;
8109 } else {
8110 ast_rwlock_wrlock(&globalslock);
8111 headp = &globals;
8114 if (value) {
8115 if (headp == &globals)
8116 ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
8117 newvariable = ast_var_assign(name, value);
8118 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
8121 if (chan)
8122 ast_channel_unlock(chan);
8123 else
8124 ast_rwlock_unlock(&globalslock);
8127 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
8129 struct ast_var_t *newvariable;
8130 struct varshead *headp;
8131 const char *nametail = name;
8133 if (name[strlen(name) - 1] == ')') {
8134 char *function = ast_strdupa(name);
8136 ast_func_write(chan, function, value);
8137 return;
8140 if (chan) {
8141 ast_channel_lock(chan);
8142 headp = &chan->varshead;
8143 } else {
8144 ast_rwlock_wrlock(&globalslock);
8145 headp = &globals;
8148 /* For comparison purposes, we have to strip leading underscores */
8149 if (*nametail == '_') {
8150 nametail++;
8151 if (*nametail == '_')
8152 nametail++;
8155 AST_LIST_TRAVERSE (headp, newvariable, entries) {
8156 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
8157 /* there is already such a variable, delete it */
8158 AST_LIST_REMOVE(headp, newvariable, entries);
8159 ast_var_delete(newvariable);
8160 break;
8164 if (value) {
8165 if (headp == &globals)
8166 ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
8167 newvariable = ast_var_assign(name, value);
8168 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
8169 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
8170 "Channel: %s\r\n"
8171 "Variable: %s\r\n"
8172 "Value: %s\r\n"
8173 "Uniqueid: %s\r\n",
8174 chan ? chan->name : "none", name, value,
8175 chan ? chan->uniqueid : "none");
8178 if (chan)
8179 ast_channel_unlock(chan);
8180 else
8181 ast_rwlock_unlock(&globalslock);
8184 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
8186 char *name, *value, *mydata;
8188 if (ast_compat_app_set) {
8189 return pbx_builtin_setvar_multiple(chan, data);
8192 if (ast_strlen_zero(data)) {
8193 ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
8194 return 0;
8197 mydata = ast_strdupa(data);
8198 name = strsep(&mydata, "=");
8199 value = mydata;
8200 if (strchr(name, ' '))
8201 ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
8203 pbx_builtin_setvar_helper(chan, name, value);
8204 return(0);
8207 int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *vdata)
8209 char *data;
8210 int x;
8211 AST_DECLARE_APP_ARGS(args,
8212 AST_APP_ARG(pair)[24];
8214 AST_DECLARE_APP_ARGS(pair,
8215 AST_APP_ARG(name);
8216 AST_APP_ARG(value);
8219 if (ast_strlen_zero(vdata)) {
8220 ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
8221 return 0;
8224 data = ast_strdupa(vdata);
8225 AST_STANDARD_APP_ARGS(args, data);
8227 for (x = 0; x < args.argc; x++) {
8228 AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
8229 if (pair.argc == 2) {
8230 pbx_builtin_setvar_helper(chan, pair.name, pair.value);
8231 if (strchr(pair.name, ' '))
8232 ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
8233 } else if (!chan) {
8234 ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '='\n", pair.name);
8235 } else {
8236 ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, chan->exten, chan->context, chan->priority);
8240 return 0;
8243 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
8245 char *name;
8246 char *value;
8247 char *channel;
8248 char tmp[VAR_BUF_SIZE];
8249 static int deprecation_warning = 0;
8251 if (ast_strlen_zero(data)) {
8252 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
8253 return 0;
8255 tmp[0] = 0;
8256 if (!deprecation_warning) {
8257 ast_log(LOG_WARNING, "ImportVar is deprecated. Please use Set(varname=${IMPORT(channel,variable)}) instead.\n");
8258 deprecation_warning = 1;
8261 value = ast_strdupa(data);
8262 name = strsep(&value,"=");
8263 channel = strsep(&value,",");
8264 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
8265 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
8266 if (chan2) {
8267 char *s = alloca(strlen(value) + 4);
8268 if (s) {
8269 sprintf(s, "${%s}", value);
8270 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
8272 ast_channel_unlock(chan2);
8274 pbx_builtin_setvar_helper(chan, name, tmp);
8277 return(0);
8280 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
8282 return 0;
8285 void pbx_builtin_clear_globals(void)
8287 struct ast_var_t *vardata;
8289 ast_rwlock_wrlock(&globalslock);
8290 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
8291 ast_var_delete(vardata);
8292 ast_rwlock_unlock(&globalslock);
8295 int pbx_checkcondition(const char *condition)
8297 int res;
8298 if (ast_strlen_zero(condition)) { /* NULL or empty strings are false */
8299 return 0;
8300 } else if (sscanf(condition, "%d", &res) == 1) { /* Numbers are evaluated for truth */
8301 return res;
8302 } else { /* Strings are true */
8303 return 1;
8307 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
8309 char *condition, *branch1, *branch2, *branch;
8310 char *stringp;
8312 if (ast_strlen_zero(data)) {
8313 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
8314 return 0;
8317 stringp = ast_strdupa(data);
8318 condition = strsep(&stringp,"?");
8319 branch1 = strsep(&stringp,":");
8320 branch2 = strsep(&stringp,"");
8321 branch = pbx_checkcondition(condition) ? branch1 : branch2;
8323 if (ast_strlen_zero(branch)) {
8324 ast_debug(1, "Not taking any branch\n");
8325 return 0;
8328 return pbx_builtin_goto(chan, branch);
8331 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
8333 char tmp[256];
8334 char *number = tmp;
8335 char *options;
8337 if (ast_strlen_zero(data)) {
8338 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
8339 return -1;
8341 ast_copy_string(tmp, data, sizeof(tmp));
8342 strsep(&number, ",");
8343 options = strsep(&number, ",");
8344 if (options) {
8345 if ( strcasecmp(options, "f") && strcasecmp(options, "m") &&
8346 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
8347 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
8348 return -1;
8352 if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
8353 ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
8356 return 0;
8359 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
8361 int res = 0;
8363 if (data)
8364 res = ast_say_digit_str(chan, data, "", chan->language);
8365 return res;
8368 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
8370 int res = 0;
8372 if (data)
8373 res = ast_say_character_str(chan, data, "", chan->language);
8374 return res;
8377 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
8379 int res = 0;
8381 if (data)
8382 res = ast_say_phonetic_str(chan, data, "", chan->language);
8383 return res;
8386 static void device_state_cb(const struct ast_event *event, void *unused)
8388 const char *device;
8389 struct statechange *sc;
8391 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
8392 if (ast_strlen_zero(device)) {
8393 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
8394 return;
8397 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(device) + 1)))
8398 return;
8399 strcpy(sc->dev, device);
8400 if (ast_taskprocessor_push(device_state_tps, handle_statechange, sc) < 0) {
8401 ast_free(sc);
8405 int load_pbx(void)
8407 int x;
8409 /* Initialize the PBX */
8410 ast_verb(1, "Asterisk PBX Core Initializing\n");
8411 if (!(device_state_tps = ast_taskprocessor_get("pbx-core", 0))) {
8412 ast_log(LOG_WARNING, "failed to create pbx-core taskprocessor\n");
8415 ast_verb(1, "Registering builtin applications:\n");
8416 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
8417 __ast_custom_function_register(&exception_function, NULL);
8419 /* Register builtin applications */
8420 for (x = 0; x < sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
8421 ast_verb(1, "[%s]\n", builtins[x].name);
8422 if (ast_register_application2(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description, NULL)) {
8423 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
8424 return -1;
8428 /* Register manager application */
8429 ast_manager_register2("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan, "List dialplan", mandescr_show_dialplan);
8431 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, device_state_cb, NULL,
8432 AST_EVENT_IE_END))) {
8433 return -1;
8436 return 0;
8438 static int conlock_wrlock_version = 0;
8440 int ast_wrlock_contexts_version(void)
8442 return conlock_wrlock_version;
8446 * Lock context list functions ...
8448 int ast_wrlock_contexts()
8450 int res = ast_rwlock_wrlock(&conlock);
8451 if (!res)
8452 ast_atomic_fetchadd_int(&conlock_wrlock_version, 1);
8453 return res;
8456 int ast_rdlock_contexts()
8458 return ast_rwlock_rdlock(&conlock);
8461 int ast_unlock_contexts()
8463 return ast_rwlock_unlock(&conlock);
8467 * Lock context ...
8469 int ast_wrlock_context(struct ast_context *con)
8471 return ast_rwlock_wrlock(&con->lock);
8474 int ast_rdlock_context(struct ast_context *con)
8476 return ast_rwlock_rdlock(&con->lock);
8479 int ast_unlock_context(struct ast_context *con)
8481 return ast_rwlock_unlock(&con->lock);
8485 * Name functions ...
8487 const char *ast_get_context_name(struct ast_context *con)
8489 return con ? con->name : NULL;
8492 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
8494 return exten ? exten->parent : NULL;
8497 const char *ast_get_extension_name(struct ast_exten *exten)
8499 return exten ? exten->exten : NULL;
8502 const char *ast_get_extension_label(struct ast_exten *exten)
8504 return exten ? exten->label : NULL;
8507 const char *ast_get_include_name(struct ast_include *inc)
8509 return inc ? inc->name : NULL;
8512 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
8514 return ip ? ip->pattern : NULL;
8517 int ast_get_extension_priority(struct ast_exten *exten)
8519 return exten ? exten->priority : -1;
8523 * Registrar info functions ...
8525 const char *ast_get_context_registrar(struct ast_context *c)
8527 return c ? c->registrar : NULL;
8530 const char *ast_get_extension_registrar(struct ast_exten *e)
8532 return e ? e->registrar : NULL;
8535 const char *ast_get_include_registrar(struct ast_include *i)
8537 return i ? i->registrar : NULL;
8540 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
8542 return ip ? ip->registrar : NULL;
8545 int ast_get_extension_matchcid(struct ast_exten *e)
8547 return e ? e->matchcid : 0;
8550 const char *ast_get_extension_cidmatch(struct ast_exten *e)
8552 return e ? e->cidmatch : NULL;
8555 const char *ast_get_extension_app(struct ast_exten *e)
8557 return e ? e->app : NULL;
8560 void *ast_get_extension_app_data(struct ast_exten *e)
8562 return e ? e->data : NULL;
8565 const char *ast_get_switch_name(struct ast_sw *sw)
8567 return sw ? sw->name : NULL;
8570 const char *ast_get_switch_data(struct ast_sw *sw)
8572 return sw ? sw->data : NULL;
8575 int ast_get_switch_eval(struct ast_sw *sw)
8577 return sw->eval;
8580 const char *ast_get_switch_registrar(struct ast_sw *sw)
8582 return sw ? sw->registrar : NULL;
8586 * Walking functions ...
8588 struct ast_context *ast_walk_contexts(struct ast_context *con)
8590 return con ? con->next : contexts;
8593 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
8594 struct ast_exten *exten)
8596 if (!exten)
8597 return con ? con->root : NULL;
8598 else
8599 return exten->next;
8602 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
8603 struct ast_sw *sw)
8605 if (!sw)
8606 return con ? AST_LIST_FIRST(&con->alts) : NULL;
8607 else
8608 return AST_LIST_NEXT(sw, list);
8611 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
8612 struct ast_exten *priority)
8614 return priority ? priority->peer : exten;
8617 struct ast_include *ast_walk_context_includes(struct ast_context *con,
8618 struct ast_include *inc)
8620 if (!inc)
8621 return con ? con->includes : NULL;
8622 else
8623 return inc->next;
8626 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
8627 struct ast_ignorepat *ip)
8629 if (!ip)
8630 return con ? con->ignorepats : NULL;
8631 else
8632 return ip->next;
8635 int ast_context_verify_includes(struct ast_context *con)
8637 struct ast_include *inc = NULL;
8638 int res = 0;
8640 while ( (inc = ast_walk_context_includes(con, inc)) ) {
8641 if (ast_context_find(inc->rname))
8642 continue;
8644 res = -1;
8645 ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
8646 ast_get_context_name(con), inc->rname);
8647 break;
8650 return res;
8654 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
8656 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
8658 if (!chan)
8659 return -2;
8661 if (context == NULL)
8662 context = chan->context;
8663 if (exten == NULL)
8664 exten = chan->exten;
8666 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
8667 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
8668 return goto_func(chan, context, exten, priority);
8669 else
8670 return -3;
8673 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
8675 return __ast_goto_if_exists(chan, context, exten, priority, 0);
8678 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
8680 return __ast_goto_if_exists(chan, context, exten, priority, 1);
8683 static int pbx_parseable_goto(struct ast_channel *chan, const char *goto_string, int async)
8685 char *exten, *pri, *context;
8686 char *stringp;
8687 int ipri;
8688 int mode = 0;
8690 if (ast_strlen_zero(goto_string)) {
8691 ast_log(LOG_WARNING, "Goto requires an argument ([[context,]extension,]priority)\n");
8692 return -1;
8694 stringp = ast_strdupa(goto_string);
8695 context = strsep(&stringp, ","); /* guaranteed non-null */
8696 exten = strsep(&stringp, ",");
8697 pri = strsep(&stringp, ",");
8698 if (!exten) { /* Only a priority in this one */
8699 pri = context;
8700 exten = NULL;
8701 context = NULL;
8702 } else if (!pri) { /* Only an extension and priority in this one */
8703 pri = exten;
8704 exten = context;
8705 context = NULL;
8707 if (*pri == '+') {
8708 mode = 1;
8709 pri++;
8710 } else if (*pri == '-') {
8711 mode = -1;
8712 pri++;
8714 if (sscanf(pri, "%d", &ipri) != 1) {
8715 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
8716 pri, chan->cid.cid_num)) < 1) {
8717 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
8718 return -1;
8719 } else
8720 mode = 0;
8722 /* At this point we have a priority and maybe an extension and a context */
8724 if (mode)
8725 ipri = chan->priority + (ipri * mode);
8727 if (async)
8728 ast_async_goto(chan, context, exten, ipri);
8729 else
8730 ast_explicit_goto(chan, context, exten, ipri);
8732 return 0;
8736 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
8738 return pbx_parseable_goto(chan, goto_string, 0);
8741 int ast_async_parseable_goto(struct ast_channel *chan, const char *goto_string)
8743 return pbx_parseable_goto(chan, goto_string, 1);