Let's also include aclocal.m4
[asterisk-bristuff.git] / main / pbx.c
blobb9bdc9ba5017e0e4b47f5473a3f3a2b68a3d3624
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_test_flag(c, AST_FLAG_BRIDGE_HANGUP_RUN) && 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);
2557 ast_clear_flag(c, AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
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 if (ast_channel_masquerade(tmpchan, chan)) {
4613 /* Failed to set up the masquerade. It's probably chan_local
4614 * in the middle of optimizing itself out. Sad. :( */
4615 ast_hangup(tmpchan);
4616 tmpchan = NULL;
4617 res = -1;
4618 } else {
4619 /* Grab the locks and get going */
4620 ast_channel_lock(tmpchan);
4621 ast_do_masquerade(tmpchan);
4622 ast_channel_unlock(tmpchan);
4623 /* Start the PBX going on our stolen channel */
4624 if (ast_pbx_start(tmpchan)) {
4625 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
4626 ast_hangup(tmpchan);
4627 res = -1;
4632 ast_channel_unlock(chan);
4633 return res;
4636 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
4638 struct ast_channel *chan;
4639 int res = -1;
4641 chan = ast_get_channel_by_name_locked(channame);
4642 if (chan) {
4643 res = ast_async_goto(chan, context, exten, priority);
4644 ast_channel_unlock(chan);
4646 return res;
4649 /*! \brief copy a string skipping whitespace */
4650 static int ext_strncpy(char *dst, const char *src, int len)
4652 int count=0;
4654 while (*src && (count < len - 1)) {
4655 switch(*src) {
4656 case ' ':
4657 /* otherwise exten => [a-b],1,... doesn't work */
4658 /* case '-': */
4659 /* Ignore */
4660 break;
4661 default:
4662 *dst = *src;
4663 dst++;
4665 src++;
4666 count++;
4668 *dst = '\0';
4670 return count;
4673 /*! \brief add the extension in the priority chain.
4674 * returns 0 on success, -1 on failure
4676 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
4677 struct ast_exten *el, struct ast_exten *e, int replace)
4679 struct ast_exten *ep;
4681 for (ep = NULL; e ; ep = e, e = e->peer) {
4682 if (e->priority >= tmp->priority)
4683 break;
4685 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
4686 ep->peer = tmp;
4687 return 0; /* success */
4689 if (e->priority == tmp->priority) {
4690 /* Can't have something exactly the same. Is this a
4691 replacement? If so, replace, otherwise, bonk. */
4692 if (!replace) {
4693 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
4694 if (tmp->datad)
4695 tmp->datad(tmp->data);
4696 free(tmp);
4697 return -1;
4699 /* we are replacing e, so copy the link fields and then update
4700 * whoever pointed to e to point to us
4702 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
4703 tmp->peer = e->peer; /* always meaningful */
4704 if (ep) /* We're in the peer list, just insert ourselves */
4705 ep->peer = tmp;
4706 else if (el) /* We're the first extension. Take over e's functions */
4707 el->next = tmp;
4708 else /* We're the very first extension. */
4709 con->root = tmp;
4710 if (tmp->priority == PRIORITY_HINT)
4711 ast_change_hint(e,tmp);
4712 /* Destroy the old one */
4713 if (e->datad)
4714 e->datad(e->data);
4715 free(e);
4716 } else { /* Slip ourselves in just before e */
4717 tmp->peer = e;
4718 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
4719 if (ep) /* Easy enough, we're just in the peer list */
4720 ep->peer = tmp;
4721 else { /* we are the first in some peer list, so link in the ext list */
4722 if (el)
4723 el->next = tmp; /* in the middle... */
4724 else
4725 con->root = tmp; /* ... or at the head */
4726 e->next = NULL; /* e is no more at the head, so e->next must be reset */
4728 /* And immediately return success. */
4729 if (tmp->priority == PRIORITY_HINT)
4730 ast_add_hint(tmp);
4732 return 0;
4735 /*! \brief
4736 * Main interface to add extensions to the list for out context.
4738 * We sort extensions in order of matching preference, so that we can
4739 * stop the search as soon as we find a suitable match.
4740 * This ordering also takes care of wildcards such as '.' (meaning
4741 * "one or more of any character") and '!' (which is 'earlymatch',
4742 * meaning "zero or more of any character" but also impacts the
4743 * return value from CANMATCH and EARLYMATCH.
4745 * The extension match rules defined in the devmeeting 2006.05.05 are
4746 * quite simple: WE SELECT THE LONGEST MATCH.
4747 * In detail, "longest" means the number of matched characters in
4748 * the extension. In case of ties (e.g. _XXX and 333) in the length
4749 * of a pattern, we give priority to entries with the smallest cardinality
4750 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
4751 * while the latter has 7, etc.
4752 * In case of same cardinality, the first element in the range counts.
4753 * If we still have a tie, any final '!' will make this as a possibly
4754 * less specific pattern.
4756 * EBUSY - can't lock
4757 * EEXIST - extension with the same priority exist and no replace is set
4760 int ast_add_extension2(struct ast_context *con,
4761 int replace, const char *extension, int priority, const char *label, const char *callerid,
4762 const char *application, void *data, void (*datad)(void *),
4763 const char *registrar)
4766 * Sort extensions (or patterns) according to the rules indicated above.
4767 * These are implemented by the function ext_cmp()).
4768 * All priorities for the same ext/pattern/cid are kept in a list,
4769 * using the 'peer' field as a link field..
4771 struct ast_exten *tmp, *e, *el = NULL;
4772 int res;
4773 int length;
4774 char *p;
4775 char expand_buf[VAR_BUF_SIZE] = { 0, };
4777 /* if we are adding a hint, and there are global variables, and the hint
4778 contains variable references, then expand them
4780 ast_mutex_lock(&globalslock);
4781 if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
4782 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
4783 application = expand_buf;
4785 ast_mutex_unlock(&globalslock);
4787 length = sizeof(struct ast_exten);
4788 length += strlen(extension) + 1;
4789 length += strlen(application) + 1;
4790 if (label)
4791 length += strlen(label) + 1;
4792 if (callerid)
4793 length += strlen(callerid) + 1;
4794 else
4795 length ++; /* just the '\0' */
4797 /* Be optimistic: Build the extension structure first */
4798 if (!(tmp = ast_calloc(1, length)))
4799 return -1;
4801 /* use p as dst in assignments, as the fields are const char * */
4802 p = tmp->stuff;
4803 if (label) {
4804 tmp->label = p;
4805 strcpy(p, label);
4806 p += strlen(label) + 1;
4808 tmp->exten = p;
4809 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
4810 tmp->priority = priority;
4811 tmp->cidmatch = p; /* but use p for assignments below */
4812 if (callerid) {
4813 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
4814 tmp->matchcid = 1;
4815 } else {
4816 *p++ = '\0';
4817 tmp->matchcid = 0;
4819 tmp->app = p;
4820 strcpy(p, application);
4821 tmp->parent = con;
4822 tmp->data = data;
4823 tmp->datad = datad;
4824 tmp->registrar = registrar;
4826 ast_mutex_lock(&con->lock);
4827 res = 0; /* some compilers will think it is uninitialized otherwise */
4828 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
4829 res = ext_cmp(e->exten, extension);
4830 if (res == 0) { /* extension match, now look at cidmatch */
4831 if (!e->matchcid && !tmp->matchcid)
4832 res = 0;
4833 else if (tmp->matchcid && !e->matchcid)
4834 res = 1;
4835 else if (e->matchcid && !tmp->matchcid)
4836 res = -1;
4837 else
4838 res = strcasecmp(e->cidmatch, tmp->cidmatch);
4840 if (res >= 0)
4841 break;
4843 if (e && res == 0) { /* exact match, insert in the pri chain */
4844 res = add_pri(con, tmp, el, e, replace);
4845 ast_mutex_unlock(&con->lock);
4846 if (res < 0) {
4847 errno = EEXIST; /* XXX do we care ? */
4848 return 0; /* XXX should we return -1 maybe ? */
4850 } else {
4852 * not an exact match, this is the first entry with this pattern,
4853 * so insert in the main list right before 'e' (if any)
4855 tmp->next = e;
4856 if (el)
4857 el->next = tmp;
4858 else
4859 con->root = tmp;
4860 ast_mutex_unlock(&con->lock);
4861 if (tmp->priority == PRIORITY_HINT)
4862 ast_add_hint(tmp);
4864 if (option_debug) {
4865 if (tmp->matchcid) {
4866 if (option_debug)
4867 ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
4868 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4869 } else {
4870 if (option_debug)
4871 ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
4872 tmp->exten, tmp->priority, con->name);
4875 if (option_verbose > 2) {
4876 if (tmp->matchcid) {
4877 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
4878 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4879 } else {
4880 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
4881 tmp->exten, tmp->priority, con->name);
4884 return 0;
4887 struct async_stat {
4888 pthread_t p;
4889 struct ast_channel *chan;
4890 char context[AST_MAX_CONTEXT];
4891 char exten[AST_MAX_EXTENSION];
4892 int priority;
4893 int timeout;
4894 char app[AST_MAX_EXTENSION];
4895 char appdata[1024];
4898 static void *async_wait(void *data)
4900 struct async_stat *as = data;
4901 struct ast_channel *chan = as->chan;
4902 int timeout = as->timeout;
4903 int res;
4904 struct ast_frame *f;
4905 struct ast_app *app;
4907 while (timeout && (chan->_state != AST_STATE_UP)) {
4908 res = ast_waitfor(chan, timeout);
4909 if (res < 1)
4910 break;
4911 if (timeout > -1)
4912 timeout = res;
4913 f = ast_read(chan);
4914 if (!f)
4915 break;
4916 if (f->frametype == AST_FRAME_CONTROL) {
4917 if ((f->subclass == AST_CONTROL_BUSY) ||
4918 (f->subclass == AST_CONTROL_CONGESTION) ) {
4919 ast_frfree(f);
4920 break;
4923 ast_frfree(f);
4925 if (chan->_state == AST_STATE_UP) {
4926 if (!ast_strlen_zero(as->app)) {
4927 app = pbx_findapp(as->app);
4928 if (app) {
4929 if (option_verbose > 2)
4930 ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
4931 pbx_exec(chan, app, as->appdata);
4932 } else
4933 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
4934 } else {
4935 if (!ast_strlen_zero(as->context))
4936 ast_copy_string(chan->context, as->context, sizeof(chan->context));
4937 if (!ast_strlen_zero(as->exten))
4938 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
4939 if (as->priority > 0)
4940 chan->priority = as->priority;
4941 /* Run the PBX */
4942 if (ast_pbx_run(chan)) {
4943 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
4944 } else {
4945 /* PBX will have taken care of this */
4946 chan = NULL;
4950 free(as);
4951 if (chan)
4952 ast_hangup(chan);
4953 return NULL;
4956 /*! Function to post an empty cdr after a spool call fails.
4958 * This function posts an empty cdr for a failed spool call
4961 static int ast_pbx_outgoing_cdr_failed(void)
4963 /* allocate a channel */
4964 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
4966 if (!chan)
4967 return -1; /* failure */
4969 if (!chan->cdr) {
4970 /* allocation of the cdr failed */
4971 ast_channel_free(chan); /* free the channel */
4972 return -1; /* return failure */
4975 /* allocation of the cdr was successful */
4976 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
4977 ast_cdr_start(chan->cdr); /* record the start and stop time */
4978 ast_cdr_end(chan->cdr);
4979 ast_cdr_failed(chan->cdr); /* set the status to failed */
4980 ast_cdr_detach(chan->cdr); /* post and free the record */
4981 chan->cdr = NULL;
4982 ast_channel_free(chan); /* free the channel */
4984 return 0; /* success */
4987 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)
4989 struct ast_channel *chan;
4990 struct async_stat *as;
4991 int res = -1, cdr_res = -1;
4992 struct outgoing_helper oh;
4993 pthread_attr_t attr;
4995 if (sync) {
4996 LOAD_OH(oh);
4997 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
4998 if (channel) {
4999 *channel = chan;
5000 if (chan)
5001 ast_channel_lock(chan);
5003 if (chan) {
5004 if (chan->_state == AST_STATE_UP) {
5005 res = 0;
5006 if (option_verbose > 3)
5007 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5009 if (sync > 1) {
5010 if (channel)
5011 ast_channel_unlock(chan);
5012 if (ast_pbx_run(chan)) {
5013 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5014 if (channel)
5015 *channel = NULL;
5016 ast_hangup(chan);
5017 chan = NULL;
5018 res = -1;
5020 } else {
5021 if (ast_pbx_start(chan)) {
5022 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
5023 if (channel) {
5024 *channel = NULL;
5025 ast_channel_unlock(chan);
5027 ast_hangup(chan);
5028 res = -1;
5030 chan = NULL;
5032 } else {
5033 if (option_verbose > 3)
5034 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5036 if (chan->cdr) { /* update the cdr */
5037 /* here we update the status of the call, which sould be busy.
5038 * if that fails then we set the status to failed */
5039 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5040 ast_cdr_failed(chan->cdr);
5043 if (channel) {
5044 *channel = NULL;
5045 ast_channel_unlock(chan);
5047 ast_hangup(chan);
5048 chan = NULL;
5052 if (res < 0) { /* the call failed for some reason */
5053 if (*reason == 0) { /* if the call failed (not busy or no answer)
5054 * update the cdr with the failed message */
5055 cdr_res = ast_pbx_outgoing_cdr_failed();
5056 if (cdr_res != 0) {
5057 res = cdr_res;
5058 goto outgoing_exten_cleanup;
5062 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
5063 /* check if "failed" exists */
5064 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
5065 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
5066 if (chan) {
5067 char failed_reason[4] = "";
5068 if (!ast_strlen_zero(context))
5069 ast_copy_string(chan->context, context, sizeof(chan->context));
5070 set_ext_pri(chan, "failed", 1);
5071 ast_set_variables(chan, vars);
5072 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
5073 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
5074 if (account)
5075 ast_cdr_setaccount(chan, account);
5076 if (ast_pbx_run(chan)) {
5077 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5078 ast_hangup(chan);
5080 chan = NULL;
5084 } else {
5085 if (!(as = ast_calloc(1, sizeof(*as)))) {
5086 res = -1;
5087 goto outgoing_exten_cleanup;
5089 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
5090 if (channel) {
5091 *channel = chan;
5092 if (chan)
5093 ast_channel_lock(chan);
5095 if (!chan) {
5096 free(as);
5097 res = -1;
5098 goto outgoing_exten_cleanup;
5100 as->chan = chan;
5101 ast_copy_string(as->context, context, sizeof(as->context));
5102 set_ext_pri(as->chan, exten, priority);
5103 as->timeout = timeout;
5104 ast_set_variables(chan, vars);
5105 if (account)
5106 ast_cdr_setaccount(chan, account);
5107 pthread_attr_init(&attr);
5108 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5109 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5110 ast_log(LOG_WARNING, "Failed to start async wait\n");
5111 free(as);
5112 if (channel) {
5113 *channel = NULL;
5114 ast_channel_unlock(chan);
5116 ast_hangup(chan);
5117 res = -1;
5118 pthread_attr_destroy(&attr);
5119 goto outgoing_exten_cleanup;
5121 pthread_attr_destroy(&attr);
5122 res = 0;
5124 outgoing_exten_cleanup:
5125 ast_variables_destroy(vars);
5126 return res;
5129 struct app_tmp {
5130 char app[256];
5131 char data[256];
5132 struct ast_channel *chan;
5133 pthread_t t;
5136 /*! \brief run the application and free the descriptor once done */
5137 static void *ast_pbx_run_app(void *data)
5139 struct app_tmp *tmp = data;
5140 struct ast_app *app;
5141 app = pbx_findapp(tmp->app);
5142 if (app) {
5143 if (option_verbose > 3)
5144 ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
5145 pbx_exec(tmp->chan, app, tmp->data);
5146 } else
5147 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
5148 ast_hangup(tmp->chan);
5149 free(tmp);
5150 return NULL;
5153 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)
5155 struct ast_channel *chan;
5156 struct app_tmp *tmp;
5157 int res = -1, cdr_res = -1;
5158 struct outgoing_helper oh;
5159 pthread_attr_t attr;
5161 memset(&oh, 0, sizeof(oh));
5162 oh.vars = vars;
5163 oh.account = account;
5165 if (locked_channel)
5166 *locked_channel = NULL;
5167 if (ast_strlen_zero(app)) {
5168 res = -1;
5169 goto outgoing_app_cleanup;
5171 if (sync) {
5172 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5173 if (chan) {
5174 ast_set_variables(chan, vars);
5175 if (account)
5176 ast_cdr_setaccount(chan, account);
5177 if (chan->_state == AST_STATE_UP) {
5178 res = 0;
5179 if (option_verbose > 3)
5180 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5181 tmp = ast_calloc(1, sizeof(*tmp));
5182 if (!tmp)
5183 res = -1;
5184 else {
5185 ast_copy_string(tmp->app, app, sizeof(tmp->app));
5186 if (appdata)
5187 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
5188 tmp->chan = chan;
5189 if (sync > 1) {
5190 if (locked_channel)
5191 ast_channel_unlock(chan);
5192 ast_pbx_run_app(tmp);
5193 } else {
5194 pthread_attr_init(&attr);
5195 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5196 if (locked_channel)
5197 ast_channel_lock(chan);
5198 if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
5199 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
5200 free(tmp);
5201 if (locked_channel)
5202 ast_channel_unlock(chan);
5203 ast_hangup(chan);
5204 res = -1;
5205 } else {
5206 if (locked_channel)
5207 *locked_channel = chan;
5209 pthread_attr_destroy(&attr);
5212 } else {
5213 if (option_verbose > 3)
5214 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5215 if (chan->cdr) { /* update the cdr */
5216 /* here we update the status of the call, which sould be busy.
5217 * if that fails then we set the status to failed */
5218 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5219 ast_cdr_failed(chan->cdr);
5221 ast_hangup(chan);
5225 if (res < 0) { /* the call failed for some reason */
5226 if (*reason == 0) { /* if the call failed (not busy or no answer)
5227 * update the cdr with the failed message */
5228 cdr_res = ast_pbx_outgoing_cdr_failed();
5229 if (cdr_res != 0) {
5230 res = cdr_res;
5231 goto outgoing_app_cleanup;
5236 } else {
5237 struct async_stat *as;
5238 if (!(as = ast_calloc(1, sizeof(*as)))) {
5239 res = -1;
5240 goto outgoing_app_cleanup;
5242 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5243 if (!chan) {
5244 free(as);
5245 res = -1;
5246 goto outgoing_app_cleanup;
5248 as->chan = chan;
5249 ast_copy_string(as->app, app, sizeof(as->app));
5250 if (appdata)
5251 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
5252 as->timeout = timeout;
5253 ast_set_variables(chan, vars);
5254 if (account)
5255 ast_cdr_setaccount(chan, account);
5256 /* Start a new thread, and get something handling this channel. */
5257 pthread_attr_init(&attr);
5258 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5259 if (locked_channel)
5260 ast_channel_lock(chan);
5261 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5262 ast_log(LOG_WARNING, "Failed to start async wait\n");
5263 free(as);
5264 if (locked_channel)
5265 ast_channel_unlock(chan);
5266 ast_hangup(chan);
5267 res = -1;
5268 pthread_attr_destroy(&attr);
5269 goto outgoing_app_cleanup;
5270 } else {
5271 if (locked_channel)
5272 *locked_channel = chan;
5274 pthread_attr_destroy(&attr);
5275 res = 0;
5277 outgoing_app_cleanup:
5278 ast_variables_destroy(vars);
5279 return res;
5282 void __ast_context_destroy(struct ast_context *con, const char *registrar)
5284 struct ast_context *tmp, *tmpl=NULL;
5285 struct ast_include *tmpi;
5286 struct ast_sw *sw;
5287 struct ast_exten *e, *el, *en;
5288 struct ast_ignorepat *ipi;
5290 for (tmp = contexts; tmp; ) {
5291 struct ast_context *next; /* next starting point */
5292 for (; tmp; tmpl = tmp, tmp = tmp->next) {
5293 if (option_debug)
5294 ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
5295 if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
5296 (!con || !strcasecmp(tmp->name, con->name)) )
5297 break; /* found it */
5299 if (!tmp) /* not found, we are done */
5300 break;
5301 ast_mutex_lock(&tmp->lock);
5302 if (option_debug)
5303 ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
5304 next = tmp->next;
5305 if (tmpl)
5306 tmpl->next = next;
5307 else
5308 contexts = next;
5309 /* Okay, now we're safe to let it go -- in a sense, we were
5310 ready to let it go as soon as we locked it. */
5311 ast_mutex_unlock(&tmp->lock);
5312 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
5313 struct ast_include *tmpil = tmpi;
5314 tmpi = tmpi->next;
5315 free(tmpil);
5317 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
5318 struct ast_ignorepat *ipl = ipi;
5319 ipi = ipi->next;
5320 free(ipl);
5322 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
5323 free(sw);
5324 for (e = tmp->root; e;) {
5325 for (en = e->peer; en;) {
5326 el = en;
5327 en = en->peer;
5328 destroy_exten(el);
5330 el = e;
5331 e = e->next;
5332 destroy_exten(el);
5334 ast_mutex_destroy(&tmp->lock);
5335 free(tmp);
5336 /* if we have a specific match, we are done, otherwise continue */
5337 tmp = con ? NULL : next;
5341 void ast_context_destroy(struct ast_context *con, const char *registrar)
5343 ast_wrlock_contexts();
5344 __ast_context_destroy(con,registrar);
5345 ast_unlock_contexts();
5348 static void wait_for_hangup(struct ast_channel *chan, void *data)
5350 int res;
5351 struct ast_frame *f;
5352 int waittime;
5354 if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
5355 waittime = -1;
5356 if (waittime > -1) {
5357 ast_safe_sleep(chan, waittime * 1000);
5358 } else do {
5359 res = ast_waitfor(chan, -1);
5360 if (res < 0)
5361 return;
5362 f = ast_read(chan);
5363 if (f)
5364 ast_frfree(f);
5365 } while(f);
5369 * \ingroup applications
5371 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
5373 ast_indicate(chan, AST_CONTROL_PROGRESS);
5374 return 0;
5378 * \ingroup applications
5380 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
5382 ast_indicate(chan, AST_CONTROL_RINGING);
5383 return 0;
5387 * \ingroup applications
5389 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
5391 ast_indicate(chan, AST_CONTROL_BUSY);
5392 /* Don't change state of an UP channel, just indicate
5393 busy in audio */
5394 if (chan->_state != AST_STATE_UP)
5395 ast_setstate(chan, AST_STATE_BUSY);
5396 wait_for_hangup(chan, data);
5397 return -1;
5401 * \ingroup applications
5403 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
5405 ast_indicate(chan, AST_CONTROL_CONGESTION);
5406 /* Don't change state of an UP channel, just indicate
5407 congestion in audio */
5408 if (chan->_state != AST_STATE_UP)
5409 ast_setstate(chan, AST_STATE_BUSY);
5410 wait_for_hangup(chan, data);
5411 return -1;
5415 * \ingroup applications
5417 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
5419 int delay = 0;
5420 int res;
5422 if (chan->_state == AST_STATE_UP)
5423 delay = 0;
5424 else if (!ast_strlen_zero(data))
5425 delay = atoi(data);
5427 res = ast_answer(chan);
5428 if (res)
5429 return res;
5431 if (delay)
5432 res = ast_safe_sleep(chan, delay);
5434 return res;
5437 AST_APP_OPTIONS(resetcdr_opts, {
5438 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
5439 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
5440 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
5444 * \ingroup applications
5446 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
5448 char *args;
5449 struct ast_flags flags = { 0 };
5451 if (!ast_strlen_zero(data)) {
5452 args = ast_strdupa(data);
5453 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
5456 ast_cdr_reset(chan->cdr, &flags);
5458 return 0;
5462 * \ingroup applications
5464 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
5466 /* Copy the AMA Flags as specified */
5467 ast_cdr_setamaflags(chan, data ? data : "");
5468 return 0;
5472 * \ingroup applications
5474 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
5476 if (!ast_strlen_zero(data)) {
5477 int cause;
5478 char *endptr;
5480 if ((cause = ast_str2cause(data)) > -1) {
5481 chan->hangupcause = cause;
5482 return -1;
5485 cause = strtol((const char *) data, &endptr, 10);
5486 if (cause != 0 || (data != endptr)) {
5487 chan->hangupcause = cause;
5488 return -1;
5491 ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
5494 if (!chan->hangupcause) {
5495 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
5498 return -1;
5502 * \ingroup applications
5504 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
5506 int res=0;
5507 char *s, *ts;
5508 struct ast_timing timing;
5510 if (ast_strlen_zero(data)) {
5511 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
5512 return -1;
5515 ts = s = ast_strdupa(data);
5517 /* Separate the Goto path */
5518 strsep(&ts,"?");
5520 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
5521 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
5522 res = pbx_builtin_goto(chan, ts);
5524 return res;
5528 * \ingroup applications
5530 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
5532 char *s, *appname;
5533 struct ast_timing timing;
5534 struct ast_app *app;
5535 static const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
5537 if (ast_strlen_zero(data)) {
5538 ast_log(LOG_WARNING, "%s\n", usage);
5539 return -1;
5542 appname = ast_strdupa(data);
5544 s = strsep(&appname,"?"); /* Separate the timerange and application name/data */
5545 if (!appname) { /* missing application */
5546 ast_log(LOG_WARNING, "%s\n", usage);
5547 return -1;
5550 if (!ast_build_timing(&timing, s)) {
5551 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
5552 return -1;
5555 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
5556 return 0;
5558 /* now split appname|appargs */
5559 if ((s = strchr(appname, '|')))
5560 *s++ = '\0';
5562 if ((app = pbx_findapp(appname))) {
5563 return pbx_exec(chan, app, S_OR(s, ""));
5564 } else {
5565 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
5566 return -1;
5571 * \ingroup applications
5573 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
5575 double s;
5576 int ms;
5578 /* Wait for "n" seconds */
5579 if (data && (s = atof(data)) > 0) {
5580 ms = s * 1000.0;
5581 return ast_safe_sleep(chan, ms);
5583 return 0;
5587 * \ingroup applications
5589 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
5591 int ms, res;
5592 double sec;
5593 struct ast_flags flags = {0};
5594 char *opts[1] = { NULL };
5595 char *parse;
5596 AST_DECLARE_APP_ARGS(args,
5597 AST_APP_ARG(timeout);
5598 AST_APP_ARG(options);
5601 if (!ast_strlen_zero(data)) {
5602 parse = ast_strdupa(data);
5603 AST_STANDARD_APP_ARGS(args, parse);
5604 } else
5605 memset(&args, 0, sizeof(args));
5607 if (args.options)
5608 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
5610 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
5611 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
5612 } else if (ast_test_flag(&flags, WAITEXTEN_MOH))
5613 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
5615 /* Wait for "n" seconds */
5616 if (args.timeout && (sec = atof(args.timeout)) > 0.0)
5617 ms = 1000 * sec;
5618 else if (chan->pbx)
5619 ms = chan->pbx->rtimeout * 1000;
5620 else
5621 ms = 10000;
5622 res = ast_waitfordigit(chan, ms);
5623 if (!res) {
5624 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
5625 if (option_verbose > 2)
5626 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
5627 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
5628 if (option_verbose > 2)
5629 ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
5630 res = -1;
5631 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
5632 if (option_verbose > 2)
5633 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
5634 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
5635 } else {
5636 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
5637 res = -1;
5641 if (ast_test_flag(&flags, WAITEXTEN_MOH))
5642 ast_indicate(chan, AST_CONTROL_UNHOLD);
5644 return res;
5648 * \ingroup applications
5650 static int pbx_builtin_background(struct ast_channel *chan, void *data)
5652 int res = 0;
5653 struct ast_flags flags = {0};
5654 char *parse;
5655 AST_DECLARE_APP_ARGS(args,
5656 AST_APP_ARG(filename);
5657 AST_APP_ARG(options);
5658 AST_APP_ARG(lang);
5659 AST_APP_ARG(context);
5662 if (ast_strlen_zero(data)) {
5663 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
5664 return -1;
5667 parse = ast_strdupa(data);
5669 AST_STANDARD_APP_ARGS(args, parse);
5671 if (ast_strlen_zero(args.lang))
5672 args.lang = (char *)chan->language; /* XXX this is const */
5674 if (ast_strlen_zero(args.context))
5675 args.context = chan->context;
5677 if (args.options) {
5678 if (!strcasecmp(args.options, "skip"))
5679 flags.flags = BACKGROUND_SKIP;
5680 else if (!strcasecmp(args.options, "noanswer"))
5681 flags.flags = BACKGROUND_NOANSWER;
5682 else
5683 ast_app_parse_options(background_opts, &flags, NULL, args.options);
5686 /* Answer if need be */
5687 if (chan->_state != AST_STATE_UP) {
5688 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
5689 return 0;
5690 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
5691 res = ast_answer(chan);
5695 if (!res) {
5696 char *back = args.filename;
5697 char *front;
5698 ast_stopstream(chan); /* Stop anything playing */
5699 /* Stream the list of files */
5700 while (!res && (front = strsep(&back, "&")) ) {
5701 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
5702 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
5703 res = 0;
5704 break;
5706 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
5707 res = ast_waitstream(chan, "");
5708 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
5709 res = ast_waitstream_exten(chan, args.context);
5710 } else {
5711 res = ast_waitstream(chan, AST_DIGIT_ANY);
5713 ast_stopstream(chan);
5716 if (args.context != chan->context && res) {
5717 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
5718 ast_copy_string(chan->context, args.context, sizeof(chan->context));
5719 chan->priority = 0;
5720 res = 0;
5722 return res;
5725 /*! Goto
5726 * \ingroup applications
5728 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
5730 int res = ast_parseable_goto(chan, data);
5731 if (!res && (option_verbose > 2))
5732 ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
5733 return res;
5737 int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
5739 struct ast_var_t *variables;
5740 const char *var, *val;
5741 int total = 0;
5743 if (!chan)
5744 return 0;
5746 memset(buf, 0, size);
5748 ast_channel_lock(chan);
5750 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
5751 if ((var=ast_var_name(variables)) && (val=ast_var_value(variables))
5752 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
5754 if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
5755 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
5756 break;
5757 } else
5758 total++;
5759 } else
5760 break;
5763 ast_channel_unlock(chan);
5765 return total;
5768 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
5770 struct ast_var_t *variables;
5771 const char *ret = NULL;
5772 int i;
5773 struct varshead *places[2] = { NULL, &globals };
5775 if (!name)
5776 return NULL;
5778 if (chan) {
5779 ast_channel_lock(chan);
5780 places[0] = &chan->varshead;
5783 for (i = 0; i < 2; i++) {
5784 if (!places[i])
5785 continue;
5786 if (places[i] == &globals)
5787 ast_mutex_lock(&globalslock);
5788 AST_LIST_TRAVERSE(places[i], variables, entries) {
5789 if (!strcmp(name, ast_var_name(variables))) {
5790 ret = ast_var_value(variables);
5791 break;
5794 if (places[i] == &globals)
5795 ast_mutex_unlock(&globalslock);
5796 if (ret)
5797 break;
5800 if (chan)
5801 ast_channel_unlock(chan);
5803 return ret;
5806 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
5808 struct ast_var_t *newvariable;
5809 struct varshead *headp;
5811 if (name[strlen(name)-1] == ')') {
5812 char *function = ast_strdupa(name);
5814 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
5815 ast_func_write(chan, function, value);
5816 return;
5819 if (chan) {
5820 ast_channel_lock(chan);
5821 headp = &chan->varshead;
5822 } else {
5823 ast_mutex_lock(&globalslock);
5824 headp = &globals;
5827 if (value) {
5828 if ((option_verbose > 1) && (headp == &globals))
5829 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5830 newvariable = ast_var_assign(name, value);
5831 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5834 if (chan)
5835 ast_channel_unlock(chan);
5836 else
5837 ast_mutex_unlock(&globalslock);
5840 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
5842 struct ast_var_t *newvariable;
5843 struct varshead *headp;
5844 const char *nametail = name;
5846 if (name[strlen(name)-1] == ')') {
5847 char *function = ast_strdupa(name);
5849 ast_func_write(chan, function, value);
5850 return;
5853 if (chan) {
5854 ast_channel_lock(chan);
5855 headp = &chan->varshead;
5856 } else {
5857 ast_mutex_lock(&globalslock);
5858 headp = &globals;
5861 /* For comparison purposes, we have to strip leading underscores */
5862 if (*nametail == '_') {
5863 nametail++;
5864 if (*nametail == '_')
5865 nametail++;
5868 AST_LIST_TRAVERSE (headp, newvariable, entries) {
5869 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
5870 /* there is already such a variable, delete it */
5871 AST_LIST_REMOVE(headp, newvariable, entries);
5872 ast_var_delete(newvariable);
5873 break;
5877 if (value) {
5878 if ((option_verbose > 1) && (headp == &globals))
5879 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5880 newvariable = ast_var_assign(name, value);
5881 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5884 if (chan)
5885 ast_channel_unlock(chan);
5886 else
5887 ast_mutex_unlock(&globalslock);
5890 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
5892 char *name, *value, *mydata;
5893 int argc;
5894 char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
5895 int global = 0;
5896 int x;
5898 if (ast_strlen_zero(data)) {
5899 ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
5900 return 0;
5903 mydata = ast_strdupa(data);
5904 argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
5906 /* check for a trailing flags argument */
5907 if ((argc > 1) && !strchr(argv[argc-1], '=')) {
5908 argc--;
5909 if (strchr(argv[argc], 'g')) {
5910 ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
5911 global = 1;
5915 if (argc > 1)
5916 ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
5918 for (x = 0; x < argc; x++) {
5919 name = argv[x];
5920 if ((value = strchr(name, '='))) {
5921 *value++ = '\0';
5922 pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
5923 } else
5924 ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
5927 return(0);
5930 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
5932 char *name;
5933 char *value;
5934 char *channel;
5935 char tmp[VAR_BUF_SIZE]="";
5937 if (ast_strlen_zero(data)) {
5938 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5939 return 0;
5942 value = ast_strdupa(data);
5943 name = strsep(&value,"=");
5944 channel = strsep(&value,"|");
5945 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
5946 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
5947 if (chan2) {
5948 char *s = alloca(strlen(value) + 4);
5949 if (s) {
5950 sprintf(s, "${%s}", value);
5951 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
5953 ast_channel_unlock(chan2);
5955 pbx_builtin_setvar_helper(chan, name, tmp);
5958 return(0);
5961 /*! \todo XXX overwrites data ? */
5962 static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
5964 char *name;
5965 char *stringp = data;
5966 static int dep_warning = 0;
5968 if (ast_strlen_zero(data)) {
5969 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5970 return 0;
5973 name = strsep(&stringp, "=");
5975 if (!dep_warning) {
5976 dep_warning = 1;
5977 ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
5980 /*! \todo XXX watch out, leading whitespace ? */
5981 pbx_builtin_setvar_helper(NULL, name, stringp);
5983 return(0);
5986 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
5988 return 0;
5991 void pbx_builtin_clear_globals(void)
5993 struct ast_var_t *vardata;
5995 ast_mutex_lock(&globalslock);
5996 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
5997 ast_var_delete(vardata);
5998 ast_mutex_unlock(&globalslock);
6001 int pbx_checkcondition(const char *condition)
6003 if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
6004 return 0;
6005 else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
6006 return atoi(condition);
6007 else /* Strings are true */
6008 return 1;
6011 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
6013 char *condition, *branch1, *branch2, *branch;
6014 int rc;
6015 char *stringp;
6017 if (ast_strlen_zero(data)) {
6018 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
6019 return 0;
6022 stringp = ast_strdupa(data);
6023 condition = strsep(&stringp,"?");
6024 branch1 = strsep(&stringp,":");
6025 branch2 = strsep(&stringp,"");
6026 branch = pbx_checkcondition(condition) ? branch1 : branch2;
6028 if (ast_strlen_zero(branch)) {
6029 if (option_debug)
6030 ast_log(LOG_DEBUG, "Not taking any branch\n");
6031 return 0;
6034 rc = pbx_builtin_goto(chan, branch);
6036 return rc;
6039 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
6041 char tmp[256];
6042 char *number = tmp;
6043 char *options;
6045 if (ast_strlen_zero(data)) {
6046 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
6047 return -1;
6049 ast_copy_string(tmp, data, sizeof(tmp));
6050 strsep(&number, "|");
6051 options = strsep(&number, "|");
6052 if (options) {
6053 if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
6054 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
6055 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
6056 return -1;
6060 if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
6061 ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
6064 return 0;
6067 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
6069 int res = 0;
6071 if (data)
6072 res = ast_say_digit_str(chan, data, "", chan->language);
6073 return res;
6076 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
6078 int res = 0;
6080 if (data)
6081 res = ast_say_character_str(chan, data, "", chan->language);
6082 return res;
6085 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
6087 int res = 0;
6089 if (data)
6090 res = ast_say_phonetic_str(chan, data, "", chan->language);
6091 return res;
6094 int load_pbx(void)
6096 int x;
6098 /* Initialize the PBX */
6099 if (option_verbose) {
6100 ast_verbose( "Asterisk PBX Core Initializing\n");
6101 ast_verbose( "Registering builtin applications:\n");
6103 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
6105 /* Register builtin applications */
6106 for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
6107 if (option_verbose)
6108 ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
6109 if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
6110 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
6111 return -1;
6114 return 0;
6118 * Lock context list functions ...
6120 int ast_lock_contexts()
6122 return ast_rwlock_wrlock(&conlock);
6125 int ast_rdlock_contexts(void)
6127 return ast_rwlock_rdlock(&conlock);
6130 int ast_wrlock_contexts(void)
6132 return ast_rwlock_wrlock(&conlock);
6135 int ast_unlock_contexts()
6137 return ast_rwlock_unlock(&conlock);
6141 * Lock context ...
6143 int ast_lock_context(struct ast_context *con)
6145 return ast_mutex_lock(&con->lock);
6148 int ast_unlock_context(struct ast_context *con)
6150 return ast_mutex_unlock(&con->lock);
6154 * Name functions ...
6156 const char *ast_get_context_name(struct ast_context *con)
6158 return con ? con->name : NULL;
6161 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
6163 return exten ? exten->parent : NULL;
6166 const char *ast_get_extension_name(struct ast_exten *exten)
6168 return exten ? exten->exten : NULL;
6171 const char *ast_get_extension_label(struct ast_exten *exten)
6173 return exten ? exten->label : NULL;
6176 const char *ast_get_include_name(struct ast_include *inc)
6178 return inc ? inc->name : NULL;
6181 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
6183 return ip ? ip->pattern : NULL;
6186 int ast_get_extension_priority(struct ast_exten *exten)
6188 return exten ? exten->priority : -1;
6192 * Registrar info functions ...
6194 const char *ast_get_context_registrar(struct ast_context *c)
6196 return c ? c->registrar : NULL;
6199 const char *ast_get_extension_registrar(struct ast_exten *e)
6201 return e ? e->registrar : NULL;
6204 const char *ast_get_include_registrar(struct ast_include *i)
6206 return i ? i->registrar : NULL;
6209 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
6211 return ip ? ip->registrar : NULL;
6214 int ast_get_extension_matchcid(struct ast_exten *e)
6216 return e ? e->matchcid : 0;
6219 const char *ast_get_extension_cidmatch(struct ast_exten *e)
6221 return e ? e->cidmatch : NULL;
6224 const char *ast_get_extension_app(struct ast_exten *e)
6226 return e ? e->app : NULL;
6229 void *ast_get_extension_app_data(struct ast_exten *e)
6231 return e ? e->data : NULL;
6234 const char *ast_get_switch_name(struct ast_sw *sw)
6236 return sw ? sw->name : NULL;
6239 const char *ast_get_switch_data(struct ast_sw *sw)
6241 return sw ? sw->data : NULL;
6244 const char *ast_get_switch_registrar(struct ast_sw *sw)
6246 return sw ? sw->registrar : NULL;
6250 * Walking functions ...
6252 struct ast_context *ast_walk_contexts(struct ast_context *con)
6254 return con ? con->next : contexts;
6257 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
6258 struct ast_exten *exten)
6260 if (!exten)
6261 return con ? con->root : NULL;
6262 else
6263 return exten->next;
6266 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
6267 struct ast_sw *sw)
6269 if (!sw)
6270 return con ? AST_LIST_FIRST(&con->alts) : NULL;
6271 else
6272 return AST_LIST_NEXT(sw, list);
6275 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
6276 struct ast_exten *priority)
6278 return priority ? priority->peer : exten;
6281 struct ast_include *ast_walk_context_includes(struct ast_context *con,
6282 struct ast_include *inc)
6284 if (!inc)
6285 return con ? con->includes : NULL;
6286 else
6287 return inc->next;
6290 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
6291 struct ast_ignorepat *ip)
6293 if (!ip)
6294 return con ? con->ignorepats : NULL;
6295 else
6296 return ip->next;
6299 int ast_context_verify_includes(struct ast_context *con)
6301 struct ast_include *inc = NULL;
6302 int res = 0;
6304 while ( (inc = ast_walk_context_includes(con, inc)) ) {
6305 if (ast_context_find(inc->rname))
6306 continue;
6308 res = -1;
6309 ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
6310 ast_get_context_name(con), inc->rname);
6311 break;
6314 return res;
6318 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
6320 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
6322 if (!chan)
6323 return -2;
6325 if (context == NULL)
6326 context = chan->context;
6327 if (exten == NULL)
6328 exten = chan->exten;
6330 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
6331 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
6332 return goto_func(chan, context, exten, priority);
6333 else
6334 return -3;
6337 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
6339 return __ast_goto_if_exists(chan, context, exten, priority, 0);
6342 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
6344 return __ast_goto_if_exists(chan, context, exten, priority, 1);
6347 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
6349 char *exten, *pri, *context;
6350 char *stringp;
6351 int ipri;
6352 int mode = 0;
6354 if (ast_strlen_zero(goto_string)) {
6355 ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
6356 return -1;
6358 stringp = ast_strdupa(goto_string);
6359 context = strsep(&stringp, "|"); /* guaranteed non-null */
6360 exten = strsep(&stringp, "|");
6361 pri = strsep(&stringp, "|");
6362 if (!exten) { /* Only a priority in this one */
6363 pri = context;
6364 exten = NULL;
6365 context = NULL;
6366 } else if (!pri) { /* Only an extension and priority in this one */
6367 pri = exten;
6368 exten = context;
6369 context = NULL;
6371 if (*pri == '+') {
6372 mode = 1;
6373 pri++;
6374 } else if (*pri == '-') {
6375 mode = -1;
6376 pri++;
6378 if (sscanf(pri, "%d", &ipri) != 1) {
6379 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
6380 pri, chan->cid.cid_num)) < 1) {
6381 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
6382 return -1;
6383 } else
6384 mode = 0;
6386 /* At this point we have a priority and maybe an extension and a context */
6388 if (mode)
6389 ipri = chan->priority + (ipri * mode);
6391 ast_explicit_goto(chan, context, exten, ipri);
6392 return 0;