remove the dlfcn compatibility stuff, because no platforms that Asterisk currently...
[asterisk-bristuff.git] / main / pbx.c
blobf4e5a150cd9106a106eb7d55b779003568dff15c
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, 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 <sys/types.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <sys/time.h>
39 #include <limits.h>
41 #include "asterisk/lock.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/options.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/file.h"
48 #include "asterisk/callerid.h"
49 #include "asterisk/cdr.h"
50 #include "asterisk/config.h"
51 #include "asterisk/term.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/threadstorage.h"
65 /*!
66 * \note I M P O R T A N T :
68 * The speed of extension handling will likely be among the most important
69 * aspects of this PBX. The switching scheme as it exists right now isn't
70 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
71 * of priorities, but a constant search time here would be great ;-)
75 #ifdef LOW_MEMORY
76 #define EXT_DATA_SIZE 256
77 #else
78 #define EXT_DATA_SIZE 8192
79 #endif
81 #define SWITCH_DATA_LENGTH 256
83 #define VAR_BUF_SIZE 4096
85 #define VAR_NORMAL 1
86 #define VAR_SOFTTRAN 2
87 #define VAR_HARDTRAN 3
89 #define BACKGROUND_SKIP (1 << 0)
90 #define BACKGROUND_NOANSWER (1 << 1)
91 #define BACKGROUND_MATCHEXTEN (1 << 2)
92 #define BACKGROUND_PLAYBACK (1 << 3)
94 AST_APP_OPTIONS(background_opts, {
95 AST_APP_OPTION('s', BACKGROUND_SKIP),
96 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
97 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
98 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
99 });
101 #define WAITEXTEN_MOH (1 << 0)
103 AST_APP_OPTIONS(waitexten_opts, {
104 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
107 struct ast_context;
109 AST_THREADSTORAGE(switch_data, switch_data_init);
112 \brief ast_exten: An extension
113 The dialplan is saved as a linked list with each context
114 having it's own linked list of extensions - one item per
115 priority.
117 struct ast_exten {
118 char *exten; /*!< Extension name */
119 int matchcid; /*!< Match caller id ? */
120 const char *cidmatch; /*!< Caller id to match for this extension */
121 int priority; /*!< Priority */
122 const char *label; /*!< Label */
123 struct ast_context *parent; /*!< The context this extension belongs to */
124 const char *app; /*!< Application to execute */
125 void *data; /*!< Data to use (arguments) */
126 void (*datad)(void *); /*!< Data destructor */
127 struct ast_exten *peer; /*!< Next higher priority with our extension */
128 const char *registrar; /*!< Registrar */
129 struct ast_exten *next; /*!< Extension with a greater ID */
130 char stuff[0];
133 /*! \brief ast_include: include= support in extensions.conf */
134 struct ast_include {
135 const char *name;
136 const char *rname; /*!< Context to include */
137 const char *registrar; /*!< Registrar */
138 int hastime; /*!< If time construct exists */
139 struct ast_timing timing; /*!< time construct */
140 struct ast_include *next; /*!< Link them together */
141 char stuff[0];
144 /*! \brief ast_sw: Switch statement in extensions.conf */
145 struct ast_sw {
146 char *name;
147 const char *registrar; /*!< Registrar */
148 char *data; /*!< Data load */
149 int eval;
150 AST_LIST_ENTRY(ast_sw) list;
151 char stuff[0];
154 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
155 struct ast_ignorepat {
156 const char *registrar;
157 struct ast_ignorepat *next;
158 const char pattern[0];
161 /*! \brief ast_context: An extension context */
162 struct ast_context {
163 ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
164 struct ast_exten *root; /*!< The root of the list of extensions */
165 struct ast_context *next; /*!< Link them together */
166 struct ast_include *includes; /*!< Include other contexts */
167 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
168 const char *registrar; /*!< Registrar */
169 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
170 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
171 char name[0]; /*!< Name of the context */
175 /*! \brief ast_app: A registered application */
176 struct ast_app {
177 int (*execute)(struct ast_channel *chan, void *data);
178 const char *synopsis; /*!< Synopsis text for 'show applications' */
179 const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
180 AST_LIST_ENTRY(ast_app) list; /*!< Next app in list */
181 struct module *module; /*!< Module this app belongs to */
182 char name[0]; /*!< Name of the application */
185 /*! \brief ast_state_cb: An extension state notify register item */
186 struct ast_state_cb {
187 int id;
188 void *data;
189 ast_state_cb_type callback;
190 struct ast_state_cb *next;
193 /*! \brief Structure for dial plan hints
195 \note Hints are pointers from an extension in the dialplan to one or
196 more devices (tech/name) */
197 struct ast_hint {
198 struct ast_exten *exten; /*!< Extension */
199 int laststate; /*!< Last known state */
200 struct ast_state_cb *callbacks; /*!< Callback list for this extension */
201 AST_LIST_ENTRY(ast_hint) list; /*!< Pointer to next hint in list */
204 static const struct cfextension_states {
205 int extension_state;
206 const char * const text;
207 } extension_states[] = {
208 { AST_EXTENSION_NOT_INUSE, "Idle" },
209 { AST_EXTENSION_INUSE, "InUse" },
210 { AST_EXTENSION_BUSY, "Busy" },
211 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
212 { AST_EXTENSION_RINGING, "Ringing" },
213 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
214 { AST_EXTENSION_ONHOLD, "Hold" },
215 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
218 static int pbx_builtin_answer(struct ast_channel *, void *);
219 static int pbx_builtin_goto(struct ast_channel *, void *);
220 static int pbx_builtin_hangup(struct ast_channel *, void *);
221 static int pbx_builtin_background(struct ast_channel *, void *);
222 static int pbx_builtin_wait(struct ast_channel *, void *);
223 static int pbx_builtin_waitexten(struct ast_channel *, void *);
224 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
225 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
226 static int pbx_builtin_ringing(struct ast_channel *, void *);
227 static int pbx_builtin_progress(struct ast_channel *, void *);
228 static int pbx_builtin_congestion(struct ast_channel *, void *);
229 static int pbx_builtin_busy(struct ast_channel *, void *);
230 static int pbx_builtin_setglobalvar(struct ast_channel *, void *);
231 static int pbx_builtin_noop(struct ast_channel *, void *);
232 static int pbx_builtin_gotoif(struct ast_channel *, void *);
233 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
234 static int pbx_builtin_execiftime(struct ast_channel *, void *);
235 static int pbx_builtin_saynumber(struct ast_channel *, void *);
236 static int pbx_builtin_saydigits(struct ast_channel *, void *);
237 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
238 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
239 int pbx_builtin_setvar(struct ast_channel *, void *);
240 static int pbx_builtin_importvar(struct ast_channel *, void *);
242 AST_MUTEX_DEFINE_STATIC(globalslock);
243 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
245 static int autofallthrough = 1;
247 AST_MUTEX_DEFINE_STATIC(maxcalllock);
248 static int countcalls;
250 static AST_LIST_HEAD_STATIC(acf_root, ast_custom_function);
252 /*! \brief Declaration of builtin applications */
253 static struct pbx_builtin {
254 char name[AST_MAX_APP];
255 int (*execute)(struct ast_channel *chan, void *data);
256 char *synopsis;
257 char *description;
258 } builtins[] =
260 /* These applications are built into the PBX core and do not
261 need separate modules */
263 { "Answer", pbx_builtin_answer,
264 "Answer a channel if ringing",
265 " Answer([delay]): If the call has not been answered, this application will\n"
266 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
267 "Asterisk will wait this number of milliseconds before returning to\n"
268 "the dialplan after answering the call.\n"
271 { "BackGround", pbx_builtin_background,
272 "Play an audio file while waiting for digits of an extension to go to.",
273 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
274 "This application will play the given list of files (do not put extension)\n"
275 "while waiting for an extension to be dialed by the calling channel. To\n"
276 "continue waiting for digits after this application has finished playing\n"
277 "files, the WaitExten application should be used. The 'langoverride' option\n"
278 "explicitly specifies which language to attempt to use for the requested sound\n"
279 "files. If a 'context' is specified, this is the dialplan context that this\n"
280 "application will use when exiting to a dialed extension."
281 " If one of the requested sound files does not exist, call processing will be\n"
282 "terminated.\n"
283 " Options:\n"
284 " s - Causes the playback of the message to be skipped\n"
285 " if the channel is not in the 'up' state (i.e. it\n"
286 " hasn't been answered yet). If this happens, the\n"
287 " application will return immediately.\n"
288 " n - Don't answer the channel before playing the files.\n"
289 " m - Only break if a digit hit matches a one digit\n"
290 " extension in the destination context.\n"
291 "See Also: Playback (application) -- Play sound file(s) to the channel,\n"
292 " that cannot be interrupted\n"
295 { "Busy", pbx_builtin_busy,
296 "Indicate the Busy condition",
297 " Busy([timeout]): This application will indicate the busy condition to\n"
298 "the calling channel. If the optional timeout is specified, the calling channel\n"
299 "will be hung up after the specified number of seconds. Otherwise, this\n"
300 "application will wait until the calling channel hangs up.\n"
303 { "Congestion", pbx_builtin_congestion,
304 "Indicate the Congestion condition",
305 " Congestion([timeout]): This application will indicate the congestion\n"
306 "condition to the calling channel. If the optional timeout is specified, the\n"
307 "calling channel will be hung up after the specified number of seconds.\n"
308 "Otherwise, this application will wait until the calling channel hangs up.\n"
311 { "Goto", pbx_builtin_goto,
312 "Jump to a particular priority, extension, or context",
313 " Goto([[context|]extension|]priority): This application will set the current\n"
314 "context, extension, and priority in the channel structure. After it completes, the\n"
315 "pbx engine will continue dialplan execution at the specified location.\n"
316 "If no specific extension, or extension and context, are specified, then this\n"
317 "application will just set the specified priority of the current extension.\n"
318 " At least a priority is required as an argument, or the goto will return a -1,\n"
319 "and the channel and call will be terminated.\n"
320 " If the location that is put into the channel information is bogus, and asterisk cannot\n"
321 "find that location in the dialplan,\n"
322 "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
323 "extension in the current context. If that does not exist, it will try to execute the\n"
324 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
325 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
326 "What this means is that, for example, you specify a context that does not exist, then\n"
327 "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
330 { "GotoIf", pbx_builtin_gotoif,
331 "Conditional goto",
332 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
333 "context, extension, and priority in the channel structure based on the evaluation of\n"
334 "the given condition. After this application completes, the\n"
335 "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
336 "The channel will continue at\n"
337 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
338 "false. The labels are specified with the same syntax as used within the Goto\n"
339 "application. If the label chosen by the condition is omitted, no jump is\n"
340 "performed, and the execution passes to the next instruction.\n"
341 "If the target location is bogus, and does not exist, the execution engine will try \n"
342 "to find and execute the code in the 'i' (invalid)\n"
343 "extension in the current context. If that does not exist, it will try to execute the\n"
344 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
345 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
346 "Remember that this command can set the current context, and if the context specified\n"
347 "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
348 "the channel and call will both be terminated!\n"
351 { "GotoIfTime", pbx_builtin_gotoiftime,
352 "Conditional Goto based on the current time",
353 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
354 "This application will set the context, extension, and priority in the channel structure\n"
355 "if the current time matches the given time specification. Otherwise, nothing is done.\n"
356 "Further information on the time specification can be found in examples\n"
357 "illustrating how to do time-based context includes in the dialplan.\n"
358 "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
361 { "ExecIfTime", pbx_builtin_execiftime,
362 "Conditional application execution based on the current time",
363 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
364 "This application will execute the specified dialplan application, with optional\n"
365 "arguments, if the current time matches the given time specification.\n"
368 { "Hangup", pbx_builtin_hangup,
369 "Hang up the calling channel",
370 " Hangup([causecode]): This application will hang up the calling channel.\n"
371 "If a causecode is given the channel's hangup cause will be set to the given\n"
372 "value.\n"
375 { "NoOp", pbx_builtin_noop,
376 "Do Nothing",
377 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
378 "purposes. Any text that is provided as arguments to this application can be\n"
379 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
380 "variables or functions without having any effect."
383 { "Progress", pbx_builtin_progress,
384 "Indicate progress",
385 " Progress(): This application will request that in-band progress information\n"
386 "be provided to the calling channel.\n"
389 { "ResetCDR", pbx_builtin_resetcdr,
390 "Resets the Call Data Record",
391 " ResetCDR([options]): This application causes the Call Data Record to be\n"
392 "reset.\n"
393 " Options:\n"
394 " w -- Store the current CDR record before resetting it.\n"
395 " a -- Store any stacked records.\n"
396 " v -- Save CDR variables.\n"
399 { "Ringing", pbx_builtin_ringing,
400 "Indicate ringing tone",
401 " Ringing(): This application will request that the channel indicate a ringing\n"
402 "tone to the user.\n"
405 { "SayNumber", pbx_builtin_saynumber,
406 "Say Number",
407 " SayNumber(digits[,gender]): This application will play the sounds that\n"
408 "correspond to the given number. Optionally, a gender may be specified.\n"
409 "This will use the language that is currently set for the channel. See the\n"
410 "LANGUAGE function for more information on setting the language for the channel.\n"
413 { "SayDigits", pbx_builtin_saydigits,
414 "Say Digits",
415 " SayDigits(digits): This application will play the sounds that correspond\n"
416 "to the digits of the given number. This will use the language that is currently\n"
417 "set for the channel. See the LANGUAGE function for more information on setting\n"
418 "the language for the channel.\n"
421 { "SayAlpha", pbx_builtin_saycharacters,
422 "Say Alpha",
423 " SayAlpha(string): This application will play the sounds that correspond to\n"
424 "the letters of the given string.\n"
427 { "SayPhonetic", pbx_builtin_sayphonetic,
428 "Say Phonetic",
429 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
430 "alphabet that correspond to the letters in the given string.\n"
433 { "SetAMAFlags", pbx_builtin_setamaflags,
434 "Set the AMA Flags",
435 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
436 " billing purposes.\n"
439 { "SetGlobalVar", pbx_builtin_setglobalvar,
440 "Set a global variable to a given value",
441 " SetGlobalVar(variable=value): This application sets a given global variable to\n"
442 "the specified value.\n"
443 "\n\nThis application is deprecated in favor of Set(GLOBAL(var)=value)\n"
446 { "Set", pbx_builtin_setvar,
447 "Set channel variable(s) or function value(s)",
448 " Set(name1=value1|name2=value2|..[|options])\n"
449 "This function can be used to set the value of channel variables or dialplan\n"
450 "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
451 "if the variable name is prefixed with _, the variable will be inherited into\n"
452 "channels created from the current channel. If the variable name is prefixed\n"
453 "with __, the variable will be inherited into channels created from the current\n"
454 "channel and all children channels.\n"
455 " Options:\n"
456 " g - Set variable globally instead of on the channel\n"
457 " (applies only to variables, not functions)\n"
458 "\n\nThe use of Set to set multiple variables at once and the g flag have both\n"
459 "been deprecated. Please use multiple Set calls and the GLOBAL() dialplan\n"
460 "function instead.\n"
463 { "ImportVar", pbx_builtin_importvar,
464 "Import a variable from a channel into a new variable",
465 " ImportVar(newvar=channelname|variable): This application imports a variable\n"
466 "from the specified channel (as opposed to the current one) and stores it as\n"
467 "a variable in the current channel (the channel that is calling this\n"
468 "application). Variables created by this application have the same inheritance\n"
469 "properties as those created with the Set application. See the documentation for\n"
470 "Set for more information.\n"
473 { "Wait", pbx_builtin_wait,
474 "Waits for some time",
475 " Wait(seconds): This application waits for a specified number of seconds.\n"
476 "Then, dialplan execution will continue at the next priority.\n"
477 " Note that the seconds can be passed with fractions of a second. For example,\n"
478 "'1.5' will ask the application to wait for 1.5 seconds.\n"
481 { "WaitExten", pbx_builtin_waitexten,
482 "Waits for an extension to be entered",
483 " WaitExten([seconds][|options]): This application waits for the user to enter\n"
484 "a new extension for a specified number of seconds.\n"
485 " Note that the seconds can be passed with fractions of a second. For example,\n"
486 "'1.5' will ask the application to wait for 1.5 seconds.\n"
487 " Options:\n"
488 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
489 " Optionally, specify the class for music on hold within parenthesis.\n"
490 "See Also: Playback(application), Background(application).\n"
495 static struct ast_context *contexts;
496 AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
498 static AST_LIST_HEAD_STATIC(apps, ast_app);
500 static AST_LIST_HEAD_STATIC(switches, ast_switch);
502 static int stateid = 1;
503 /* WARNING:
504 When holding this list's lock, do _not_ do anything that will cause conlock
505 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
506 function will take the locks in conlock/hints order, so any other
507 paths that require both locks must also take them in that order.
509 static AST_LIST_HEAD_STATIC(hints, ast_hint);
510 struct ast_state_cb *statecbs;
513 \note This function is special. It saves the stack so that no matter
514 how many times it is called, it returns to the same place */
515 int pbx_exec(struct ast_channel *c, /*!< Channel */
516 struct ast_app *app, /*!< Application */
517 void *data) /*!< Data for execution */
519 int res;
521 const char *saved_c_appl;
522 const char *saved_c_data;
524 if (c->cdr && !ast_check_hangup(c))
525 ast_cdr_setapp(c->cdr, app->name, data);
527 /* save channel values */
528 saved_c_appl= c->appl;
529 saved_c_data= c->data;
531 c->appl = app->name;
532 c->data = data;
533 /* XXX remember what to to when we have linked apps to modules */
534 if (app->module) {
535 /* XXX LOCAL_USER_ADD(app->module) */
537 res = app->execute(c, S_OR(data, ""));
538 if (app->module) {
539 /* XXX LOCAL_USER_REMOVE(app->module) */
541 /* restore channel values */
542 c->appl = saved_c_appl;
543 c->data = saved_c_data;
544 return res;
548 /*! Go no deeper than this through includes (not counting loops) */
549 #define AST_PBX_MAX_STACK 128
551 /*! \brief Find application handle in linked list
553 struct ast_app *pbx_findapp(const char *app)
555 struct ast_app *tmp;
557 AST_LIST_LOCK(&apps);
558 AST_LIST_TRAVERSE(&apps, tmp, list) {
559 if (!strcasecmp(tmp->name, app))
560 break;
562 AST_LIST_UNLOCK(&apps);
564 return tmp;
567 static struct ast_switch *pbx_findswitch(const char *sw)
569 struct ast_switch *asw;
571 AST_LIST_LOCK(&switches);
572 AST_LIST_TRAVERSE(&switches, asw, list) {
573 if (!strcasecmp(asw->name, sw))
574 break;
576 AST_LIST_UNLOCK(&switches);
578 return asw;
581 static inline int include_valid(struct ast_include *i)
583 if (!i->hastime)
584 return 1;
586 return ast_check_timing(&(i->timing));
589 static void pbx_destroy(struct ast_pbx *p)
591 free(p);
595 * Special characters used in patterns:
596 * '_' underscore is the leading character of a pattern.
597 * In other position it is treated as a regular char.
598 * ' ' '-' space and '-' are separator and ignored.
599 * . one or more of any character. Only allowed at the end of
600 * a pattern.
601 * ! zero or more of anything. Also impacts the result of CANMATCH
602 * and MATCHMORE. Only allowed at the end of a pattern.
603 * In the core routine, ! causes a match with a return code of 2.
604 * In turn, depending on the search mode: (XXX check if it is implemented)
605 * - E_MATCH retuns 1 (does match)
606 * - E_MATCHMORE returns 0 (no match)
607 * - E_CANMATCH returns 1 (does match)
609 * / should not appear as it is considered the separator of the CID info.
610 * XXX at the moment we may stop on this char.
612 * X Z N match ranges 0-9, 1-9, 2-9 respectively.
613 * [ denotes the start of a set of character. Everything inside
614 * is considered literally. We can have ranges a-d and individual
615 * characters. A '[' and '-' can be considered literally if they
616 * are just before ']'.
617 * XXX currently there is no way to specify ']' in a range, nor \ is
618 * considered specially.
620 * When we compare a pattern with a specific extension, all characters in the extension
621 * itself are considered literally with the only exception of '-' which is considered
622 * as a separator and thus ignored.
623 * XXX do we want to consider space as a separator as well ?
624 * XXX do we want to consider the separators in non-patterns as well ?
628 * \brief helper functions to sort extensions and patterns in the desired way,
629 * so that more specific patterns appear first.
631 * ext_cmp1 compares individual characters (or sets of), returning
632 * an int where bits 0-7 are the ASCII code of the first char in the set,
633 * while bit 8-15 are the cardinality of the set minus 1.
634 * This way more specific patterns (smaller cardinality) appear first.
635 * Wildcards have a special value, so that we can directly compare them to
636 * sets by subtracting the two values. In particular:
637 * 0x000xx one character, xx
638 * 0x0yyxx yy character set starting with xx
639 * 0x10000 '.' (one or more of anything)
640 * 0x20000 '!' (zero or more of anything)
641 * 0x30000 NUL (end of string)
642 * 0x40000 error in set.
643 * The pointer to the string is advanced according to needs.
644 * NOTES:
645 * 1. the empty set is equivalent to NUL.
646 * 2. given that a full set has always 0 as the first element,
647 * we could encode the special cases as 0xffXX where XX
648 * is 1, 2, 3, 4 as used above.
650 static int ext_cmp1(const char **p)
652 uint32_t chars[8];
653 int c, cmin = 0xff, count = 0;
654 const char *end;
656 /* load, sign extend and advance pointer until we find
657 * a valid character.
659 while ( (c = *(*p)++) && (c == ' ' || c == '-') )
660 ; /* ignore some characters */
662 /* always return unless we have a set of chars */
663 switch (c) {
664 default: /* ordinary character */
665 return 0x0000 | (c & 0xff);
667 case 'N': /* 2..9 */
668 return 0x0700 | '2' ;
670 case 'X': /* 0..9 */
671 return 0x0900 | '0';
673 case 'Z': /* 1..9 */
674 return 0x0800 | '1';
676 case '.': /* wildcard */
677 return 0x10000;
679 case '!': /* earlymatch */
680 return 0x20000; /* less specific than NULL */
682 case '\0': /* empty string */
683 *p = NULL;
684 return 0x30000;
686 case '[': /* pattern */
687 break;
689 /* locate end of set */
690 end = strchr(*p, ']');
692 if (end == NULL) {
693 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
694 return 0x40000; /* XXX make this entry go last... */
697 bzero(chars, sizeof(chars)); /* clear all chars in the set */
698 for (; *p < end ; (*p)++) {
699 unsigned char c1, c2; /* first-last char in range */
700 c1 = (unsigned char)((*p)[0]);
701 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
702 c2 = (unsigned char)((*p)[2]);
703 *p += 2; /* skip a total of 3 chars */
704 } else /* individual character */
705 c2 = c1;
706 if (c1 < cmin)
707 cmin = c1;
708 for (; c1 <= c2; c1++) {
709 uint32_t mask = 1 << (c1 % 32);
710 if ( (chars[ c1 / 32 ] & mask) == 0)
711 count += 0x100;
712 chars[ c1 / 32 ] |= mask;
715 (*p)++;
716 return count == 0 ? 0x30000 : (count | cmin);
720 * \brief the full routine to compare extensions in rules.
722 static int ext_cmp(const char *a, const char *b)
724 /* make sure non-patterns come first.
725 * If a is not a pattern, it either comes first or
726 * we use strcmp to compare the strings.
728 int ret = 0;
730 if (a[0] != '_')
731 return (b[0] == '_') ? -1 : strcmp(a, b);
733 /* Now we know a is a pattern; if b is not, a comes first */
734 if (b[0] != '_')
735 return 1;
736 #if 0 /* old mode for ext matching */
737 return strcmp(a, b);
738 #endif
739 /* ok we need full pattern sorting routine */
740 while (!ret && a && b)
741 ret = ext_cmp1(&a) - ext_cmp1(&b);
742 if (ret == 0)
743 return 0;
744 else
745 return (ret > 0) ? 1 : -1;
749 * When looking up extensions, we can have different requests
750 * identified by the 'action' argument, as follows.
751 * Note that the coding is such that the low 4 bits are the
752 * third argument to extension_match_core.
754 enum ext_match_t {
755 E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
756 E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
757 E_MATCH = 0x02, /* extension is an exact match */
758 E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
759 E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
760 E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
764 * Internal function for ast_extension_{match|close}
765 * return 0 on no-match, 1 on match, 2 on early match.
766 * mode is as follows:
767 * E_MATCH success only on exact match
768 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
769 * E_CANMATCH either of the above.
772 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
774 mode &= E_MATCH_MASK; /* only consider the relevant bits */
776 if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
777 return 1;
779 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
780 int ld = strlen(data), lp = strlen(pattern);
782 if (lp < ld) /* pattern too short, cannot match */
783 return 0;
784 /* depending on the mode, accept full or partial match or both */
785 if (mode == E_MATCH)
786 return !strcmp(pattern, data); /* 1 on match, 0 on fail */
787 if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
788 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
789 else
790 return 0;
792 pattern++; /* skip leading _ */
794 * XXX below we stop at '/' which is a separator for the CID info. However we should
795 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
797 while (*data && *pattern && *pattern != '/') {
798 const char *end;
800 if (*data == '-') { /* skip '-' in data (just a separator) */
801 data++;
802 continue;
804 switch (toupper(*pattern)) {
805 case '[': /* a range */
806 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
807 if (end == NULL) {
808 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
809 return 0; /* unconditional failure */
811 for (pattern++; pattern != end; pattern++) {
812 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
813 if (*data >= pattern[0] && *data <= pattern[2])
814 break; /* match found */
815 else {
816 pattern += 2; /* skip a total of 3 chars */
817 continue;
819 } else if (*data == pattern[0])
820 break; /* match found */
822 if (pattern == end)
823 return 0;
824 pattern = end; /* skip and continue */
825 break;
826 case 'N':
827 if (*data < '2' || *data > '9')
828 return 0;
829 break;
830 case 'X':
831 if (*data < '0' || *data > '9')
832 return 0;
833 break;
834 case 'Z':
835 if (*data < '1' || *data > '9')
836 return 0;
837 break;
838 case '.': /* Must match, even with more digits */
839 return 1;
840 case '!': /* Early match */
841 return 2;
842 case ' ':
843 case '-': /* Ignore these in patterns */
844 data--; /* compensate the final data++ */
845 break;
846 default:
847 if (*data != *pattern)
848 return 0;
850 data++;
851 pattern++;
853 if (*data) /* data longer than pattern, no match */
854 return 0;
856 * match so far, but ran off the end of the data.
857 * Depending on what is next, determine match or not.
859 if (*pattern == '\0' || *pattern == '/') /* exact match */
860 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
861 else if (*pattern == '!') /* early match */
862 return 2;
863 else /* partial match */
864 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
868 * Wrapper around _extension_match_core() to do performance measurement
869 * using the profiling code.
871 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
873 int i;
874 static int prof_id = -2; /* marker for 'unallocated' id */
875 if (prof_id == -2)
876 prof_id = ast_add_profile("ext_match", 0);
877 ast_mark(prof_id, 1);
878 i = _extension_match_core(pattern, data, mode);
879 ast_mark(prof_id, 0);
880 return i;
883 int ast_extension_match(const char *pattern, const char *data)
885 return extension_match_core(pattern, data, E_MATCH);
888 int ast_extension_close(const char *pattern, const char *data, int needmore)
890 if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
891 ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
892 return extension_match_core(pattern, data, needmore);
895 struct ast_context *ast_context_find(const char *name)
897 struct ast_context *tmp = NULL;
899 ast_rdlock_contexts();
901 while ( (tmp = ast_walk_contexts(tmp)) ) {
902 if (!name || !strcasecmp(name, tmp->name))
903 break;
906 ast_unlock_contexts();
908 return tmp;
911 #define STATUS_NO_CONTEXT 1
912 #define STATUS_NO_EXTENSION 2
913 #define STATUS_NO_PRIORITY 3
914 #define STATUS_NO_LABEL 4
915 #define STATUS_SUCCESS 5
917 static int matchcid(const char *cidpattern, const char *callerid)
919 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
920 failing to get a number should count as a match, otherwise not */
922 if (ast_strlen_zero(callerid))
923 return ast_strlen_zero(cidpattern) ? 1 : 0;
925 return ast_extension_match(cidpattern, callerid);
928 /* request and result for pbx_find_extension */
929 struct pbx_find_info {
930 #if 0
931 const char *context;
932 const char *exten;
933 int priority;
934 #endif
936 char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
937 int stacklen; /* modified during the search */
938 int status; /* set on return */
939 struct ast_switch *swo; /* set on return */
940 const char *data; /* set on return */
941 const char *foundcontext; /* set on return */
944 static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
945 struct ast_context *bypass, struct pbx_find_info *q,
946 const char *context, const char *exten, int priority,
947 const char *label, const char *callerid, enum ext_match_t action)
949 int x, res;
950 struct ast_context *tmp;
951 struct ast_exten *e, *eroot;
952 struct ast_include *i;
953 struct ast_sw *sw;
954 char *tmpdata = NULL;
956 /* Initialize status if appropriate */
957 if (q->stacklen == 0) {
958 q->status = STATUS_NO_CONTEXT;
959 q->swo = NULL;
960 q->data = NULL;
961 q->foundcontext = NULL;
963 /* Check for stack overflow */
964 if (q->stacklen >= AST_PBX_MAX_STACK) {
965 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
966 return NULL;
968 /* Check first to see if we've already been checked */
969 for (x = 0; x < q->stacklen; x++) {
970 if (!strcasecmp(q->incstack[x], context))
971 return NULL;
973 if (bypass) /* bypass means we only look there */
974 tmp = bypass;
975 else { /* look in contexts */
976 tmp = NULL;
977 while ((tmp = ast_walk_contexts(tmp)) ) {
978 if (!strcmp(tmp->name, context))
979 break;
981 if (!tmp)
982 return NULL;
984 if (q->status < STATUS_NO_EXTENSION)
985 q->status = STATUS_NO_EXTENSION;
987 /* scan the list trying to match extension and CID */
988 eroot = NULL;
989 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
990 int match = extension_match_core(eroot->exten, exten, action);
991 /* 0 on fail, 1 on match, 2 on earlymatch */
993 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
994 continue; /* keep trying */
995 if (match == 2 && action == E_MATCHMORE) {
996 /* We match an extension ending in '!'.
997 * The decision in this case is final and is NULL (no match).
999 return NULL;
1001 /* found entry, now look for the right priority */
1002 if (q->status < STATUS_NO_PRIORITY)
1003 q->status = STATUS_NO_PRIORITY;
1004 e = NULL;
1005 while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
1006 /* Match label or priority */
1007 if (action == E_FINDLABEL) {
1008 if (q->status < STATUS_NO_LABEL)
1009 q->status = STATUS_NO_LABEL;
1010 if (label && e->label && !strcmp(label, e->label))
1011 break; /* found it */
1012 } else if (e->priority == priority) {
1013 break; /* found it */
1014 } /* else keep searching */
1016 if (e) { /* found a valid match */
1017 q->status = STATUS_SUCCESS;
1018 q->foundcontext = context;
1019 return e;
1022 /* Check alternative switches */
1023 AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
1024 struct ast_switch *asw = pbx_findswitch(sw->name);
1025 ast_switch_f *aswf = NULL;
1026 char *datap;
1028 if (!asw) {
1029 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
1030 continue;
1032 /* Substitute variables now */
1033 if (sw->eval) {
1034 if (!(tmpdata = ast_threadstorage_get(&switch_data, 512))) {
1035 ast_log(LOG_WARNING, "Can't evaluate switch?!");
1036 continue;
1038 pbx_substitute_variables_helper(chan, sw->data, tmpdata, 512);
1041 /* equivalent of extension_match_core() at the switch level */
1042 if (action == E_CANMATCH)
1043 aswf = asw->canmatch;
1044 else if (action == E_MATCHMORE)
1045 aswf = asw->matchmore;
1046 else /* action == E_MATCH */
1047 aswf = asw->exists;
1048 datap = sw->eval ? tmpdata : sw->data;
1049 if (!aswf)
1050 res = 0;
1051 else {
1052 if (chan)
1053 ast_autoservice_start(chan);
1054 res = aswf(chan, context, exten, priority, callerid, datap);
1055 if (chan)
1056 ast_autoservice_stop(chan);
1058 if (res) { /* Got a match */
1059 q->swo = asw;
1060 q->data = datap;
1061 q->foundcontext = context;
1062 /* XXX keep status = STATUS_NO_CONTEXT ? */
1063 return NULL;
1066 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
1067 /* Now try any includes we have in this context */
1068 for (i = tmp->includes; i; i = i->next) {
1069 if (include_valid(i)) {
1070 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
1071 return e;
1072 if (q->swo)
1073 return NULL;
1076 return NULL;
1079 /*! \brief extract offset:length from variable name.
1080 * Returns 1 if there is a offset:length part, which is
1081 * trimmed off (values go into variables)
1083 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
1085 int parens=0;
1087 *offset = 0;
1088 *length = INT_MAX;
1089 *isfunc = 0;
1090 for (; *var; var++) {
1091 if (*var == '(') {
1092 (*isfunc)++;
1093 parens++;
1094 } else if (*var == ')') {
1095 parens--;
1096 } else if (*var == ':' && parens == 0) {
1097 *var++ = '\0';
1098 sscanf(var, "%d:%d", offset, length);
1099 return 1; /* offset:length valid */
1102 return 0;
1105 /*! \brief takes a substring. It is ok to call with value == workspace.
1107 * offset < 0 means start from the end of the string and set the beginning
1108 * to be that many characters back.
1109 * length is the length of the substring. A value less than 0 means to leave
1110 * that many off the end.
1111 * Always return a copy in workspace.
1113 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
1115 char *ret = workspace;
1116 int lr; /* length of the input string after the copy */
1118 ast_copy_string(workspace, value, workspace_len); /* always make a copy */
1120 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
1122 /* Quick check if no need to do anything */
1123 if (offset == 0 && length >= lr) /* take the whole string */
1124 return ret;
1126 if (offset < 0) { /* translate negative offset into positive ones */
1127 offset = lr + offset;
1128 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
1129 offset = 0;
1132 /* too large offset result in empty string so we know what to return */
1133 if (offset >= lr)
1134 return ret + lr; /* the final '\0' */
1136 ret += offset; /* move to the start position */
1137 if (length >= 0 && length < lr - offset) /* truncate if necessary */
1138 ret[length] = '\0';
1139 else if (length < 0) {
1140 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
1141 ret[lr + length - offset] = '\0';
1142 else
1143 ret[0] = '\0';
1146 return ret;
1149 /*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables
1150 ---*/
1151 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
1153 const char not_found = '\0';
1154 char *tmpvar;
1155 const char *s; /* the result */
1156 int offset, length;
1157 int i, need_substring;
1158 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
1160 if (c) {
1161 ast_channel_lock(c);
1162 places[0] = &c->varshead;
1165 * Make a copy of var because parse_variable_name() modifies the string.
1166 * Then if called directly, we might need to run substring() on the result;
1167 * remember this for later in 'need_substring', 'offset' and 'length'
1169 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
1170 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
1173 * Look first into predefined variables, then into variable lists.
1174 * Variable 's' points to the result, according to the following rules:
1175 * s == &not_found (set at the beginning) means that we did not find a
1176 * matching variable and need to look into more places.
1177 * If s != &not_found, s is a valid result string as follows:
1178 * s = NULL if the variable does not have a value;
1179 * you typically do this when looking for an unset predefined variable.
1180 * s = workspace if the result has been assembled there;
1181 * typically done when the result is built e.g. with an snprintf(),
1182 * so we don't need to do an additional copy.
1183 * s != workspace in case we have a string, that needs to be copied
1184 * (the ast_copy_string is done once for all at the end).
1185 * Typically done when the result is already available in some string.
1187 s = &not_found; /* default value */
1188 if (c) { /* This group requires a valid channel */
1189 /* Names with common parts are looked up a piece at a time using strncmp. */
1190 if (!strncmp(var, "CALL", 4)) {
1191 if (!strncmp(var + 4, "ING", 3)) {
1192 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
1193 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
1194 s = workspace;
1195 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
1196 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
1197 s = workspace;
1198 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
1199 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
1200 s = workspace;
1201 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
1202 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
1203 s = workspace;
1206 } else if (!strcmp(var, "HINT")) {
1207 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
1208 } else if (!strcmp(var, "HINTNAME")) {
1209 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
1210 } else if (!strcmp(var, "EXTEN")) {
1211 s = c->exten;
1212 } else if (!strcmp(var, "CONTEXT")) {
1213 s = c->context;
1214 } else if (!strcmp(var, "PRIORITY")) {
1215 snprintf(workspace, workspacelen, "%d", c->priority);
1216 s = workspace;
1217 } else if (!strcmp(var, "CHANNEL")) {
1218 s = c->name;
1219 } else if (!strcmp(var, "UNIQUEID")) {
1220 s = c->uniqueid;
1221 } else if (!strcmp(var, "HANGUPCAUSE")) {
1222 snprintf(workspace, workspacelen, "%d", c->hangupcause);
1223 s = workspace;
1226 if (s == &not_found) { /* look for more */
1227 if (!strcmp(var, "EPOCH")) {
1228 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
1229 s = workspace;
1230 } else if (!strcmp(var, "SYSTEMNAME")) {
1231 s = ast_config_AST_SYSTEM_NAME;
1234 /* if not found, look into chanvars or global vars */
1235 for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
1236 struct ast_var_t *variables;
1237 if (!places[i])
1238 continue;
1239 if (places[i] == &globals)
1240 ast_mutex_lock(&globalslock);
1241 AST_LIST_TRAVERSE(places[i], variables, entries) {
1242 if (strcasecmp(ast_var_name(variables), var)==0) {
1243 s = ast_var_value(variables);
1244 break;
1247 if (places[i] == &globals)
1248 ast_mutex_unlock(&globalslock);
1250 if (s == &not_found || s == NULL)
1251 *ret = NULL;
1252 else {
1253 if (s != workspace)
1254 ast_copy_string(workspace, s, workspacelen);
1255 *ret = workspace;
1256 if (need_substring)
1257 *ret = substring(*ret, offset, length, workspace, workspacelen);
1260 if (c)
1261 ast_channel_unlock(c);
1264 /*! \brief CLI function to show installed custom functions
1265 \addtogroup CLI_functions
1267 static int handle_show_functions_deprecated(int fd, int argc, char *argv[])
1269 struct ast_custom_function *acf;
1270 int count_acf = 0;
1271 int like = 0;
1273 if (argc == 4 && (!strcmp(argv[2], "like")) ) {
1274 like = 1;
1275 } else if (argc != 2) {
1276 return RESULT_SHOWUSAGE;
1279 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1281 AST_LIST_LOCK(&acf_root);
1282 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1283 if (!like || strstr(acf->name, argv[3])) {
1284 count_acf++;
1285 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1288 AST_LIST_UNLOCK(&acf_root);
1290 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1292 return RESULT_SUCCESS;
1294 static int handle_show_functions(int fd, int argc, char *argv[])
1296 struct ast_custom_function *acf;
1297 int count_acf = 0;
1298 int like = 0;
1300 if (argc == 5 && (!strcmp(argv[3], "like")) ) {
1301 like = 1;
1302 } else if (argc != 3) {
1303 return RESULT_SHOWUSAGE;
1306 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1308 AST_LIST_LOCK(&acf_root);
1309 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1310 if (!like || strstr(acf->name, argv[4])) {
1311 count_acf++;
1312 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1315 AST_LIST_UNLOCK(&acf_root);
1317 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1319 return RESULT_SUCCESS;
1322 static int handle_show_function_deprecated(int fd, int argc, char *argv[])
1324 struct ast_custom_function *acf;
1325 /* Maximum number of characters added by terminal coloring is 22 */
1326 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1327 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1328 char stxtitle[40], *syntax = NULL;
1329 int synopsis_size, description_size, syntax_size;
1331 if (argc < 3)
1332 return RESULT_SHOWUSAGE;
1334 if (!(acf = ast_custom_function_find(argv[2]))) {
1335 ast_cli(fd, "No function by that name registered.\n");
1336 return RESULT_FAILURE;
1340 if (acf->synopsis)
1341 synopsis_size = strlen(acf->synopsis) + 23;
1342 else
1343 synopsis_size = strlen("Not available") + 23;
1344 synopsis = alloca(synopsis_size);
1346 if (acf->desc)
1347 description_size = strlen(acf->desc) + 23;
1348 else
1349 description_size = strlen("Not available") + 23;
1350 description = alloca(description_size);
1352 if (acf->syntax)
1353 syntax_size = strlen(acf->syntax) + 23;
1354 else
1355 syntax_size = strlen("Not available") + 23;
1356 syntax = alloca(syntax_size);
1358 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1359 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1360 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1361 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1362 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1363 term_color(syntax,
1364 acf->syntax ? acf->syntax : "Not available",
1365 COLOR_CYAN, 0, syntax_size);
1366 term_color(synopsis,
1367 acf->synopsis ? acf->synopsis : "Not available",
1368 COLOR_CYAN, 0, synopsis_size);
1369 term_color(description,
1370 acf->desc ? acf->desc : "Not available",
1371 COLOR_CYAN, 0, description_size);
1373 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1375 return RESULT_SUCCESS;
1378 static int handle_show_function(int fd, int argc, char *argv[])
1380 struct ast_custom_function *acf;
1381 /* Maximum number of characters added by terminal coloring is 22 */
1382 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1383 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1384 char stxtitle[40], *syntax = NULL;
1385 int synopsis_size, description_size, syntax_size;
1387 if (argc < 4)
1388 return RESULT_SHOWUSAGE;
1390 if (!(acf = ast_custom_function_find(argv[3]))) {
1391 ast_cli(fd, "No function by that name registered.\n");
1392 return RESULT_FAILURE;
1396 if (acf->synopsis)
1397 synopsis_size = strlen(acf->synopsis) + 23;
1398 else
1399 synopsis_size = strlen("Not available") + 23;
1400 synopsis = alloca(synopsis_size);
1402 if (acf->desc)
1403 description_size = strlen(acf->desc) + 23;
1404 else
1405 description_size = strlen("Not available") + 23;
1406 description = alloca(description_size);
1408 if (acf->syntax)
1409 syntax_size = strlen(acf->syntax) + 23;
1410 else
1411 syntax_size = strlen("Not available") + 23;
1412 syntax = alloca(syntax_size);
1414 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1415 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1416 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1417 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1418 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1419 term_color(syntax,
1420 acf->syntax ? acf->syntax : "Not available",
1421 COLOR_CYAN, 0, syntax_size);
1422 term_color(synopsis,
1423 acf->synopsis ? acf->synopsis : "Not available",
1424 COLOR_CYAN, 0, synopsis_size);
1425 term_color(description,
1426 acf->desc ? acf->desc : "Not available",
1427 COLOR_CYAN, 0, description_size);
1429 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1431 return RESULT_SUCCESS;
1434 static char *complete_show_function(const char *line, const char *word, int pos, int state)
1436 struct ast_custom_function *acf;
1437 char *ret = NULL;
1438 int which = 0;
1439 int wordlen = strlen(word);
1441 /* case-insensitive for convenience in this 'complete' function */
1442 AST_LIST_LOCK(&acf_root);
1443 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1444 if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
1445 ret = strdup(acf->name);
1446 break;
1449 AST_LIST_UNLOCK(&acf_root);
1451 return ret;
1454 struct ast_custom_function *ast_custom_function_find(const char *name)
1456 struct ast_custom_function *acf = NULL;
1458 AST_LIST_LOCK(&acf_root);
1459 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1460 if (!strcmp(name, acf->name))
1461 break;
1463 AST_LIST_UNLOCK(&acf_root);
1465 return acf;
1468 int ast_custom_function_unregister(struct ast_custom_function *acf)
1470 struct ast_custom_function *cur;
1472 if (!acf)
1473 return -1;
1475 AST_LIST_LOCK(&acf_root);
1476 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1477 if (cur == acf) {
1478 AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
1479 if (option_verbose > 1)
1480 ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
1481 break;
1484 AST_LIST_TRAVERSE_SAFE_END
1485 AST_LIST_UNLOCK(&acf_root);
1487 return acf ? 0 : -1;
1490 int ast_custom_function_register(struct ast_custom_function *acf)
1492 struct ast_custom_function *cur;
1494 if (!acf)
1495 return -1;
1497 AST_LIST_LOCK(&acf_root);
1499 if (ast_custom_function_find(acf->name)) {
1500 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
1501 AST_LIST_UNLOCK(&acf_root);
1502 return -1;
1505 /* Store in alphabetical order */
1506 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1507 if (strcasecmp(acf->name, cur->name) < 0) {
1508 AST_LIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
1509 break;
1512 AST_LIST_TRAVERSE_SAFE_END
1513 if (!cur)
1514 AST_LIST_INSERT_TAIL(&acf_root, acf, acflist);
1516 AST_LIST_UNLOCK(&acf_root);
1518 if (option_verbose > 1)
1519 ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
1521 return 0;
1524 /*! \brief return a pointer to the arguments of the function,
1525 * and terminates the function name with '\\0'
1527 static char *func_args(char *function)
1529 char *args = strchr(function, '(');
1531 if (!args)
1532 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1533 else {
1534 char *p;
1535 *args++ = '\0';
1536 if ((p = strrchr(args, ')')) )
1537 *p = '\0';
1538 else
1539 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1541 return args;
1544 int ast_func_read(struct ast_channel *chan, char *function, char *workspace, size_t len)
1546 char *args = func_args(function);
1547 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1549 if (acfptr == NULL)
1550 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1551 else if (!acfptr->read)
1552 ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
1553 else
1554 return acfptr->read(chan, function, args, workspace, len);
1555 return -1;
1558 int ast_func_write(struct ast_channel *chan, char *function, const char *value)
1560 char *args = func_args(function);
1561 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1563 if (acfptr == NULL)
1564 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1565 else if (!acfptr->write)
1566 ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
1567 else
1568 return acfptr->write(chan, function, args, value);
1570 return -1;
1573 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
1575 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
1576 zero-filled */
1577 char *cp4;
1578 const char *tmp, *whereweare;
1579 int length, offset, offset2, isfunction;
1580 char *workspace = NULL;
1581 char *ltmp = NULL, *var = NULL;
1582 char *nextvar, *nextexp, *nextthing;
1583 char *vars, *vare;
1584 int pos, brackets, needsub, len;
1586 whereweare=tmp=cp1;
1587 while (!ast_strlen_zero(whereweare) && count) {
1588 /* Assume we're copying the whole remaining string */
1589 pos = strlen(whereweare);
1590 nextvar = NULL;
1591 nextexp = NULL;
1592 nextthing = strchr(whereweare, '$');
1593 if (nextthing) {
1594 switch(nextthing[1]) {
1595 case '{':
1596 nextvar = nextthing;
1597 pos = nextvar - whereweare;
1598 break;
1599 case '[':
1600 nextexp = nextthing;
1601 pos = nextexp - whereweare;
1602 break;
1603 default:
1604 pos = 1;
1608 if (pos) {
1609 /* Can't copy more than 'count' bytes */
1610 if (pos > count)
1611 pos = count;
1613 /* Copy that many bytes */
1614 memcpy(cp2, whereweare, pos);
1616 count -= pos;
1617 cp2 += pos;
1618 whereweare += pos;
1621 if (nextvar) {
1622 /* We have a variable. Find the start and end, and determine
1623 if we are going to have to recursively call ourselves on the
1624 contents */
1625 vars = vare = nextvar + 2;
1626 brackets = 1;
1627 needsub = 0;
1629 /* Find the end of it */
1630 while (brackets && *vare) {
1631 if ((vare[0] == '$') && (vare[1] == '{')) {
1632 needsub++;
1633 } else if (vare[0] == '{') {
1634 brackets++;
1635 } else if (vare[0] == '}') {
1636 brackets--;
1637 } else if ((vare[0] == '$') && (vare[1] == '['))
1638 needsub++;
1639 vare++;
1641 if (brackets)
1642 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
1643 len = vare - vars - 1;
1645 /* Skip totally over variable string */
1646 whereweare += (len + 3);
1648 if (!var)
1649 var = alloca(VAR_BUF_SIZE);
1651 /* Store variable name (and truncate) */
1652 ast_copy_string(var, vars, len + 1);
1654 /* Substitute if necessary */
1655 if (needsub) {
1656 if (!ltmp)
1657 ltmp = alloca(VAR_BUF_SIZE);
1659 memset(ltmp, 0, VAR_BUF_SIZE);
1660 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1661 vars = ltmp;
1662 } else {
1663 vars = var;
1666 if (!workspace)
1667 workspace = alloca(VAR_BUF_SIZE);
1669 workspace[0] = '\0';
1671 parse_variable_name(vars, &offset, &offset2, &isfunction);
1672 if (isfunction) {
1673 /* Evaluate function */
1674 if (c || !headp)
1675 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1676 else {
1677 struct varshead old;
1678 struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
1679 if (c) {
1680 memcpy(&old, &c->varshead, sizeof(old));
1681 memcpy(&c->varshead, headp, sizeof(c->varshead));
1682 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1683 /* Don't deallocate the varshead that was passed in */
1684 memcpy(&c->varshead, &old, sizeof(c->varshead));
1685 ast_channel_free(c);
1686 } else
1687 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1690 if (option_debug)
1691 ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
1692 } else {
1693 /* Retrieve variable value */
1694 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
1696 if (cp4) {
1697 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
1699 length = strlen(cp4);
1700 if (length > count)
1701 length = count;
1702 memcpy(cp2, cp4, length);
1703 count -= length;
1704 cp2 += length;
1706 } else if (nextexp) {
1707 /* We have an expression. Find the start and end, and determine
1708 if we are going to have to recursively call ourselves on the
1709 contents */
1710 vars = vare = nextexp + 2;
1711 brackets = 1;
1712 needsub = 0;
1714 /* Find the end of it */
1715 while(brackets && *vare) {
1716 if ((vare[0] == '$') && (vare[1] == '[')) {
1717 needsub++;
1718 brackets++;
1719 vare++;
1720 } else if (vare[0] == '[') {
1721 brackets++;
1722 } else if (vare[0] == ']') {
1723 brackets--;
1724 } else if ((vare[0] == '$') && (vare[1] == '{')) {
1725 needsub++;
1726 vare++;
1728 vare++;
1730 if (brackets)
1731 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
1732 len = vare - vars - 1;
1734 /* Skip totally over expression */
1735 whereweare += (len + 3);
1737 if (!var)
1738 var = alloca(VAR_BUF_SIZE);
1740 /* Store variable name (and truncate) */
1741 ast_copy_string(var, vars, len + 1);
1743 /* Substitute if necessary */
1744 if (needsub) {
1745 if (!ltmp)
1746 ltmp = alloca(VAR_BUF_SIZE);
1748 memset(ltmp, 0, VAR_BUF_SIZE);
1749 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1750 vars = ltmp;
1751 } else {
1752 vars = var;
1755 length = ast_expr(vars, cp2, count);
1757 if (length) {
1758 if (option_debug)
1759 ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
1760 count -= length;
1761 cp2 += length;
1767 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
1769 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
1772 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
1774 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
1777 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
1779 memset(passdata, 0, datalen);
1781 /* No variables or expressions in e->data, so why scan it? */
1782 if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
1783 ast_copy_string(passdata, e->data, datalen);
1784 return;
1787 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
1790 /*!
1791 * \brief The return value depends on the action:
1793 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
1794 * and return 0 on failure, -1 on match;
1795 * E_FINDLABEL maps the label to a priority, and returns
1796 * the priority on success, ... XXX
1797 * E_SPAWN, spawn an application,
1798 * and return 0 on success, -1 on failure.
1800 * \note The channel is auto-serviced in this function, because doing an extension
1801 * match may block for a long time. For example, if the lookup has to use a network
1802 * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
1803 * auto-service code will queue up any important signalling frames to be processed
1804 * after this is done.
1806 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
1807 const char *context, const char *exten, int priority,
1808 const char *label, const char *callerid, enum ext_match_t action)
1810 struct ast_exten *e;
1811 struct ast_app *app;
1812 int res;
1813 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
1814 char passdata[EXT_DATA_SIZE];
1816 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
1818 ast_rdlock_contexts();
1819 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
1820 if (e) {
1821 if (matching_action) {
1822 ast_unlock_contexts();
1823 return -1; /* success, we found it */
1824 } else if (action == E_FINDLABEL) { /* map the label to a priority */
1825 res = e->priority;
1826 ast_unlock_contexts();
1827 return res; /* the priority we were looking for */
1828 } else { /* spawn */
1829 app = pbx_findapp(e->app);
1830 ast_unlock_contexts();
1831 if (!app) {
1832 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
1833 return -1;
1835 if (c->context != context)
1836 ast_copy_string(c->context, context, sizeof(c->context));
1837 if (c->exten != exten)
1838 ast_copy_string(c->exten, exten, sizeof(c->exten));
1839 c->priority = priority;
1840 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
1841 if (option_debug) {
1842 ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
1844 if (option_verbose > 2) {
1845 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
1846 ast_verbose( VERBOSE_PREFIX_3 "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
1847 exten, context, priority,
1848 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
1849 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
1850 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
1851 "in new stack");
1853 manager_event(EVENT_FLAG_CALL, "Newexten",
1854 "Channel: %s\r\n"
1855 "Context: %s\r\n"
1856 "Extension: %s\r\n"
1857 "Priority: %d\r\n"
1858 "Application: %s\r\n"
1859 "AppData: %s\r\n"
1860 "Uniqueid: %s\r\n",
1861 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
1862 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
1864 } else if (q.swo) { /* not found here, but in another switch */
1865 ast_unlock_contexts();
1866 if (matching_action) {
1867 return -1;
1868 } else {
1869 if (!q.swo->exec) {
1870 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
1871 res = -1;
1873 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
1875 } else { /* not found anywhere, see what happened */
1876 ast_unlock_contexts();
1877 switch (q.status) {
1878 case STATUS_NO_CONTEXT:
1879 if (!matching_action)
1880 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
1881 break;
1882 case STATUS_NO_EXTENSION:
1883 if (!matching_action)
1884 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
1885 break;
1886 case STATUS_NO_PRIORITY:
1887 if (!matching_action)
1888 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
1889 break;
1890 case STATUS_NO_LABEL:
1891 if (context)
1892 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
1893 break;
1894 default:
1895 if (option_debug)
1896 ast_log(LOG_DEBUG, "Shouldn't happen!\n");
1899 return (matching_action) ? 0 : -1;
1903 /*! \brief ast_hint_extension: Find hint for given extension in context */
1904 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
1906 struct ast_exten *e;
1907 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
1909 ast_rdlock_contexts();
1910 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
1911 ast_unlock_contexts();
1913 return e;
1916 /*! \brief ast_extensions_state2: Check state of extension by using hints */
1917 static int ast_extension_state2(struct ast_exten *e)
1919 char hint[AST_MAX_EXTENSION];
1920 char *cur, *rest;
1921 int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
1922 int busy = 0, inuse = 0, ring = 0;
1924 if (!e)
1925 return -1;
1927 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
1929 rest = hint; /* One or more devices separated with a & character */
1930 while ( (cur = strsep(&rest, "&")) ) {
1931 int res = ast_device_state(cur);
1932 switch (res) {
1933 case AST_DEVICE_NOT_INUSE:
1934 allunavailable = 0;
1935 allbusy = 0;
1936 allonhold = 0;
1937 break;
1938 case AST_DEVICE_INUSE:
1939 inuse = 1;
1940 allunavailable = 0;
1941 allfree = 0;
1942 allonhold = 0;
1943 break;
1944 case AST_DEVICE_RINGING:
1945 ring = 1;
1946 allunavailable = 0;
1947 allfree = 0;
1948 allonhold = 0;
1949 break;
1950 case AST_DEVICE_RINGINUSE:
1951 inuse = 1;
1952 ring = 1;
1953 allunavailable = 0;
1954 allfree = 0;
1955 allonhold = 0;
1956 break;
1957 case AST_DEVICE_ONHOLD:
1958 allunavailable = 0;
1959 allfree = 0;
1960 break;
1961 case AST_DEVICE_BUSY:
1962 allunavailable = 0;
1963 allfree = 0;
1964 allonhold = 0;
1965 busy = 1;
1966 break;
1967 case AST_DEVICE_UNAVAILABLE:
1968 case AST_DEVICE_INVALID:
1969 allbusy = 0;
1970 allfree = 0;
1971 allonhold = 0;
1972 break;
1973 default:
1974 allunavailable = 0;
1975 allbusy = 0;
1976 allfree = 0;
1977 allonhold = 0;
1981 if (!inuse && ring)
1982 return AST_EXTENSION_RINGING;
1983 if (inuse && ring)
1984 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
1985 if (inuse)
1986 return AST_EXTENSION_INUSE;
1987 if (allfree)
1988 return AST_EXTENSION_NOT_INUSE;
1989 if (allonhold)
1990 return AST_EXTENSION_ONHOLD;
1991 if (allbusy)
1992 return AST_EXTENSION_BUSY;
1993 if (allunavailable)
1994 return AST_EXTENSION_UNAVAILABLE;
1995 if (busy)
1996 return AST_EXTENSION_INUSE;
1998 return AST_EXTENSION_NOT_INUSE;
2001 /*! \brief ast_extension_state2str: Return extension_state as string */
2002 const char *ast_extension_state2str(int extension_state)
2004 int i;
2006 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
2007 if (extension_states[i].extension_state == extension_state)
2008 return extension_states[i].text;
2010 return "Unknown";
2013 /*! \brief ast_extension_state: Check extension state for an extension by using hint */
2014 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
2016 struct ast_exten *e;
2018 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
2019 if (!e)
2020 return -1; /* No hint, return -1 */
2022 return ast_extension_state2(e); /* Check all devices in the hint */
2025 void ast_hint_state_changed(const char *device)
2027 struct ast_hint *hint;
2029 AST_LIST_LOCK(&hints);
2031 AST_LIST_TRAVERSE(&hints, hint, list) {
2032 struct ast_state_cb *cblist;
2033 char buf[AST_MAX_EXTENSION];
2034 char *parse = buf;
2035 char *cur;
2036 int state;
2038 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
2039 while ( (cur = strsep(&parse, "&")) ) {
2040 if (!strcasecmp(cur, device))
2041 break;
2043 if (!cur)
2044 continue;
2046 /* Get device state for this hint */
2047 state = ast_extension_state2(hint->exten);
2049 if ((state == -1) || (state == hint->laststate))
2050 continue;
2052 /* Device state changed since last check - notify the watchers */
2054 /* For general callbacks */
2055 for (cblist = statecbs; cblist; cblist = cblist->next)
2056 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2058 /* For extension callbacks */
2059 for (cblist = hint->callbacks; cblist; cblist = cblist->next)
2060 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2062 hint->laststate = state; /* record we saw the change */
2065 AST_LIST_UNLOCK(&hints);
2068 /*! \brief ast_extension_state_add: Add watcher for extension states */
2069 int ast_extension_state_add(const char *context, const char *exten,
2070 ast_state_cb_type callback, void *data)
2072 struct ast_hint *hint;
2073 struct ast_state_cb *cblist;
2074 struct ast_exten *e;
2076 /* If there's no context and extension: add callback to statecbs list */
2077 if (!context && !exten) {
2078 AST_LIST_LOCK(&hints);
2080 for (cblist = statecbs; cblist; cblist = cblist->next) {
2081 if (cblist->callback == callback) {
2082 cblist->data = data;
2083 AST_LIST_UNLOCK(&hints);
2084 return 0;
2088 /* Now insert the callback */
2089 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2090 AST_LIST_UNLOCK(&hints);
2091 return -1;
2093 cblist->id = 0;
2094 cblist->callback = callback;
2095 cblist->data = data;
2097 cblist->next = statecbs;
2098 statecbs = cblist;
2100 AST_LIST_UNLOCK(&hints);
2101 return 0;
2104 if (!context || !exten)
2105 return -1;
2107 /* This callback type is for only one hint, so get the hint */
2108 e = ast_hint_extension(NULL, context, exten);
2109 if (!e) {
2110 return -1;
2113 /* Find the hint in the list of hints */
2114 AST_LIST_LOCK(&hints);
2116 AST_LIST_TRAVERSE(&hints, hint, list) {
2117 if (hint->exten == e)
2118 break;
2121 if (!hint) {
2122 /* We have no hint, sorry */
2123 AST_LIST_UNLOCK(&hints);
2124 return -1;
2127 /* Now insert the callback in the callback list */
2128 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2129 AST_LIST_UNLOCK(&hints);
2130 return -1;
2132 cblist->id = stateid++; /* Unique ID for this callback */
2133 cblist->callback = callback; /* Pointer to callback routine */
2134 cblist->data = data; /* Data for the callback */
2136 cblist->next = hint->callbacks;
2137 hint->callbacks = cblist;
2139 AST_LIST_UNLOCK(&hints);
2140 return cblist->id;
2143 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */
2144 int ast_extension_state_del(int id, ast_state_cb_type callback)
2146 struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
2147 int ret = -1;
2149 if (!id && !callback)
2150 return -1;
2152 AST_LIST_LOCK(&hints);
2154 if (!id) { /* id == 0 is a callback without extension */
2155 for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
2156 if ((*p_cur)->callback == callback)
2157 break;
2159 } else { /* callback with extension, find the callback based on ID */
2160 struct ast_hint *hint;
2161 AST_LIST_TRAVERSE(&hints, hint, list) {
2162 for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
2163 if ((*p_cur)->id == id)
2164 break;
2166 if (*p_cur) /* found in the inner loop */
2167 break;
2170 if (p_cur && *p_cur) {
2171 struct ast_state_cb *cur = *p_cur;
2172 *p_cur = cur->next;
2173 free(cur);
2174 ret = 0;
2176 AST_LIST_UNLOCK(&hints);
2177 return ret;
2180 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
2181 static int ast_add_hint(struct ast_exten *e)
2183 struct ast_hint *hint;
2185 if (!e)
2186 return -1;
2188 AST_LIST_LOCK(&hints);
2190 /* Search if hint exists, do nothing */
2191 AST_LIST_TRAVERSE(&hints, hint, list) {
2192 if (hint->exten == e) {
2193 AST_LIST_UNLOCK(&hints);
2194 if (option_debug > 1)
2195 ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2196 return -1;
2200 if (option_debug > 1)
2201 ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2203 if (!(hint = ast_calloc(1, sizeof(*hint)))) {
2204 AST_LIST_UNLOCK(&hints);
2205 return -1;
2207 /* Initialize and insert new item at the top */
2208 hint->exten = e;
2209 hint->laststate = ast_extension_state2(e);
2210 AST_LIST_INSERT_HEAD(&hints, hint, list);
2212 AST_LIST_UNLOCK(&hints);
2213 return 0;
2216 /*! \brief ast_change_hint: Change hint for an extension */
2217 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
2219 struct ast_hint *hint;
2220 int res = -1;
2222 AST_LIST_LOCK(&hints);
2223 AST_LIST_TRAVERSE(&hints, hint, list) {
2224 if (hint->exten == oe) {
2225 hint->exten = ne;
2226 res = 0;
2227 break;
2230 AST_LIST_UNLOCK(&hints);
2232 return res;
2235 /*! \brief ast_remove_hint: Remove hint from extension */
2236 static int ast_remove_hint(struct ast_exten *e)
2238 /* Cleanup the Notifys if hint is removed */
2239 struct ast_hint *hint;
2240 struct ast_state_cb *cblist, *cbprev;
2241 int res = -1;
2243 if (!e)
2244 return -1;
2246 AST_LIST_LOCK(&hints);
2247 AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
2248 if (hint->exten == e) {
2249 cbprev = NULL;
2250 cblist = hint->callbacks;
2251 while (cblist) {
2252 /* Notify with -1 and remove all callbacks */
2253 cbprev = cblist;
2254 cblist = cblist->next;
2255 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
2256 free(cbprev);
2258 hint->callbacks = NULL;
2259 AST_LIST_REMOVE_CURRENT(&hints, list);
2260 free(hint);
2261 res = 0;
2262 break;
2265 AST_LIST_TRAVERSE_SAFE_END
2266 AST_LIST_UNLOCK(&hints);
2268 return res;
2272 /*! \brief ast_get_hint: Get hint for channel */
2273 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
2275 struct ast_exten *e = ast_hint_extension(c, context, exten);
2277 if (e) {
2278 if (hint)
2279 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
2280 if (name) {
2281 const char *tmp = ast_get_extension_app_data(e);
2282 if (tmp)
2283 ast_copy_string(name, tmp, namesize);
2285 return -1;
2287 return 0;
2290 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2292 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH);
2295 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
2297 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL);
2300 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
2302 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
2305 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2307 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH);
2310 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2312 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE);
2315 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2317 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN);
2320 /* helper function to set extension and priority */
2321 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
2323 ast_channel_lock(c);
2324 ast_copy_string(c->exten, exten, sizeof(c->exten));
2325 c->priority = pri;
2326 ast_channel_unlock(c);
2330 * \brief collect digits from the channel into the buffer,
2331 * return -1 on error, 0 on timeout or done.
2333 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
2335 int digit;
2337 buf[pos] = '\0'; /* make sure it is properly terminated */
2338 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
2339 /* As long as we're willing to wait, and as long as it's not defined,
2340 keep reading digits until we can't possibly get a right answer anymore. */
2341 digit = ast_waitfordigit(c, waittime * 1000);
2342 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2343 c->_softhangup = 0;
2344 } else {
2345 if (!digit) /* No entry */
2346 break;
2347 if (digit < 0) /* Error, maybe a hangup */
2348 return -1;
2349 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
2350 buf[pos++] = digit;
2351 buf[pos] = '\0';
2353 waittime = c->pbx->dtimeout;
2356 return 0;
2359 static int __ast_pbx_run(struct ast_channel *c)
2361 int found = 0; /* set if we find at least one match */
2362 int res = 0;
2363 int autoloopflag;
2364 int error = 0; /* set an error conditions */
2366 /* A little initial setup here */
2367 if (c->pbx) {
2368 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
2369 /* XXX and now what ? */
2370 free(c->pbx);
2372 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
2373 return -1;
2374 /* Set reasonable defaults */
2375 c->pbx->rtimeout = 10;
2376 c->pbx->dtimeout = 5;
2378 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
2379 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2381 /* Start by trying whatever the channel is set to */
2382 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2383 /* If not successful fall back to 's' */
2384 if (option_verbose > 1)
2385 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
2386 /* XXX the original code used the existing priority in the call to
2387 * ast_exists_extension(), and reset it to 1 afterwards.
2388 * I believe the correct thing is to set it to 1 immediately.
2390 set_ext_pri(c, "s", 1);
2391 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2392 /* JK02: And finally back to default if everything else failed */
2393 if (option_verbose > 1)
2394 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
2395 ast_copy_string(c->context, "default", sizeof(c->context));
2398 for (;;) {
2399 char dst_exten[256]; /* buffer to accumulate digits */
2400 int pos = 0; /* XXX should check bounds */
2401 int digit = 0;
2403 /* loop on priorities in this context/exten */
2404 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2405 found = 1;
2406 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2407 /* Something bad happened, or a hangup has been requested. */
2408 if (strchr("0123456789ABCDEF*#", res)) {
2409 if (option_debug)
2410 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
2411 pos = 0;
2412 dst_exten[pos++] = digit = res;
2413 dst_exten[pos] = '\0';
2414 break;
2416 if (res == AST_PBX_KEEPALIVE) {
2417 if (option_debug)
2418 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2419 if (option_verbose > 1)
2420 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2421 error = 1;
2422 break;
2424 if (option_debug)
2425 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2426 if (option_verbose > 1)
2427 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2428 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2429 c->_softhangup = 0;
2430 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2431 /* atimeout, nothing bad */
2432 } else {
2433 if (c->cdr)
2434 ast_cdr_update(c);
2435 error = 1;
2436 break;
2439 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2440 c->_softhangup = 0;
2441 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
2442 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
2443 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2444 c->whentohangup = 0;
2445 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2446 } else if (c->_softhangup) {
2447 if (option_debug)
2448 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
2449 c->exten, c->priority);
2450 error = 1;
2451 break;
2453 c->priority++;
2454 } /* end while - from here on we can use 'break' to go out */
2455 if (error)
2456 break;
2458 /* XXX we get here on non-existing extension or a keypress or hangup ? */
2460 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2461 /* If there is no match at priority 1, it is not a valid extension anymore.
2462 * Try to continue at "i", 1 or exit if the latter does not exist.
2464 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2465 if (option_verbose > 2)
2466 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2467 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2468 set_ext_pri(c, "i", 1);
2469 } else {
2470 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2471 c->name, c->exten, c->context);
2472 error = 1; /* we know what to do with it */
2473 break;
2475 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2476 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2477 c->_softhangup = 0;
2478 } else { /* keypress received, get more digits for a full extension */
2479 int waittime = 0;
2480 if (digit)
2481 waittime = c->pbx->dtimeout;
2482 else if (!autofallthrough)
2483 waittime = c->pbx->rtimeout;
2484 if (!waittime) {
2485 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2486 if (!status)
2487 status = "UNKNOWN";
2488 if (option_verbose > 2)
2489 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2490 if (!strcasecmp(status, "CONGESTION"))
2491 res = pbx_builtin_congestion(c, "10");
2492 else if (!strcasecmp(status, "CHANUNAVAIL"))
2493 res = pbx_builtin_congestion(c, "10");
2494 else if (!strcasecmp(status, "BUSY"))
2495 res = pbx_builtin_busy(c, "10");
2496 error = 1; /* XXX disable message */
2497 break; /* exit from the 'for' loop */
2500 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
2501 break;
2502 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
2503 set_ext_pri(c, dst_exten, 1);
2504 else {
2505 /* No such extension */
2506 if (!ast_strlen_zero(dst_exten)) {
2507 /* An invalid extension */
2508 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2509 if (option_verbose > 2)
2510 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
2511 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
2512 set_ext_pri(c, "i", 1);
2513 } else {
2514 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
2515 found = 1; /* XXX disable message */
2516 break;
2518 } else {
2519 /* A simple timeout */
2520 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2521 if (option_verbose > 2)
2522 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
2523 set_ext_pri(c, "t", 1);
2524 } else {
2525 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2526 found = 1; /* XXX disable message */
2527 break;
2531 if (c->cdr) {
2532 if (option_verbose > 2)
2533 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
2534 ast_cdr_update(c);
2538 if (!found && !error)
2539 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2540 if (res != AST_PBX_KEEPALIVE)
2541 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
2542 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2543 set_ext_pri(c, "h", 1);
2544 while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2545 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2546 /* Something bad happened, or a hangup has been requested. */
2547 if (option_debug)
2548 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2549 if (option_verbose > 1)
2550 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2551 break;
2553 c->priority++;
2556 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2558 pbx_destroy(c->pbx);
2559 c->pbx = NULL;
2560 if (res != AST_PBX_KEEPALIVE)
2561 ast_hangup(c);
2562 return 0;
2565 /* Returns 0 on success, non-zero if call limit was reached */
2566 static int increase_call_count(const struct ast_channel *c)
2568 int failed = 0;
2569 double curloadavg;
2570 ast_mutex_lock(&maxcalllock);
2571 if (option_maxcalls) {
2572 if (countcalls >= option_maxcalls) {
2573 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2574 failed = -1;
2577 if (option_maxload) {
2578 getloadavg(&curloadavg, 1);
2579 if (curloadavg >= option_maxload) {
2580 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2581 failed = -1;
2584 if (!failed)
2585 countcalls++;
2586 ast_mutex_unlock(&maxcalllock);
2588 return failed;
2591 static void decrease_call_count(void)
2593 ast_mutex_lock(&maxcalllock);
2594 if (countcalls > 0)
2595 countcalls--;
2596 ast_mutex_unlock(&maxcalllock);
2599 static void destroy_exten(struct ast_exten *e)
2601 if (e->priority == PRIORITY_HINT)
2602 ast_remove_hint(e);
2604 if (e->datad)
2605 e->datad(e->data);
2606 free(e);
2609 static void *pbx_thread(void *data)
2611 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2612 answer this channel and get it going.
2614 /* NOTE:
2615 The launcher of this function _MUST_ increment 'countcalls'
2616 before invoking the function; it will be decremented when the
2617 PBX has finished running on the channel
2619 struct ast_channel *c = data;
2621 __ast_pbx_run(c);
2622 decrease_call_count();
2624 pthread_exit(NULL);
2626 return NULL;
2629 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2631 pthread_t t;
2632 pthread_attr_t attr;
2634 if (!c) {
2635 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2636 return AST_PBX_FAILED;
2639 if (increase_call_count(c))
2640 return AST_PBX_CALL_LIMIT;
2642 /* Start a new thread, and get something handling this channel. */
2643 pthread_attr_init(&attr);
2644 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2645 if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
2646 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2647 pthread_attr_destroy(&attr);
2648 return AST_PBX_FAILED;
2650 pthread_attr_destroy(&attr);
2652 return AST_PBX_SUCCESS;
2655 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2657 enum ast_pbx_result res = AST_PBX_SUCCESS;
2659 if (increase_call_count(c))
2660 return AST_PBX_CALL_LIMIT;
2662 res = __ast_pbx_run(c);
2663 decrease_call_count();
2665 return res;
2668 int ast_active_calls(void)
2670 return countcalls;
2673 int pbx_set_autofallthrough(int newval)
2675 int oldval = autofallthrough;
2676 autofallthrough = newval;
2677 return oldval;
2680 /* lookup for a context with a given name,
2681 * return with conlock held if found, NULL if not found
2683 static struct ast_context *find_context_locked(const char *context)
2685 struct ast_context *c = NULL;
2687 ast_rdlock_contexts();
2688 while ( (c = ast_walk_contexts(c)) ) {
2689 if (!strcmp(ast_get_context_name(c), context))
2690 return c;
2692 ast_unlock_contexts();
2694 return NULL;
2698 * This function locks contexts list by &conlist, search for the right context
2699 * structure, leave context list locked and call ast_context_remove_include2
2700 * which removes include, unlock contexts list and return ...
2702 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2704 int ret = -1;
2705 struct ast_context *c = find_context_locked(context);
2707 if (c) {
2708 /* found, remove include from this context ... */
2709 ret = ast_context_remove_include2(c, include, registrar);
2710 ast_unlock_contexts();
2712 return ret;
2716 * When we call this function, &conlock lock must be locked, because when
2717 * we giving *con argument, some process can remove/change this context
2718 * and after that there can be segfault.
2720 * This function locks given context, removes include, unlock context and
2721 * return.
2723 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2725 struct ast_include *i, *pi = NULL;
2726 int ret = -1;
2728 ast_mutex_lock(&con->lock);
2730 /* find our include */
2731 for (i = con->includes; i; pi = i, i = i->next) {
2732 if (!strcmp(i->name, include) &&
2733 (!registrar || !strcmp(i->registrar, registrar))) {
2734 /* remove from list */
2735 if (pi)
2736 pi->next = i->next;
2737 else
2738 con->includes = i->next;
2739 /* free include and return */
2740 free(i);
2741 ret = 0;
2742 break;
2746 ast_mutex_unlock(&con->lock);
2747 return ret;
2751 * \note This function locks contexts list by &conlist, search for the rigt context
2752 * structure, leave context list locked and call ast_context_remove_switch2
2753 * which removes switch, unlock contexts list and return ...
2755 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2757 int ret = -1; /* default error return */
2758 struct ast_context *c = find_context_locked(context);
2760 if (c) {
2761 /* remove switch from this context ... */
2762 ret = ast_context_remove_switch2(c, sw, data, registrar);
2763 ast_unlock_contexts();
2765 return ret;
2769 * \brief This function locks given context, removes switch, unlock context and
2770 * return.
2771 * \note When we call this function, &conlock lock must be locked, because when
2772 * we giving *con argument, some process can remove/change this context
2773 * and after that there can be segfault.
2776 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2778 struct ast_sw *i;
2779 int ret = -1;
2781 ast_mutex_lock(&con->lock);
2783 /* walk switches */
2784 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
2785 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2786 (!registrar || !strcmp(i->registrar, registrar))) {
2787 /* found, remove from list */
2788 AST_LIST_REMOVE_CURRENT(&con->alts, list);
2789 free(i); /* free switch and return */
2790 ret = 0;
2791 break;
2794 AST_LIST_TRAVERSE_SAFE_END
2796 ast_mutex_unlock(&con->lock);
2798 return ret;
2802 * \note This functions lock contexts list, search for the right context,
2803 * call ast_context_remove_extension2, unlock contexts list and return.
2804 * In this function we are using
2806 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2808 return ast_context_remove_extension_callerid(context, extension, priority, NULL, 0, registrar);
2811 int ast_context_remove_extension_callerid(const char *context, const char *extension, int priority, const char *callerid, int matchcid, const char *registrar)
2813 int ret = -1; /* default error return */
2814 struct ast_context *c = find_context_locked(context);
2816 if (c) { /* ... remove extension ... */
2817 ret = ast_context_remove_extension_callerid2(c, extension, priority, callerid, matchcid, registrar);
2818 ast_unlock_contexts();
2820 return ret;
2824 * \brief This functionc locks given context, search for the right extension and
2825 * fires out all peer in this extensions with given priority. If priority
2826 * is set to 0, all peers are removed. After that, unlock context and
2827 * return.
2828 * \note When do you want to call this function, make sure that &conlock is locked,
2829 * because some process can handle with your *con context before you lock
2830 * it.
2833 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2835 return ast_context_remove_extension_callerid2(con, extension, priority, NULL, 0, registrar);
2838 int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, int priority, const char *callerid, int matchcid, const char *registrar)
2840 struct ast_exten *exten, *prev_exten = NULL;
2841 struct ast_exten *peer;
2842 struct ast_exten *previous_peer = NULL;
2843 struct ast_exten *next_peer = NULL;
2844 int found = 0;
2846 ast_mutex_lock(&con->lock);
2848 /* scan the extension list to find first matching extension-registrar */
2849 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
2850 if (!strcmp(exten->exten, extension) &&
2851 (!registrar || !strcmp(exten->registrar, registrar)))
2852 break;
2854 if (!exten) {
2855 /* we can't find right extension */
2856 ast_mutex_unlock(&con->lock);
2857 return -1;
2860 /* scan the priority list to remove extension with exten->priority == priority */
2861 for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
2862 peer && !strcmp(peer->exten, extension);
2863 peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
2864 if ((priority == 0 || peer->priority == priority) &&
2865 (!callerid || !matchcid || (matchcid && !strcmp(peer->cidmatch, callerid))) &&
2866 (!registrar || !strcmp(peer->registrar, registrar) )) {
2867 found = 1;
2869 /* we are first priority extension? */
2870 if (!previous_peer) {
2872 * We are first in the priority chain, so must update the extension chain.
2873 * The next node is either the next priority or the next extension
2875 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
2877 if (!prev_exten) { /* change the root... */
2878 con->root = next_node;
2879 } else {
2880 prev_exten->next = next_node; /* unlink */
2882 if (peer->peer) { /* update the new head of the pri list */
2883 peer->peer->next = peer->next;
2885 } else { /* easy, we are not first priority in extension */
2886 previous_peer->peer = peer->peer;
2889 /* now, free whole priority extension */
2890 destroy_exten(peer);
2891 } else {
2892 previous_peer = peer;
2895 ast_mutex_unlock(&con->lock);
2896 return found ? 0 : -1;
2901 * \note This function locks contexts list by &conlist, searches for the right context
2902 * structure, and locks the macrolock mutex in that context.
2903 * macrolock is used to limit a macro to be executed by one call at a time.
2905 int ast_context_lockmacro(const char *context)
2907 struct ast_context *c = NULL;
2908 int ret = -1;
2910 ast_rdlock_contexts();
2912 while ((c = ast_walk_contexts(c))) {
2913 if (!strcmp(ast_get_context_name(c), context)) {
2914 ret = 0;
2915 break;
2919 ast_unlock_contexts();
2921 /* if we found context, lock macrolock */
2922 if (ret == 0)
2923 ret = ast_mutex_lock(&c->macrolock);
2925 return ret;
2929 * \note This function locks contexts list by &conlist, searches for the right context
2930 * structure, and unlocks the macrolock mutex in that context.
2931 * macrolock is used to limit a macro to be executed by one call at a time.
2933 int ast_context_unlockmacro(const char *context)
2935 struct ast_context *c = NULL;
2936 int ret = -1;
2938 ast_rdlock_contexts();
2940 while ((c = ast_walk_contexts(c))) {
2941 if (!strcmp(ast_get_context_name(c), context)) {
2942 ret = 0;
2943 break;
2947 ast_unlock_contexts();
2949 /* if we found context, unlock macrolock */
2950 if (ret == 0)
2951 ret = ast_mutex_unlock(&c->macrolock);
2953 return ret;
2956 /*! \brief Dynamically register a new dial plan application */
2957 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
2959 struct ast_app *tmp, *cur = NULL;
2960 char tmps[80];
2961 int length;
2963 AST_LIST_LOCK(&apps);
2964 AST_LIST_TRAVERSE(&apps, tmp, list) {
2965 if (!strcasecmp(app, tmp->name)) {
2966 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2967 AST_LIST_UNLOCK(&apps);
2968 return -1;
2972 length = sizeof(*tmp) + strlen(app) + 1;
2974 if (!(tmp = ast_calloc(1, length))) {
2975 AST_LIST_UNLOCK(&apps);
2976 return -1;
2979 strcpy(tmp->name, app);
2980 tmp->execute = execute;
2981 tmp->synopsis = synopsis;
2982 tmp->description = description;
2984 /* Store in alphabetical order */
2985 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
2986 if (strcasecmp(tmp->name, cur->name) < 0) {
2987 AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
2988 break;
2991 AST_LIST_TRAVERSE_SAFE_END
2992 if (!cur)
2993 AST_LIST_INSERT_TAIL(&apps, tmp, list);
2995 if (option_verbose > 1)
2996 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
2998 AST_LIST_UNLOCK(&apps);
3000 return 0;
3004 * Append to the list. We don't have a tail pointer because we need
3005 * to scan the list anyways to check for duplicates during insertion.
3007 int ast_register_switch(struct ast_switch *sw)
3009 struct ast_switch *tmp;
3011 AST_LIST_LOCK(&switches);
3012 AST_LIST_TRAVERSE(&switches, tmp, list) {
3013 if (!strcasecmp(tmp->name, sw->name)) {
3014 AST_LIST_UNLOCK(&switches);
3015 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
3016 return -1;
3019 AST_LIST_INSERT_TAIL(&switches, sw, list);
3020 AST_LIST_UNLOCK(&switches);
3022 return 0;
3025 void ast_unregister_switch(struct ast_switch *sw)
3027 AST_LIST_LOCK(&switches);
3028 AST_LIST_REMOVE(&switches, sw, list);
3029 AST_LIST_UNLOCK(&switches);
3033 * Help for CLI commands ...
3035 static char show_applications_help[] =
3036 "Usage: core show applications [{like|describing} <text>]\n"
3037 " List applications which are currently available.\n"
3038 " If 'like', <text> will be a substring of the app name\n"
3039 " If 'describing', <text> will be a substring of the description\n";
3041 static char show_functions_help[] =
3042 "Usage: core show functions [like <text>]\n"
3043 " List builtin functions, optionally only those matching a given string\n";
3045 static char show_switches_help[] =
3046 "Usage: core show switches\n"
3047 " List registered switches\n";
3049 static char show_hints_help[] =
3050 "Usage: core show hints\n"
3051 " List registered hints\n";
3053 static char show_globals_help[] =
3054 "Usage: core show globals\n"
3055 " List current global dialplan variables and their values\n";
3057 static char show_application_help[] =
3058 "Usage: core show application <application> [<application> [<application> [...]]]\n"
3059 " Describes a particular application.\n";
3061 static char show_function_help[] =
3062 "Usage: core show function <function>\n"
3063 " Describe a particular dialplan function.\n";
3065 static char show_dialplan_help[] =
3066 "Usage: dialplan show [exten@][context]\n"
3067 " Show dialplan\n";
3069 static char set_global_help[] =
3070 "Usage: core set global <name> <value>\n"
3071 " Set global dialplan variable <name> to <value>\n";
3075 * \brief 'show application' CLI command implementation functions ...
3079 * There is a possibility to show informations about more than one
3080 * application at one time. You can type 'show application Dial Echo' and
3081 * you will see informations about these two applications ...
3083 static char *complete_show_application(const char *line, const char *word, int pos, int state)
3085 struct ast_app *a;
3086 char *ret = NULL;
3087 int which = 0;
3088 int wordlen = strlen(word);
3090 /* return the n-th [partial] matching entry */
3091 AST_LIST_LOCK(&apps);
3092 AST_LIST_TRAVERSE(&apps, a, list) {
3093 if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
3094 ret = strdup(a->name);
3095 break;
3098 AST_LIST_UNLOCK(&apps);
3100 return ret;
3103 static int handle_show_application_deprecated(int fd, int argc, char *argv[])
3105 struct ast_app *a;
3106 int app, no_registered_app = 1;
3108 if (argc < 3)
3109 return RESULT_SHOWUSAGE;
3111 /* ... go through all applications ... */
3112 AST_LIST_LOCK(&apps);
3113 AST_LIST_TRAVERSE(&apps, a, list) {
3114 /* ... compare this application name with all arguments given
3115 * to 'show application' command ... */
3116 for (app = 2; app < argc; app++) {
3117 if (!strcasecmp(a->name, argv[app])) {
3118 /* Maximum number of characters added by terminal coloring is 22 */
3119 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3120 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3121 int synopsis_size, description_size;
3123 no_registered_app = 0;
3125 if (a->synopsis)
3126 synopsis_size = strlen(a->synopsis) + 23;
3127 else
3128 synopsis_size = strlen("Not available") + 23;
3129 synopsis = alloca(synopsis_size);
3131 if (a->description)
3132 description_size = strlen(a->description) + 23;
3133 else
3134 description_size = strlen("Not available") + 23;
3135 description = alloca(description_size);
3137 if (synopsis && description) {
3138 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3139 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3140 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3141 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3142 term_color(synopsis,
3143 a->synopsis ? a->synopsis : "Not available",
3144 COLOR_CYAN, 0, synopsis_size);
3145 term_color(description,
3146 a->description ? a->description : "Not available",
3147 COLOR_CYAN, 0, description_size);
3149 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3150 } else {
3151 /* ... one of our applications, show info ...*/
3152 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3153 "[Synopsis]\n %s\n\n"
3154 "[Description]\n%s\n",
3155 a->name,
3156 a->synopsis ? a->synopsis : "Not available",
3157 a->description ? a->description : "Not available");
3162 AST_LIST_UNLOCK(&apps);
3164 /* we found at least one app? no? */
3165 if (no_registered_app) {
3166 ast_cli(fd, "Your application(s) is (are) not registered\n");
3167 return RESULT_FAILURE;
3170 return RESULT_SUCCESS;
3173 static int handle_show_application(int fd, int argc, char *argv[])
3175 struct ast_app *a;
3176 int app, no_registered_app = 1;
3178 if (argc < 4)
3179 return RESULT_SHOWUSAGE;
3181 /* ... go through all applications ... */
3182 AST_LIST_LOCK(&apps);
3183 AST_LIST_TRAVERSE(&apps, a, list) {
3184 /* ... compare this application name with all arguments given
3185 * to 'show application' command ... */
3186 for (app = 3; app < argc; app++) {
3187 if (!strcasecmp(a->name, argv[app])) {
3188 /* Maximum number of characters added by terminal coloring is 22 */
3189 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3190 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3191 int synopsis_size, description_size;
3193 no_registered_app = 0;
3195 if (a->synopsis)
3196 synopsis_size = strlen(a->synopsis) + 23;
3197 else
3198 synopsis_size = strlen("Not available") + 23;
3199 synopsis = alloca(synopsis_size);
3201 if (a->description)
3202 description_size = strlen(a->description) + 23;
3203 else
3204 description_size = strlen("Not available") + 23;
3205 description = alloca(description_size);
3207 if (synopsis && description) {
3208 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3209 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3210 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3211 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3212 term_color(synopsis,
3213 a->synopsis ? a->synopsis : "Not available",
3214 COLOR_CYAN, 0, synopsis_size);
3215 term_color(description,
3216 a->description ? a->description : "Not available",
3217 COLOR_CYAN, 0, description_size);
3219 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3220 } else {
3221 /* ... one of our applications, show info ...*/
3222 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3223 "[Synopsis]\n %s\n\n"
3224 "[Description]\n%s\n",
3225 a->name,
3226 a->synopsis ? a->synopsis : "Not available",
3227 a->description ? a->description : "Not available");
3232 AST_LIST_UNLOCK(&apps);
3234 /* we found at least one app? no? */
3235 if (no_registered_app) {
3236 ast_cli(fd, "Your application(s) is (are) not registered\n");
3237 return RESULT_FAILURE;
3240 return RESULT_SUCCESS;
3243 /*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
3244 static int handle_show_hints(int fd, int argc, char *argv[])
3246 struct ast_hint *hint;
3247 int num = 0;
3248 int watchers;
3249 struct ast_state_cb *watcher;
3251 if (AST_LIST_EMPTY(&hints)) {
3252 ast_cli(fd, "There are no registered dialplan hints\n");
3253 return RESULT_SUCCESS;
3255 /* ... we have hints ... */
3256 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
3257 AST_LIST_LOCK(&hints);
3258 AST_LIST_TRAVERSE(&hints, hint, list) {
3259 watchers = 0;
3260 for (watcher = hint->callbacks; watcher; watcher = watcher->next)
3261 watchers++;
3262 ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
3263 ast_get_extension_name(hint->exten),
3264 ast_get_context_name(ast_get_extension_context(hint->exten)),
3265 ast_get_extension_app(hint->exten),
3266 ast_extension_state2str(hint->laststate), watchers);
3267 num++;
3269 ast_cli(fd, "----------------\n");
3270 ast_cli(fd, "- %d hints registered\n", num);
3271 AST_LIST_UNLOCK(&hints);
3272 return RESULT_SUCCESS;
3275 /*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
3276 static int handle_show_switches(int fd, int argc, char *argv[])
3278 struct ast_switch *sw;
3280 AST_LIST_LOCK(&switches);
3282 if (AST_LIST_EMPTY(&switches)) {
3283 AST_LIST_UNLOCK(&switches);
3284 ast_cli(fd, "There are no registered alternative switches\n");
3285 return RESULT_SUCCESS;
3288 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
3289 AST_LIST_TRAVERSE(&switches, sw, list)
3290 ast_cli(fd, "%s: %s\n", sw->name, sw->description);
3292 AST_LIST_UNLOCK(&switches);
3294 return RESULT_SUCCESS;
3298 * 'show applications' CLI command implementation functions ...
3300 static int handle_show_applications_deprecated(int fd, int argc, char *argv[])
3302 struct ast_app *a;
3303 int like = 0, describing = 0;
3304 int total_match = 0; /* Number of matches in like clause */
3305 int total_apps = 0; /* Number of apps registered */
3307 AST_LIST_LOCK(&apps);
3309 if (AST_LIST_EMPTY(&apps)) {
3310 ast_cli(fd, "There are no registered applications\n");
3311 AST_LIST_UNLOCK(&apps);
3312 return -1;
3315 /* show applications like <keyword> */
3316 if ((argc == 4) && (!strcmp(argv[2], "like"))) {
3317 like = 1;
3318 } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
3319 describing = 1;
3322 /* show applications describing <keyword1> [<keyword2>] [...] */
3323 if ((!like) && (!describing)) {
3324 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3325 } else {
3326 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3329 AST_LIST_TRAVERSE(&apps, a, list) {
3330 int printapp = 0;
3331 total_apps++;
3332 if (like) {
3333 if (strcasestr(a->name, argv[3])) {
3334 printapp = 1;
3335 total_match++;
3337 } else if (describing) {
3338 if (a->description) {
3339 /* Match all words on command line */
3340 int i;
3341 printapp = 1;
3342 for (i = 3; i < argc; i++) {
3343 if (!strcasestr(a->description, argv[i])) {
3344 printapp = 0;
3345 } else {
3346 total_match++;
3350 } else {
3351 printapp = 1;
3354 if (printapp) {
3355 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3358 if ((!like) && (!describing)) {
3359 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3360 } else {
3361 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3364 AST_LIST_UNLOCK(&apps);
3366 return RESULT_SUCCESS;
3368 static int handle_show_applications(int fd, int argc, char *argv[])
3370 struct ast_app *a;
3371 int like = 0, describing = 0;
3372 int total_match = 0; /* Number of matches in like clause */
3373 int total_apps = 0; /* Number of apps registered */
3375 AST_LIST_LOCK(&apps);
3377 if (AST_LIST_EMPTY(&apps)) {
3378 ast_cli(fd, "There are no registered applications\n");
3379 AST_LIST_UNLOCK(&apps);
3380 return -1;
3383 /* core list applications like <keyword> */
3384 if ((argc == 5) && (!strcmp(argv[3], "like"))) {
3385 like = 1;
3386 } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) {
3387 describing = 1;
3390 /* core list applications describing <keyword1> [<keyword2>] [...] */
3391 if ((!like) && (!describing)) {
3392 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3393 } else {
3394 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3397 AST_LIST_TRAVERSE(&apps, a, list) {
3398 int printapp = 0;
3399 total_apps++;
3400 if (like) {
3401 if (strcasestr(a->name, argv[4])) {
3402 printapp = 1;
3403 total_match++;
3405 } else if (describing) {
3406 if (a->description) {
3407 /* Match all words on command line */
3408 int i;
3409 printapp = 1;
3410 for (i = 4; i < argc; i++) {
3411 if (!strcasestr(a->description, argv[i])) {
3412 printapp = 0;
3413 } else {
3414 total_match++;
3418 } else {
3419 printapp = 1;
3422 if (printapp) {
3423 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3426 if ((!like) && (!describing)) {
3427 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3428 } else {
3429 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3432 AST_LIST_UNLOCK(&apps);
3434 return RESULT_SUCCESS;
3437 static char *complete_show_applications_deprecated(const char *line, const char *word, int pos, int state)
3439 static char* choices[] = { "like", "describing", NULL };
3441 return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
3444 static char *complete_show_applications(const char *line, const char *word, int pos, int state)
3446 static char* choices[] = { "like", "describing", NULL };
3448 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
3452 * 'show dialplan' CLI command implementation functions ...
3454 static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
3455 int state)
3457 struct ast_context *c = NULL;
3458 char *ret = NULL;
3459 int which = 0;
3460 int wordlen;
3462 /* we are do completion of [exten@]context on second position only */
3463 if (pos != 2)
3464 return NULL;
3466 ast_rdlock_contexts();
3468 wordlen = strlen(word);
3470 /* walk through all contexts and return the n-th match */
3471 while ( (c = ast_walk_contexts(c)) ) {
3472 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
3473 ret = ast_strdup(ast_get_context_name(c));
3474 break;
3478 ast_unlock_contexts();
3480 return ret;
3483 struct dialplan_counters {
3484 int total_context;
3485 int total_exten;
3486 int total_prio;
3487 int context_existence;
3488 int extension_existence;
3491 /*! \brief helper function to print an extension */
3492 static void print_ext(struct ast_exten *e, char * buf, int buflen)
3494 int prio = ast_get_extension_priority(e);
3495 if (prio == PRIORITY_HINT) {
3496 snprintf(buf, buflen, "hint: %s",
3497 ast_get_extension_app(e));
3498 } else {
3499 snprintf(buf, buflen, "%d. %s(%s)",
3500 prio, ast_get_extension_app(e),
3501 (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
3505 /* XXX not verified */
3506 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[])
3508 struct ast_context *c = NULL;
3509 int res = 0, old_total_exten = dpc->total_exten;
3511 ast_rdlock_contexts();
3513 /* walk all contexts ... */
3514 while ( (c = ast_walk_contexts(c)) ) {
3515 struct ast_exten *e;
3516 struct ast_include *i;
3517 struct ast_ignorepat *ip;
3518 char buf[256], buf2[256];
3519 int context_info_printed = 0;
3521 if (context && strcmp(ast_get_context_name(c), context))
3522 continue; /* skip this one, name doesn't match */
3524 dpc->context_existence = 1;
3526 ast_lock_context(c);
3528 /* are we looking for exten too? if yes, we print context
3529 * only if we find our extension.
3530 * Otherwise print context even if empty ?
3531 * XXX i am not sure how the rinclude is handled.
3532 * I think it ought to go inside.
3534 if (!exten) {
3535 dpc->total_context++;
3536 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3537 ast_get_context_name(c), ast_get_context_registrar(c));
3538 context_info_printed = 1;
3541 /* walk extensions ... */
3542 e = NULL;
3543 while ( (e = ast_walk_context_extensions(c, e)) ) {
3544 struct ast_exten *p;
3546 if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
3547 continue; /* skip, extension match failed */
3549 dpc->extension_existence = 1;
3551 /* may we print context info? */
3552 if (!context_info_printed) {
3553 dpc->total_context++;
3554 if (rinclude) { /* TODO Print more info about rinclude */
3555 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
3556 ast_get_context_name(c), ast_get_context_registrar(c));
3557 } else {
3558 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3559 ast_get_context_name(c), ast_get_context_registrar(c));
3561 context_info_printed = 1;
3563 dpc->total_prio++;
3565 /* write extension name and first peer */
3566 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
3568 print_ext(e, buf2, sizeof(buf2));
3570 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
3571 ast_get_extension_registrar(e));
3573 dpc->total_exten++;
3574 /* walk next extension peers */
3575 p = e; /* skip the first one, we already got it */
3576 while ( (p = ast_walk_extension_priorities(e, p)) ) {
3577 const char *el = ast_get_extension_label(p);
3578 dpc->total_prio++;
3579 if (el)
3580 snprintf(buf, sizeof(buf), " [%s]", el);
3581 else
3582 buf[0] = '\0';
3583 print_ext(p, buf2, sizeof(buf2));
3585 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
3586 ast_get_extension_registrar(p));
3590 /* walk included and write info ... */
3591 i = NULL;
3592 while ( (i = ast_walk_context_includes(c, i)) ) {
3593 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
3594 if (exten) {
3595 /* Check all includes for the requested extension */
3596 if (includecount >= AST_PBX_MAX_STACK) {
3597 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
3598 } else {
3599 int dupe=0;
3600 int x;
3601 for (x=0;x<includecount;x++) {
3602 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
3603 dupe++;
3604 break;
3607 if (!dupe) {
3608 includes[includecount] = ast_get_include_name(i);
3609 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
3610 } else {
3611 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
3614 } else {
3615 ast_cli(fd, " Include => %-45s [%s]\n",
3616 buf, ast_get_include_registrar(i));
3620 /* walk ignore patterns and write info ... */
3621 ip = NULL;
3622 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
3623 const char *ipname = ast_get_ignorepat_name(ip);
3624 char ignorepat[AST_MAX_EXTENSION];
3625 snprintf(buf, sizeof(buf), "'%s'", ipname);
3626 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
3627 if (!exten || ast_extension_match(ignorepat, exten)) {
3628 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
3629 buf, ast_get_ignorepat_registrar(ip));
3632 if (!rinclude) {
3633 struct ast_sw *sw = NULL;
3634 while ( (sw = ast_walk_context_switches(c, sw)) ) {
3635 snprintf(buf, sizeof(buf), "'%s/%s'",
3636 ast_get_switch_name(sw),
3637 ast_get_switch_data(sw));
3638 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
3639 buf, ast_get_switch_registrar(sw));
3643 ast_unlock_context(c);
3645 /* if we print something in context, make an empty line */
3646 if (context_info_printed)
3647 ast_cli(fd, "\r\n");
3649 ast_unlock_contexts();
3651 return (dpc->total_exten == old_total_exten) ? -1 : res;
3654 static int handle_show_dialplan(int fd, int argc, char *argv[])
3656 char *exten = NULL, *context = NULL;
3657 /* Variables used for different counters */
3658 struct dialplan_counters counters;
3660 const char *incstack[AST_PBX_MAX_STACK];
3661 memset(&counters, 0, sizeof(counters));
3663 if (argc != 2 && argc != 3)
3664 return RESULT_SHOWUSAGE;
3666 /* we obtain [exten@]context? if yes, split them ... */
3667 if (argc == 3) {
3668 if (strchr(argv[2], '@')) { /* split into exten & context */
3669 context = ast_strdupa(argv[2]);
3670 exten = strsep(&context, "@");
3671 /* change empty strings to NULL */
3672 if (ast_strlen_zero(exten))
3673 exten = NULL;
3674 } else { /* no '@' char, only context given */
3675 context = argv[2];
3677 if (ast_strlen_zero(context))
3678 context = NULL;
3680 /* else Show complete dial plan, context and exten are NULL */
3681 show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
3683 /* check for input failure and throw some error messages */
3684 if (context && !counters.context_existence) {
3685 ast_cli(fd, "There is no existence of '%s' context\n", context);
3686 return RESULT_FAILURE;
3689 if (exten && !counters.extension_existence) {
3690 if (context)
3691 ast_cli(fd, "There is no existence of %s@%s extension\n",
3692 exten, context);
3693 else
3694 ast_cli(fd,
3695 "There is no existence of '%s' extension in all contexts\n",
3696 exten);
3697 return RESULT_FAILURE;
3700 ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
3701 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
3702 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
3703 counters.total_context, counters.total_context == 1 ? "context" : "contexts");
3705 /* everything ok */
3706 return RESULT_SUCCESS;
3709 /*! \brief CLI support for listing global variables in a parseable way */
3710 static int handle_show_globals(int fd, int argc, char *argv[])
3712 int i = 0;
3713 struct ast_var_t *newvariable;
3715 ast_mutex_lock(&globalslock);
3716 AST_LIST_TRAVERSE (&globals, newvariable, entries) {
3717 i++;
3718 ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
3720 ast_mutex_unlock(&globalslock);
3721 ast_cli(fd, "\n -- %d variables\n", i);
3723 return RESULT_SUCCESS;
3726 /*! \brief CLI support for setting global variables */
3727 static int handle_set_global_deprecated(int fd, int argc, char *argv[])
3729 if (argc != 4)
3730 return RESULT_SHOWUSAGE;
3732 pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
3733 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[2], argv[3]);
3735 return RESULT_SUCCESS;
3739 static int handle_set_global(int fd, int argc, char *argv[])
3741 if (argc != 5)
3742 return RESULT_SHOWUSAGE;
3744 pbx_builtin_setvar_helper(NULL, argv[3], argv[4]);
3745 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]);
3747 return RESULT_SUCCESS;
3753 * CLI entries for upper commands ...
3755 static struct ast_cli_entry cli_show_applications_deprecated = {
3756 { "show", "applications", NULL },
3757 handle_show_applications_deprecated, NULL,
3758 NULL, complete_show_applications_deprecated };
3760 static struct ast_cli_entry cli_show_functions_deprecated = {
3761 { "show", "functions", NULL },
3762 handle_show_functions_deprecated, NULL,
3763 NULL };
3765 static struct ast_cli_entry cli_show_switches_deprecated = {
3766 { "show", "switches", NULL },
3767 handle_show_switches, NULL,
3768 NULL };
3770 static struct ast_cli_entry cli_show_hints_deprecated = {
3771 { "show", "hints", NULL },
3772 handle_show_hints, NULL,
3773 NULL };
3775 static struct ast_cli_entry cli_show_globals_deprecated = {
3776 { "show", "globals", NULL },
3777 handle_show_globals, NULL,
3778 NULL };
3780 static struct ast_cli_entry cli_show_function_deprecated = {
3781 { "show" , "function", NULL },
3782 handle_show_function_deprecated, NULL,
3783 NULL, complete_show_function };
3785 static struct ast_cli_entry cli_show_application_deprecated = {
3786 { "show", "application", NULL },
3787 handle_show_application_deprecated, NULL,
3788 NULL, complete_show_application };
3790 static struct ast_cli_entry cli_show_dialplan_deprecated = {
3791 { "show", "dialplan", NULL },
3792 handle_show_dialplan, NULL,
3793 NULL, complete_show_dialplan_context };
3795 static struct ast_cli_entry cli_set_global_deprecated = {
3796 { "set", "global", NULL },
3797 handle_set_global_deprecated, NULL,
3798 NULL };
3800 static struct ast_cli_entry pbx_cli[] = {
3801 { { "core", "show", "applications", NULL },
3802 handle_show_applications, "Shows registered dialplan applications",
3803 show_applications_help, complete_show_applications, &cli_show_applications_deprecated },
3805 { { "core", "show", "functions", NULL },
3806 handle_show_functions, "Shows registered dialplan functions",
3807 show_functions_help, NULL, &cli_show_functions_deprecated },
3809 { { "core", "show", "switches", NULL },
3810 handle_show_switches, "Show alternative switches",
3811 show_switches_help, NULL, &cli_show_switches_deprecated },
3813 { { "core", "show", "hints", NULL },
3814 handle_show_hints, "Show dialplan hints",
3815 show_hints_help, NULL, &cli_show_hints_deprecated },
3817 { { "core", "show", "globals", NULL },
3818 handle_show_globals, "Show global dialplan variables",
3819 show_globals_help, NULL, &cli_show_globals_deprecated },
3821 { { "core", "show" , "function", NULL },
3822 handle_show_function, "Describe a specific dialplan function",
3823 show_function_help, complete_show_function, &cli_show_function_deprecated },
3825 { { "core", "show", "application", NULL },
3826 handle_show_application, "Describe a specific dialplan application",
3827 show_application_help, complete_show_application, &cli_show_application_deprecated },
3829 { { "core", "set", "global", NULL },
3830 handle_set_global, "Set global dialplan variable",
3831 set_global_help, NULL, &cli_set_global_deprecated },
3833 { { "dialplan", "show", NULL },
3834 handle_show_dialplan, "Show dialplan",
3835 show_dialplan_help, complete_show_dialplan_context, &cli_show_dialplan_deprecated },
3838 int ast_unregister_application(const char *app)
3840 struct ast_app *tmp;
3842 AST_LIST_LOCK(&apps);
3843 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
3844 if (!strcasecmp(app, tmp->name)) {
3845 AST_LIST_REMOVE_CURRENT(&apps, list);
3846 if (option_verbose > 1)
3847 ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
3848 free(tmp);
3849 break;
3852 AST_LIST_TRAVERSE_SAFE_END
3853 AST_LIST_UNLOCK(&apps);
3855 return tmp ? 0 : -1;
3858 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
3860 struct ast_context *tmp, **local_contexts;
3861 int length = sizeof(struct ast_context) + strlen(name) + 1;
3863 if (!extcontexts) {
3864 ast_rdlock_contexts();
3865 local_contexts = &contexts;
3866 } else
3867 local_contexts = extcontexts;
3869 for (tmp = *local_contexts; tmp; tmp = tmp->next) {
3870 if (!strcasecmp(tmp->name, name)) {
3871 if (!existsokay) {
3872 ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
3873 tmp = NULL;
3875 if (!extcontexts)
3876 ast_unlock_contexts();
3877 return tmp;
3881 if (!extcontexts)
3882 ast_unlock_contexts();
3884 if ((tmp = ast_calloc(1, length))) {
3885 ast_mutex_init(&tmp->lock);
3886 ast_mutex_init(&tmp->macrolock);
3887 strcpy(tmp->name, name);
3888 tmp->registrar = registrar;
3889 if (!extcontexts)
3890 ast_wrlock_contexts();
3891 tmp->next = *local_contexts;
3892 *local_contexts = tmp;
3893 if (!extcontexts)
3894 ast_unlock_contexts();
3895 if (option_debug)
3896 ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
3897 if (option_verbose > 2)
3898 ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
3901 return tmp;
3904 struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3906 return __ast_context_create(extcontexts, name, registrar, 0);
3909 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3911 return __ast_context_create(extcontexts, name, registrar, 1);
3913 void __ast_context_destroy(struct ast_context *con, const char *registrar);
3915 struct store_hint {
3916 char *context;
3917 char *exten;
3918 struct ast_state_cb *callbacks;
3919 int laststate;
3920 AST_LIST_ENTRY(store_hint) list;
3921 char data[1];
3924 AST_LIST_HEAD(store_hints, store_hint);
3926 /* XXX this does not check that multiple contexts are merged */
3927 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
3929 struct ast_context *tmp, *lasttmp = NULL;
3930 struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
3931 struct store_hint *this;
3932 struct ast_hint *hint;
3933 struct ast_exten *exten;
3934 int length;
3935 struct ast_state_cb *thiscb, *prevcb;
3937 /* it is very important that this function hold the hint list lock _and_ the conlock
3938 during its operation; not only do we need to ensure that the list of contexts
3939 and extensions does not change, but also that no hint callbacks (watchers) are
3940 added or removed during the merge/delete process
3942 in addition, the locks _must_ be taken in this order, because there are already
3943 other code paths that use this order
3945 ast_wrlock_contexts();
3946 AST_LIST_LOCK(&hints);
3948 /* preserve all watchers for hints associated with this registrar */
3949 AST_LIST_TRAVERSE(&hints, hint, list) {
3950 if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
3951 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
3952 if (!(this = ast_calloc(1, length)))
3953 continue;
3954 this->callbacks = hint->callbacks;
3955 hint->callbacks = NULL;
3956 this->laststate = hint->laststate;
3957 this->context = this->data;
3958 strcpy(this->data, hint->exten->parent->name);
3959 this->exten = this->data + strlen(this->context) + 1;
3960 strcpy(this->exten, hint->exten->exten);
3961 AST_LIST_INSERT_HEAD(&store, this, list);
3965 tmp = *extcontexts;
3966 if (registrar) {
3967 /* XXX remove previous contexts from same registrar */
3968 if (option_debug)
3969 ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
3970 __ast_context_destroy(NULL,registrar);
3971 while (tmp) {
3972 lasttmp = tmp;
3973 tmp = tmp->next;
3975 } else {
3976 /* XXX remove contexts with the same name */
3977 while (tmp) {
3978 ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
3979 __ast_context_destroy(tmp,tmp->registrar);
3980 lasttmp = tmp;
3981 tmp = tmp->next;
3984 if (lasttmp) {
3985 lasttmp->next = contexts;
3986 contexts = *extcontexts;
3987 *extcontexts = NULL;
3988 } else
3989 ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
3991 /* restore the watchers for hints that can be found; notify those that
3992 cannot be restored
3994 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
3995 struct pbx_find_info q = { .stacklen = 0 };
3996 exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
3997 /* Find the hint in the list of hints */
3998 AST_LIST_TRAVERSE(&hints, hint, list) {
3999 if (hint->exten == exten)
4000 break;
4002 if (!exten || !hint) {
4003 /* this hint has been removed, notify the watchers */
4004 prevcb = NULL;
4005 thiscb = this->callbacks;
4006 while (thiscb) {
4007 prevcb = thiscb;
4008 thiscb = thiscb->next;
4009 prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
4010 free(prevcb);
4012 } else {
4013 thiscb = this->callbacks;
4014 while (thiscb->next)
4015 thiscb = thiscb->next;
4016 thiscb->next = hint->callbacks;
4017 hint->callbacks = this->callbacks;
4018 hint->laststate = this->laststate;
4020 free(this);
4023 AST_LIST_UNLOCK(&hints);
4024 ast_unlock_contexts();
4026 return;
4030 * errno values
4031 * EBUSY - can't lock
4032 * ENOENT - no existence of context
4034 int ast_context_add_include(const char *context, const char *include, const char *registrar)
4036 int ret = -1;
4037 struct ast_context *c = find_context_locked(context);
4039 if (c) {
4040 ret = ast_context_add_include2(c, include, registrar);
4041 ast_unlock_contexts();
4043 return ret;
4046 /*! \brief Helper for get_range.
4047 * return the index of the matching entry, starting from 1.
4048 * If names is not supplied, try numeric values.
4050 static int lookup_name(const char *s, char *const names[], int max)
4052 int i;
4054 if (names) {
4055 for (i = 0; names[i]; i++) {
4056 if (!strcasecmp(s, names[i]))
4057 return i+1;
4059 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
4060 return i;
4062 return 0; /* error return */
4065 /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
4066 * names, if supplied, is an array of names that should be mapped to numbers.
4068 static unsigned get_range(char *src, int max, char *const names[], const char *msg)
4070 int s, e; /* start and ending position */
4071 unsigned int mask = 0;
4073 /* Check for whole range */
4074 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
4075 s = 0;
4076 e = max - 1;
4077 } else {
4078 /* Get start and ending position */
4079 char *c = strchr(src, '-');
4080 if (c)
4081 *c++ = '\0';
4082 /* Find the start */
4083 s = lookup_name(src, names, max);
4084 if (!s) {
4085 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
4086 return 0;
4088 s--;
4089 if (c) { /* find end of range */
4090 e = lookup_name(c, names, max);
4091 if (!e) {
4092 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
4093 return 0;
4095 e--;
4096 } else
4097 e = s;
4099 /* Fill the mask. Remember that ranges are cyclic */
4100 mask = 1 << e; /* initialize with last element */
4101 while (s != e) {
4102 if (s >= max) {
4103 s = 0;
4104 mask |= (1 << s);
4105 } else {
4106 mask |= (1 << s);
4107 s++;
4110 return mask;
4113 /*! \brief store a bitmask of valid times, one bit each 2 minute */
4114 static void get_timerange(struct ast_timing *i, char *times)
4116 char *e;
4117 int x;
4118 int s1, s2;
4119 int e1, e2;
4120 /* int cth, ctm; */
4122 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
4123 memset(i->minmask, 0, sizeof(i->minmask));
4125 /* 2-minutes per bit, since the mask has only 32 bits :( */
4126 /* Star is all times */
4127 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
4128 for (x=0; x<24; x++)
4129 i->minmask[x] = 0x3fffffff; /* 30 bits */
4130 return;
4132 /* Otherwise expect a range */
4133 e = strchr(times, '-');
4134 if (!e) {
4135 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
4136 return;
4138 *e++ = '\0';
4139 /* XXX why skip non digits ? */
4140 while (*e && !isdigit(*e))
4141 e++;
4142 if (!*e) {
4143 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
4144 return;
4146 if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
4147 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
4148 return;
4150 if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
4151 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
4152 return;
4154 /* XXX this needs to be optimized */
4155 #if 1
4156 s1 = s1 * 30 + s2/2;
4157 if ((s1 < 0) || (s1 >= 24*30)) {
4158 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
4159 return;
4161 e1 = e1 * 30 + e2/2;
4162 if ((e1 < 0) || (e1 >= 24*30)) {
4163 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
4164 return;
4166 /* Go through the time and enable each appropriate bit */
4167 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
4168 i->minmask[x/30] |= (1 << (x % 30));
4170 /* Do the last one */
4171 i->minmask[x/30] |= (1 << (x % 30));
4172 #else
4173 for (cth=0; cth<24; cth++) {
4174 /* Initialize masks to blank */
4175 i->minmask[cth] = 0;
4176 for (ctm=0; ctm<30; ctm++) {
4177 if (
4178 /* First hour with more than one hour */
4179 (((cth == s1) && (ctm >= s2)) &&
4180 ((cth < e1)))
4181 /* Only one hour */
4182 || (((cth == s1) && (ctm >= s2)) &&
4183 ((cth == e1) && (ctm <= e2)))
4184 /* In between first and last hours (more than 2 hours) */
4185 || ((cth > s1) &&
4186 (cth < e1))
4187 /* Last hour with more than one hour */
4188 || ((cth > s1) &&
4189 ((cth == e1) && (ctm <= e2)))
4191 i->minmask[cth] |= (1 << (ctm / 2));
4194 #endif
4195 /* All done */
4196 return;
4199 static char *days[] =
4201 "sun",
4202 "mon",
4203 "tue",
4204 "wed",
4205 "thu",
4206 "fri",
4207 "sat",
4208 NULL,
4211 static char *months[] =
4213 "jan",
4214 "feb",
4215 "mar",
4216 "apr",
4217 "may",
4218 "jun",
4219 "jul",
4220 "aug",
4221 "sep",
4222 "oct",
4223 "nov",
4224 "dec",
4225 NULL,
4228 int ast_build_timing(struct ast_timing *i, const char *info_in)
4230 char info_save[256];
4231 char *info;
4233 /* Check for empty just in case */
4234 if (ast_strlen_zero(info_in))
4235 return 0;
4236 /* make a copy just in case we were passed a static string */
4237 ast_copy_string(info_save, info_in, sizeof(info_save));
4238 info = info_save;
4239 /* Assume everything except time */
4240 i->monthmask = 0xfff; /* 12 bits */
4241 i->daymask = 0x7fffffffU; /* 31 bits */
4242 i->dowmask = 0x7f; /* 7 bits */
4243 /* on each call, use strsep() to move info to the next argument */
4244 get_timerange(i, strsep(&info, "|"));
4245 if (info)
4246 i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
4247 if (info)
4248 i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
4249 if (info)
4250 i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
4251 return 1;
4254 int ast_check_timing(const struct ast_timing *i)
4256 struct tm tm;
4257 time_t t = time(NULL);
4259 ast_localtime(&t, &tm, NULL);
4261 /* If it's not the right month, return */
4262 if (!(i->monthmask & (1 << tm.tm_mon)))
4263 return 0;
4265 /* If it's not that time of the month.... */
4266 /* Warning, tm_mday has range 1..31! */
4267 if (!(i->daymask & (1 << (tm.tm_mday-1))))
4268 return 0;
4270 /* If it's not the right day of the week */
4271 if (!(i->dowmask & (1 << tm.tm_wday)))
4272 return 0;
4274 /* Sanity check the hour just to be safe */
4275 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
4276 ast_log(LOG_WARNING, "Insane time...\n");
4277 return 0;
4280 /* Now the tough part, we calculate if it fits
4281 in the right time based on min/hour */
4282 if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
4283 return 0;
4285 /* If we got this far, then we're good */
4286 return 1;
4290 * errno values
4291 * ENOMEM - out of memory
4292 * EBUSY - can't lock
4293 * EEXIST - already included
4294 * EINVAL - there is no existence of context for inclusion
4296 int ast_context_add_include2(struct ast_context *con, const char *value,
4297 const char *registrar)
4299 struct ast_include *new_include;
4300 char *c;
4301 struct ast_include *i, *il = NULL; /* include, include_last */
4302 int length;
4303 char *p;
4305 length = sizeof(struct ast_include);
4306 length += 2 * (strlen(value) + 1);
4308 /* allocate new include structure ... */
4309 if (!(new_include = ast_calloc(1, length)))
4310 return -1;
4311 /* Fill in this structure. Use 'p' for assignments, as the fields
4312 * in the structure are 'const char *'
4314 p = new_include->stuff;
4315 new_include->name = p;
4316 strcpy(p, value);
4317 p += strlen(value) + 1;
4318 new_include->rname = p;
4319 strcpy(p, value);
4320 /* Strip off timing info, and process if it is there */
4321 if ( (c = strchr(p, '|')) ) {
4322 *c++ = '\0';
4323 new_include->hastime = ast_build_timing(&(new_include->timing), c);
4325 new_include->next = NULL;
4326 new_include->registrar = registrar;
4328 ast_mutex_lock(&con->lock);
4330 /* ... go to last include and check if context is already included too... */
4331 for (i = con->includes; i; i = i->next) {
4332 if (!strcasecmp(i->name, new_include->name)) {
4333 free(new_include);
4334 ast_mutex_unlock(&con->lock);
4335 errno = EEXIST;
4336 return -1;
4338 il = i;
4341 /* ... include new context into context list, unlock, return */
4342 if (il)
4343 il->next = new_include;
4344 else
4345 con->includes = new_include;
4346 if (option_verbose > 2)
4347 ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
4348 ast_mutex_unlock(&con->lock);
4350 return 0;
4354 * errno values
4355 * EBUSY - can't lock
4356 * ENOENT - no existence of context
4358 int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
4360 int ret = -1;
4361 struct ast_context *c = find_context_locked(context);
4363 if (c) { /* found, add switch to this context */
4364 ret = ast_context_add_switch2(c, sw, data, eval, registrar);
4365 ast_unlock_contexts();
4367 return ret;
4371 * errno values
4372 * ENOMEM - out of memory
4373 * EBUSY - can't lock
4374 * EEXIST - already included
4375 * EINVAL - there is no existence of context for inclusion
4377 int ast_context_add_switch2(struct ast_context *con, const char *value,
4378 const char *data, int eval, const char *registrar)
4380 struct ast_sw *new_sw;
4381 struct ast_sw *i;
4382 int length;
4383 char *p;
4385 length = sizeof(struct ast_sw);
4386 length += strlen(value) + 1;
4387 if (data)
4388 length += strlen(data);
4389 length++;
4391 /* allocate new sw structure ... */
4392 if (!(new_sw = ast_calloc(1, length)))
4393 return -1;
4394 /* ... fill in this structure ... */
4395 p = new_sw->stuff;
4396 new_sw->name = p;
4397 strcpy(new_sw->name, value);
4398 p += strlen(value) + 1;
4399 new_sw->data = p;
4400 if (data) {
4401 strcpy(new_sw->data, data);
4402 p += strlen(data) + 1;
4403 } else {
4404 strcpy(new_sw->data, "");
4405 p++;
4407 new_sw->eval = eval;
4408 new_sw->registrar = registrar;
4410 /* ... try to lock this context ... */
4411 ast_mutex_lock(&con->lock);
4413 /* ... go to last sw and check if context is already swd too... */
4414 AST_LIST_TRAVERSE(&con->alts, i, list) {
4415 if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
4416 free(new_sw);
4417 ast_mutex_unlock(&con->lock);
4418 errno = EEXIST;
4419 return -1;
4423 /* ... sw new context into context list, unlock, return */
4424 AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
4426 if (option_verbose > 2)
4427 ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
4429 ast_mutex_unlock(&con->lock);
4431 return 0;
4435 * EBUSY - can't lock
4436 * ENOENT - there is not context existence
4438 int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
4440 int ret = -1;
4441 struct ast_context *c = find_context_locked(context);
4443 if (c) {
4444 ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
4445 ast_unlock_contexts();
4447 return ret;
4450 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
4452 struct ast_ignorepat *ip, *ipl = NULL;
4454 ast_mutex_lock(&con->lock);
4456 for (ip = con->ignorepats; ip; ip = ip->next) {
4457 if (!strcmp(ip->pattern, ignorepat) &&
4458 (!registrar || (registrar == ip->registrar))) {
4459 if (ipl) {
4460 ipl->next = ip->next;
4461 free(ip);
4462 } else {
4463 con->ignorepats = ip->next;
4464 free(ip);
4466 ast_mutex_unlock(&con->lock);
4467 return 0;
4469 ipl = ip;
4472 ast_mutex_unlock(&con->lock);
4473 errno = EINVAL;
4474 return -1;
4478 * EBUSY - can't lock
4479 * ENOENT - there is no existence of context
4481 int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
4483 int ret = -1;
4484 struct ast_context *c = find_context_locked(context);
4486 if (c) {
4487 ret = ast_context_add_ignorepat2(c, value, registrar);
4488 ast_unlock_contexts();
4490 return ret;
4493 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
4495 struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
4496 int length;
4497 length = sizeof(struct ast_ignorepat);
4498 length += strlen(value) + 1;
4499 if (!(ignorepat = ast_calloc(1, length)))
4500 return -1;
4501 /* The cast to char * is because we need to write the initial value.
4502 * The field is not supposed to be modified otherwise
4504 strcpy((char *)ignorepat->pattern, value);
4505 ignorepat->next = NULL;
4506 ignorepat->registrar = registrar;
4507 ast_mutex_lock(&con->lock);
4508 for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
4509 ignorepatl = ignorepatc;
4510 if (!strcasecmp(ignorepatc->pattern, value)) {
4511 /* Already there */
4512 ast_mutex_unlock(&con->lock);
4513 errno = EEXIST;
4514 return -1;
4517 if (ignorepatl)
4518 ignorepatl->next = ignorepat;
4519 else
4520 con->ignorepats = ignorepat;
4521 ast_mutex_unlock(&con->lock);
4522 return 0;
4526 int ast_ignore_pattern(const char *context, const char *pattern)
4528 struct ast_context *con = ast_context_find(context);
4529 if (con) {
4530 struct ast_ignorepat *pat;
4531 for (pat = con->ignorepats; pat; pat = pat->next) {
4532 if (ast_extension_match(pat->pattern, pattern))
4533 return 1;
4537 return 0;
4541 * EBUSY - can't lock
4542 * ENOENT - no existence of context
4545 int ast_add_extension(const char *context, int replace, const char *extension,
4546 int priority, const char *label, const char *callerid,
4547 const char *application, void *data, void (*datad)(void *), const char *registrar)
4549 int ret = -1;
4550 struct ast_context *c = find_context_locked(context);
4552 if (c) {
4553 ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
4554 application, data, datad, registrar);
4555 ast_unlock_contexts();
4557 return ret;
4560 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4562 if (!chan)
4563 return -1;
4565 ast_channel_lock(chan);
4567 if (!ast_strlen_zero(context))
4568 ast_copy_string(chan->context, context, sizeof(chan->context));
4569 if (!ast_strlen_zero(exten))
4570 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
4571 if (priority > -1) {
4572 chan->priority = priority;
4573 /* see flag description in channel.h for explanation */
4574 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
4575 chan->priority--;
4578 ast_channel_unlock(chan);
4580 return 0;
4583 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4585 int res = 0;
4587 ast_channel_lock(chan);
4589 if (chan->pbx) { /* This channel is currently in the PBX */
4590 ast_explicit_goto(chan, context, exten, priority);
4591 ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
4592 } else {
4593 /* In order to do it when the channel doesn't really exist within
4594 the PBX, we have to make a new channel, masquerade, and start the PBX
4595 at the new location */
4596 struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
4597 if (!tmpchan) {
4598 res = -1;
4599 } else {
4600 if (chan->cdr) {
4601 ast_cdr_discard(tmpchan->cdr);
4602 tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
4604 /* Make formats okay */
4605 tmpchan->readformat = chan->readformat;
4606 tmpchan->writeformat = chan->writeformat;
4607 /* Setup proper location */
4608 ast_explicit_goto(tmpchan,
4609 S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
4611 /* Masquerade into temp channel */
4612 ast_channel_masquerade(tmpchan, chan);
4614 /* Grab the locks and get going */
4615 ast_channel_lock(tmpchan);
4616 ast_do_masquerade(tmpchan);
4617 ast_channel_unlock(tmpchan);
4618 /* Start the PBX going on our stolen channel */
4619 if (ast_pbx_start(tmpchan)) {
4620 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
4621 ast_hangup(tmpchan);
4622 res = -1;
4626 ast_channel_unlock(chan);
4627 return res;
4630 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
4632 struct ast_channel *chan;
4633 int res = -1;
4635 chan = ast_get_channel_by_name_locked(channame);
4636 if (chan) {
4637 res = ast_async_goto(chan, context, exten, priority);
4638 ast_channel_unlock(chan);
4640 return res;
4643 /*! \brief copy a string skipping whitespace */
4644 static int ext_strncpy(char *dst, const char *src, int len)
4646 int count=0;
4648 while (*src && (count < len - 1)) {
4649 switch(*src) {
4650 case ' ':
4651 /* otherwise exten => [a-b],1,... doesn't work */
4652 /* case '-': */
4653 /* Ignore */
4654 break;
4655 default:
4656 *dst = *src;
4657 dst++;
4659 src++;
4660 count++;
4662 *dst = '\0';
4664 return count;
4667 /*! \brief add the extension in the priority chain.
4668 * returns 0 on success, -1 on failure
4670 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
4671 struct ast_exten *el, struct ast_exten *e, int replace)
4673 struct ast_exten *ep;
4675 for (ep = NULL; e ; ep = e, e = e->peer) {
4676 if (e->priority >= tmp->priority)
4677 break;
4679 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
4680 ep->peer = tmp;
4681 return 0; /* success */
4683 if (e->priority == tmp->priority) {
4684 /* Can't have something exactly the same. Is this a
4685 replacement? If so, replace, otherwise, bonk. */
4686 if (!replace) {
4687 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
4688 if (tmp->datad)
4689 tmp->datad(tmp->data);
4690 free(tmp);
4691 return -1;
4693 /* we are replacing e, so copy the link fields and then update
4694 * whoever pointed to e to point to us
4696 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
4697 tmp->peer = e->peer; /* always meaningful */
4698 if (ep) /* We're in the peer list, just insert ourselves */
4699 ep->peer = tmp;
4700 else if (el) /* We're the first extension. Take over e's functions */
4701 el->next = tmp;
4702 else /* We're the very first extension. */
4703 con->root = tmp;
4704 if (tmp->priority == PRIORITY_HINT)
4705 ast_change_hint(e,tmp);
4706 /* Destroy the old one */
4707 if (e->datad)
4708 e->datad(e->data);
4709 free(e);
4710 } else { /* Slip ourselves in just before e */
4711 tmp->peer = e;
4712 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
4713 if (ep) /* Easy enough, we're just in the peer list */
4714 ep->peer = tmp;
4715 else { /* we are the first in some peer list, so link in the ext list */
4716 if (el)
4717 el->next = tmp; /* in the middle... */
4718 else
4719 con->root = tmp; /* ... or at the head */
4720 e->next = NULL; /* e is no more at the head, so e->next must be reset */
4722 /* And immediately return success. */
4723 if (tmp->priority == PRIORITY_HINT)
4724 ast_add_hint(tmp);
4726 return 0;
4729 /*! \brief
4730 * Main interface to add extensions to the list for out context.
4732 * We sort extensions in order of matching preference, so that we can
4733 * stop the search as soon as we find a suitable match.
4734 * This ordering also takes care of wildcards such as '.' (meaning
4735 * "one or more of any character") and '!' (which is 'earlymatch',
4736 * meaning "zero or more of any character" but also impacts the
4737 * return value from CANMATCH and EARLYMATCH.
4739 * The extension match rules defined in the devmeeting 2006.05.05 are
4740 * quite simple: WE SELECT THE LONGEST MATCH.
4741 * In detail, "longest" means the number of matched characters in
4742 * the extension. In case of ties (e.g. _XXX and 333) in the length
4743 * of a pattern, we give priority to entries with the smallest cardinality
4744 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
4745 * while the latter has 7, etc.
4746 * In case of same cardinality, the first element in the range counts.
4747 * If we still have a tie, any final '!' will make this as a possibly
4748 * less specific pattern.
4750 * EBUSY - can't lock
4751 * EEXIST - extension with the same priority exist and no replace is set
4754 int ast_add_extension2(struct ast_context *con,
4755 int replace, const char *extension, int priority, const char *label, const char *callerid,
4756 const char *application, void *data, void (*datad)(void *),
4757 const char *registrar)
4760 * Sort extensions (or patterns) according to the rules indicated above.
4761 * These are implemented by the function ext_cmp()).
4762 * All priorities for the same ext/pattern/cid are kept in a list,
4763 * using the 'peer' field as a link field..
4765 struct ast_exten *tmp, *e, *el = NULL;
4766 int res;
4767 int length;
4768 char *p;
4769 char expand_buf[VAR_BUF_SIZE] = { 0, };
4771 /* if we are adding a hint, and there are global variables, and the hint
4772 contains variable references, then expand them
4774 ast_mutex_lock(&globalslock);
4775 if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
4776 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
4777 application = expand_buf;
4779 ast_mutex_unlock(&globalslock);
4781 length = sizeof(struct ast_exten);
4782 length += strlen(extension) + 1;
4783 length += strlen(application) + 1;
4784 if (label)
4785 length += strlen(label) + 1;
4786 if (callerid)
4787 length += strlen(callerid) + 1;
4788 else
4789 length ++; /* just the '\0' */
4791 /* Be optimistic: Build the extension structure first */
4792 if (!(tmp = ast_calloc(1, length)))
4793 return -1;
4795 /* use p as dst in assignments, as the fields are const char * */
4796 p = tmp->stuff;
4797 if (label) {
4798 tmp->label = p;
4799 strcpy(p, label);
4800 p += strlen(label) + 1;
4802 tmp->exten = p;
4803 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
4804 tmp->priority = priority;
4805 tmp->cidmatch = p; /* but use p for assignments below */
4806 if (callerid) {
4807 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
4808 tmp->matchcid = 1;
4809 } else {
4810 *p++ = '\0';
4811 tmp->matchcid = 0;
4813 tmp->app = p;
4814 strcpy(p, application);
4815 tmp->parent = con;
4816 tmp->data = data;
4817 tmp->datad = datad;
4818 tmp->registrar = registrar;
4820 ast_mutex_lock(&con->lock);
4821 res = 0; /* some compilers will think it is uninitialized otherwise */
4822 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
4823 res = ext_cmp(e->exten, extension);
4824 if (res == 0) { /* extension match, now look at cidmatch */
4825 if (!e->matchcid && !tmp->matchcid)
4826 res = 0;
4827 else if (tmp->matchcid && !e->matchcid)
4828 res = 1;
4829 else if (e->matchcid && !tmp->matchcid)
4830 res = -1;
4831 else
4832 res = strcasecmp(e->cidmatch, tmp->cidmatch);
4834 if (res >= 0)
4835 break;
4837 if (e && res == 0) { /* exact match, insert in the pri chain */
4838 res = add_pri(con, tmp, el, e, replace);
4839 ast_mutex_unlock(&con->lock);
4840 if (res < 0) {
4841 errno = EEXIST; /* XXX do we care ? */
4842 return 0; /* XXX should we return -1 maybe ? */
4844 } else {
4846 * not an exact match, this is the first entry with this pattern,
4847 * so insert in the main list right before 'e' (if any)
4849 tmp->next = e;
4850 if (el)
4851 el->next = tmp;
4852 else
4853 con->root = tmp;
4854 ast_mutex_unlock(&con->lock);
4855 if (tmp->priority == PRIORITY_HINT)
4856 ast_add_hint(tmp);
4858 if (option_debug) {
4859 if (tmp->matchcid) {
4860 if (option_debug)
4861 ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
4862 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4863 } else {
4864 if (option_debug)
4865 ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
4866 tmp->exten, tmp->priority, con->name);
4869 if (option_verbose > 2) {
4870 if (tmp->matchcid) {
4871 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
4872 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4873 } else {
4874 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
4875 tmp->exten, tmp->priority, con->name);
4878 return 0;
4881 struct async_stat {
4882 pthread_t p;
4883 struct ast_channel *chan;
4884 char context[AST_MAX_CONTEXT];
4885 char exten[AST_MAX_EXTENSION];
4886 int priority;
4887 int timeout;
4888 char app[AST_MAX_EXTENSION];
4889 char appdata[1024];
4892 static void *async_wait(void *data)
4894 struct async_stat *as = data;
4895 struct ast_channel *chan = as->chan;
4896 int timeout = as->timeout;
4897 int res;
4898 struct ast_frame *f;
4899 struct ast_app *app;
4901 while (timeout && (chan->_state != AST_STATE_UP)) {
4902 res = ast_waitfor(chan, timeout);
4903 if (res < 1)
4904 break;
4905 if (timeout > -1)
4906 timeout = res;
4907 f = ast_read(chan);
4908 if (!f)
4909 break;
4910 if (f->frametype == AST_FRAME_CONTROL) {
4911 if ((f->subclass == AST_CONTROL_BUSY) ||
4912 (f->subclass == AST_CONTROL_CONGESTION) ) {
4913 ast_frfree(f);
4914 break;
4917 ast_frfree(f);
4919 if (chan->_state == AST_STATE_UP) {
4920 if (!ast_strlen_zero(as->app)) {
4921 app = pbx_findapp(as->app);
4922 if (app) {
4923 if (option_verbose > 2)
4924 ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
4925 pbx_exec(chan, app, as->appdata);
4926 } else
4927 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
4928 } else {
4929 if (!ast_strlen_zero(as->context))
4930 ast_copy_string(chan->context, as->context, sizeof(chan->context));
4931 if (!ast_strlen_zero(as->exten))
4932 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
4933 if (as->priority > 0)
4934 chan->priority = as->priority;
4935 /* Run the PBX */
4936 if (ast_pbx_run(chan)) {
4937 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
4938 } else {
4939 /* PBX will have taken care of this */
4940 chan = NULL;
4944 free(as);
4945 if (chan)
4946 ast_hangup(chan);
4947 return NULL;
4950 /*! Function to post an empty cdr after a spool call fails.
4952 * This function posts an empty cdr for a failed spool call
4955 static int ast_pbx_outgoing_cdr_failed(void)
4957 /* allocate a channel */
4958 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
4960 if (!chan)
4961 return -1; /* failure */
4963 if (!chan->cdr) {
4964 /* allocation of the cdr failed */
4965 ast_channel_free(chan); /* free the channel */
4966 return -1; /* return failure */
4969 /* allocation of the cdr was successful */
4970 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
4971 ast_cdr_start(chan->cdr); /* record the start and stop time */
4972 ast_cdr_end(chan->cdr);
4973 ast_cdr_failed(chan->cdr); /* set the status to failed */
4974 ast_cdr_detach(chan->cdr); /* post and free the record */
4975 ast_channel_free(chan); /* free the channel */
4977 return 0; /* success */
4980 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 sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
4982 struct ast_channel *chan;
4983 struct async_stat *as;
4984 int res = -1, cdr_res = -1;
4985 struct outgoing_helper oh;
4986 pthread_attr_t attr;
4988 if (sync) {
4989 LOAD_OH(oh);
4990 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
4991 if (channel) {
4992 *channel = chan;
4993 if (chan)
4994 ast_channel_lock(chan);
4996 if (chan) {
4997 if (chan->_state == AST_STATE_UP) {
4998 res = 0;
4999 if (option_verbose > 3)
5000 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5002 if (sync > 1) {
5003 if (channel)
5004 ast_channel_unlock(chan);
5005 if (ast_pbx_run(chan)) {
5006 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5007 if (channel)
5008 *channel = NULL;
5009 ast_hangup(chan);
5010 chan = NULL;
5011 res = -1;
5013 } else {
5014 if (ast_pbx_start(chan)) {
5015 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
5016 if (channel) {
5017 *channel = NULL;
5018 ast_channel_unlock(chan);
5020 ast_hangup(chan);
5021 res = -1;
5023 chan = NULL;
5025 } else {
5026 if (option_verbose > 3)
5027 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5029 if (chan->cdr) { /* update the cdr */
5030 /* here we update the status of the call, which sould be busy.
5031 * if that fails then we set the status to failed */
5032 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5033 ast_cdr_failed(chan->cdr);
5036 if (channel) {
5037 *channel = NULL;
5038 ast_channel_unlock(chan);
5040 ast_hangup(chan);
5041 chan = NULL;
5045 if (res < 0) { /* the call failed for some reason */
5046 if (*reason == 0) { /* if the call failed (not busy or no answer)
5047 * update the cdr with the failed message */
5048 cdr_res = ast_pbx_outgoing_cdr_failed();
5049 if (cdr_res != 0) {
5050 res = cdr_res;
5051 goto outgoing_exten_cleanup;
5055 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
5056 /* check if "failed" exists */
5057 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
5058 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
5059 if (chan) {
5060 char failed_reason[4] = "";
5061 if (!ast_strlen_zero(context))
5062 ast_copy_string(chan->context, context, sizeof(chan->context));
5063 set_ext_pri(chan, "failed", 1);
5064 ast_set_variables(chan, vars);
5065 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
5066 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
5067 if (account)
5068 ast_cdr_setaccount(chan, account);
5069 if (ast_pbx_run(chan)) {
5070 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5071 ast_hangup(chan);
5073 chan = NULL;
5077 } else {
5078 if (!(as = ast_calloc(1, sizeof(*as)))) {
5079 res = -1;
5080 goto outgoing_exten_cleanup;
5082 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
5083 if (channel) {
5084 *channel = chan;
5085 if (chan)
5086 ast_channel_lock(chan);
5088 if (!chan) {
5089 free(as);
5090 res = -1;
5091 goto outgoing_exten_cleanup;
5093 as->chan = chan;
5094 ast_copy_string(as->context, context, sizeof(as->context));
5095 set_ext_pri(as->chan, exten, priority);
5096 as->timeout = timeout;
5097 ast_set_variables(chan, vars);
5098 if (account)
5099 ast_cdr_setaccount(chan, account);
5100 pthread_attr_init(&attr);
5101 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5102 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5103 ast_log(LOG_WARNING, "Failed to start async wait\n");
5104 free(as);
5105 if (channel) {
5106 *channel = NULL;
5107 ast_channel_unlock(chan);
5109 ast_hangup(chan);
5110 res = -1;
5111 pthread_attr_destroy(&attr);
5112 goto outgoing_exten_cleanup;
5114 pthread_attr_destroy(&attr);
5115 res = 0;
5117 outgoing_exten_cleanup:
5118 ast_variables_destroy(vars);
5119 return res;
5122 struct app_tmp {
5123 char app[256];
5124 char data[256];
5125 struct ast_channel *chan;
5126 pthread_t t;
5129 /*! \brief run the application and free the descriptor once done */
5130 static void *ast_pbx_run_app(void *data)
5132 struct app_tmp *tmp = data;
5133 struct ast_app *app;
5134 app = pbx_findapp(tmp->app);
5135 if (app) {
5136 if (option_verbose > 3)
5137 ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
5138 pbx_exec(tmp->chan, app, tmp->data);
5139 } else
5140 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
5141 ast_hangup(tmp->chan);
5142 free(tmp);
5143 return NULL;
5146 int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
5148 struct ast_channel *chan;
5149 struct app_tmp *tmp;
5150 int res = -1, cdr_res = -1;
5151 struct outgoing_helper oh;
5152 pthread_attr_t attr;
5154 memset(&oh, 0, sizeof(oh));
5155 oh.vars = vars;
5156 oh.account = account;
5158 if (locked_channel)
5159 *locked_channel = NULL;
5160 if (ast_strlen_zero(app)) {
5161 res = -1;
5162 goto outgoing_app_cleanup;
5164 if (sync) {
5165 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5166 if (chan) {
5167 ast_set_variables(chan, vars);
5168 if (account)
5169 ast_cdr_setaccount(chan, account);
5170 if (chan->_state == AST_STATE_UP) {
5171 res = 0;
5172 if (option_verbose > 3)
5173 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5174 tmp = ast_calloc(1, sizeof(*tmp));
5175 if (!tmp)
5176 res = -1;
5177 else {
5178 ast_copy_string(tmp->app, app, sizeof(tmp->app));
5179 if (appdata)
5180 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
5181 tmp->chan = chan;
5182 if (sync > 1) {
5183 if (locked_channel)
5184 ast_channel_unlock(chan);
5185 ast_pbx_run_app(tmp);
5186 } else {
5187 pthread_attr_init(&attr);
5188 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5189 if (locked_channel)
5190 ast_channel_lock(chan);
5191 if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
5192 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
5193 free(tmp);
5194 if (locked_channel)
5195 ast_channel_unlock(chan);
5196 ast_hangup(chan);
5197 res = -1;
5198 } else {
5199 if (locked_channel)
5200 *locked_channel = chan;
5202 pthread_attr_destroy(&attr);
5205 } else {
5206 if (option_verbose > 3)
5207 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5208 if (chan->cdr) { /* update the cdr */
5209 /* here we update the status of the call, which sould be busy.
5210 * if that fails then we set the status to failed */
5211 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5212 ast_cdr_failed(chan->cdr);
5214 ast_hangup(chan);
5218 if (res < 0) { /* the call failed for some reason */
5219 if (*reason == 0) { /* if the call failed (not busy or no answer)
5220 * update the cdr with the failed message */
5221 cdr_res = ast_pbx_outgoing_cdr_failed();
5222 if (cdr_res != 0) {
5223 res = cdr_res;
5224 goto outgoing_app_cleanup;
5229 } else {
5230 struct async_stat *as;
5231 if (!(as = ast_calloc(1, sizeof(*as)))) {
5232 res = -1;
5233 goto outgoing_app_cleanup;
5235 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5236 if (!chan) {
5237 free(as);
5238 res = -1;
5239 goto outgoing_app_cleanup;
5241 as->chan = chan;
5242 ast_copy_string(as->app, app, sizeof(as->app));
5243 if (appdata)
5244 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
5245 as->timeout = timeout;
5246 ast_set_variables(chan, vars);
5247 if (account)
5248 ast_cdr_setaccount(chan, account);
5249 /* Start a new thread, and get something handling this channel. */
5250 pthread_attr_init(&attr);
5251 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5252 if (locked_channel)
5253 ast_channel_lock(chan);
5254 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5255 ast_log(LOG_WARNING, "Failed to start async wait\n");
5256 free(as);
5257 if (locked_channel)
5258 ast_channel_unlock(chan);
5259 ast_hangup(chan);
5260 res = -1;
5261 pthread_attr_destroy(&attr);
5262 goto outgoing_app_cleanup;
5263 } else {
5264 if (locked_channel)
5265 *locked_channel = chan;
5267 pthread_attr_destroy(&attr);
5268 res = 0;
5270 outgoing_app_cleanup:
5271 ast_variables_destroy(vars);
5272 return res;
5275 void __ast_context_destroy(struct ast_context *con, const char *registrar)
5277 struct ast_context *tmp, *tmpl=NULL;
5278 struct ast_include *tmpi;
5279 struct ast_sw *sw;
5280 struct ast_exten *e, *el, *en;
5281 struct ast_ignorepat *ipi;
5283 for (tmp = contexts; tmp; ) {
5284 struct ast_context *next; /* next starting point */
5285 for (; tmp; tmpl = tmp, tmp = tmp->next) {
5286 if (option_debug)
5287 ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
5288 if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
5289 (!con || !strcasecmp(tmp->name, con->name)) )
5290 break; /* found it */
5292 if (!tmp) /* not found, we are done */
5293 break;
5294 ast_mutex_lock(&tmp->lock);
5295 if (option_debug)
5296 ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
5297 next = tmp->next;
5298 if (tmpl)
5299 tmpl->next = next;
5300 else
5301 contexts = next;
5302 /* Okay, now we're safe to let it go -- in a sense, we were
5303 ready to let it go as soon as we locked it. */
5304 ast_mutex_unlock(&tmp->lock);
5305 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
5306 struct ast_include *tmpil = tmpi;
5307 tmpi = tmpi->next;
5308 free(tmpil);
5310 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
5311 struct ast_ignorepat *ipl = ipi;
5312 ipi = ipi->next;
5313 free(ipl);
5315 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
5316 free(sw);
5317 for (e = tmp->root; e;) {
5318 for (en = e->peer; en;) {
5319 el = en;
5320 en = en->peer;
5321 destroy_exten(el);
5323 el = e;
5324 e = e->next;
5325 destroy_exten(el);
5327 ast_mutex_destroy(&tmp->lock);
5328 free(tmp);
5329 /* if we have a specific match, we are done, otherwise continue */
5330 tmp = con ? NULL : next;
5334 void ast_context_destroy(struct ast_context *con, const char *registrar)
5336 ast_wrlock_contexts();
5337 __ast_context_destroy(con,registrar);
5338 ast_unlock_contexts();
5341 static void wait_for_hangup(struct ast_channel *chan, void *data)
5343 int res;
5344 struct ast_frame *f;
5345 int waittime;
5347 if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
5348 waittime = -1;
5349 if (waittime > -1) {
5350 ast_safe_sleep(chan, waittime * 1000);
5351 } else do {
5352 res = ast_waitfor(chan, -1);
5353 if (res < 0)
5354 return;
5355 f = ast_read(chan);
5356 if (f)
5357 ast_frfree(f);
5358 } while(f);
5362 * \ingroup applications
5364 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
5366 ast_indicate(chan, AST_CONTROL_PROGRESS);
5367 return 0;
5371 * \ingroup applications
5373 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
5375 ast_indicate(chan, AST_CONTROL_RINGING);
5376 return 0;
5380 * \ingroup applications
5382 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
5384 ast_indicate(chan, AST_CONTROL_BUSY);
5385 /* Don't change state of an UP channel, just indicate
5386 busy in audio */
5387 if (chan->_state != AST_STATE_UP)
5388 ast_setstate(chan, AST_STATE_BUSY);
5389 wait_for_hangup(chan, data);
5390 return -1;
5394 * \ingroup applications
5396 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
5398 ast_indicate(chan, AST_CONTROL_CONGESTION);
5399 /* Don't change state of an UP channel, just indicate
5400 congestion in audio */
5401 if (chan->_state != AST_STATE_UP)
5402 ast_setstate(chan, AST_STATE_BUSY);
5403 wait_for_hangup(chan, data);
5404 return -1;
5408 * \ingroup applications
5410 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
5412 int delay = 0;
5413 int res;
5415 if (chan->_state == AST_STATE_UP)
5416 delay = 0;
5417 else if (!ast_strlen_zero(data))
5418 delay = atoi(data);
5420 res = ast_answer(chan);
5421 if (res)
5422 return res;
5424 if (delay)
5425 res = ast_safe_sleep(chan, delay);
5427 return res;
5430 AST_APP_OPTIONS(resetcdr_opts, {
5431 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
5432 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
5433 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
5437 * \ingroup applications
5439 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
5441 char *args;
5442 struct ast_flags flags = { 0 };
5444 if (!ast_strlen_zero(data)) {
5445 args = ast_strdupa(data);
5446 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
5449 ast_cdr_reset(chan->cdr, &flags);
5451 return 0;
5455 * \ingroup applications
5457 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
5459 /* Copy the AMA Flags as specified */
5460 ast_cdr_setamaflags(chan, data ? data : "");
5461 return 0;
5465 * \ingroup applications
5467 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
5469 if (!ast_strlen_zero(data)) {
5470 int cause;
5471 char *endptr;
5473 if ((cause = ast_str2cause(data)) > -1) {
5474 chan->hangupcause = cause;
5475 return -1;
5478 cause = strtol((const char *) data, &endptr, 10);
5479 if (cause != 0 || (data != endptr)) {
5480 chan->hangupcause = cause;
5481 return -1;
5484 ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
5487 if (!chan->hangupcause) {
5488 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
5491 return -1;
5495 * \ingroup applications
5497 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
5499 int res=0;
5500 char *s, *ts;
5501 struct ast_timing timing;
5503 if (ast_strlen_zero(data)) {
5504 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
5505 return -1;
5508 ts = s = ast_strdupa(data);
5510 /* Separate the Goto path */
5511 strsep(&ts,"?");
5513 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
5514 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
5515 res = pbx_builtin_goto(chan, ts);
5517 return res;
5521 * \ingroup applications
5523 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
5525 char *s, *appname;
5526 struct ast_timing timing;
5527 struct ast_app *app;
5528 static const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
5530 if (ast_strlen_zero(data)) {
5531 ast_log(LOG_WARNING, "%s\n", usage);
5532 return -1;
5535 appname = ast_strdupa(data);
5537 s = strsep(&appname,"?"); /* Separate the timerange and application name/data */
5538 if (!appname) { /* missing application */
5539 ast_log(LOG_WARNING, "%s\n", usage);
5540 return -1;
5543 if (!ast_build_timing(&timing, s)) {
5544 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
5545 return -1;
5548 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
5549 return 0;
5551 /* now split appname|appargs */
5552 if ((s = strchr(appname, '|')))
5553 *s++ = '\0';
5555 if ((app = pbx_findapp(appname))) {
5556 return pbx_exec(chan, app, S_OR(s, ""));
5557 } else {
5558 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
5559 return -1;
5564 * \ingroup applications
5566 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
5568 double s;
5569 int ms;
5571 /* Wait for "n" seconds */
5572 if (data && (s = atof(data)) > 0) {
5573 ms = s * 1000.0;
5574 return ast_safe_sleep(chan, ms);
5576 return 0;
5580 * \ingroup applications
5582 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
5584 int ms, res;
5585 double sec;
5586 struct ast_flags flags = {0};
5587 char *opts[1] = { NULL };
5588 char *parse;
5589 AST_DECLARE_APP_ARGS(args,
5590 AST_APP_ARG(timeout);
5591 AST_APP_ARG(options);
5594 if (!ast_strlen_zero(data)) {
5595 parse = ast_strdupa(data);
5596 AST_STANDARD_APP_ARGS(args, parse);
5597 } else
5598 memset(&args, 0, sizeof(args));
5600 if (args.options)
5601 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
5603 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
5604 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
5605 } else if (ast_test_flag(&flags, WAITEXTEN_MOH))
5606 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
5608 /* Wait for "n" seconds */
5609 if (args.timeout && (sec = atof(args.timeout)) > 0.0)
5610 ms = 1000 * sec;
5611 else if (chan->pbx)
5612 ms = chan->pbx->rtimeout * 1000;
5613 else
5614 ms = 10000;
5615 res = ast_waitfordigit(chan, ms);
5616 if (!res) {
5617 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
5618 if (option_verbose > 2)
5619 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
5620 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
5621 if (option_verbose > 2)
5622 ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
5623 res = -1;
5624 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
5625 if (option_verbose > 2)
5626 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
5627 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
5628 } else {
5629 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
5630 res = -1;
5634 if (ast_test_flag(&flags, WAITEXTEN_MOH))
5635 ast_indicate(chan, AST_CONTROL_UNHOLD);
5637 return res;
5641 * \ingroup applications
5643 static int pbx_builtin_background(struct ast_channel *chan, void *data)
5645 int res = 0;
5646 struct ast_flags flags = {0};
5647 char *parse;
5648 AST_DECLARE_APP_ARGS(args,
5649 AST_APP_ARG(filename);
5650 AST_APP_ARG(options);
5651 AST_APP_ARG(lang);
5652 AST_APP_ARG(context);
5655 if (ast_strlen_zero(data)) {
5656 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
5657 return -1;
5660 parse = ast_strdupa(data);
5662 AST_STANDARD_APP_ARGS(args, parse);
5664 if (ast_strlen_zero(args.lang))
5665 args.lang = (char *)chan->language; /* XXX this is const */
5667 if (ast_strlen_zero(args.context))
5668 args.context = chan->context;
5670 if (args.options) {
5671 if (!strcasecmp(args.options, "skip"))
5672 flags.flags = BACKGROUND_SKIP;
5673 else if (!strcasecmp(args.options, "noanswer"))
5674 flags.flags = BACKGROUND_NOANSWER;
5675 else
5676 ast_app_parse_options(background_opts, &flags, NULL, args.options);
5679 /* Answer if need be */
5680 if (chan->_state != AST_STATE_UP) {
5681 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
5682 return 0;
5683 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
5684 res = ast_answer(chan);
5688 if (!res) {
5689 char *back = args.filename;
5690 char *front;
5691 ast_stopstream(chan); /* Stop anything playing */
5692 /* Stream the list of files */
5693 while (!res && (front = strsep(&back, "&")) ) {
5694 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
5695 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
5696 res = 0;
5697 break;
5699 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
5700 res = ast_waitstream(chan, "");
5701 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
5702 res = ast_waitstream_exten(chan, args.context);
5703 } else {
5704 res = ast_waitstream(chan, AST_DIGIT_ANY);
5706 ast_stopstream(chan);
5709 if (args.context != chan->context && res) {
5710 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
5711 ast_copy_string(chan->context, args.context, sizeof(chan->context));
5712 chan->priority = 0;
5713 res = 0;
5715 return res;
5718 /*! Goto
5719 * \ingroup applications
5721 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
5723 int res = ast_parseable_goto(chan, data);
5724 if (!res && (option_verbose > 2))
5725 ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
5726 return res;
5730 int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
5732 struct ast_var_t *variables;
5733 const char *var, *val;
5734 int total = 0;
5736 if (!chan)
5737 return 0;
5739 memset(buf, 0, size);
5741 ast_channel_lock(chan);
5743 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
5744 if ((var=ast_var_name(variables)) && (val=ast_var_value(variables))
5745 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
5747 if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
5748 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
5749 break;
5750 } else
5751 total++;
5752 } else
5753 break;
5756 ast_channel_unlock(chan);
5758 return total;
5761 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
5763 struct ast_var_t *variables;
5764 const char *ret = NULL;
5765 int i;
5766 struct varshead *places[2] = { NULL, &globals };
5768 if (!name)
5769 return NULL;
5771 if (chan) {
5772 ast_channel_lock(chan);
5773 places[0] = &chan->varshead;
5776 for (i = 0; i < 2; i++) {
5777 if (!places[i])
5778 continue;
5779 if (places[i] == &globals)
5780 ast_mutex_lock(&globalslock);
5781 AST_LIST_TRAVERSE(places[i], variables, entries) {
5782 if (!strcmp(name, ast_var_name(variables))) {
5783 ret = ast_var_value(variables);
5784 break;
5787 if (places[i] == &globals)
5788 ast_mutex_unlock(&globalslock);
5789 if (ret)
5790 break;
5793 if (chan)
5794 ast_channel_unlock(chan);
5796 return ret;
5799 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
5801 struct ast_var_t *newvariable;
5802 struct varshead *headp;
5804 if (name[strlen(name)-1] == ')') {
5805 char *function = ast_strdupa(name);
5807 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
5808 ast_func_write(chan, function, value);
5809 return;
5812 if (chan) {
5813 ast_channel_lock(chan);
5814 headp = &chan->varshead;
5815 } else {
5816 ast_mutex_lock(&globalslock);
5817 headp = &globals;
5820 if (value) {
5821 if ((option_verbose > 1) && (headp == &globals))
5822 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5823 newvariable = ast_var_assign(name, value);
5824 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5827 if (chan)
5828 ast_channel_unlock(chan);
5829 else
5830 ast_mutex_unlock(&globalslock);
5833 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
5835 struct ast_var_t *newvariable;
5836 struct varshead *headp;
5837 const char *nametail = name;
5839 if (name[strlen(name)-1] == ')') {
5840 char *function = ast_strdupa(name);
5842 ast_func_write(chan, function, value);
5843 return;
5846 if (chan) {
5847 ast_channel_lock(chan);
5848 headp = &chan->varshead;
5849 } else {
5850 ast_mutex_lock(&globalslock);
5851 headp = &globals;
5854 /* For comparison purposes, we have to strip leading underscores */
5855 if (*nametail == '_') {
5856 nametail++;
5857 if (*nametail == '_')
5858 nametail++;
5861 AST_LIST_TRAVERSE (headp, newvariable, entries) {
5862 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
5863 /* there is already such a variable, delete it */
5864 AST_LIST_REMOVE(headp, newvariable, entries);
5865 ast_var_delete(newvariable);
5866 break;
5870 if (value) {
5871 if ((option_verbose > 1) && (headp == &globals))
5872 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5873 newvariable = ast_var_assign(name, value);
5874 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5877 if (chan)
5878 ast_channel_unlock(chan);
5879 else
5880 ast_mutex_unlock(&globalslock);
5883 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
5885 char *name, *value, *mydata;
5886 int argc;
5887 char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
5888 int global = 0;
5889 int x;
5891 if (ast_strlen_zero(data)) {
5892 ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
5893 return 0;
5896 mydata = ast_strdupa(data);
5897 argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
5899 /* check for a trailing flags argument */
5900 if ((argc > 1) && !strchr(argv[argc-1], '=')) {
5901 argc--;
5902 if (strchr(argv[argc], 'g')) {
5903 ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
5904 global = 1;
5908 if (argc > 1)
5909 ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
5911 for (x = 0; x < argc; x++) {
5912 name = argv[x];
5913 if ((value = strchr(name, '='))) {
5914 *value++ = '\0';
5915 pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
5916 } else
5917 ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
5920 return(0);
5923 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
5925 char *name;
5926 char *value;
5927 char *channel;
5928 char tmp[VAR_BUF_SIZE]="";
5930 if (ast_strlen_zero(data)) {
5931 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5932 return 0;
5935 value = ast_strdupa(data);
5936 name = strsep(&value,"=");
5937 channel = strsep(&value,"|");
5938 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
5939 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
5940 if (chan2) {
5941 char *s = alloca(strlen(value) + 4);
5942 if (s) {
5943 sprintf(s, "${%s}", value);
5944 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
5946 ast_channel_unlock(chan2);
5948 pbx_builtin_setvar_helper(chan, name, tmp);
5951 return(0);
5954 /*! \todo XXX overwrites data ? */
5955 static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
5957 char *name;
5958 char *stringp = data;
5959 static int dep_warning = 0;
5961 if (ast_strlen_zero(data)) {
5962 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5963 return 0;
5966 name = strsep(&stringp, "=");
5968 if (!dep_warning) {
5969 dep_warning = 1;
5970 ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
5973 /*! \todo XXX watch out, leading whitespace ? */
5974 pbx_builtin_setvar_helper(NULL, name, stringp);
5976 return(0);
5979 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
5981 return 0;
5984 void pbx_builtin_clear_globals(void)
5986 struct ast_var_t *vardata;
5988 ast_mutex_lock(&globalslock);
5989 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
5990 ast_var_delete(vardata);
5991 ast_mutex_unlock(&globalslock);
5994 int pbx_checkcondition(const char *condition)
5996 if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
5997 return 0;
5998 else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
5999 return atoi(condition);
6000 else /* Strings are true */
6001 return 1;
6004 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
6006 char *condition, *branch1, *branch2, *branch;
6007 int rc;
6008 char *stringp;
6010 if (ast_strlen_zero(data)) {
6011 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
6012 return 0;
6015 stringp = ast_strdupa(data);
6016 condition = strsep(&stringp,"?");
6017 branch1 = strsep(&stringp,":");
6018 branch2 = strsep(&stringp,"");
6019 branch = pbx_checkcondition(condition) ? branch1 : branch2;
6021 if (ast_strlen_zero(branch)) {
6022 if (option_debug)
6023 ast_log(LOG_DEBUG, "Not taking any branch\n");
6024 return 0;
6027 rc = pbx_builtin_goto(chan, branch);
6029 return rc;
6032 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
6034 char tmp[256];
6035 char *number = tmp;
6036 char *options;
6038 if (ast_strlen_zero(data)) {
6039 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
6040 return -1;
6042 ast_copy_string(tmp, data, sizeof(tmp));
6043 strsep(&number, "|");
6044 options = strsep(&number, "|");
6045 if (options) {
6046 if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
6047 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
6048 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
6049 return -1;
6053 if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
6054 ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
6057 return 0;
6060 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
6062 int res = 0;
6064 if (data)
6065 res = ast_say_digit_str(chan, data, "", chan->language);
6066 return res;
6069 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
6071 int res = 0;
6073 if (data)
6074 res = ast_say_character_str(chan, data, "", chan->language);
6075 return res;
6078 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
6080 int res = 0;
6082 if (data)
6083 res = ast_say_phonetic_str(chan, data, "", chan->language);
6084 return res;
6087 int load_pbx(void)
6089 int x;
6091 /* Initialize the PBX */
6092 if (option_verbose) {
6093 ast_verbose( "Asterisk PBX Core Initializing\n");
6094 ast_verbose( "Registering builtin applications:\n");
6096 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
6098 /* Register builtin applications */
6099 for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
6100 if (option_verbose)
6101 ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
6102 if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
6103 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
6104 return -1;
6107 return 0;
6111 * Lock context list functions ...
6113 int ast_lock_contexts()
6115 return ast_rwlock_wrlock(&conlock);
6118 int ast_rdlock_contexts(void)
6120 return ast_rwlock_rdlock(&conlock);
6123 int ast_wrlock_contexts(void)
6125 return ast_rwlock_wrlock(&conlock);
6128 int ast_unlock_contexts()
6130 return ast_rwlock_unlock(&conlock);
6134 * Lock context ...
6136 int ast_lock_context(struct ast_context *con)
6138 return ast_mutex_lock(&con->lock);
6141 int ast_unlock_context(struct ast_context *con)
6143 return ast_mutex_unlock(&con->lock);
6147 * Name functions ...
6149 const char *ast_get_context_name(struct ast_context *con)
6151 return con ? con->name : NULL;
6154 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
6156 return exten ? exten->parent : NULL;
6159 const char *ast_get_extension_name(struct ast_exten *exten)
6161 return exten ? exten->exten : NULL;
6164 const char *ast_get_extension_label(struct ast_exten *exten)
6166 return exten ? exten->label : NULL;
6169 const char *ast_get_include_name(struct ast_include *inc)
6171 return inc ? inc->name : NULL;
6174 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
6176 return ip ? ip->pattern : NULL;
6179 int ast_get_extension_priority(struct ast_exten *exten)
6181 return exten ? exten->priority : -1;
6185 * Registrar info functions ...
6187 const char *ast_get_context_registrar(struct ast_context *c)
6189 return c ? c->registrar : NULL;
6192 const char *ast_get_extension_registrar(struct ast_exten *e)
6194 return e ? e->registrar : NULL;
6197 const char *ast_get_include_registrar(struct ast_include *i)
6199 return i ? i->registrar : NULL;
6202 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
6204 return ip ? ip->registrar : NULL;
6207 int ast_get_extension_matchcid(struct ast_exten *e)
6209 return e ? e->matchcid : 0;
6212 const char *ast_get_extension_cidmatch(struct ast_exten *e)
6214 return e ? e->cidmatch : NULL;
6217 const char *ast_get_extension_app(struct ast_exten *e)
6219 return e ? e->app : NULL;
6222 void *ast_get_extension_app_data(struct ast_exten *e)
6224 return e ? e->data : NULL;
6227 const char *ast_get_switch_name(struct ast_sw *sw)
6229 return sw ? sw->name : NULL;
6232 const char *ast_get_switch_data(struct ast_sw *sw)
6234 return sw ? sw->data : NULL;
6237 const char *ast_get_switch_registrar(struct ast_sw *sw)
6239 return sw ? sw->registrar : NULL;
6243 * Walking functions ...
6245 struct ast_context *ast_walk_contexts(struct ast_context *con)
6247 return con ? con->next : contexts;
6250 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
6251 struct ast_exten *exten)
6253 if (!exten)
6254 return con ? con->root : NULL;
6255 else
6256 return exten->next;
6259 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
6260 struct ast_sw *sw)
6262 if (!sw)
6263 return con ? AST_LIST_FIRST(&con->alts) : NULL;
6264 else
6265 return AST_LIST_NEXT(sw, list);
6268 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
6269 struct ast_exten *priority)
6271 return priority ? priority->peer : exten;
6274 struct ast_include *ast_walk_context_includes(struct ast_context *con,
6275 struct ast_include *inc)
6277 if (!inc)
6278 return con ? con->includes : NULL;
6279 else
6280 return inc->next;
6283 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
6284 struct ast_ignorepat *ip)
6286 if (!ip)
6287 return con ? con->ignorepats : NULL;
6288 else
6289 return ip->next;
6292 int ast_context_verify_includes(struct ast_context *con)
6294 struct ast_include *inc = NULL;
6295 int res = 0;
6297 while ( (inc = ast_walk_context_includes(con, inc)) ) {
6298 if (ast_context_find(inc->rname))
6299 continue;
6301 res = -1;
6302 ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
6303 ast_get_context_name(con), inc->rname);
6304 break;
6307 return res;
6311 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
6313 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
6315 if (!chan)
6316 return -2;
6318 if (context == NULL)
6319 context = chan->context;
6320 if (exten == NULL)
6321 exten = chan->exten;
6323 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
6324 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
6325 return goto_func(chan, context, exten, priority);
6326 else
6327 return -3;
6330 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
6332 return __ast_goto_if_exists(chan, context, exten, priority, 0);
6335 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
6337 return __ast_goto_if_exists(chan, context, exten, priority, 1);
6340 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
6342 char *exten, *pri, *context;
6343 char *stringp;
6344 int ipri;
6345 int mode = 0;
6347 if (ast_strlen_zero(goto_string)) {
6348 ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
6349 return -1;
6351 stringp = ast_strdupa(goto_string);
6352 context = strsep(&stringp, "|"); /* guaranteed non-null */
6353 exten = strsep(&stringp, "|");
6354 pri = strsep(&stringp, "|");
6355 if (!exten) { /* Only a priority in this one */
6356 pri = context;
6357 exten = NULL;
6358 context = NULL;
6359 } else if (!pri) { /* Only an extension and priority in this one */
6360 pri = exten;
6361 exten = context;
6362 context = NULL;
6364 if (*pri == '+') {
6365 mode = 1;
6366 pri++;
6367 } else if (*pri == '-') {
6368 mode = -1;
6369 pri++;
6371 if (sscanf(pri, "%d", &ipri) != 1) {
6372 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
6373 pri, chan->cid.cid_num)) < 1) {
6374 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
6375 return -1;
6376 } else
6377 mode = 0;
6379 /* At this point we have a priority and maybe an extension and a context */
6381 if (mode)
6382 ipri = chan->priority + (ipri * mode);
6384 ast_explicit_goto(chan, context, exten, ipri);
6385 return 0;