Merge changes from team/russell/iax2_find_callno and iax2_find_callno_1.4
[asterisk-bristuff.git] / main / pbx.c
blobd6564db4f6b41a7892154c69a8e478a7b2cee05d
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 if (c->amaflags) {
2375 if (!c->cdr) {
2376 c->cdr = ast_cdr_alloc();
2377 if (!c->cdr) {
2378 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
2379 free(c->pbx);
2380 return -1;
2382 ast_cdr_init(c->cdr, c);
2385 /* Set reasonable defaults */
2386 c->pbx->rtimeout = 10;
2387 c->pbx->dtimeout = 5;
2389 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
2390 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2392 /* Start by trying whatever the channel is set to */
2393 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2394 /* If not successful fall back to 's' */
2395 if (option_verbose > 1)
2396 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);
2397 /* XXX the original code used the existing priority in the call to
2398 * ast_exists_extension(), and reset it to 1 afterwards.
2399 * I believe the correct thing is to set it to 1 immediately.
2401 set_ext_pri(c, "s", 1);
2402 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2403 /* JK02: And finally back to default if everything else failed */
2404 if (option_verbose > 1)
2405 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);
2406 ast_copy_string(c->context, "default", sizeof(c->context));
2409 if (c->cdr && ast_tvzero(c->cdr->start))
2410 ast_cdr_start(c->cdr);
2411 for (;;) {
2412 char dst_exten[256]; /* buffer to accumulate digits */
2413 int pos = 0; /* XXX should check bounds */
2414 int digit = 0;
2416 /* loop on priorities in this context/exten */
2417 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2418 found = 1;
2419 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2420 /* Something bad happened, or a hangup has been requested. */
2421 if (strchr("0123456789ABCDEF*#", res)) {
2422 if (option_debug)
2423 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
2424 pos = 0;
2425 dst_exten[pos++] = digit = res;
2426 dst_exten[pos] = '\0';
2427 break;
2429 if (res == AST_PBX_KEEPALIVE) {
2430 if (option_debug)
2431 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2432 if (option_verbose > 1)
2433 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2434 error = 1;
2435 break;
2437 if (option_debug)
2438 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2439 if (option_verbose > 1)
2440 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2441 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2442 c->_softhangup = 0;
2443 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2444 /* atimeout, nothing bad */
2445 } else {
2446 if (c->cdr)
2447 ast_cdr_update(c);
2448 error = 1;
2449 break;
2452 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2453 c->_softhangup = 0;
2454 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
2455 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
2456 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2457 c->whentohangup = 0;
2458 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2459 } else if (c->_softhangup) {
2460 if (option_debug)
2461 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
2462 c->exten, c->priority);
2463 error = 1;
2464 break;
2466 c->priority++;
2467 } /* end while - from here on we can use 'break' to go out */
2468 if (error)
2469 break;
2471 /* XXX we get here on non-existing extension or a keypress or hangup ? */
2473 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2474 /* If there is no match at priority 1, it is not a valid extension anymore.
2475 * Try to continue at "i", 1 or exit if the latter does not exist.
2477 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2478 if (option_verbose > 2)
2479 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2480 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2481 set_ext_pri(c, "i", 1);
2482 } else {
2483 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2484 c->name, c->exten, c->context);
2485 error = 1; /* we know what to do with it */
2486 break;
2488 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2489 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2490 c->_softhangup = 0;
2491 } else { /* keypress received, get more digits for a full extension */
2492 int waittime = 0;
2493 if (digit)
2494 waittime = c->pbx->dtimeout;
2495 else if (!autofallthrough)
2496 waittime = c->pbx->rtimeout;
2497 if (!waittime) {
2498 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2499 if (!status)
2500 status = "UNKNOWN";
2501 if (option_verbose > 2)
2502 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2503 if (!strcasecmp(status, "CONGESTION"))
2504 res = pbx_builtin_congestion(c, "10");
2505 else if (!strcasecmp(status, "CHANUNAVAIL"))
2506 res = pbx_builtin_congestion(c, "10");
2507 else if (!strcasecmp(status, "BUSY"))
2508 res = pbx_builtin_busy(c, "10");
2509 error = 1; /* XXX disable message */
2510 break; /* exit from the 'for' loop */
2513 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
2514 break;
2515 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
2516 set_ext_pri(c, dst_exten, 1);
2517 else {
2518 /* No such extension */
2519 if (!ast_strlen_zero(dst_exten)) {
2520 /* An invalid extension */
2521 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2522 if (option_verbose > 2)
2523 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
2524 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
2525 set_ext_pri(c, "i", 1);
2526 } else {
2527 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
2528 found = 1; /* XXX disable message */
2529 break;
2531 } else {
2532 /* A simple timeout */
2533 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2534 if (option_verbose > 2)
2535 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
2536 set_ext_pri(c, "t", 1);
2537 } else {
2538 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2539 found = 1; /* XXX disable message */
2540 break;
2544 if (c->cdr) {
2545 if (option_verbose > 2)
2546 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
2547 ast_cdr_update(c);
2551 if (!found && !error)
2552 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2553 if (res != AST_PBX_KEEPALIVE)
2554 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
2555 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2556 if (c->cdr && ast_opt_end_cdr_before_h_exten)
2557 ast_cdr_end(c->cdr);
2558 set_ext_pri(c, "h", 1);
2559 while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2560 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2561 /* Something bad happened, or a hangup has been requested. */
2562 if (option_debug)
2563 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2564 if (option_verbose > 1)
2565 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2566 break;
2568 c->priority++;
2571 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2573 pbx_destroy(c->pbx);
2574 c->pbx = NULL;
2575 if (res != AST_PBX_KEEPALIVE)
2576 ast_hangup(c);
2577 return 0;
2580 /* Returns 0 on success, non-zero if call limit was reached */
2581 static int increase_call_count(const struct ast_channel *c)
2583 int failed = 0;
2584 double curloadavg;
2585 ast_mutex_lock(&maxcalllock);
2586 if (option_maxcalls) {
2587 if (countcalls >= option_maxcalls) {
2588 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2589 failed = -1;
2592 if (option_maxload) {
2593 getloadavg(&curloadavg, 1);
2594 if (curloadavg >= option_maxload) {
2595 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2596 failed = -1;
2599 if (!failed)
2600 countcalls++;
2601 ast_mutex_unlock(&maxcalllock);
2603 return failed;
2606 static void decrease_call_count(void)
2608 ast_mutex_lock(&maxcalllock);
2609 if (countcalls > 0)
2610 countcalls--;
2611 ast_mutex_unlock(&maxcalllock);
2614 static void destroy_exten(struct ast_exten *e)
2616 if (e->priority == PRIORITY_HINT)
2617 ast_remove_hint(e);
2619 if (e->datad)
2620 e->datad(e->data);
2621 free(e);
2624 static void *pbx_thread(void *data)
2626 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2627 answer this channel and get it going.
2629 /* NOTE:
2630 The launcher of this function _MUST_ increment 'countcalls'
2631 before invoking the function; it will be decremented when the
2632 PBX has finished running on the channel
2634 struct ast_channel *c = data;
2636 __ast_pbx_run(c);
2637 decrease_call_count();
2639 pthread_exit(NULL);
2641 return NULL;
2644 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2646 pthread_t t;
2647 pthread_attr_t attr;
2649 if (!c) {
2650 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2651 return AST_PBX_FAILED;
2654 if (increase_call_count(c))
2655 return AST_PBX_CALL_LIMIT;
2657 /* Start a new thread, and get something handling this channel. */
2658 pthread_attr_init(&attr);
2659 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2660 if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
2661 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2662 pthread_attr_destroy(&attr);
2663 return AST_PBX_FAILED;
2665 pthread_attr_destroy(&attr);
2667 return AST_PBX_SUCCESS;
2670 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2672 enum ast_pbx_result res = AST_PBX_SUCCESS;
2674 if (increase_call_count(c))
2675 return AST_PBX_CALL_LIMIT;
2677 res = __ast_pbx_run(c);
2678 decrease_call_count();
2680 return res;
2683 int ast_active_calls(void)
2685 return countcalls;
2688 int pbx_set_autofallthrough(int newval)
2690 int oldval = autofallthrough;
2691 autofallthrough = newval;
2692 return oldval;
2695 /* lookup for a context with a given name,
2696 * return with conlock held if found, NULL if not found
2698 static struct ast_context *find_context_locked(const char *context)
2700 struct ast_context *c = NULL;
2702 ast_rdlock_contexts();
2703 while ( (c = ast_walk_contexts(c)) ) {
2704 if (!strcmp(ast_get_context_name(c), context))
2705 return c;
2707 ast_unlock_contexts();
2709 return NULL;
2713 * This function locks contexts list by &conlist, search for the right context
2714 * structure, leave context list locked and call ast_context_remove_include2
2715 * which removes include, unlock contexts list and return ...
2717 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2719 int ret = -1;
2720 struct ast_context *c = find_context_locked(context);
2722 if (c) {
2723 /* found, remove include from this context ... */
2724 ret = ast_context_remove_include2(c, include, registrar);
2725 ast_unlock_contexts();
2727 return ret;
2731 * When we call this function, &conlock lock must be locked, because when
2732 * we giving *con argument, some process can remove/change this context
2733 * and after that there can be segfault.
2735 * This function locks given context, removes include, unlock context and
2736 * return.
2738 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2740 struct ast_include *i, *pi = NULL;
2741 int ret = -1;
2743 ast_mutex_lock(&con->lock);
2745 /* find our include */
2746 for (i = con->includes; i; pi = i, i = i->next) {
2747 if (!strcmp(i->name, include) &&
2748 (!registrar || !strcmp(i->registrar, registrar))) {
2749 /* remove from list */
2750 if (pi)
2751 pi->next = i->next;
2752 else
2753 con->includes = i->next;
2754 /* free include and return */
2755 free(i);
2756 ret = 0;
2757 break;
2761 ast_mutex_unlock(&con->lock);
2762 return ret;
2766 * \note This function locks contexts list by &conlist, search for the rigt context
2767 * structure, leave context list locked and call ast_context_remove_switch2
2768 * which removes switch, unlock contexts list and return ...
2770 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2772 int ret = -1; /* default error return */
2773 struct ast_context *c = find_context_locked(context);
2775 if (c) {
2776 /* remove switch from this context ... */
2777 ret = ast_context_remove_switch2(c, sw, data, registrar);
2778 ast_unlock_contexts();
2780 return ret;
2784 * \brief This function locks given context, removes switch, unlock context and
2785 * return.
2786 * \note When we call this function, &conlock lock must be locked, because when
2787 * we giving *con argument, some process can remove/change this context
2788 * and after that there can be segfault.
2791 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2793 struct ast_sw *i;
2794 int ret = -1;
2796 ast_mutex_lock(&con->lock);
2798 /* walk switches */
2799 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
2800 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2801 (!registrar || !strcmp(i->registrar, registrar))) {
2802 /* found, remove from list */
2803 AST_LIST_REMOVE_CURRENT(&con->alts, list);
2804 free(i); /* free switch and return */
2805 ret = 0;
2806 break;
2809 AST_LIST_TRAVERSE_SAFE_END
2811 ast_mutex_unlock(&con->lock);
2813 return ret;
2817 * \note This functions lock contexts list, search for the right context,
2818 * call ast_context_remove_extension2, unlock contexts list and return.
2819 * In this function we are using
2821 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2823 int ret = -1; /* default error return */
2824 struct ast_context *c = find_context_locked(context);
2826 if (c) { /* ... remove extension ... */
2827 ret = ast_context_remove_extension2(c, extension, priority, registrar);
2828 ast_unlock_contexts();
2830 return ret;
2834 * \brief This functionc locks given context, search for the right extension and
2835 * fires out all peer in this extensions with given priority. If priority
2836 * is set to 0, all peers are removed. After that, unlock context and
2837 * return.
2838 * \note When do you want to call this function, make sure that &conlock is locked,
2839 * because some process can handle with your *con context before you lock
2840 * it.
2843 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2845 struct ast_exten *exten, *prev_exten = NULL;
2846 struct ast_exten *peer;
2848 ast_mutex_lock(&con->lock);
2850 /* scan the extension list to find matching extension-registrar */
2851 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
2852 if (!strcmp(exten->exten, extension) &&
2853 (!registrar || !strcmp(exten->registrar, registrar)))
2854 break;
2856 if (!exten) {
2857 /* we can't find right extension */
2858 ast_mutex_unlock(&con->lock);
2859 return -1;
2862 /* should we free all peers in this extension? (priority == 0)? */
2863 if (priority == 0) {
2864 /* remove this extension from context list */
2865 if (prev_exten)
2866 prev_exten->next = exten->next;
2867 else
2868 con->root = exten->next;
2870 /* fire out all peers */
2871 while ( (peer = exten) ) {
2872 exten = peer->peer; /* prepare for next entry */
2873 destroy_exten(peer);
2875 } else {
2876 /* scan the priority list to remove extension with exten->priority == priority */
2877 struct ast_exten *previous_peer = NULL;
2879 for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
2880 if (peer->priority == priority &&
2881 (!registrar || !strcmp(peer->registrar, registrar) ))
2882 break; /* found our priority */
2884 if (!peer) { /* not found */
2885 ast_mutex_unlock(&con->lock);
2886 return -1;
2888 /* we are first priority extension? */
2889 if (!previous_peer) {
2891 * We are first in the priority chain, so must update the extension chain.
2892 * The next node is either the next priority or the next extension
2894 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
2896 if (!prev_exten) /* change the root... */
2897 con->root = next_node;
2898 else
2899 prev_exten->next = next_node; /* unlink */
2900 if (peer->peer) /* XXX update the new head of the pri list */
2901 peer->peer->next = peer->next;
2902 } else { /* easy, we are not first priority in extension */
2903 previous_peer->peer = peer->peer;
2906 /* now, free whole priority extension */
2907 destroy_exten(peer);
2908 /* XXX should we return -1 ? */
2910 ast_mutex_unlock(&con->lock);
2911 return 0;
2916 * \note This function locks contexts list by &conlist, searches for the right context
2917 * structure, and locks the macrolock mutex in that context.
2918 * macrolock is used to limit a macro to be executed by one call at a time.
2920 int ast_context_lockmacro(const char *context)
2922 struct ast_context *c = NULL;
2923 int ret = -1;
2925 ast_rdlock_contexts();
2927 while ((c = ast_walk_contexts(c))) {
2928 if (!strcmp(ast_get_context_name(c), context)) {
2929 ret = 0;
2930 break;
2934 ast_unlock_contexts();
2936 /* if we found context, lock macrolock */
2937 if (ret == 0)
2938 ret = ast_mutex_lock(&c->macrolock);
2940 return ret;
2944 * \note This function locks contexts list by &conlist, searches for the right context
2945 * structure, and unlocks the macrolock mutex in that context.
2946 * macrolock is used to limit a macro to be executed by one call at a time.
2948 int ast_context_unlockmacro(const char *context)
2950 struct ast_context *c = NULL;
2951 int ret = -1;
2953 ast_rdlock_contexts();
2955 while ((c = ast_walk_contexts(c))) {
2956 if (!strcmp(ast_get_context_name(c), context)) {
2957 ret = 0;
2958 break;
2962 ast_unlock_contexts();
2964 /* if we found context, unlock macrolock */
2965 if (ret == 0)
2966 ret = ast_mutex_unlock(&c->macrolock);
2968 return ret;
2971 /*! \brief Dynamically register a new dial plan application */
2972 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
2974 struct ast_app *tmp, *cur = NULL;
2975 char tmps[80];
2976 int length;
2978 AST_LIST_LOCK(&apps);
2979 AST_LIST_TRAVERSE(&apps, tmp, list) {
2980 if (!strcasecmp(app, tmp->name)) {
2981 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2982 AST_LIST_UNLOCK(&apps);
2983 return -1;
2987 length = sizeof(*tmp) + strlen(app) + 1;
2989 if (!(tmp = ast_calloc(1, length))) {
2990 AST_LIST_UNLOCK(&apps);
2991 return -1;
2994 strcpy(tmp->name, app);
2995 tmp->execute = execute;
2996 tmp->synopsis = synopsis;
2997 tmp->description = description;
2999 /* Store in alphabetical order */
3000 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
3001 if (strcasecmp(tmp->name, cur->name) < 0) {
3002 AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
3003 break;
3006 AST_LIST_TRAVERSE_SAFE_END
3007 if (!cur)
3008 AST_LIST_INSERT_TAIL(&apps, tmp, list);
3010 if (option_verbose > 1)
3011 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
3013 AST_LIST_UNLOCK(&apps);
3015 return 0;
3019 * Append to the list. We don't have a tail pointer because we need
3020 * to scan the list anyways to check for duplicates during insertion.
3022 int ast_register_switch(struct ast_switch *sw)
3024 struct ast_switch *tmp;
3026 AST_LIST_LOCK(&switches);
3027 AST_LIST_TRAVERSE(&switches, tmp, list) {
3028 if (!strcasecmp(tmp->name, sw->name)) {
3029 AST_LIST_UNLOCK(&switches);
3030 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
3031 return -1;
3034 AST_LIST_INSERT_TAIL(&switches, sw, list);
3035 AST_LIST_UNLOCK(&switches);
3037 return 0;
3040 void ast_unregister_switch(struct ast_switch *sw)
3042 AST_LIST_LOCK(&switches);
3043 AST_LIST_REMOVE(&switches, sw, list);
3044 AST_LIST_UNLOCK(&switches);
3048 * Help for CLI commands ...
3050 static char show_applications_help[] =
3051 "Usage: core show applications [{like|describing} <text>]\n"
3052 " List applications which are currently available.\n"
3053 " If 'like', <text> will be a substring of the app name\n"
3054 " If 'describing', <text> will be a substring of the description\n";
3056 static char show_functions_help[] =
3057 "Usage: core show functions [like <text>]\n"
3058 " List builtin functions, optionally only those matching a given string\n";
3060 static char show_switches_help[] =
3061 "Usage: core show switches\n"
3062 " List registered switches\n";
3064 static char show_hints_help[] =
3065 "Usage: core show hints\n"
3066 " List registered hints\n";
3068 static char show_globals_help[] =
3069 "Usage: core show globals\n"
3070 " List current global dialplan variables and their values\n";
3072 static char show_application_help[] =
3073 "Usage: core show application <application> [<application> [<application> [...]]]\n"
3074 " Describes a particular application.\n";
3076 static char show_function_help[] =
3077 "Usage: core show function <function>\n"
3078 " Describe a particular dialplan function.\n";
3080 static char show_dialplan_help[] =
3081 "Usage: dialplan show [exten@][context]\n"
3082 " Show dialplan\n";
3084 static char set_global_help[] =
3085 "Usage: core set global <name> <value>\n"
3086 " Set global dialplan variable <name> to <value>\n";
3090 * \brief 'show application' CLI command implementation functions ...
3094 * There is a possibility to show informations about more than one
3095 * application at one time. You can type 'show application Dial Echo' and
3096 * you will see informations about these two applications ...
3098 static char *complete_show_application(const char *line, const char *word, int pos, int state)
3100 struct ast_app *a;
3101 char *ret = NULL;
3102 int which = 0;
3103 int wordlen = strlen(word);
3105 /* return the n-th [partial] matching entry */
3106 AST_LIST_LOCK(&apps);
3107 AST_LIST_TRAVERSE(&apps, a, list) {
3108 if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
3109 ret = strdup(a->name);
3110 break;
3113 AST_LIST_UNLOCK(&apps);
3115 return ret;
3118 static int handle_show_application_deprecated(int fd, int argc, char *argv[])
3120 struct ast_app *a;
3121 int app, no_registered_app = 1;
3123 if (argc < 3)
3124 return RESULT_SHOWUSAGE;
3126 /* ... go through all applications ... */
3127 AST_LIST_LOCK(&apps);
3128 AST_LIST_TRAVERSE(&apps, a, list) {
3129 /* ... compare this application name with all arguments given
3130 * to 'show application' command ... */
3131 for (app = 2; app < argc; app++) {
3132 if (!strcasecmp(a->name, argv[app])) {
3133 /* Maximum number of characters added by terminal coloring is 22 */
3134 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3135 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3136 int synopsis_size, description_size;
3138 no_registered_app = 0;
3140 if (a->synopsis)
3141 synopsis_size = strlen(a->synopsis) + 23;
3142 else
3143 synopsis_size = strlen("Not available") + 23;
3144 synopsis = alloca(synopsis_size);
3146 if (a->description)
3147 description_size = strlen(a->description) + 23;
3148 else
3149 description_size = strlen("Not available") + 23;
3150 description = alloca(description_size);
3152 if (synopsis && description) {
3153 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3154 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3155 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3156 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3157 term_color(synopsis,
3158 a->synopsis ? a->synopsis : "Not available",
3159 COLOR_CYAN, 0, synopsis_size);
3160 term_color(description,
3161 a->description ? a->description : "Not available",
3162 COLOR_CYAN, 0, description_size);
3164 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3165 } else {
3166 /* ... one of our applications, show info ...*/
3167 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3168 "[Synopsis]\n %s\n\n"
3169 "[Description]\n%s\n",
3170 a->name,
3171 a->synopsis ? a->synopsis : "Not available",
3172 a->description ? a->description : "Not available");
3177 AST_LIST_UNLOCK(&apps);
3179 /* we found at least one app? no? */
3180 if (no_registered_app) {
3181 ast_cli(fd, "Your application(s) is (are) not registered\n");
3182 return RESULT_FAILURE;
3185 return RESULT_SUCCESS;
3188 static int handle_show_application(int fd, int argc, char *argv[])
3190 struct ast_app *a;
3191 int app, no_registered_app = 1;
3193 if (argc < 4)
3194 return RESULT_SHOWUSAGE;
3196 /* ... go through all applications ... */
3197 AST_LIST_LOCK(&apps);
3198 AST_LIST_TRAVERSE(&apps, a, list) {
3199 /* ... compare this application name with all arguments given
3200 * to 'show application' command ... */
3201 for (app = 3; app < argc; app++) {
3202 if (!strcasecmp(a->name, argv[app])) {
3203 /* Maximum number of characters added by terminal coloring is 22 */
3204 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3205 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3206 int synopsis_size, description_size;
3208 no_registered_app = 0;
3210 if (a->synopsis)
3211 synopsis_size = strlen(a->synopsis) + 23;
3212 else
3213 synopsis_size = strlen("Not available") + 23;
3214 synopsis = alloca(synopsis_size);
3216 if (a->description)
3217 description_size = strlen(a->description) + 23;
3218 else
3219 description_size = strlen("Not available") + 23;
3220 description = alloca(description_size);
3222 if (synopsis && description) {
3223 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3224 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3225 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3226 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3227 term_color(synopsis,
3228 a->synopsis ? a->synopsis : "Not available",
3229 COLOR_CYAN, 0, synopsis_size);
3230 term_color(description,
3231 a->description ? a->description : "Not available",
3232 COLOR_CYAN, 0, description_size);
3234 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3235 } else {
3236 /* ... one of our applications, show info ...*/
3237 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3238 "[Synopsis]\n %s\n\n"
3239 "[Description]\n%s\n",
3240 a->name,
3241 a->synopsis ? a->synopsis : "Not available",
3242 a->description ? a->description : "Not available");
3247 AST_LIST_UNLOCK(&apps);
3249 /* we found at least one app? no? */
3250 if (no_registered_app) {
3251 ast_cli(fd, "Your application(s) is (are) not registered\n");
3252 return RESULT_FAILURE;
3255 return RESULT_SUCCESS;
3258 /*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
3259 static int handle_show_hints(int fd, int argc, char *argv[])
3261 struct ast_hint *hint;
3262 int num = 0;
3263 int watchers;
3264 struct ast_state_cb *watcher;
3266 if (AST_LIST_EMPTY(&hints)) {
3267 ast_cli(fd, "There are no registered dialplan hints\n");
3268 return RESULT_SUCCESS;
3270 /* ... we have hints ... */
3271 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
3272 AST_LIST_LOCK(&hints);
3273 AST_LIST_TRAVERSE(&hints, hint, list) {
3274 watchers = 0;
3275 for (watcher = hint->callbacks; watcher; watcher = watcher->next)
3276 watchers++;
3277 ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
3278 ast_get_extension_name(hint->exten),
3279 ast_get_context_name(ast_get_extension_context(hint->exten)),
3280 ast_get_extension_app(hint->exten),
3281 ast_extension_state2str(hint->laststate), watchers);
3282 num++;
3284 ast_cli(fd, "----------------\n");
3285 ast_cli(fd, "- %d hints registered\n", num);
3286 AST_LIST_UNLOCK(&hints);
3287 return RESULT_SUCCESS;
3290 /*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
3291 static int handle_show_switches(int fd, int argc, char *argv[])
3293 struct ast_switch *sw;
3295 AST_LIST_LOCK(&switches);
3297 if (AST_LIST_EMPTY(&switches)) {
3298 AST_LIST_UNLOCK(&switches);
3299 ast_cli(fd, "There are no registered alternative switches\n");
3300 return RESULT_SUCCESS;
3303 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
3304 AST_LIST_TRAVERSE(&switches, sw, list)
3305 ast_cli(fd, "%s: %s\n", sw->name, sw->description);
3307 AST_LIST_UNLOCK(&switches);
3309 return RESULT_SUCCESS;
3313 * 'show applications' CLI command implementation functions ...
3315 static int handle_show_applications_deprecated(int fd, int argc, char *argv[])
3317 struct ast_app *a;
3318 int like = 0, describing = 0;
3319 int total_match = 0; /* Number of matches in like clause */
3320 int total_apps = 0; /* Number of apps registered */
3322 AST_LIST_LOCK(&apps);
3324 if (AST_LIST_EMPTY(&apps)) {
3325 ast_cli(fd, "There are no registered applications\n");
3326 AST_LIST_UNLOCK(&apps);
3327 return -1;
3330 /* show applications like <keyword> */
3331 if ((argc == 4) && (!strcmp(argv[2], "like"))) {
3332 like = 1;
3333 } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
3334 describing = 1;
3337 /* show applications describing <keyword1> [<keyword2>] [...] */
3338 if ((!like) && (!describing)) {
3339 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3340 } else {
3341 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3344 AST_LIST_TRAVERSE(&apps, a, list) {
3345 int printapp = 0;
3346 total_apps++;
3347 if (like) {
3348 if (strcasestr(a->name, argv[3])) {
3349 printapp = 1;
3350 total_match++;
3352 } else if (describing) {
3353 if (a->description) {
3354 /* Match all words on command line */
3355 int i;
3356 printapp = 1;
3357 for (i = 3; i < argc; i++) {
3358 if (!strcasestr(a->description, argv[i])) {
3359 printapp = 0;
3360 } else {
3361 total_match++;
3365 } else {
3366 printapp = 1;
3369 if (printapp) {
3370 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3373 if ((!like) && (!describing)) {
3374 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3375 } else {
3376 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3379 AST_LIST_UNLOCK(&apps);
3381 return RESULT_SUCCESS;
3383 static int handle_show_applications(int fd, int argc, char *argv[])
3385 struct ast_app *a;
3386 int like = 0, describing = 0;
3387 int total_match = 0; /* Number of matches in like clause */
3388 int total_apps = 0; /* Number of apps registered */
3390 AST_LIST_LOCK(&apps);
3392 if (AST_LIST_EMPTY(&apps)) {
3393 ast_cli(fd, "There are no registered applications\n");
3394 AST_LIST_UNLOCK(&apps);
3395 return -1;
3398 /* core list applications like <keyword> */
3399 if ((argc == 5) && (!strcmp(argv[3], "like"))) {
3400 like = 1;
3401 } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) {
3402 describing = 1;
3405 /* core list applications describing <keyword1> [<keyword2>] [...] */
3406 if ((!like) && (!describing)) {
3407 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3408 } else {
3409 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3412 AST_LIST_TRAVERSE(&apps, a, list) {
3413 int printapp = 0;
3414 total_apps++;
3415 if (like) {
3416 if (strcasestr(a->name, argv[4])) {
3417 printapp = 1;
3418 total_match++;
3420 } else if (describing) {
3421 if (a->description) {
3422 /* Match all words on command line */
3423 int i;
3424 printapp = 1;
3425 for (i = 4; i < argc; i++) {
3426 if (!strcasestr(a->description, argv[i])) {
3427 printapp = 0;
3428 } else {
3429 total_match++;
3433 } else {
3434 printapp = 1;
3437 if (printapp) {
3438 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3441 if ((!like) && (!describing)) {
3442 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3443 } else {
3444 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3447 AST_LIST_UNLOCK(&apps);
3449 return RESULT_SUCCESS;
3452 static char *complete_show_applications_deprecated(const char *line, const char *word, int pos, int state)
3454 static char* choices[] = { "like", "describing", NULL };
3456 return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
3459 static char *complete_show_applications(const char *line, const char *word, int pos, int state)
3461 static char* choices[] = { "like", "describing", NULL };
3463 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
3467 * 'show dialplan' CLI command implementation functions ...
3469 static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
3470 int state)
3472 struct ast_context *c = NULL;
3473 char *ret = NULL;
3474 int which = 0;
3475 int wordlen;
3477 /* we are do completion of [exten@]context on second position only */
3478 if (pos != 2)
3479 return NULL;
3481 ast_rdlock_contexts();
3483 wordlen = strlen(word);
3485 /* walk through all contexts and return the n-th match */
3486 while ( (c = ast_walk_contexts(c)) ) {
3487 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
3488 ret = ast_strdup(ast_get_context_name(c));
3489 break;
3493 ast_unlock_contexts();
3495 return ret;
3498 struct dialplan_counters {
3499 int total_context;
3500 int total_exten;
3501 int total_prio;
3502 int context_existence;
3503 int extension_existence;
3506 /*! \brief helper function to print an extension */
3507 static void print_ext(struct ast_exten *e, char * buf, int buflen)
3509 int prio = ast_get_extension_priority(e);
3510 if (prio == PRIORITY_HINT) {
3511 snprintf(buf, buflen, "hint: %s",
3512 ast_get_extension_app(e));
3513 } else {
3514 snprintf(buf, buflen, "%d. %s(%s)",
3515 prio, ast_get_extension_app(e),
3516 (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
3520 /* XXX not verified */
3521 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[])
3523 struct ast_context *c = NULL;
3524 int res = 0, old_total_exten = dpc->total_exten;
3526 ast_rdlock_contexts();
3528 /* walk all contexts ... */
3529 while ( (c = ast_walk_contexts(c)) ) {
3530 struct ast_exten *e;
3531 struct ast_include *i;
3532 struct ast_ignorepat *ip;
3533 char buf[256], buf2[256];
3534 int context_info_printed = 0;
3536 if (context && strcmp(ast_get_context_name(c), context))
3537 continue; /* skip this one, name doesn't match */
3539 dpc->context_existence = 1;
3541 ast_lock_context(c);
3543 /* are we looking for exten too? if yes, we print context
3544 * only if we find our extension.
3545 * Otherwise print context even if empty ?
3546 * XXX i am not sure how the rinclude is handled.
3547 * I think it ought to go inside.
3549 if (!exten) {
3550 dpc->total_context++;
3551 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3552 ast_get_context_name(c), ast_get_context_registrar(c));
3553 context_info_printed = 1;
3556 /* walk extensions ... */
3557 e = NULL;
3558 while ( (e = ast_walk_context_extensions(c, e)) ) {
3559 struct ast_exten *p;
3561 if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
3562 continue; /* skip, extension match failed */
3564 dpc->extension_existence = 1;
3566 /* may we print context info? */
3567 if (!context_info_printed) {
3568 dpc->total_context++;
3569 if (rinclude) { /* TODO Print more info about rinclude */
3570 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
3571 ast_get_context_name(c), ast_get_context_registrar(c));
3572 } else {
3573 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3574 ast_get_context_name(c), ast_get_context_registrar(c));
3576 context_info_printed = 1;
3578 dpc->total_prio++;
3580 /* write extension name and first peer */
3581 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
3583 print_ext(e, buf2, sizeof(buf2));
3585 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
3586 ast_get_extension_registrar(e));
3588 dpc->total_exten++;
3589 /* walk next extension peers */
3590 p = e; /* skip the first one, we already got it */
3591 while ( (p = ast_walk_extension_priorities(e, p)) ) {
3592 const char *el = ast_get_extension_label(p);
3593 dpc->total_prio++;
3594 if (el)
3595 snprintf(buf, sizeof(buf), " [%s]", el);
3596 else
3597 buf[0] = '\0';
3598 print_ext(p, buf2, sizeof(buf2));
3600 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
3601 ast_get_extension_registrar(p));
3605 /* walk included and write info ... */
3606 i = NULL;
3607 while ( (i = ast_walk_context_includes(c, i)) ) {
3608 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
3609 if (exten) {
3610 /* Check all includes for the requested extension */
3611 if (includecount >= AST_PBX_MAX_STACK) {
3612 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
3613 } else {
3614 int dupe=0;
3615 int x;
3616 for (x=0;x<includecount;x++) {
3617 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
3618 dupe++;
3619 break;
3622 if (!dupe) {
3623 includes[includecount] = ast_get_include_name(i);
3624 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
3625 } else {
3626 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
3629 } else {
3630 ast_cli(fd, " Include => %-45s [%s]\n",
3631 buf, ast_get_include_registrar(i));
3635 /* walk ignore patterns and write info ... */
3636 ip = NULL;
3637 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
3638 const char *ipname = ast_get_ignorepat_name(ip);
3639 char ignorepat[AST_MAX_EXTENSION];
3640 snprintf(buf, sizeof(buf), "'%s'", ipname);
3641 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
3642 if (!exten || ast_extension_match(ignorepat, exten)) {
3643 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
3644 buf, ast_get_ignorepat_registrar(ip));
3647 if (!rinclude) {
3648 struct ast_sw *sw = NULL;
3649 while ( (sw = ast_walk_context_switches(c, sw)) ) {
3650 snprintf(buf, sizeof(buf), "'%s/%s'",
3651 ast_get_switch_name(sw),
3652 ast_get_switch_data(sw));
3653 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
3654 buf, ast_get_switch_registrar(sw));
3658 ast_unlock_context(c);
3660 /* if we print something in context, make an empty line */
3661 if (context_info_printed)
3662 ast_cli(fd, "\r\n");
3664 ast_unlock_contexts();
3666 return (dpc->total_exten == old_total_exten) ? -1 : res;
3669 static int handle_show_dialplan(int fd, int argc, char *argv[])
3671 char *exten = NULL, *context = NULL;
3672 /* Variables used for different counters */
3673 struct dialplan_counters counters;
3675 const char *incstack[AST_PBX_MAX_STACK];
3676 memset(&counters, 0, sizeof(counters));
3678 if (argc != 2 && argc != 3)
3679 return RESULT_SHOWUSAGE;
3681 /* we obtain [exten@]context? if yes, split them ... */
3682 if (argc == 3) {
3683 if (strchr(argv[2], '@')) { /* split into exten & context */
3684 context = ast_strdupa(argv[2]);
3685 exten = strsep(&context, "@");
3686 /* change empty strings to NULL */
3687 if (ast_strlen_zero(exten))
3688 exten = NULL;
3689 } else { /* no '@' char, only context given */
3690 context = argv[2];
3692 if (ast_strlen_zero(context))
3693 context = NULL;
3695 /* else Show complete dial plan, context and exten are NULL */
3696 show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
3698 /* check for input failure and throw some error messages */
3699 if (context && !counters.context_existence) {
3700 ast_cli(fd, "There is no existence of '%s' context\n", context);
3701 return RESULT_FAILURE;
3704 if (exten && !counters.extension_existence) {
3705 if (context)
3706 ast_cli(fd, "There is no existence of %s@%s extension\n",
3707 exten, context);
3708 else
3709 ast_cli(fd,
3710 "There is no existence of '%s' extension in all contexts\n",
3711 exten);
3712 return RESULT_FAILURE;
3715 ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
3716 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
3717 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
3718 counters.total_context, counters.total_context == 1 ? "context" : "contexts");
3720 /* everything ok */
3721 return RESULT_SUCCESS;
3724 /*! \brief CLI support for listing global variables in a parseable way */
3725 static int handle_show_globals(int fd, int argc, char *argv[])
3727 int i = 0;
3728 struct ast_var_t *newvariable;
3730 ast_mutex_lock(&globalslock);
3731 AST_LIST_TRAVERSE (&globals, newvariable, entries) {
3732 i++;
3733 ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
3735 ast_mutex_unlock(&globalslock);
3736 ast_cli(fd, "\n -- %d variables\n", i);
3738 return RESULT_SUCCESS;
3741 /*! \brief CLI support for setting global variables */
3742 static int handle_set_global_deprecated(int fd, int argc, char *argv[])
3744 if (argc != 4)
3745 return RESULT_SHOWUSAGE;
3747 pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
3748 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[2], argv[3]);
3750 return RESULT_SUCCESS;
3754 static int handle_set_global(int fd, int argc, char *argv[])
3756 if (argc != 5)
3757 return RESULT_SHOWUSAGE;
3759 pbx_builtin_setvar_helper(NULL, argv[3], argv[4]);
3760 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]);
3762 return RESULT_SUCCESS;
3768 * CLI entries for upper commands ...
3770 static struct ast_cli_entry cli_show_applications_deprecated = {
3771 { "show", "applications", NULL },
3772 handle_show_applications_deprecated, NULL,
3773 NULL, complete_show_applications_deprecated };
3775 static struct ast_cli_entry cli_show_functions_deprecated = {
3776 { "show", "functions", NULL },
3777 handle_show_functions_deprecated, NULL,
3778 NULL };
3780 static struct ast_cli_entry cli_show_switches_deprecated = {
3781 { "show", "switches", NULL },
3782 handle_show_switches, NULL,
3783 NULL };
3785 static struct ast_cli_entry cli_show_hints_deprecated = {
3786 { "show", "hints", NULL },
3787 handle_show_hints, NULL,
3788 NULL };
3790 static struct ast_cli_entry cli_show_globals_deprecated = {
3791 { "show", "globals", NULL },
3792 handle_show_globals, NULL,
3793 NULL };
3795 static struct ast_cli_entry cli_show_function_deprecated = {
3796 { "show" , "function", NULL },
3797 handle_show_function_deprecated, NULL,
3798 NULL, complete_show_function };
3800 static struct ast_cli_entry cli_show_application_deprecated = {
3801 { "show", "application", NULL },
3802 handle_show_application_deprecated, NULL,
3803 NULL, complete_show_application };
3805 static struct ast_cli_entry cli_show_dialplan_deprecated = {
3806 { "show", "dialplan", NULL },
3807 handle_show_dialplan, NULL,
3808 NULL, complete_show_dialplan_context };
3810 static struct ast_cli_entry cli_set_global_deprecated = {
3811 { "set", "global", NULL },
3812 handle_set_global_deprecated, NULL,
3813 NULL };
3815 static struct ast_cli_entry pbx_cli[] = {
3816 { { "core", "show", "applications", NULL },
3817 handle_show_applications, "Shows registered dialplan applications",
3818 show_applications_help, complete_show_applications, &cli_show_applications_deprecated },
3820 { { "core", "show", "functions", NULL },
3821 handle_show_functions, "Shows registered dialplan functions",
3822 show_functions_help, NULL, &cli_show_functions_deprecated },
3824 { { "core", "show", "switches", NULL },
3825 handle_show_switches, "Show alternative switches",
3826 show_switches_help, NULL, &cli_show_switches_deprecated },
3828 { { "core", "show", "hints", NULL },
3829 handle_show_hints, "Show dialplan hints",
3830 show_hints_help, NULL, &cli_show_hints_deprecated },
3832 { { "core", "show", "globals", NULL },
3833 handle_show_globals, "Show global dialplan variables",
3834 show_globals_help, NULL, &cli_show_globals_deprecated },
3836 { { "core", "show" , "function", NULL },
3837 handle_show_function, "Describe a specific dialplan function",
3838 show_function_help, complete_show_function, &cli_show_function_deprecated },
3840 { { "core", "show", "application", NULL },
3841 handle_show_application, "Describe a specific dialplan application",
3842 show_application_help, complete_show_application, &cli_show_application_deprecated },
3844 { { "core", "set", "global", NULL },
3845 handle_set_global, "Set global dialplan variable",
3846 set_global_help, NULL, &cli_set_global_deprecated },
3848 { { "dialplan", "show", NULL },
3849 handle_show_dialplan, "Show dialplan",
3850 show_dialplan_help, complete_show_dialplan_context, &cli_show_dialplan_deprecated },
3853 int ast_unregister_application(const char *app)
3855 struct ast_app *tmp;
3857 AST_LIST_LOCK(&apps);
3858 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
3859 if (!strcasecmp(app, tmp->name)) {
3860 AST_LIST_REMOVE_CURRENT(&apps, list);
3861 if (option_verbose > 1)
3862 ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
3863 free(tmp);
3864 break;
3867 AST_LIST_TRAVERSE_SAFE_END
3868 AST_LIST_UNLOCK(&apps);
3870 return tmp ? 0 : -1;
3873 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
3875 struct ast_context *tmp, **local_contexts;
3876 int length = sizeof(struct ast_context) + strlen(name) + 1;
3878 if (!extcontexts) {
3879 ast_rdlock_contexts();
3880 local_contexts = &contexts;
3881 } else
3882 local_contexts = extcontexts;
3884 for (tmp = *local_contexts; tmp; tmp = tmp->next) {
3885 if (!strcasecmp(tmp->name, name)) {
3886 if (!existsokay) {
3887 ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
3888 tmp = NULL;
3890 if (!extcontexts)
3891 ast_unlock_contexts();
3892 return tmp;
3896 if (!extcontexts)
3897 ast_unlock_contexts();
3899 if ((tmp = ast_calloc(1, length))) {
3900 ast_mutex_init(&tmp->lock);
3901 ast_mutex_init(&tmp->macrolock);
3902 strcpy(tmp->name, name);
3903 tmp->registrar = registrar;
3904 if (!extcontexts)
3905 ast_wrlock_contexts();
3906 tmp->next = *local_contexts;
3907 *local_contexts = tmp;
3908 if (!extcontexts)
3909 ast_unlock_contexts();
3910 if (option_debug)
3911 ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
3912 if (option_verbose > 2)
3913 ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
3916 return tmp;
3919 struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3921 return __ast_context_create(extcontexts, name, registrar, 0);
3924 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3926 return __ast_context_create(extcontexts, name, registrar, 1);
3928 void __ast_context_destroy(struct ast_context *con, const char *registrar);
3930 struct store_hint {
3931 char *context;
3932 char *exten;
3933 struct ast_state_cb *callbacks;
3934 int laststate;
3935 AST_LIST_ENTRY(store_hint) list;
3936 char data[1];
3939 AST_LIST_HEAD(store_hints, store_hint);
3941 /* XXX this does not check that multiple contexts are merged */
3942 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
3944 struct ast_context *tmp, *lasttmp = NULL;
3945 struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
3946 struct store_hint *this;
3947 struct ast_hint *hint;
3948 struct ast_exten *exten;
3949 int length;
3950 struct ast_state_cb *thiscb, *prevcb;
3952 /* it is very important that this function hold the hint list lock _and_ the conlock
3953 during its operation; not only do we need to ensure that the list of contexts
3954 and extensions does not change, but also that no hint callbacks (watchers) are
3955 added or removed during the merge/delete process
3957 in addition, the locks _must_ be taken in this order, because there are already
3958 other code paths that use this order
3960 ast_wrlock_contexts();
3961 AST_LIST_LOCK(&hints);
3963 /* preserve all watchers for hints associated with this registrar */
3964 AST_LIST_TRAVERSE(&hints, hint, list) {
3965 if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
3966 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
3967 if (!(this = ast_calloc(1, length)))
3968 continue;
3969 this->callbacks = hint->callbacks;
3970 hint->callbacks = NULL;
3971 this->laststate = hint->laststate;
3972 this->context = this->data;
3973 strcpy(this->data, hint->exten->parent->name);
3974 this->exten = this->data + strlen(this->context) + 1;
3975 strcpy(this->exten, hint->exten->exten);
3976 AST_LIST_INSERT_HEAD(&store, this, list);
3980 tmp = *extcontexts;
3981 if (registrar) {
3982 /* XXX remove previous contexts from same registrar */
3983 if (option_debug)
3984 ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
3985 __ast_context_destroy(NULL,registrar);
3986 while (tmp) {
3987 lasttmp = tmp;
3988 tmp = tmp->next;
3990 } else {
3991 /* XXX remove contexts with the same name */
3992 while (tmp) {
3993 ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
3994 __ast_context_destroy(tmp,tmp->registrar);
3995 lasttmp = tmp;
3996 tmp = tmp->next;
3999 if (lasttmp) {
4000 lasttmp->next = contexts;
4001 contexts = *extcontexts;
4002 *extcontexts = NULL;
4003 } else
4004 ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
4006 /* restore the watchers for hints that can be found; notify those that
4007 cannot be restored
4009 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
4010 struct pbx_find_info q = { .stacklen = 0 };
4011 exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
4012 /* Find the hint in the list of hints */
4013 AST_LIST_TRAVERSE(&hints, hint, list) {
4014 if (hint->exten == exten)
4015 break;
4017 if (!exten || !hint) {
4018 /* this hint has been removed, notify the watchers */
4019 prevcb = NULL;
4020 thiscb = this->callbacks;
4021 while (thiscb) {
4022 prevcb = thiscb;
4023 thiscb = thiscb->next;
4024 prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
4025 free(prevcb);
4027 } else {
4028 thiscb = this->callbacks;
4029 while (thiscb->next)
4030 thiscb = thiscb->next;
4031 thiscb->next = hint->callbacks;
4032 hint->callbacks = this->callbacks;
4033 hint->laststate = this->laststate;
4035 free(this);
4038 AST_LIST_UNLOCK(&hints);
4039 ast_unlock_contexts();
4041 return;
4045 * errno values
4046 * EBUSY - can't lock
4047 * ENOENT - no existence of context
4049 int ast_context_add_include(const char *context, const char *include, const char *registrar)
4051 int ret = -1;
4052 struct ast_context *c = find_context_locked(context);
4054 if (c) {
4055 ret = ast_context_add_include2(c, include, registrar);
4056 ast_unlock_contexts();
4058 return ret;
4061 /*! \brief Helper for get_range.
4062 * return the index of the matching entry, starting from 1.
4063 * If names is not supplied, try numeric values.
4065 static int lookup_name(const char *s, char *const names[], int max)
4067 int i;
4069 if (names) {
4070 for (i = 0; names[i]; i++) {
4071 if (!strcasecmp(s, names[i]))
4072 return i+1;
4074 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
4075 return i;
4077 return 0; /* error return */
4080 /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
4081 * names, if supplied, is an array of names that should be mapped to numbers.
4083 static unsigned get_range(char *src, int max, char *const names[], const char *msg)
4085 int s, e; /* start and ending position */
4086 unsigned int mask = 0;
4088 /* Check for whole range */
4089 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
4090 s = 0;
4091 e = max - 1;
4092 } else {
4093 /* Get start and ending position */
4094 char *c = strchr(src, '-');
4095 if (c)
4096 *c++ = '\0';
4097 /* Find the start */
4098 s = lookup_name(src, names, max);
4099 if (!s) {
4100 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
4101 return 0;
4103 s--;
4104 if (c) { /* find end of range */
4105 e = lookup_name(c, names, max);
4106 if (!e) {
4107 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
4108 return 0;
4110 e--;
4111 } else
4112 e = s;
4114 /* Fill the mask. Remember that ranges are cyclic */
4115 mask = 1 << e; /* initialize with last element */
4116 while (s != e) {
4117 if (s >= max) {
4118 s = 0;
4119 mask |= (1 << s);
4120 } else {
4121 mask |= (1 << s);
4122 s++;
4125 return mask;
4128 /*! \brief store a bitmask of valid times, one bit each 2 minute */
4129 static void get_timerange(struct ast_timing *i, char *times)
4131 char *e;
4132 int x;
4133 int s1, s2;
4134 int e1, e2;
4135 /* int cth, ctm; */
4137 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
4138 memset(i->minmask, 0, sizeof(i->minmask));
4140 /* 2-minutes per bit, since the mask has only 32 bits :( */
4141 /* Star is all times */
4142 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
4143 for (x=0; x<24; x++)
4144 i->minmask[x] = 0x3fffffff; /* 30 bits */
4145 return;
4147 /* Otherwise expect a range */
4148 e = strchr(times, '-');
4149 if (!e) {
4150 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
4151 return;
4153 *e++ = '\0';
4154 /* XXX why skip non digits ? */
4155 while (*e && !isdigit(*e))
4156 e++;
4157 if (!*e) {
4158 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
4159 return;
4161 if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
4162 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
4163 return;
4165 if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
4166 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
4167 return;
4169 /* XXX this needs to be optimized */
4170 #if 1
4171 s1 = s1 * 30 + s2/2;
4172 if ((s1 < 0) || (s1 >= 24*30)) {
4173 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
4174 return;
4176 e1 = e1 * 30 + e2/2;
4177 if ((e1 < 0) || (e1 >= 24*30)) {
4178 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
4179 return;
4181 /* Go through the time and enable each appropriate bit */
4182 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
4183 i->minmask[x/30] |= (1 << (x % 30));
4185 /* Do the last one */
4186 i->minmask[x/30] |= (1 << (x % 30));
4187 #else
4188 for (cth=0; cth<24; cth++) {
4189 /* Initialize masks to blank */
4190 i->minmask[cth] = 0;
4191 for (ctm=0; ctm<30; ctm++) {
4192 if (
4193 /* First hour with more than one hour */
4194 (((cth == s1) && (ctm >= s2)) &&
4195 ((cth < e1)))
4196 /* Only one hour */
4197 || (((cth == s1) && (ctm >= s2)) &&
4198 ((cth == e1) && (ctm <= e2)))
4199 /* In between first and last hours (more than 2 hours) */
4200 || ((cth > s1) &&
4201 (cth < e1))
4202 /* Last hour with more than one hour */
4203 || ((cth > s1) &&
4204 ((cth == e1) && (ctm <= e2)))
4206 i->minmask[cth] |= (1 << (ctm / 2));
4209 #endif
4210 /* All done */
4211 return;
4214 static char *days[] =
4216 "sun",
4217 "mon",
4218 "tue",
4219 "wed",
4220 "thu",
4221 "fri",
4222 "sat",
4223 NULL,
4226 static char *months[] =
4228 "jan",
4229 "feb",
4230 "mar",
4231 "apr",
4232 "may",
4233 "jun",
4234 "jul",
4235 "aug",
4236 "sep",
4237 "oct",
4238 "nov",
4239 "dec",
4240 NULL,
4243 int ast_build_timing(struct ast_timing *i, const char *info_in)
4245 char info_save[256];
4246 char *info;
4248 /* Check for empty just in case */
4249 if (ast_strlen_zero(info_in))
4250 return 0;
4251 /* make a copy just in case we were passed a static string */
4252 ast_copy_string(info_save, info_in, sizeof(info_save));
4253 info = info_save;
4254 /* Assume everything except time */
4255 i->monthmask = 0xfff; /* 12 bits */
4256 i->daymask = 0x7fffffffU; /* 31 bits */
4257 i->dowmask = 0x7f; /* 7 bits */
4258 /* on each call, use strsep() to move info to the next argument */
4259 get_timerange(i, strsep(&info, "|"));
4260 if (info)
4261 i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
4262 if (info)
4263 i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
4264 if (info)
4265 i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
4266 return 1;
4269 int ast_check_timing(const struct ast_timing *i)
4271 struct tm tm;
4272 time_t t = time(NULL);
4274 ast_localtime(&t, &tm, NULL);
4276 /* If it's not the right month, return */
4277 if (!(i->monthmask & (1 << tm.tm_mon)))
4278 return 0;
4280 /* If it's not that time of the month.... */
4281 /* Warning, tm_mday has range 1..31! */
4282 if (!(i->daymask & (1 << (tm.tm_mday-1))))
4283 return 0;
4285 /* If it's not the right day of the week */
4286 if (!(i->dowmask & (1 << tm.tm_wday)))
4287 return 0;
4289 /* Sanity check the hour just to be safe */
4290 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
4291 ast_log(LOG_WARNING, "Insane time...\n");
4292 return 0;
4295 /* Now the tough part, we calculate if it fits
4296 in the right time based on min/hour */
4297 if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
4298 return 0;
4300 /* If we got this far, then we're good */
4301 return 1;
4305 * errno values
4306 * ENOMEM - out of memory
4307 * EBUSY - can't lock
4308 * EEXIST - already included
4309 * EINVAL - there is no existence of context for inclusion
4311 int ast_context_add_include2(struct ast_context *con, const char *value,
4312 const char *registrar)
4314 struct ast_include *new_include;
4315 char *c;
4316 struct ast_include *i, *il = NULL; /* include, include_last */
4317 int length;
4318 char *p;
4320 length = sizeof(struct ast_include);
4321 length += 2 * (strlen(value) + 1);
4323 /* allocate new include structure ... */
4324 if (!(new_include = ast_calloc(1, length)))
4325 return -1;
4326 /* Fill in this structure. Use 'p' for assignments, as the fields
4327 * in the structure are 'const char *'
4329 p = new_include->stuff;
4330 new_include->name = p;
4331 strcpy(p, value);
4332 p += strlen(value) + 1;
4333 new_include->rname = p;
4334 strcpy(p, value);
4335 /* Strip off timing info, and process if it is there */
4336 if ( (c = strchr(p, '|')) ) {
4337 *c++ = '\0';
4338 new_include->hastime = ast_build_timing(&(new_include->timing), c);
4340 new_include->next = NULL;
4341 new_include->registrar = registrar;
4343 ast_mutex_lock(&con->lock);
4345 /* ... go to last include and check if context is already included too... */
4346 for (i = con->includes; i; i = i->next) {
4347 if (!strcasecmp(i->name, new_include->name)) {
4348 free(new_include);
4349 ast_mutex_unlock(&con->lock);
4350 errno = EEXIST;
4351 return -1;
4353 il = i;
4356 /* ... include new context into context list, unlock, return */
4357 if (il)
4358 il->next = new_include;
4359 else
4360 con->includes = new_include;
4361 if (option_verbose > 2)
4362 ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
4363 ast_mutex_unlock(&con->lock);
4365 return 0;
4369 * errno values
4370 * EBUSY - can't lock
4371 * ENOENT - no existence of context
4373 int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
4375 int ret = -1;
4376 struct ast_context *c = find_context_locked(context);
4378 if (c) { /* found, add switch to this context */
4379 ret = ast_context_add_switch2(c, sw, data, eval, registrar);
4380 ast_unlock_contexts();
4382 return ret;
4386 * errno values
4387 * ENOMEM - out of memory
4388 * EBUSY - can't lock
4389 * EEXIST - already included
4390 * EINVAL - there is no existence of context for inclusion
4392 int ast_context_add_switch2(struct ast_context *con, const char *value,
4393 const char *data, int eval, const char *registrar)
4395 struct ast_sw *new_sw;
4396 struct ast_sw *i;
4397 int length;
4398 char *p;
4400 length = sizeof(struct ast_sw);
4401 length += strlen(value) + 1;
4402 if (data)
4403 length += strlen(data);
4404 length++;
4406 /* allocate new sw structure ... */
4407 if (!(new_sw = ast_calloc(1, length)))
4408 return -1;
4409 /* ... fill in this structure ... */
4410 p = new_sw->stuff;
4411 new_sw->name = p;
4412 strcpy(new_sw->name, value);
4413 p += strlen(value) + 1;
4414 new_sw->data = p;
4415 if (data) {
4416 strcpy(new_sw->data, data);
4417 p += strlen(data) + 1;
4418 } else {
4419 strcpy(new_sw->data, "");
4420 p++;
4422 new_sw->eval = eval;
4423 new_sw->registrar = registrar;
4425 /* ... try to lock this context ... */
4426 ast_mutex_lock(&con->lock);
4428 /* ... go to last sw and check if context is already swd too... */
4429 AST_LIST_TRAVERSE(&con->alts, i, list) {
4430 if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
4431 free(new_sw);
4432 ast_mutex_unlock(&con->lock);
4433 errno = EEXIST;
4434 return -1;
4438 /* ... sw new context into context list, unlock, return */
4439 AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
4441 if (option_verbose > 2)
4442 ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
4444 ast_mutex_unlock(&con->lock);
4446 return 0;
4450 * EBUSY - can't lock
4451 * ENOENT - there is not context existence
4453 int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
4455 int ret = -1;
4456 struct ast_context *c = find_context_locked(context);
4458 if (c) {
4459 ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
4460 ast_unlock_contexts();
4462 return ret;
4465 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
4467 struct ast_ignorepat *ip, *ipl = NULL;
4469 ast_mutex_lock(&con->lock);
4471 for (ip = con->ignorepats; ip; ip = ip->next) {
4472 if (!strcmp(ip->pattern, ignorepat) &&
4473 (!registrar || (registrar == ip->registrar))) {
4474 if (ipl) {
4475 ipl->next = ip->next;
4476 free(ip);
4477 } else {
4478 con->ignorepats = ip->next;
4479 free(ip);
4481 ast_mutex_unlock(&con->lock);
4482 return 0;
4484 ipl = ip;
4487 ast_mutex_unlock(&con->lock);
4488 errno = EINVAL;
4489 return -1;
4493 * EBUSY - can't lock
4494 * ENOENT - there is no existence of context
4496 int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
4498 int ret = -1;
4499 struct ast_context *c = find_context_locked(context);
4501 if (c) {
4502 ret = ast_context_add_ignorepat2(c, value, registrar);
4503 ast_unlock_contexts();
4505 return ret;
4508 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
4510 struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
4511 int length;
4512 length = sizeof(struct ast_ignorepat);
4513 length += strlen(value) + 1;
4514 if (!(ignorepat = ast_calloc(1, length)))
4515 return -1;
4516 /* The cast to char * is because we need to write the initial value.
4517 * The field is not supposed to be modified otherwise
4519 strcpy((char *)ignorepat->pattern, value);
4520 ignorepat->next = NULL;
4521 ignorepat->registrar = registrar;
4522 ast_mutex_lock(&con->lock);
4523 for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
4524 ignorepatl = ignorepatc;
4525 if (!strcasecmp(ignorepatc->pattern, value)) {
4526 /* Already there */
4527 ast_mutex_unlock(&con->lock);
4528 errno = EEXIST;
4529 return -1;
4532 if (ignorepatl)
4533 ignorepatl->next = ignorepat;
4534 else
4535 con->ignorepats = ignorepat;
4536 ast_mutex_unlock(&con->lock);
4537 return 0;
4541 int ast_ignore_pattern(const char *context, const char *pattern)
4543 struct ast_context *con = ast_context_find(context);
4544 if (con) {
4545 struct ast_ignorepat *pat;
4546 for (pat = con->ignorepats; pat; pat = pat->next) {
4547 if (ast_extension_match(pat->pattern, pattern))
4548 return 1;
4552 return 0;
4556 * EBUSY - can't lock
4557 * ENOENT - no existence of context
4560 int ast_add_extension(const char *context, int replace, const char *extension,
4561 int priority, const char *label, const char *callerid,
4562 const char *application, void *data, void (*datad)(void *), const char *registrar)
4564 int ret = -1;
4565 struct ast_context *c = find_context_locked(context);
4567 if (c) {
4568 ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
4569 application, data, datad, registrar);
4570 ast_unlock_contexts();
4572 return ret;
4575 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4577 if (!chan)
4578 return -1;
4580 ast_channel_lock(chan);
4582 if (!ast_strlen_zero(context))
4583 ast_copy_string(chan->context, context, sizeof(chan->context));
4584 if (!ast_strlen_zero(exten))
4585 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
4586 if (priority > -1) {
4587 chan->priority = priority;
4588 /* see flag description in channel.h for explanation */
4589 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
4590 chan->priority--;
4593 ast_channel_unlock(chan);
4595 return 0;
4598 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4600 int res = 0;
4602 ast_channel_lock(chan);
4604 if (chan->pbx) { /* This channel is currently in the PBX */
4605 ast_explicit_goto(chan, context, exten, priority);
4606 ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
4607 } else {
4608 /* In order to do it when the channel doesn't really exist within
4609 the PBX, we have to make a new channel, masquerade, and start the PBX
4610 at the new location */
4611 struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
4612 if (chan->cdr) {
4613 ast_cdr_discard(tmpchan->cdr);
4614 tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
4616 if (!tmpchan)
4617 res = -1;
4618 else {
4619 /* Make formats okay */
4620 tmpchan->readformat = chan->readformat;
4621 tmpchan->writeformat = chan->writeformat;
4622 /* Setup proper location */
4623 ast_explicit_goto(tmpchan,
4624 S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
4626 /* Masquerade into temp channel */
4627 ast_channel_masquerade(tmpchan, chan);
4629 /* Grab the locks and get going */
4630 ast_channel_lock(tmpchan);
4631 ast_do_masquerade(tmpchan);
4632 ast_channel_unlock(tmpchan);
4633 /* Start the PBX going on our stolen channel */
4634 if (ast_pbx_start(tmpchan)) {
4635 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
4636 ast_hangup(tmpchan);
4637 res = -1;
4641 ast_channel_unlock(chan);
4642 return res;
4645 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
4647 struct ast_channel *chan;
4648 int res = -1;
4650 chan = ast_get_channel_by_name_locked(channame);
4651 if (chan) {
4652 res = ast_async_goto(chan, context, exten, priority);
4653 ast_channel_unlock(chan);
4655 return res;
4658 /*! \brief copy a string skipping whitespace */
4659 static int ext_strncpy(char *dst, const char *src, int len)
4661 int count=0;
4663 while (*src && (count < len - 1)) {
4664 switch(*src) {
4665 case ' ':
4666 /* otherwise exten => [a-b],1,... doesn't work */
4667 /* case '-': */
4668 /* Ignore */
4669 break;
4670 default:
4671 *dst = *src;
4672 dst++;
4674 src++;
4675 count++;
4677 *dst = '\0';
4679 return count;
4682 /*! \brief add the extension in the priority chain.
4683 * returns 0 on success, -1 on failure
4685 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
4686 struct ast_exten *el, struct ast_exten *e, int replace)
4688 struct ast_exten *ep;
4690 for (ep = NULL; e ; ep = e, e = e->peer) {
4691 if (e->priority >= tmp->priority)
4692 break;
4694 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
4695 ep->peer = tmp;
4696 return 0; /* success */
4698 if (e->priority == tmp->priority) {
4699 /* Can't have something exactly the same. Is this a
4700 replacement? If so, replace, otherwise, bonk. */
4701 if (!replace) {
4702 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
4703 if (tmp->datad)
4704 tmp->datad(tmp->data);
4705 free(tmp);
4706 return -1;
4708 /* we are replacing e, so copy the link fields and then update
4709 * whoever pointed to e to point to us
4711 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
4712 tmp->peer = e->peer; /* always meaningful */
4713 if (ep) /* We're in the peer list, just insert ourselves */
4714 ep->peer = tmp;
4715 else if (el) /* We're the first extension. Take over e's functions */
4716 el->next = tmp;
4717 else /* We're the very first extension. */
4718 con->root = tmp;
4719 if (tmp->priority == PRIORITY_HINT)
4720 ast_change_hint(e,tmp);
4721 /* Destroy the old one */
4722 if (e->datad)
4723 e->datad(e->data);
4724 free(e);
4725 } else { /* Slip ourselves in just before e */
4726 tmp->peer = e;
4727 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
4728 if (ep) /* Easy enough, we're just in the peer list */
4729 ep->peer = tmp;
4730 else { /* we are the first in some peer list, so link in the ext list */
4731 if (el)
4732 el->next = tmp; /* in the middle... */
4733 else
4734 con->root = tmp; /* ... or at the head */
4735 e->next = NULL; /* e is no more at the head, so e->next must be reset */
4737 /* And immediately return success. */
4738 if (tmp->priority == PRIORITY_HINT)
4739 ast_add_hint(tmp);
4741 return 0;
4744 /*! \brief
4745 * Main interface to add extensions to the list for out context.
4747 * We sort extensions in order of matching preference, so that we can
4748 * stop the search as soon as we find a suitable match.
4749 * This ordering also takes care of wildcards such as '.' (meaning
4750 * "one or more of any character") and '!' (which is 'earlymatch',
4751 * meaning "zero or more of any character" but also impacts the
4752 * return value from CANMATCH and EARLYMATCH.
4754 * The extension match rules defined in the devmeeting 2006.05.05 are
4755 * quite simple: WE SELECT THE LONGEST MATCH.
4756 * In detail, "longest" means the number of matched characters in
4757 * the extension. In case of ties (e.g. _XXX and 333) in the length
4758 * of a pattern, we give priority to entries with the smallest cardinality
4759 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
4760 * while the latter has 7, etc.
4761 * In case of same cardinality, the first element in the range counts.
4762 * If we still have a tie, any final '!' will make this as a possibly
4763 * less specific pattern.
4765 * EBUSY - can't lock
4766 * EEXIST - extension with the same priority exist and no replace is set
4769 int ast_add_extension2(struct ast_context *con,
4770 int replace, const char *extension, int priority, const char *label, const char *callerid,
4771 const char *application, void *data, void (*datad)(void *),
4772 const char *registrar)
4775 * Sort extensions (or patterns) according to the rules indicated above.
4776 * These are implemented by the function ext_cmp()).
4777 * All priorities for the same ext/pattern/cid are kept in a list,
4778 * using the 'peer' field as a link field..
4780 struct ast_exten *tmp, *e, *el = NULL;
4781 int res;
4782 int length;
4783 char *p;
4784 char expand_buf[VAR_BUF_SIZE] = { 0, };
4786 /* if we are adding a hint, and there are global variables, and the hint
4787 contains variable references, then expand them
4789 ast_mutex_lock(&globalslock);
4790 if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
4791 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
4792 application = expand_buf;
4794 ast_mutex_unlock(&globalslock);
4796 length = sizeof(struct ast_exten);
4797 length += strlen(extension) + 1;
4798 length += strlen(application) + 1;
4799 if (label)
4800 length += strlen(label) + 1;
4801 if (callerid)
4802 length += strlen(callerid) + 1;
4803 else
4804 length ++; /* just the '\0' */
4806 /* Be optimistic: Build the extension structure first */
4807 if (!(tmp = ast_calloc(1, length)))
4808 return -1;
4810 /* use p as dst in assignments, as the fields are const char * */
4811 p = tmp->stuff;
4812 if (label) {
4813 tmp->label = p;
4814 strcpy(p, label);
4815 p += strlen(label) + 1;
4817 tmp->exten = p;
4818 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
4819 tmp->priority = priority;
4820 tmp->cidmatch = p; /* but use p for assignments below */
4821 if (callerid) {
4822 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
4823 tmp->matchcid = 1;
4824 } else {
4825 *p++ = '\0';
4826 tmp->matchcid = 0;
4828 tmp->app = p;
4829 strcpy(p, application);
4830 tmp->parent = con;
4831 tmp->data = data;
4832 tmp->datad = datad;
4833 tmp->registrar = registrar;
4835 ast_mutex_lock(&con->lock);
4836 res = 0; /* some compilers will think it is uninitialized otherwise */
4837 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
4838 res = ext_cmp(e->exten, extension);
4839 if (res == 0) { /* extension match, now look at cidmatch */
4840 if (!e->matchcid && !tmp->matchcid)
4841 res = 0;
4842 else if (tmp->matchcid && !e->matchcid)
4843 res = 1;
4844 else if (e->matchcid && !tmp->matchcid)
4845 res = -1;
4846 else
4847 res = strcasecmp(e->cidmatch, tmp->cidmatch);
4849 if (res >= 0)
4850 break;
4852 if (e && res == 0) { /* exact match, insert in the pri chain */
4853 res = add_pri(con, tmp, el, e, replace);
4854 ast_mutex_unlock(&con->lock);
4855 if (res < 0) {
4856 errno = EEXIST; /* XXX do we care ? */
4857 return 0; /* XXX should we return -1 maybe ? */
4859 } else {
4861 * not an exact match, this is the first entry with this pattern,
4862 * so insert in the main list right before 'e' (if any)
4864 tmp->next = e;
4865 if (el)
4866 el->next = tmp;
4867 else
4868 con->root = tmp;
4869 ast_mutex_unlock(&con->lock);
4870 if (tmp->priority == PRIORITY_HINT)
4871 ast_add_hint(tmp);
4873 if (option_debug) {
4874 if (tmp->matchcid) {
4875 if (option_debug)
4876 ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
4877 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4878 } else {
4879 if (option_debug)
4880 ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
4881 tmp->exten, tmp->priority, con->name);
4884 if (option_verbose > 2) {
4885 if (tmp->matchcid) {
4886 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
4887 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4888 } else {
4889 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
4890 tmp->exten, tmp->priority, con->name);
4893 return 0;
4896 struct async_stat {
4897 pthread_t p;
4898 struct ast_channel *chan;
4899 char context[AST_MAX_CONTEXT];
4900 char exten[AST_MAX_EXTENSION];
4901 int priority;
4902 int timeout;
4903 char app[AST_MAX_EXTENSION];
4904 char appdata[1024];
4907 static void *async_wait(void *data)
4909 struct async_stat *as = data;
4910 struct ast_channel *chan = as->chan;
4911 int timeout = as->timeout;
4912 int res;
4913 struct ast_frame *f;
4914 struct ast_app *app;
4916 while (timeout && (chan->_state != AST_STATE_UP)) {
4917 res = ast_waitfor(chan, timeout);
4918 if (res < 1)
4919 break;
4920 if (timeout > -1)
4921 timeout = res;
4922 f = ast_read(chan);
4923 if (!f)
4924 break;
4925 if (f->frametype == AST_FRAME_CONTROL) {
4926 if ((f->subclass == AST_CONTROL_BUSY) ||
4927 (f->subclass == AST_CONTROL_CONGESTION) ) {
4928 ast_frfree(f);
4929 break;
4932 ast_frfree(f);
4934 if (chan->_state == AST_STATE_UP) {
4935 if (!ast_strlen_zero(as->app)) {
4936 app = pbx_findapp(as->app);
4937 if (app) {
4938 if (option_verbose > 2)
4939 ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
4940 pbx_exec(chan, app, as->appdata);
4941 } else
4942 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
4943 } else {
4944 if (!ast_strlen_zero(as->context))
4945 ast_copy_string(chan->context, as->context, sizeof(chan->context));
4946 if (!ast_strlen_zero(as->exten))
4947 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
4948 if (as->priority > 0)
4949 chan->priority = as->priority;
4950 /* Run the PBX */
4951 if (ast_pbx_run(chan)) {
4952 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
4953 } else {
4954 /* PBX will have taken care of this */
4955 chan = NULL;
4959 free(as);
4960 if (chan)
4961 ast_hangup(chan);
4962 return NULL;
4965 /*! Function to post an empty cdr after a spool call fails.
4967 * This function posts an empty cdr for a failed spool call
4970 static int ast_pbx_outgoing_cdr_failed(void)
4972 /* allocate a channel */
4973 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
4975 if (!chan)
4976 return -1; /* failure */
4978 if (!chan->cdr) {
4979 /* allocation of the cdr failed */
4980 ast_channel_free(chan); /* free the channel */
4981 return -1; /* return failure */
4984 /* allocation of the cdr was successful */
4985 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
4986 ast_cdr_start(chan->cdr); /* record the start and stop time */
4987 ast_cdr_end(chan->cdr);
4988 ast_cdr_failed(chan->cdr); /* set the status to failed */
4989 ast_cdr_detach(chan->cdr); /* post and free the record */
4990 ast_channel_free(chan); /* free the channel */
4992 return 0; /* success */
4995 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)
4997 struct ast_channel *chan;
4998 struct async_stat *as;
4999 int res = -1, cdr_res = -1;
5000 struct outgoing_helper oh;
5001 pthread_attr_t attr;
5003 if (sync) {
5004 LOAD_OH(oh);
5005 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5006 if (channel) {
5007 *channel = chan;
5008 if (chan)
5009 ast_channel_lock(chan);
5011 if (chan) {
5012 if (chan->_state == AST_STATE_UP) {
5013 res = 0;
5014 if (option_verbose > 3)
5015 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5017 if (sync > 1) {
5018 if (channel)
5019 ast_channel_unlock(chan);
5020 if (ast_pbx_run(chan)) {
5021 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5022 if (channel)
5023 *channel = NULL;
5024 ast_hangup(chan);
5025 chan = NULL;
5026 res = -1;
5028 } else {
5029 if (ast_pbx_start(chan)) {
5030 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
5031 if (channel) {
5032 *channel = NULL;
5033 ast_channel_unlock(chan);
5035 ast_hangup(chan);
5036 res = -1;
5038 chan = NULL;
5040 } else {
5041 if (option_verbose > 3)
5042 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5044 if (chan->cdr) { /* update the cdr */
5045 /* here we update the status of the call, which sould be busy.
5046 * if that fails then we set the status to failed */
5047 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5048 ast_cdr_failed(chan->cdr);
5051 if (channel) {
5052 *channel = NULL;
5053 ast_channel_unlock(chan);
5055 ast_hangup(chan);
5056 chan = NULL;
5060 if (res < 0) { /* the call failed for some reason */
5061 if (*reason == 0) { /* if the call failed (not busy or no answer)
5062 * update the cdr with the failed message */
5063 cdr_res = ast_pbx_outgoing_cdr_failed();
5064 if (cdr_res != 0) {
5065 res = cdr_res;
5066 goto outgoing_exten_cleanup;
5070 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
5071 /* check if "failed" exists */
5072 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
5073 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
5074 if (chan) {
5075 char failed_reason[4] = "";
5076 if (!ast_strlen_zero(context))
5077 ast_copy_string(chan->context, context, sizeof(chan->context));
5078 set_ext_pri(chan, "failed", 1);
5079 ast_set_variables(chan, vars);
5080 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
5081 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
5082 if (account)
5083 ast_cdr_setaccount(chan, account);
5084 if (ast_pbx_run(chan)) {
5085 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5086 ast_hangup(chan);
5088 chan = NULL;
5092 } else {
5093 if (!(as = ast_calloc(1, sizeof(*as)))) {
5094 res = -1;
5095 goto outgoing_exten_cleanup;
5097 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
5098 if (channel) {
5099 *channel = chan;
5100 if (chan)
5101 ast_channel_lock(chan);
5103 if (!chan) {
5104 free(as);
5105 res = -1;
5106 goto outgoing_exten_cleanup;
5108 as->chan = chan;
5109 ast_copy_string(as->context, context, sizeof(as->context));
5110 set_ext_pri(as->chan, exten, priority);
5111 as->timeout = timeout;
5112 ast_set_variables(chan, vars);
5113 if (account)
5114 ast_cdr_setaccount(chan, account);
5115 pthread_attr_init(&attr);
5116 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5117 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5118 ast_log(LOG_WARNING, "Failed to start async wait\n");
5119 free(as);
5120 if (channel) {
5121 *channel = NULL;
5122 ast_channel_unlock(chan);
5124 ast_hangup(chan);
5125 res = -1;
5126 pthread_attr_destroy(&attr);
5127 goto outgoing_exten_cleanup;
5129 pthread_attr_destroy(&attr);
5130 res = 0;
5132 outgoing_exten_cleanup:
5133 ast_variables_destroy(vars);
5134 return res;
5137 struct app_tmp {
5138 char app[256];
5139 char data[256];
5140 struct ast_channel *chan;
5141 pthread_t t;
5144 /*! \brief run the application and free the descriptor once done */
5145 static void *ast_pbx_run_app(void *data)
5147 struct app_tmp *tmp = data;
5148 struct ast_app *app;
5149 app = pbx_findapp(tmp->app);
5150 if (app) {
5151 if (option_verbose > 3)
5152 ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
5153 pbx_exec(tmp->chan, app, tmp->data);
5154 } else
5155 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
5156 ast_hangup(tmp->chan);
5157 free(tmp);
5158 return NULL;
5161 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)
5163 struct ast_channel *chan;
5164 struct app_tmp *tmp;
5165 int res = -1, cdr_res = -1;
5166 struct outgoing_helper oh;
5167 pthread_attr_t attr;
5169 memset(&oh, 0, sizeof(oh));
5170 oh.vars = vars;
5171 oh.account = account;
5173 if (locked_channel)
5174 *locked_channel = NULL;
5175 if (ast_strlen_zero(app)) {
5176 res = -1;
5177 goto outgoing_app_cleanup;
5179 if (sync) {
5180 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5181 if (chan) {
5182 if (!chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
5183 chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
5184 if(!chan->cdr) {
5185 /* allocation of the cdr failed */
5186 free(chan->pbx);
5187 res = -1;
5188 goto outgoing_app_cleanup;
5190 /* allocation of the cdr was successful */
5191 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
5192 ast_cdr_start(chan->cdr);
5194 ast_set_variables(chan, vars);
5195 if (account)
5196 ast_cdr_setaccount(chan, account);
5197 if (chan->_state == AST_STATE_UP) {
5198 res = 0;
5199 if (option_verbose > 3)
5200 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5201 tmp = ast_calloc(1, sizeof(*tmp));
5202 if (!tmp)
5203 res = -1;
5204 else {
5205 ast_copy_string(tmp->app, app, sizeof(tmp->app));
5206 if (appdata)
5207 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
5208 tmp->chan = chan;
5209 if (sync > 1) {
5210 if (locked_channel)
5211 ast_channel_unlock(chan);
5212 ast_pbx_run_app(tmp);
5213 } else {
5214 pthread_attr_init(&attr);
5215 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5216 if (locked_channel)
5217 ast_channel_lock(chan);
5218 if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
5219 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
5220 free(tmp);
5221 if (locked_channel)
5222 ast_channel_unlock(chan);
5223 ast_hangup(chan);
5224 res = -1;
5225 } else {
5226 if (locked_channel)
5227 *locked_channel = chan;
5229 pthread_attr_destroy(&attr);
5232 } else {
5233 if (option_verbose > 3)
5234 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5235 if (chan->cdr) { /* update the cdr */
5236 /* here we update the status of the call, which sould be busy.
5237 * if that fails then we set the status to failed */
5238 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5239 ast_cdr_failed(chan->cdr);
5241 ast_hangup(chan);
5245 if (res < 0) { /* the call failed for some reason */
5246 if (*reason == 0) { /* if the call failed (not busy or no answer)
5247 * update the cdr with the failed message */
5248 cdr_res = ast_pbx_outgoing_cdr_failed();
5249 if (cdr_res != 0) {
5250 res = cdr_res;
5251 goto outgoing_app_cleanup;
5256 } else {
5257 struct async_stat *as;
5258 if (!(as = ast_calloc(1, sizeof(*as)))) {
5259 res = -1;
5260 goto outgoing_app_cleanup;
5262 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5263 if (!chan) {
5264 free(as);
5265 res = -1;
5266 goto outgoing_app_cleanup;
5268 as->chan = chan;
5269 ast_copy_string(as->app, app, sizeof(as->app));
5270 if (appdata)
5271 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
5272 as->timeout = timeout;
5273 ast_set_variables(chan, vars);
5274 if (account)
5275 ast_cdr_setaccount(chan, account);
5276 /* Start a new thread, and get something handling this channel. */
5277 pthread_attr_init(&attr);
5278 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5279 if (locked_channel)
5280 ast_channel_lock(chan);
5281 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5282 ast_log(LOG_WARNING, "Failed to start async wait\n");
5283 free(as);
5284 if (locked_channel)
5285 ast_channel_unlock(chan);
5286 ast_hangup(chan);
5287 res = -1;
5288 pthread_attr_destroy(&attr);
5289 goto outgoing_app_cleanup;
5290 } else {
5291 if (locked_channel)
5292 *locked_channel = chan;
5294 pthread_attr_destroy(&attr);
5295 res = 0;
5297 outgoing_app_cleanup:
5298 ast_variables_destroy(vars);
5299 return res;
5302 void __ast_context_destroy(struct ast_context *con, const char *registrar)
5304 struct ast_context *tmp, *tmpl=NULL;
5305 struct ast_include *tmpi;
5306 struct ast_sw *sw;
5307 struct ast_exten *e, *el, *en;
5308 struct ast_ignorepat *ipi;
5310 for (tmp = contexts; tmp; ) {
5311 struct ast_context *next; /* next starting point */
5312 for (; tmp; tmpl = tmp, tmp = tmp->next) {
5313 if (option_debug)
5314 ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
5315 if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
5316 (!con || !strcasecmp(tmp->name, con->name)) )
5317 break; /* found it */
5319 if (!tmp) /* not found, we are done */
5320 break;
5321 ast_mutex_lock(&tmp->lock);
5322 if (option_debug)
5323 ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
5324 next = tmp->next;
5325 if (tmpl)
5326 tmpl->next = next;
5327 else
5328 contexts = next;
5329 /* Okay, now we're safe to let it go -- in a sense, we were
5330 ready to let it go as soon as we locked it. */
5331 ast_mutex_unlock(&tmp->lock);
5332 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
5333 struct ast_include *tmpil = tmpi;
5334 tmpi = tmpi->next;
5335 free(tmpil);
5337 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
5338 struct ast_ignorepat *ipl = ipi;
5339 ipi = ipi->next;
5340 free(ipl);
5342 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
5343 free(sw);
5344 for (e = tmp->root; e;) {
5345 for (en = e->peer; en;) {
5346 el = en;
5347 en = en->peer;
5348 destroy_exten(el);
5350 el = e;
5351 e = e->next;
5352 destroy_exten(el);
5354 ast_mutex_destroy(&tmp->lock);
5355 free(tmp);
5356 /* if we have a specific match, we are done, otherwise continue */
5357 tmp = con ? NULL : next;
5361 void ast_context_destroy(struct ast_context *con, const char *registrar)
5363 ast_wrlock_contexts();
5364 __ast_context_destroy(con,registrar);
5365 ast_unlock_contexts();
5368 static void wait_for_hangup(struct ast_channel *chan, void *data)
5370 int res;
5371 struct ast_frame *f;
5372 int waittime;
5374 if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
5375 waittime = -1;
5376 if (waittime > -1) {
5377 ast_safe_sleep(chan, waittime * 1000);
5378 } else do {
5379 res = ast_waitfor(chan, -1);
5380 if (res < 0)
5381 return;
5382 f = ast_read(chan);
5383 if (f)
5384 ast_frfree(f);
5385 } while(f);
5389 * \ingroup applications
5391 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
5393 ast_indicate(chan, AST_CONTROL_PROGRESS);
5394 return 0;
5398 * \ingroup applications
5400 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
5402 ast_indicate(chan, AST_CONTROL_RINGING);
5403 return 0;
5407 * \ingroup applications
5409 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
5411 ast_indicate(chan, AST_CONTROL_BUSY);
5412 /* Don't change state of an UP channel, just indicate
5413 busy in audio */
5414 if (chan->_state != AST_STATE_UP)
5415 ast_setstate(chan, AST_STATE_BUSY);
5416 wait_for_hangup(chan, data);
5417 return -1;
5421 * \ingroup applications
5423 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
5425 ast_indicate(chan, AST_CONTROL_CONGESTION);
5426 /* Don't change state of an UP channel, just indicate
5427 congestion in audio */
5428 if (chan->_state != AST_STATE_UP)
5429 ast_setstate(chan, AST_STATE_BUSY);
5430 wait_for_hangup(chan, data);
5431 return -1;
5435 * \ingroup applications
5437 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
5439 int delay = 0;
5440 int res;
5442 if (chan->_state == AST_STATE_UP)
5443 delay = 0;
5444 else if (!ast_strlen_zero(data))
5445 delay = atoi(data);
5447 res = ast_answer(chan);
5448 if (res)
5449 return res;
5451 if (delay)
5452 res = ast_safe_sleep(chan, delay);
5454 return res;
5457 AST_APP_OPTIONS(resetcdr_opts, {
5458 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
5459 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
5460 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
5464 * \ingroup applications
5466 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
5468 char *args;
5469 struct ast_flags flags = { 0 };
5471 if (!ast_strlen_zero(data)) {
5472 args = ast_strdupa(data);
5473 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
5476 ast_cdr_reset(chan->cdr, &flags);
5478 return 0;
5482 * \ingroup applications
5484 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
5486 /* Copy the AMA Flags as specified */
5487 ast_cdr_setamaflags(chan, data ? data : "");
5488 return 0;
5492 * \ingroup applications
5494 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
5496 if (!ast_strlen_zero(data)) {
5497 int cause;
5498 char *endptr;
5500 if ((cause = ast_str2cause(data)) > -1) {
5501 chan->hangupcause = cause;
5502 return -1;
5505 cause = strtol((const char *) data, &endptr, 10);
5506 if (cause != 0 || (data != endptr)) {
5507 chan->hangupcause = cause;
5508 return -1;
5511 ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
5514 if (!chan->hangupcause) {
5515 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
5518 return -1;
5522 * \ingroup applications
5524 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
5526 int res=0;
5527 char *s, *ts;
5528 struct ast_timing timing;
5530 if (ast_strlen_zero(data)) {
5531 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
5532 return -1;
5535 ts = s = ast_strdupa(data);
5537 /* Separate the Goto path */
5538 strsep(&ts,"?");
5540 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
5541 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
5542 res = pbx_builtin_goto(chan, ts);
5544 return res;
5548 * \ingroup applications
5550 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
5552 char *s, *appname;
5553 struct ast_timing timing;
5554 struct ast_app *app;
5555 static const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
5557 if (ast_strlen_zero(data)) {
5558 ast_log(LOG_WARNING, "%s\n", usage);
5559 return -1;
5562 appname = ast_strdupa(data);
5564 s = strsep(&appname,"?"); /* Separate the timerange and application name/data */
5565 if (!appname) { /* missing application */
5566 ast_log(LOG_WARNING, "%s\n", usage);
5567 return -1;
5570 if (!ast_build_timing(&timing, s)) {
5571 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
5572 return -1;
5575 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
5576 return 0;
5578 /* now split appname|appargs */
5579 if ((s = strchr(appname, '|')))
5580 *s++ = '\0';
5582 if ((app = pbx_findapp(appname))) {
5583 return pbx_exec(chan, app, S_OR(s, ""));
5584 } else {
5585 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
5586 return -1;
5591 * \ingroup applications
5593 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
5595 double s;
5596 int ms;
5598 /* Wait for "n" seconds */
5599 if (data && (s = atof(data)) > 0) {
5600 ms = s * 1000.0;
5601 return ast_safe_sleep(chan, ms);
5603 return 0;
5607 * \ingroup applications
5609 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
5611 int ms, res;
5612 double sec;
5613 struct ast_flags flags = {0};
5614 char *opts[1] = { NULL };
5615 char *parse;
5616 AST_DECLARE_APP_ARGS(args,
5617 AST_APP_ARG(timeout);
5618 AST_APP_ARG(options);
5621 if (!ast_strlen_zero(data)) {
5622 parse = ast_strdupa(data);
5623 AST_STANDARD_APP_ARGS(args, parse);
5624 } else
5625 memset(&args, 0, sizeof(args));
5627 if (args.options)
5628 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
5630 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
5631 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
5632 } else if (ast_test_flag(&flags, WAITEXTEN_MOH))
5633 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
5635 /* Wait for "n" seconds */
5636 if (args.timeout && (sec = atof(args.timeout)) > 0.0)
5637 ms = 1000 * sec;
5638 else if (chan->pbx)
5639 ms = chan->pbx->rtimeout * 1000;
5640 else
5641 ms = 10000;
5642 res = ast_waitfordigit(chan, ms);
5643 if (!res) {
5644 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
5645 if (option_verbose > 2)
5646 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
5647 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
5648 if (option_verbose > 2)
5649 ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
5650 res = -1;
5651 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
5652 if (option_verbose > 2)
5653 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
5654 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
5655 } else {
5656 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
5657 res = -1;
5661 if (ast_test_flag(&flags, WAITEXTEN_MOH))
5662 ast_indicate(chan, AST_CONTROL_UNHOLD);
5664 return res;
5668 * \ingroup applications
5670 static int pbx_builtin_background(struct ast_channel *chan, void *data)
5672 int res = 0;
5673 struct ast_flags flags = {0};
5674 char *parse;
5675 AST_DECLARE_APP_ARGS(args,
5676 AST_APP_ARG(filename);
5677 AST_APP_ARG(options);
5678 AST_APP_ARG(lang);
5679 AST_APP_ARG(context);
5682 if (ast_strlen_zero(data)) {
5683 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
5684 return -1;
5687 parse = ast_strdupa(data);
5689 AST_STANDARD_APP_ARGS(args, parse);
5691 if (ast_strlen_zero(args.lang))
5692 args.lang = (char *)chan->language; /* XXX this is const */
5694 if (ast_strlen_zero(args.context))
5695 args.context = chan->context;
5697 if (args.options) {
5698 if (!strcasecmp(args.options, "skip"))
5699 flags.flags = BACKGROUND_SKIP;
5700 else if (!strcasecmp(args.options, "noanswer"))
5701 flags.flags = BACKGROUND_NOANSWER;
5702 else
5703 ast_app_parse_options(background_opts, &flags, NULL, args.options);
5706 /* Answer if need be */
5707 if (chan->_state != AST_STATE_UP) {
5708 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
5709 return 0;
5710 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
5711 res = ast_answer(chan);
5715 if (!res) {
5716 char *back = args.filename;
5717 char *front;
5718 ast_stopstream(chan); /* Stop anything playing */
5719 /* Stream the list of files */
5720 while (!res && (front = strsep(&back, "&")) ) {
5721 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
5722 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
5723 res = 0;
5724 break;
5726 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
5727 res = ast_waitstream(chan, "");
5728 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
5729 res = ast_waitstream_exten(chan, args.context);
5730 } else {
5731 res = ast_waitstream(chan, AST_DIGIT_ANY);
5733 ast_stopstream(chan);
5736 if (args.context != chan->context && res) {
5737 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
5738 ast_copy_string(chan->context, args.context, sizeof(chan->context));
5739 chan->priority = 0;
5740 res = 0;
5742 return res;
5745 /*! Goto
5746 * \ingroup applications
5748 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
5750 int res = ast_parseable_goto(chan, data);
5751 if (!res && (option_verbose > 2))
5752 ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
5753 return res;
5757 int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
5759 struct ast_var_t *variables;
5760 const char *var, *val;
5761 int total = 0;
5763 if (!chan)
5764 return 0;
5766 memset(buf, 0, size);
5768 ast_channel_lock(chan);
5770 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
5771 if ((var=ast_var_name(variables)) && (val=ast_var_value(variables))
5772 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
5774 if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
5775 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
5776 break;
5777 } else
5778 total++;
5779 } else
5780 break;
5783 ast_channel_unlock(chan);
5785 return total;
5788 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
5790 struct ast_var_t *variables;
5791 const char *ret = NULL;
5792 int i;
5793 struct varshead *places[2] = { NULL, &globals };
5795 if (!name)
5796 return NULL;
5798 if (chan) {
5799 ast_channel_lock(chan);
5800 places[0] = &chan->varshead;
5803 for (i = 0; i < 2; i++) {
5804 if (!places[i])
5805 continue;
5806 if (places[i] == &globals)
5807 ast_mutex_lock(&globalslock);
5808 AST_LIST_TRAVERSE(places[i], variables, entries) {
5809 if (!strcmp(name, ast_var_name(variables))) {
5810 ret = ast_var_value(variables);
5811 break;
5814 if (places[i] == &globals)
5815 ast_mutex_unlock(&globalslock);
5816 if (ret)
5817 break;
5820 if (chan)
5821 ast_channel_unlock(chan);
5823 return ret;
5826 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
5828 struct ast_var_t *newvariable;
5829 struct varshead *headp;
5831 if (name[strlen(name)-1] == ')') {
5832 char *function = ast_strdupa(name);
5834 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
5835 ast_func_write(chan, function, value);
5836 return;
5839 if (chan) {
5840 ast_channel_lock(chan);
5841 headp = &chan->varshead;
5842 } else {
5843 ast_mutex_lock(&globalslock);
5844 headp = &globals;
5847 if (value) {
5848 if ((option_verbose > 1) && (headp == &globals))
5849 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5850 newvariable = ast_var_assign(name, value);
5851 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5854 if (chan)
5855 ast_channel_unlock(chan);
5856 else
5857 ast_mutex_unlock(&globalslock);
5860 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
5862 struct ast_var_t *newvariable;
5863 struct varshead *headp;
5864 const char *nametail = name;
5866 if (name[strlen(name)-1] == ')') {
5867 char *function = ast_strdupa(name);
5869 ast_func_write(chan, function, value);
5870 return;
5873 if (chan) {
5874 ast_channel_lock(chan);
5875 headp = &chan->varshead;
5876 } else {
5877 ast_mutex_lock(&globalslock);
5878 headp = &globals;
5881 /* For comparison purposes, we have to strip leading underscores */
5882 if (*nametail == '_') {
5883 nametail++;
5884 if (*nametail == '_')
5885 nametail++;
5888 AST_LIST_TRAVERSE (headp, newvariable, entries) {
5889 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
5890 /* there is already such a variable, delete it */
5891 AST_LIST_REMOVE(headp, newvariable, entries);
5892 ast_var_delete(newvariable);
5893 break;
5897 if (value) {
5898 if ((option_verbose > 1) && (headp == &globals))
5899 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5900 newvariable = ast_var_assign(name, value);
5901 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5904 if (chan)
5905 ast_channel_unlock(chan);
5906 else
5907 ast_mutex_unlock(&globalslock);
5910 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
5912 char *name, *value, *mydata;
5913 int argc;
5914 char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
5915 int global = 0;
5916 int x;
5918 if (ast_strlen_zero(data)) {
5919 ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
5920 return 0;
5923 mydata = ast_strdupa(data);
5924 argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
5926 /* check for a trailing flags argument */
5927 if ((argc > 1) && !strchr(argv[argc-1], '=')) {
5928 argc--;
5929 if (strchr(argv[argc], 'g')) {
5930 ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
5931 global = 1;
5935 if (argc > 1)
5936 ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
5938 for (x = 0; x < argc; x++) {
5939 name = argv[x];
5940 if ((value = strchr(name, '='))) {
5941 *value++ = '\0';
5942 pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
5943 } else
5944 ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
5947 return(0);
5950 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
5952 char *name;
5953 char *value;
5954 char *channel;
5955 char tmp[VAR_BUF_SIZE]="";
5957 if (ast_strlen_zero(data)) {
5958 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5959 return 0;
5962 value = ast_strdupa(data);
5963 name = strsep(&value,"=");
5964 channel = strsep(&value,"|");
5965 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
5966 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
5967 if (chan2) {
5968 char *s = alloca(strlen(value) + 4);
5969 if (s) {
5970 sprintf(s, "${%s}", value);
5971 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
5973 ast_channel_unlock(chan2);
5975 pbx_builtin_setvar_helper(chan, name, tmp);
5978 return(0);
5981 /*! \todo XXX overwrites data ? */
5982 static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
5984 char *name;
5985 char *stringp = data;
5986 static int dep_warning = 0;
5988 if (ast_strlen_zero(data)) {
5989 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5990 return 0;
5993 name = strsep(&stringp, "=");
5995 if (!dep_warning) {
5996 dep_warning = 1;
5997 ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
6000 /*! \todo XXX watch out, leading whitespace ? */
6001 pbx_builtin_setvar_helper(NULL, name, stringp);
6003 return(0);
6006 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
6008 return 0;
6011 void pbx_builtin_clear_globals(void)
6013 struct ast_var_t *vardata;
6015 ast_mutex_lock(&globalslock);
6016 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
6017 ast_var_delete(vardata);
6018 ast_mutex_unlock(&globalslock);
6021 int pbx_checkcondition(const char *condition)
6023 if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
6024 return 0;
6025 else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
6026 return atoi(condition);
6027 else /* Strings are true */
6028 return 1;
6031 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
6033 char *condition, *branch1, *branch2, *branch;
6034 int rc;
6035 char *stringp;
6037 if (ast_strlen_zero(data)) {
6038 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
6039 return 0;
6042 stringp = ast_strdupa(data);
6043 condition = strsep(&stringp,"?");
6044 branch1 = strsep(&stringp,":");
6045 branch2 = strsep(&stringp,"");
6046 branch = pbx_checkcondition(condition) ? branch1 : branch2;
6048 if (ast_strlen_zero(branch)) {
6049 if (option_debug)
6050 ast_log(LOG_DEBUG, "Not taking any branch\n");
6051 return 0;
6054 rc = pbx_builtin_goto(chan, branch);
6056 return rc;
6059 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
6061 char tmp[256];
6062 char *number = tmp;
6063 char *options;
6065 if (ast_strlen_zero(data)) {
6066 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
6067 return -1;
6069 ast_copy_string(tmp, data, sizeof(tmp));
6070 strsep(&number, "|");
6071 options = strsep(&number, "|");
6072 if (options) {
6073 if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
6074 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
6075 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
6076 return -1;
6080 if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
6081 ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
6084 return 0;
6087 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
6089 int res = 0;
6091 if (data)
6092 res = ast_say_digit_str(chan, data, "", chan->language);
6093 return res;
6096 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
6098 int res = 0;
6100 if (data)
6101 res = ast_say_character_str(chan, data, "", chan->language);
6102 return res;
6105 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
6107 int res = 0;
6109 if (data)
6110 res = ast_say_phonetic_str(chan, data, "", chan->language);
6111 return res;
6114 int load_pbx(void)
6116 int x;
6118 /* Initialize the PBX */
6119 if (option_verbose) {
6120 ast_verbose( "Asterisk PBX Core Initializing\n");
6121 ast_verbose( "Registering builtin applications:\n");
6123 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
6125 /* Register builtin applications */
6126 for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
6127 if (option_verbose)
6128 ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
6129 if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
6130 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
6131 return -1;
6134 return 0;
6138 * Lock context list functions ...
6140 int ast_lock_contexts()
6142 return ast_rwlock_wrlock(&conlock);
6145 int ast_rdlock_contexts(void)
6147 return ast_rwlock_rdlock(&conlock);
6150 int ast_wrlock_contexts(void)
6152 return ast_rwlock_wrlock(&conlock);
6155 int ast_unlock_contexts()
6157 return ast_rwlock_unlock(&conlock);
6161 * Lock context ...
6163 int ast_lock_context(struct ast_context *con)
6165 return ast_mutex_lock(&con->lock);
6168 int ast_unlock_context(struct ast_context *con)
6170 return ast_mutex_unlock(&con->lock);
6174 * Name functions ...
6176 const char *ast_get_context_name(struct ast_context *con)
6178 return con ? con->name : NULL;
6181 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
6183 return exten ? exten->parent : NULL;
6186 const char *ast_get_extension_name(struct ast_exten *exten)
6188 return exten ? exten->exten : NULL;
6191 const char *ast_get_extension_label(struct ast_exten *exten)
6193 return exten ? exten->label : NULL;
6196 const char *ast_get_include_name(struct ast_include *inc)
6198 return inc ? inc->name : NULL;
6201 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
6203 return ip ? ip->pattern : NULL;
6206 int ast_get_extension_priority(struct ast_exten *exten)
6208 return exten ? exten->priority : -1;
6212 * Registrar info functions ...
6214 const char *ast_get_context_registrar(struct ast_context *c)
6216 return c ? c->registrar : NULL;
6219 const char *ast_get_extension_registrar(struct ast_exten *e)
6221 return e ? e->registrar : NULL;
6224 const char *ast_get_include_registrar(struct ast_include *i)
6226 return i ? i->registrar : NULL;
6229 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
6231 return ip ? ip->registrar : NULL;
6234 int ast_get_extension_matchcid(struct ast_exten *e)
6236 return e ? e->matchcid : 0;
6239 const char *ast_get_extension_cidmatch(struct ast_exten *e)
6241 return e ? e->cidmatch : NULL;
6244 const char *ast_get_extension_app(struct ast_exten *e)
6246 return e ? e->app : NULL;
6249 void *ast_get_extension_app_data(struct ast_exten *e)
6251 return e ? e->data : NULL;
6254 const char *ast_get_switch_name(struct ast_sw *sw)
6256 return sw ? sw->name : NULL;
6259 const char *ast_get_switch_data(struct ast_sw *sw)
6261 return sw ? sw->data : NULL;
6264 const char *ast_get_switch_registrar(struct ast_sw *sw)
6266 return sw ? sw->registrar : NULL;
6270 * Walking functions ...
6272 struct ast_context *ast_walk_contexts(struct ast_context *con)
6274 return con ? con->next : contexts;
6277 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
6278 struct ast_exten *exten)
6280 if (!exten)
6281 return con ? con->root : NULL;
6282 else
6283 return exten->next;
6286 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
6287 struct ast_sw *sw)
6289 if (!sw)
6290 return con ? AST_LIST_FIRST(&con->alts) : NULL;
6291 else
6292 return AST_LIST_NEXT(sw, list);
6295 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
6296 struct ast_exten *priority)
6298 return priority ? priority->peer : exten;
6301 struct ast_include *ast_walk_context_includes(struct ast_context *con,
6302 struct ast_include *inc)
6304 if (!inc)
6305 return con ? con->includes : NULL;
6306 else
6307 return inc->next;
6310 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
6311 struct ast_ignorepat *ip)
6313 if (!ip)
6314 return con ? con->ignorepats : NULL;
6315 else
6316 return ip->next;
6319 int ast_context_verify_includes(struct ast_context *con)
6321 struct ast_include *inc = NULL;
6322 int res = 0;
6324 while ( (inc = ast_walk_context_includes(con, inc)) ) {
6325 if (ast_context_find(inc->rname))
6326 continue;
6328 res = -1;
6329 ast_log(LOG_WARNING, "Context '%s' tries includes nonexistent context '%s'\n",
6330 ast_get_context_name(con), inc->rname);
6331 break;
6334 return res;
6338 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
6340 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
6342 if (!chan)
6343 return -2;
6345 if (context == NULL)
6346 context = chan->context;
6347 if (exten == NULL)
6348 exten = chan->exten;
6350 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
6351 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
6352 return goto_func(chan, context, exten, priority);
6353 else
6354 return -3;
6357 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
6359 return __ast_goto_if_exists(chan, context, exten, priority, 0);
6362 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
6364 return __ast_goto_if_exists(chan, context, exten, priority, 1);
6367 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
6369 char *exten, *pri, *context;
6370 char *stringp;
6371 int ipri;
6372 int mode = 0;
6374 if (ast_strlen_zero(goto_string)) {
6375 ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
6376 return -1;
6378 stringp = ast_strdupa(goto_string);
6379 context = strsep(&stringp, "|"); /* guaranteed non-null */
6380 exten = strsep(&stringp, "|");
6381 pri = strsep(&stringp, "|");
6382 if (!exten) { /* Only a priority in this one */
6383 pri = context;
6384 exten = NULL;
6385 context = NULL;
6386 } else if (!pri) { /* Only an extension and priority in this one */
6387 pri = exten;
6388 exten = context;
6389 context = NULL;
6391 if (*pri == '+') {
6392 mode = 1;
6393 pri++;
6394 } else if (*pri == '-') {
6395 mode = -1;
6396 pri++;
6398 if (sscanf(pri, "%d", &ipri) != 1) {
6399 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
6400 pri, chan->cid.cid_num)) < 1) {
6401 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
6402 return -1;
6403 } else
6404 mode = 0;
6406 /* At this point we have a priority and maybe an extension and a context */
6408 if (mode)
6409 ipri = chan->priority + (ipri * mode);
6411 ast_explicit_goto(chan, context, exten, ipri);
6412 ast_cdr_update(chan);
6413 return 0;