document usage of 'transfer' configuration option for ISDN PRI switch-side transfers
[asterisk-bristuff.git] / main / pbx.c
blobe1a2d2e878eb71ea328f29e9c35ff2b2ab06ce91
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"
64 /*!
65 * \note I M P O R T A N T :
67 * The speed of extension handling will likely be among the most important
68 * aspects of this PBX. The switching scheme as it exists right now isn't
69 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
70 * of priorities, but a constant search time here would be great ;-)
74 #ifdef LOW_MEMORY
75 #define EXT_DATA_SIZE 256
76 #else
77 #define EXT_DATA_SIZE 8192
78 #endif
80 #define SWITCH_DATA_LENGTH 256
82 #define VAR_BUF_SIZE 4096
84 #define VAR_NORMAL 1
85 #define VAR_SOFTTRAN 2
86 #define VAR_HARDTRAN 3
88 #define BACKGROUND_SKIP (1 << 0)
89 #define BACKGROUND_NOANSWER (1 << 1)
90 #define BACKGROUND_MATCHEXTEN (1 << 2)
91 #define BACKGROUND_PLAYBACK (1 << 3)
93 AST_APP_OPTIONS(background_opts, {
94 AST_APP_OPTION('s', BACKGROUND_SKIP),
95 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
96 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
97 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
98 });
100 #define WAITEXTEN_MOH (1 << 0)
102 AST_APP_OPTIONS(waitexten_opts, {
103 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
106 struct ast_context;
109 \brief ast_exten: An extension
110 The dialplan is saved as a linked list with each context
111 having it's own linked list of extensions - one item per
112 priority.
114 struct ast_exten {
115 char *exten; /*!< Extension name */
116 int matchcid; /*!< Match caller id ? */
117 const char *cidmatch; /*!< Caller id to match for this extension */
118 int priority; /*!< Priority */
119 const char *label; /*!< Label */
120 struct ast_context *parent; /*!< The context this extension belongs to */
121 const char *app; /*!< Application to execute */
122 void *data; /*!< Data to use (arguments) */
123 void (*datad)(void *); /*!< Data destructor */
124 struct ast_exten *peer; /*!< Next higher priority with our extension */
125 const char *registrar; /*!< Registrar */
126 struct ast_exten *next; /*!< Extension with a greater ID */
127 char stuff[0];
130 /*! \brief ast_include: include= support in extensions.conf */
131 struct ast_include {
132 const char *name;
133 const char *rname; /*!< Context to include */
134 const char *registrar; /*!< Registrar */
135 int hastime; /*!< If time construct exists */
136 struct ast_timing timing; /*!< time construct */
137 struct ast_include *next; /*!< Link them together */
138 char stuff[0];
141 /*! \brief ast_sw: Switch statement in extensions.conf */
142 struct ast_sw {
143 char *name;
144 const char *registrar; /*!< Registrar */
145 char *data; /*!< Data load */
146 int eval;
147 AST_LIST_ENTRY(ast_sw) list;
148 char *tmpdata;
149 char stuff[0];
152 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
153 struct ast_ignorepat {
154 const char *registrar;
155 struct ast_ignorepat *next;
156 const char pattern[0];
159 /*! \brief ast_context: An extension context */
160 struct ast_context {
161 ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
162 struct ast_exten *root; /*!< The root of the list of extensions */
163 struct ast_context *next; /*!< Link them together */
164 struct ast_include *includes; /*!< Include other contexts */
165 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
166 const char *registrar; /*!< Registrar */
167 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
168 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
169 char name[0]; /*!< Name of the context */
173 /*! \brief ast_app: A registered application */
174 struct ast_app {
175 int (*execute)(struct ast_channel *chan, void *data);
176 const char *synopsis; /*!< Synopsis text for 'show applications' */
177 const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
178 AST_LIST_ENTRY(ast_app) list; /*!< Next app in list */
179 struct module *module; /*!< Module this app belongs to */
180 char name[0]; /*!< Name of the application */
183 /*! \brief ast_state_cb: An extension state notify register item */
184 struct ast_state_cb {
185 int id;
186 void *data;
187 ast_state_cb_type callback;
188 struct ast_state_cb *next;
191 /*! \brief Structure for dial plan hints
193 \note Hints are pointers from an extension in the dialplan to one or
194 more devices (tech/name) */
195 struct ast_hint {
196 struct ast_exten *exten; /*!< Extension */
197 int laststate; /*!< Last known state */
198 struct ast_state_cb *callbacks; /*!< Callback list for this extension */
199 AST_LIST_ENTRY(ast_hint) list; /*!< Pointer to next hint in list */
202 static const struct cfextension_states {
203 int extension_state;
204 const char * const text;
205 } extension_states[] = {
206 { AST_EXTENSION_NOT_INUSE, "Idle" },
207 { AST_EXTENSION_INUSE, "InUse" },
208 { AST_EXTENSION_BUSY, "Busy" },
209 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
210 { AST_EXTENSION_RINGING, "Ringing" },
211 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
212 { AST_EXTENSION_ONHOLD, "Hold" },
213 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
216 static int pbx_builtin_answer(struct ast_channel *, void *);
217 static int pbx_builtin_goto(struct ast_channel *, void *);
218 static int pbx_builtin_hangup(struct ast_channel *, void *);
219 static int pbx_builtin_background(struct ast_channel *, void *);
220 static int pbx_builtin_wait(struct ast_channel *, void *);
221 static int pbx_builtin_waitexten(struct ast_channel *, void *);
222 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
223 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
224 static int pbx_builtin_ringing(struct ast_channel *, void *);
225 static int pbx_builtin_progress(struct ast_channel *, void *);
226 static int pbx_builtin_congestion(struct ast_channel *, void *);
227 static int pbx_builtin_busy(struct ast_channel *, void *);
228 static int pbx_builtin_setglobalvar(struct ast_channel *, void *);
229 static int pbx_builtin_noop(struct ast_channel *, void *);
230 static int pbx_builtin_gotoif(struct ast_channel *, void *);
231 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
232 static int pbx_builtin_execiftime(struct ast_channel *, void *);
233 static int pbx_builtin_saynumber(struct ast_channel *, void *);
234 static int pbx_builtin_saydigits(struct ast_channel *, void *);
235 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
236 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
237 int pbx_builtin_setvar(struct ast_channel *, void *);
238 static int pbx_builtin_importvar(struct ast_channel *, void *);
240 AST_MUTEX_DEFINE_STATIC(globalslock);
241 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
243 static int autofallthrough = 1;
245 AST_MUTEX_DEFINE_STATIC(maxcalllock);
246 static int countcalls;
248 static AST_LIST_HEAD_STATIC(acf_root, ast_custom_function);
250 /*! \brief Declaration of builtin applications */
251 static struct pbx_builtin {
252 char name[AST_MAX_APP];
253 int (*execute)(struct ast_channel *chan, void *data);
254 char *synopsis;
255 char *description;
256 } builtins[] =
258 /* These applications are built into the PBX core and do not
259 need separate modules */
261 { "Answer", pbx_builtin_answer,
262 "Answer a channel if ringing",
263 " Answer([delay]): If the call has not been answered, this application will\n"
264 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
265 "Asterisk will wait this number of milliseconds before returning to\n"
266 "the dialplan after answering the call.\n"
269 { "BackGround", pbx_builtin_background,
270 "Play an audio file while waiting for digits of an extension to go to.",
271 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
272 "This application will play the given list of files (do not put extension)\n"
273 "while waiting for an extension to be dialed by the calling channel. To\n"
274 "continue waiting for digits after this application has finished playing\n"
275 "files, the WaitExten application should be used. The 'langoverride' option\n"
276 "explicitly specifies which language to attempt to use for the requested sound\n"
277 "files. If a 'context' is specified, this is the dialplan context that this\n"
278 "application will use when exiting to a dialed extension."
279 " If one of the requested sound files does not exist, call processing will be\n"
280 "terminated.\n"
281 " Options:\n"
282 " s - Causes the playback of the message to be skipped\n"
283 " if the channel is not in the 'up' state (i.e. it\n"
284 " hasn't been answered yet). If this happens, the\n"
285 " application will return immediately.\n"
286 " n - Don't answer the channel before playing the files.\n"
287 " m - Only break if a digit hit matches a one digit\n"
288 " extension in the destination context.\n"
291 { "Busy", pbx_builtin_busy,
292 "Indicate the Busy condition",
293 " Busy([timeout]): This application will indicate the busy condition to\n"
294 "the calling channel. If the optional timeout is specified, the calling channel\n"
295 "will be hung up after the specified number of seconds. Otherwise, this\n"
296 "application will wait until the calling channel hangs up.\n"
299 { "Congestion", pbx_builtin_congestion,
300 "Indicate the Congestion condition",
301 " Congestion([timeout]): This application will indicate the congestion\n"
302 "condition to the calling channel. If the optional timeout is specified, the\n"
303 "calling channel will be hung up after the specified number of seconds.\n"
304 "Otherwise, this application will wait until the calling channel hangs up.\n"
307 { "Goto", pbx_builtin_goto,
308 "Jump to a particular priority, extension, or context",
309 " Goto([[context|]extension|]priority): This application will set the current\n"
310 "context, extension, and priority in the channel structure. After it completes, the\n"
311 "pbx engine will continue dialplan execution at the specified location.\n"
312 "If no specific extension, or extension and context, are specified, then this\n"
313 "application will just set the specified priority of the current extension.\n"
314 " At least a priority is required as an argument, or the goto will return a -1,\n"
315 "and the channel and call will be terminated.\n"
316 " If the location that is put into the channel information is bogus, and asterisk cannot\n"
317 "find that location in the dialplan,\n"
318 "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
319 "extension in the current context. If that does not exist, it will try to execute the\n"
320 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
321 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
322 "What this means is that, for example, you specify a context that does not exist, then\n"
323 "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
326 { "GotoIf", pbx_builtin_gotoif,
327 "Conditional goto",
328 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
329 "context, extension, and priority in the channel structure based on the evaluation of\n"
330 "the given condition. After this application completes, the\n"
331 "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
332 "The channel will continue at\n"
333 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
334 "false. The labels are specified with the same syntax as used within the Goto\n"
335 "application. If the label chosen by the condition is omitted, no jump is\n"
336 "performed, and the execution passes to the next instruction.\n"
337 "If the target location is bogus, and does not exist, the execution engine will try \n"
338 "to find and execute the code in the 'i' (invalid)\n"
339 "extension in the current context. If that does not exist, it will try to execute the\n"
340 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
341 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
342 "Remember that this command can set the current context, and if the context specified\n"
343 "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
344 "the channel and call will both be terminated!\n"
347 { "GotoIfTime", pbx_builtin_gotoiftime,
348 "Conditional Goto based on the current time",
349 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
350 "This application will set the context, extension, and priority in the channel structure\n"
351 "if the current time matches the given time specification. Otherwise, nothing is done.\n"
352 "Further information on the time specification can be found in examples\n"
353 "illustrating how to do time-based context includes in the dialplan.\n"
354 "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
357 { "ExecIfTime", pbx_builtin_execiftime,
358 "Conditional application execution based on the current time",
359 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
360 "This application will execute the specified dialplan application, with optional\n"
361 "arguments, if the current time matches the given time specification.\n"
364 { "Hangup", pbx_builtin_hangup,
365 "Hang up the calling channel",
366 " Hangup([causecode]): This application will hang up the calling channel.\n"
367 "If a causecode is given the channel's hangup cause will be set to the given\n"
368 "value.\n"
371 { "NoOp", pbx_builtin_noop,
372 "Do Nothing",
373 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
374 "purposes. Any text that is provided as arguments to this application can be\n"
375 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
376 "variables or functions without having any effect."
379 { "Progress", pbx_builtin_progress,
380 "Indicate progress",
381 " Progress(): This application will request that in-band progress information\n"
382 "be provided to the calling channel.\n"
385 { "ResetCDR", pbx_builtin_resetcdr,
386 "Resets the Call Data Record",
387 " ResetCDR([options]): This application causes the Call Data Record to be\n"
388 "reset.\n"
389 " Options:\n"
390 " w -- Store the current CDR record before resetting it.\n"
391 " a -- Store any stacked records.\n"
392 " v -- Save CDR variables.\n"
395 { "Ringing", pbx_builtin_ringing,
396 "Indicate ringing tone",
397 " Ringing(): This application will request that the channel indicate a ringing\n"
398 "tone to the user.\n"
401 { "SayNumber", pbx_builtin_saynumber,
402 "Say Number",
403 " SayNumber(digits[,gender]): This application will play the sounds that\n"
404 "correspond to the given number. Optionally, a gender may be specified.\n"
405 "This will use the language that is currently set for the channel. See the\n"
406 "LANGUAGE function for more information on setting the language for the channel.\n"
409 { "SayDigits", pbx_builtin_saydigits,
410 "Say Digits",
411 " SayDigits(digits): This application will play the sounds that correspond\n"
412 "to the digits of the given number. This will use the language that is currently\n"
413 "set for the channel. See the LANGUAGE function for more information on setting\n"
414 "the language for the channel.\n"
417 { "SayAlpha", pbx_builtin_saycharacters,
418 "Say Alpha",
419 " SayAlpha(string): This application will play the sounds that correspond to\n"
420 "the letters of the given string.\n"
423 { "SayPhonetic", pbx_builtin_sayphonetic,
424 "Say Phonetic",
425 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
426 "alphabet that correspond to the letters in the given string.\n"
429 { "SetAMAFlags", pbx_builtin_setamaflags,
430 "Set the AMA Flags",
431 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
432 " billing purposes.\n"
435 { "SetGlobalVar", pbx_builtin_setglobalvar,
436 "Set a global variable to a given value",
437 " SetGlobalVar(variable=value): This application sets a given global variable to\n"
438 "the specified value.\n"
439 "\n\nThis application is deprecated in favor of Set(GLOBAL(var)=value)\n"
442 { "Set", pbx_builtin_setvar,
443 "Set channel variable(s) or function value(s)",
444 " Set(name1=value1|name2=value2|..[|options])\n"
445 "This function can be used to set the value of channel variables or dialplan\n"
446 "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
447 "if the variable name is prefixed with _, the variable will be inherited into\n"
448 "channels created from the current channel. If the variable name is prefixed\n"
449 "with __, the variable will be inherited into channels created from the current\n"
450 "channel and all children channels.\n"
451 " Options:\n"
452 " g - Set variable globally instead of on the channel\n"
453 " (applies only to variables, not functions)\n"
454 "\n\nThe use of Set to set multiple variables at once and the g flag have both\n"
455 "been deprecated. Please use multiple Set calls and the GLOBAL() dialplan\n"
456 "function instead.\n"
459 { "ImportVar", pbx_builtin_importvar,
460 "Import a variable from a channel into a new variable",
461 " ImportVar(newvar=channelname|variable): This application imports a variable\n"
462 "from the specified channel (as opposed to the current one) and stores it as\n"
463 "a variable in the current channel (the channel that is calling this\n"
464 "application). Variables created by this application have the same inheritance\n"
465 "properties as those created with the Set application. See the documentation for\n"
466 "Set for more information.\n"
469 { "Wait", pbx_builtin_wait,
470 "Waits for some time",
471 " Wait(seconds): This application waits for a specified number of seconds.\n"
472 "Then, dialplan execution will continue at the next priority.\n"
473 " Note that the seconds can be passed with fractions of a second. For example,\n"
474 "'1.5' will ask the application to wait for 1.5 seconds.\n"
477 { "WaitExten", pbx_builtin_waitexten,
478 "Waits for an extension to be entered",
479 " WaitExten([seconds][|options]): This application waits for the user to enter\n"
480 "a new extension for a specified number of seconds.\n"
481 " Note that the seconds can be passed with fractions of a second. For example,\n"
482 "'1.5' will ask the application to wait for 1.5 seconds.\n"
483 " Options:\n"
484 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
485 " Optionally, specify the class for music on hold within parenthesis.\n"
490 static struct ast_context *contexts;
491 AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
493 static AST_LIST_HEAD_STATIC(apps, ast_app);
495 static AST_LIST_HEAD_STATIC(switches, ast_switch);
497 static int stateid = 1;
498 /* WARNING:
499 When holding this list's lock, do _not_ do anything that will cause conlock
500 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
501 function will take the locks in conlock/hints order, so any other
502 paths that require both locks must also take them in that order.
504 static AST_LIST_HEAD_STATIC(hints, ast_hint);
505 struct ast_state_cb *statecbs;
508 \note This function is special. It saves the stack so that no matter
509 how many times it is called, it returns to the same place */
510 int pbx_exec(struct ast_channel *c, /*!< Channel */
511 struct ast_app *app, /*!< Application */
512 void *data) /*!< Data for execution */
514 int res;
516 const char *saved_c_appl;
517 const char *saved_c_data;
519 if (c->cdr && !ast_check_hangup(c))
520 ast_cdr_setapp(c->cdr, app->name, data);
522 /* save channel values */
523 saved_c_appl= c->appl;
524 saved_c_data= c->data;
526 c->appl = app->name;
527 c->data = data;
528 /* XXX remember what to to when we have linked apps to modules */
529 if (app->module) {
530 /* XXX LOCAL_USER_ADD(app->module) */
532 res = app->execute(c, data);
533 if (app->module) {
534 /* XXX LOCAL_USER_REMOVE(app->module) */
536 /* restore channel values */
537 c->appl = saved_c_appl;
538 c->data = saved_c_data;
539 return res;
543 /*! Go no deeper than this through includes (not counting loops) */
544 #define AST_PBX_MAX_STACK 128
546 /*! \brief Find application handle in linked list
548 struct ast_app *pbx_findapp(const char *app)
550 struct ast_app *tmp;
552 AST_LIST_LOCK(&apps);
553 AST_LIST_TRAVERSE(&apps, tmp, list) {
554 if (!strcasecmp(tmp->name, app))
555 break;
557 AST_LIST_UNLOCK(&apps);
559 return tmp;
562 static struct ast_switch *pbx_findswitch(const char *sw)
564 struct ast_switch *asw;
566 AST_LIST_LOCK(&switches);
567 AST_LIST_TRAVERSE(&switches, asw, list) {
568 if (!strcasecmp(asw->name, sw))
569 break;
571 AST_LIST_UNLOCK(&switches);
573 return asw;
576 static inline int include_valid(struct ast_include *i)
578 if (!i->hastime)
579 return 1;
581 return ast_check_timing(&(i->timing));
584 static void pbx_destroy(struct ast_pbx *p)
586 free(p);
590 * Special characters used in patterns:
591 * '_' underscore is the leading character of a pattern.
592 * In other position it is treated as a regular char.
593 * ' ' '-' space and '-' are separator and ignored.
594 * . one or more of any character. Only allowed at the end of
595 * a pattern.
596 * ! zero or more of anything. Also impacts the result of CANMATCH
597 * and MATCHMORE. Only allowed at the end of a pattern.
598 * In the core routine, ! causes a match with a return code of 2.
599 * In turn, depending on the search mode: (XXX check if it is implemented)
600 * - E_MATCH retuns 1 (does match)
601 * - E_MATCHMORE returns 0 (no match)
602 * - E_CANMATCH returns 1 (does match)
604 * / should not appear as it is considered the separator of the CID info.
605 * XXX at the moment we may stop on this char.
607 * X Z N match ranges 0-9, 1-9, 2-9 respectively.
608 * [ denotes the start of a set of character. Everything inside
609 * is considered literally. We can have ranges a-d and individual
610 * characters. A '[' and '-' can be considered literally if they
611 * are just before ']'.
612 * XXX currently there is no way to specify ']' in a range, nor \ is
613 * considered specially.
615 * When we compare a pattern with a specific extension, all characters in the extension
616 * itself are considered literally with the only exception of '-' which is considered
617 * as a separator and thus ignored.
618 * XXX do we want to consider space as a separator as well ?
619 * XXX do we want to consider the separators in non-patterns as well ?
623 * \brief helper functions to sort extensions and patterns in the desired way,
624 * so that more specific patterns appear first.
626 * ext_cmp1 compares individual characters (or sets of), returning
627 * an int where bits 0-7 are the ASCII code of the first char in the set,
628 * while bit 8-15 are the cardinality of the set minus 1.
629 * This way more specific patterns (smaller cardinality) appear first.
630 * Wildcards have a special value, so that we can directly compare them to
631 * sets by subtracting the two values. In particular:
632 * 0x000xx one character, xx
633 * 0x0yyxx yy character set starting with xx
634 * 0x10000 '.' (one or more of anything)
635 * 0x20000 '!' (zero or more of anything)
636 * 0x30000 NUL (end of string)
637 * 0x40000 error in set.
638 * The pointer to the string is advanced according to needs.
639 * NOTES:
640 * 1. the empty set is equivalent to NUL.
641 * 2. given that a full set has always 0 as the first element,
642 * we could encode the special cases as 0xffXX where XX
643 * is 1, 2, 3, 4 as used above.
645 static int ext_cmp1(const char **p)
647 uint32_t chars[8];
648 int c, cmin = 0xff, count = 0;
649 const char *end;
651 /* load, sign extend and advance pointer until we find
652 * a valid character.
654 while ( (c = *(*p)++) && (c == ' ' || c == '-') )
655 ; /* ignore some characters */
657 /* always return unless we have a set of chars */
658 switch (c) {
659 default: /* ordinary character */
660 return 0x0000 | (c & 0xff);
662 case 'N': /* 2..9 */
663 return 0x0700 | '2' ;
665 case 'X': /* 0..9 */
666 return 0x0900 | '0';
668 case 'Z': /* 1..9 */
669 return 0x0800 | '1';
671 case '.': /* wildcard */
672 return 0x10000;
674 case '!': /* earlymatch */
675 return 0x20000; /* less specific than NULL */
677 case '\0': /* empty string */
678 *p = NULL;
679 return 0x30000;
681 case '[': /* pattern */
682 break;
684 /* locate end of set */
685 end = strchr(*p, ']');
687 if (end == NULL) {
688 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
689 return 0x40000; /* XXX make this entry go last... */
692 bzero(chars, sizeof(chars)); /* clear all chars in the set */
693 for (; *p < end ; (*p)++) {
694 unsigned char c1, c2; /* first-last char in range */
695 c1 = (unsigned char)((*p)[0]);
696 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
697 c2 = (unsigned char)((*p)[2]);
698 *p += 2; /* skip a total of 3 chars */
699 } else /* individual character */
700 c2 = c1;
701 if (c1 < cmin)
702 cmin = c1;
703 for (; c1 <= c2; c1++) {
704 uint32_t mask = 1 << (c1 % 32);
705 if ( (chars[ c1 / 32 ] & mask) == 0)
706 count += 0x100;
707 chars[ c1 / 32 ] |= mask;
710 (*p)++;
711 return count == 0 ? 0x30000 : (count | cmin);
715 * \brief the full routine to compare extensions in rules.
717 static int ext_cmp(const char *a, const char *b)
719 /* make sure non-patterns come first.
720 * If a is not a pattern, it either comes first or
721 * we use strcmp to compare the strings.
723 int ret = 0;
725 if (a[0] != '_')
726 return (b[0] == '_') ? -1 : strcmp(a, b);
728 /* Now we know a is a pattern; if b is not, a comes first */
729 if (b[0] != '_')
730 return 1;
731 #if 0 /* old mode for ext matching */
732 return strcmp(a, b);
733 #endif
734 /* ok we need full pattern sorting routine */
735 while (!ret && a && b)
736 ret = ext_cmp1(&a) - ext_cmp1(&b);
737 if (ret == 0)
738 return 0;
739 else
740 return (ret > 0) ? 1 : -1;
744 * When looking up extensions, we can have different requests
745 * identified by the 'action' argument, as follows.
746 * Note that the coding is such that the low 4 bits are the
747 * third argument to extension_match_core.
749 enum ext_match_t {
750 E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
751 E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
752 E_MATCH = 0x02, /* extension is an exact match */
753 E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
754 E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
755 E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
759 * Internal function for ast_extension_{match|close}
760 * return 0 on no-match, 1 on match, 2 on early match.
761 * mode is as follows:
762 * E_MATCH success only on exact match
763 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
764 * E_CANMATCH either of the above.
767 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
769 mode &= E_MATCH_MASK; /* only consider the relevant bits */
771 if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
772 return 1;
774 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
775 int ld = strlen(data), lp = strlen(pattern);
777 if (lp < ld) /* pattern too short, cannot match */
778 return 0;
779 /* depending on the mode, accept full or partial match or both */
780 if (mode == E_MATCH)
781 return !strcmp(pattern, data); /* 1 on match, 0 on fail */
782 if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
783 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
784 else
785 return 0;
787 pattern++; /* skip leading _ */
789 * XXX below we stop at '/' which is a separator for the CID info. However we should
790 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
792 while (*data && *pattern && *pattern != '/') {
793 const char *end;
795 if (*data == '-') { /* skip '-' in data (just a separator) */
796 data++;
797 continue;
799 switch (toupper(*pattern)) {
800 case '[': /* a range */
801 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
802 if (end == NULL) {
803 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
804 return 0; /* unconditional failure */
806 for (pattern++; pattern != end; pattern++) {
807 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
808 if (*data >= pattern[0] && *data <= pattern[2])
809 break; /* match found */
810 else {
811 pattern += 2; /* skip a total of 3 chars */
812 continue;
814 } else if (*data == pattern[0])
815 break; /* match found */
817 if (pattern == end)
818 return 0;
819 pattern = end; /* skip and continue */
820 break;
821 case 'N':
822 if (*data < '2' || *data > '9')
823 return 0;
824 break;
825 case 'X':
826 if (*data < '0' || *data > '9')
827 return 0;
828 break;
829 case 'Z':
830 if (*data < '1' || *data > '9')
831 return 0;
832 break;
833 case '.': /* Must match, even with more digits */
834 return 1;
835 case '!': /* Early match */
836 return 2;
837 case ' ':
838 case '-': /* Ignore these in patterns */
839 data--; /* compensate the final data++ */
840 break;
841 default:
842 if (*data != *pattern)
843 return 0;
845 data++;
846 pattern++;
848 if (*data) /* data longer than pattern, no match */
849 return 0;
851 * match so far, but ran off the end of the data.
852 * Depending on what is next, determine match or not.
854 if (*pattern == '\0' || *pattern == '/') /* exact match */
855 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
856 else if (*pattern == '!') /* early match */
857 return 2;
858 else /* partial match */
859 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
863 * Wrapper around _extension_match_core() to do performance measurement
864 * using the profiling code.
866 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
868 int i;
869 static int prof_id = -2; /* marker for 'unallocated' id */
870 if (prof_id == -2)
871 prof_id = ast_add_profile("ext_match", 0);
872 ast_mark(prof_id, 1);
873 i = _extension_match_core(pattern, data, mode);
874 ast_mark(prof_id, 0);
875 return i;
878 int ast_extension_match(const char *pattern, const char *data)
880 return extension_match_core(pattern, data, E_MATCH);
883 int ast_extension_close(const char *pattern, const char *data, int needmore)
885 if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
886 ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
887 return extension_match_core(pattern, data, needmore);
890 struct ast_context *ast_context_find(const char *name)
892 struct ast_context *tmp = NULL;
894 ast_rdlock_contexts();
896 while ( (tmp = ast_walk_contexts(tmp)) ) {
897 if (!name || !strcasecmp(name, tmp->name))
898 break;
901 ast_unlock_contexts();
903 return tmp;
906 #define STATUS_NO_CONTEXT 1
907 #define STATUS_NO_EXTENSION 2
908 #define STATUS_NO_PRIORITY 3
909 #define STATUS_NO_LABEL 4
910 #define STATUS_SUCCESS 5
912 static int matchcid(const char *cidpattern, const char *callerid)
914 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
915 failing to get a number should count as a match, otherwise not */
917 if (ast_strlen_zero(callerid))
918 return ast_strlen_zero(cidpattern) ? 1 : 0;
920 return ast_extension_match(cidpattern, callerid);
923 /* request and result for pbx_find_extension */
924 struct pbx_find_info {
925 #if 0
926 const char *context;
927 const char *exten;
928 int priority;
929 #endif
931 char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
932 int stacklen; /* modified during the search */
933 int status; /* set on return */
934 struct ast_switch *swo; /* set on return */
935 const char *data; /* set on return */
936 const char *foundcontext; /* set on return */
939 static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
940 struct ast_context *bypass, struct pbx_find_info *q,
941 const char *context, const char *exten, int priority,
942 const char *label, const char *callerid, enum ext_match_t action)
944 int x, res;
945 struct ast_context *tmp;
946 struct ast_exten *e, *eroot;
947 struct ast_include *i;
948 struct ast_sw *sw;
950 /* Initialize status if appropriate */
951 if (q->stacklen == 0) {
952 q->status = STATUS_NO_CONTEXT;
953 q->swo = NULL;
954 q->data = NULL;
955 q->foundcontext = NULL;
957 /* Check for stack overflow */
958 if (q->stacklen >= AST_PBX_MAX_STACK) {
959 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
960 return NULL;
962 /* Check first to see if we've already been checked */
963 for (x = 0; x < q->stacklen; x++) {
964 if (!strcasecmp(q->incstack[x], context))
965 return NULL;
967 if (bypass) /* bypass means we only look there */
968 tmp = bypass;
969 else { /* look in contexts */
970 tmp = NULL;
971 while ((tmp = ast_walk_contexts(tmp)) ) {
972 if (!strcmp(tmp->name, context))
973 break;
975 if (!tmp)
976 return NULL;
978 if (q->status < STATUS_NO_EXTENSION)
979 q->status = STATUS_NO_EXTENSION;
981 /* scan the list trying to match extension and CID */
982 eroot = NULL;
983 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
984 int match = extension_match_core(eroot->exten, exten, action);
985 /* 0 on fail, 1 on match, 2 on earlymatch */
987 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
988 continue; /* keep trying */
989 if (match == 2 && action == E_MATCHMORE) {
990 /* We match an extension ending in '!'.
991 * The decision in this case is final and is NULL (no match).
993 return NULL;
995 /* found entry, now look for the right priority */
996 if (q->status < STATUS_NO_PRIORITY)
997 q->status = STATUS_NO_PRIORITY;
998 e = NULL;
999 while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
1000 /* Match label or priority */
1001 if (action == E_FINDLABEL) {
1002 if (q->status < STATUS_NO_LABEL)
1003 q->status = STATUS_NO_LABEL;
1004 if (label && e->label && !strcmp(label, e->label))
1005 break; /* found it */
1006 } else if (e->priority == priority) {
1007 break; /* found it */
1008 } /* else keep searching */
1010 if (e) { /* found a valid match */
1011 q->status = STATUS_SUCCESS;
1012 q->foundcontext = context;
1013 return e;
1016 /* Check alternative switches */
1017 AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
1018 struct ast_switch *asw = pbx_findswitch(sw->name);
1019 ast_switch_f *aswf = NULL;
1020 char *datap;
1022 if (!asw) {
1023 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
1024 continue;
1026 /* Substitute variables now */
1027 if (sw->eval)
1028 pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
1030 /* equivalent of extension_match_core() at the switch level */
1031 if (action == E_CANMATCH)
1032 aswf = asw->canmatch;
1033 else if (action == E_MATCHMORE)
1034 aswf = asw->matchmore;
1035 else /* action == E_MATCH */
1036 aswf = asw->exists;
1037 datap = sw->eval ? sw->tmpdata : sw->data;
1038 if (!aswf)
1039 res = 0;
1040 else {
1041 if (chan)
1042 ast_autoservice_start(chan);
1043 res = aswf(chan, context, exten, priority, callerid, datap);
1044 if (chan)
1045 ast_autoservice_stop(chan);
1047 if (res) { /* Got a match */
1048 q->swo = asw;
1049 q->data = datap;
1050 q->foundcontext = context;
1051 /* XXX keep status = STATUS_NO_CONTEXT ? */
1052 return NULL;
1055 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
1056 /* Now try any includes we have in this context */
1057 for (i = tmp->includes; i; i = i->next) {
1058 if (include_valid(i)) {
1059 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
1060 return e;
1061 if (q->swo)
1062 return NULL;
1065 return NULL;
1068 /*! \brief extract offset:length from variable name.
1069 * Returns 1 if there is a offset:length part, which is
1070 * trimmed off (values go into variables)
1072 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
1074 int parens=0;
1076 *offset = 0;
1077 *length = INT_MAX;
1078 *isfunc = 0;
1079 for (; *var; var++) {
1080 if (*var == '(') {
1081 (*isfunc)++;
1082 parens++;
1083 } else if (*var == ')') {
1084 parens--;
1085 } else if (*var == ':' && parens == 0) {
1086 *var++ = '\0';
1087 sscanf(var, "%d:%d", offset, length);
1088 return 1; /* offset:length valid */
1091 return 0;
1094 /*! \brief takes a substring. It is ok to call with value == workspace.
1096 * offset < 0 means start from the end of the string and set the beginning
1097 * to be that many characters back.
1098 * length is the length of the substring. A value less than 0 means to leave
1099 * that many off the end.
1100 * Always return a copy in workspace.
1102 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
1104 char *ret = workspace;
1105 int lr; /* length of the input string after the copy */
1107 ast_copy_string(workspace, value, workspace_len); /* always make a copy */
1109 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
1111 /* Quick check if no need to do anything */
1112 if (offset == 0 && length >= lr) /* take the whole string */
1113 return ret;
1115 if (offset < 0) { /* translate negative offset into positive ones */
1116 offset = lr + offset;
1117 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
1118 offset = 0;
1121 /* too large offset result in empty string so we know what to return */
1122 if (offset >= lr)
1123 return ret + lr; /* the final '\0' */
1125 ret += offset; /* move to the start position */
1126 if (length >= 0 && length < lr - offset) /* truncate if necessary */
1127 ret[length] = '\0';
1128 else if (length < 0) {
1129 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
1130 ret[lr + length - offset] = '\0';
1131 else
1132 ret[0] = '\0';
1135 return ret;
1138 /*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables
1139 ---*/
1140 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
1142 const char not_found = '\0';
1143 char *tmpvar;
1144 const char *s; /* the result */
1145 int offset, length;
1146 int i, need_substring;
1147 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
1149 if (c) {
1150 ast_channel_lock(c);
1151 places[0] = &c->varshead;
1154 * Make a copy of var because parse_variable_name() modifies the string.
1155 * Then if called directly, we might need to run substring() on the result;
1156 * remember this for later in 'need_substring', 'offset' and 'length'
1158 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
1159 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
1162 * Look first into predefined variables, then into variable lists.
1163 * Variable 's' points to the result, according to the following rules:
1164 * s == &not_found (set at the beginning) means that we did not find a
1165 * matching variable and need to look into more places.
1166 * If s != &not_found, s is a valid result string as follows:
1167 * s = NULL if the variable does not have a value;
1168 * you typically do this when looking for an unset predefined variable.
1169 * s = workspace if the result has been assembled there;
1170 * typically done when the result is built e.g. with an snprintf(),
1171 * so we don't need to do an additional copy.
1172 * s != workspace in case we have a string, that needs to be copied
1173 * (the ast_copy_string is done once for all at the end).
1174 * Typically done when the result is already available in some string.
1176 s = &not_found; /* default value */
1177 if (c) { /* This group requires a valid channel */
1178 /* Names with common parts are looked up a piece at a time using strncmp. */
1179 if (!strncmp(var, "CALL", 4)) {
1180 if (!strncmp(var + 4, "ING", 3)) {
1181 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
1182 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
1183 s = workspace;
1184 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
1185 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
1186 s = workspace;
1187 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
1188 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
1189 s = workspace;
1190 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
1191 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
1192 s = workspace;
1195 } else if (!strcmp(var, "HINT")) {
1196 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
1197 } else if (!strcmp(var, "HINTNAME")) {
1198 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
1199 } else if (!strcmp(var, "EXTEN")) {
1200 s = c->exten;
1201 } else if (!strcmp(var, "CONTEXT")) {
1202 s = c->context;
1203 } else if (!strcmp(var, "PRIORITY")) {
1204 snprintf(workspace, workspacelen, "%d", c->priority);
1205 s = workspace;
1206 } else if (!strcmp(var, "CHANNEL")) {
1207 s = c->name;
1208 } else if (!strcmp(var, "UNIQUEID")) {
1209 s = c->uniqueid;
1210 } else if (!strcmp(var, "HANGUPCAUSE")) {
1211 snprintf(workspace, workspacelen, "%d", c->hangupcause);
1212 s = workspace;
1215 if (s == &not_found) { /* look for more */
1216 if (!strcmp(var, "EPOCH")) {
1217 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
1218 s = workspace;
1219 } else if (!strcmp(var, "SYSTEMNAME")) {
1220 s = ast_config_AST_SYSTEM_NAME;
1223 /* if not found, look into chanvars or global vars */
1224 for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
1225 struct ast_var_t *variables;
1226 if (!places[i])
1227 continue;
1228 if (places[i] == &globals)
1229 ast_mutex_lock(&globalslock);
1230 AST_LIST_TRAVERSE(places[i], variables, entries) {
1231 if (strcasecmp(ast_var_name(variables), var)==0) {
1232 s = ast_var_value(variables);
1233 break;
1236 if (places[i] == &globals)
1237 ast_mutex_unlock(&globalslock);
1239 if (s == &not_found || s == NULL)
1240 *ret = NULL;
1241 else {
1242 if (s != workspace)
1243 ast_copy_string(workspace, s, workspacelen);
1244 *ret = workspace;
1245 if (need_substring)
1246 *ret = substring(*ret, offset, length, workspace, workspacelen);
1249 if (c)
1250 ast_channel_unlock(c);
1253 /*! \brief CLI function to show installed custom functions
1254 \addtogroup CLI_functions
1256 static int handle_show_functions_deprecated(int fd, int argc, char *argv[])
1258 struct ast_custom_function *acf;
1259 int count_acf = 0;
1260 int like = 0;
1262 if (argc == 4 && (!strcmp(argv[2], "like")) ) {
1263 like = 1;
1264 } else if (argc != 2) {
1265 return RESULT_SHOWUSAGE;
1268 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1270 AST_LIST_LOCK(&acf_root);
1271 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1272 if (!like || strstr(acf->name, argv[3])) {
1273 count_acf++;
1274 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1277 AST_LIST_UNLOCK(&acf_root);
1279 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1281 return RESULT_SUCCESS;
1283 static int handle_show_functions(int fd, int argc, char *argv[])
1285 struct ast_custom_function *acf;
1286 int count_acf = 0;
1287 int like = 0;
1289 if (argc == 5 && (!strcmp(argv[3], "like")) ) {
1290 like = 1;
1291 } else if (argc != 3) {
1292 return RESULT_SHOWUSAGE;
1295 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1297 AST_LIST_LOCK(&acf_root);
1298 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1299 if (!like || strstr(acf->name, argv[4])) {
1300 count_acf++;
1301 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1304 AST_LIST_UNLOCK(&acf_root);
1306 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1308 return RESULT_SUCCESS;
1311 static int handle_show_function_deprecated(int fd, int argc, char *argv[])
1313 struct ast_custom_function *acf;
1314 /* Maximum number of characters added by terminal coloring is 22 */
1315 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1316 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1317 char stxtitle[40], *syntax = NULL;
1318 int synopsis_size, description_size, syntax_size;
1320 if (argc < 3)
1321 return RESULT_SHOWUSAGE;
1323 if (!(acf = ast_custom_function_find(argv[2]))) {
1324 ast_cli(fd, "No function by that name registered.\n");
1325 return RESULT_FAILURE;
1329 if (acf->synopsis)
1330 synopsis_size = strlen(acf->synopsis) + 23;
1331 else
1332 synopsis_size = strlen("Not available") + 23;
1333 synopsis = alloca(synopsis_size);
1335 if (acf->desc)
1336 description_size = strlen(acf->desc) + 23;
1337 else
1338 description_size = strlen("Not available") + 23;
1339 description = alloca(description_size);
1341 if (acf->syntax)
1342 syntax_size = strlen(acf->syntax) + 23;
1343 else
1344 syntax_size = strlen("Not available") + 23;
1345 syntax = alloca(syntax_size);
1347 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1348 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1349 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1350 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1351 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1352 term_color(syntax,
1353 acf->syntax ? acf->syntax : "Not available",
1354 COLOR_CYAN, 0, syntax_size);
1355 term_color(synopsis,
1356 acf->synopsis ? acf->synopsis : "Not available",
1357 COLOR_CYAN, 0, synopsis_size);
1358 term_color(description,
1359 acf->desc ? acf->desc : "Not available",
1360 COLOR_CYAN, 0, description_size);
1362 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1364 return RESULT_SUCCESS;
1367 static int handle_show_function(int fd, int argc, char *argv[])
1369 struct ast_custom_function *acf;
1370 /* Maximum number of characters added by terminal coloring is 22 */
1371 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1372 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1373 char stxtitle[40], *syntax = NULL;
1374 int synopsis_size, description_size, syntax_size;
1376 if (argc < 4)
1377 return RESULT_SHOWUSAGE;
1379 if (!(acf = ast_custom_function_find(argv[3]))) {
1380 ast_cli(fd, "No function by that name registered.\n");
1381 return RESULT_FAILURE;
1385 if (acf->synopsis)
1386 synopsis_size = strlen(acf->synopsis) + 23;
1387 else
1388 synopsis_size = strlen("Not available") + 23;
1389 synopsis = alloca(synopsis_size);
1391 if (acf->desc)
1392 description_size = strlen(acf->desc) + 23;
1393 else
1394 description_size = strlen("Not available") + 23;
1395 description = alloca(description_size);
1397 if (acf->syntax)
1398 syntax_size = strlen(acf->syntax) + 23;
1399 else
1400 syntax_size = strlen("Not available") + 23;
1401 syntax = alloca(syntax_size);
1403 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1404 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1405 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1406 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1407 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1408 term_color(syntax,
1409 acf->syntax ? acf->syntax : "Not available",
1410 COLOR_CYAN, 0, syntax_size);
1411 term_color(synopsis,
1412 acf->synopsis ? acf->synopsis : "Not available",
1413 COLOR_CYAN, 0, synopsis_size);
1414 term_color(description,
1415 acf->desc ? acf->desc : "Not available",
1416 COLOR_CYAN, 0, description_size);
1418 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1420 return RESULT_SUCCESS;
1423 static char *complete_show_function(const char *line, const char *word, int pos, int state)
1425 struct ast_custom_function *acf;
1426 char *ret = NULL;
1427 int which = 0;
1428 int wordlen = strlen(word);
1430 /* case-insensitive for convenience in this 'complete' function */
1431 AST_LIST_LOCK(&acf_root);
1432 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1433 if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
1434 ret = strdup(acf->name);
1435 break;
1438 AST_LIST_UNLOCK(&acf_root);
1440 return ret;
1443 struct ast_custom_function *ast_custom_function_find(const char *name)
1445 struct ast_custom_function *acf = NULL;
1447 AST_LIST_LOCK(&acf_root);
1448 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1449 if (!strcmp(name, acf->name))
1450 break;
1452 AST_LIST_UNLOCK(&acf_root);
1454 return acf;
1457 int ast_custom_function_unregister(struct ast_custom_function *acf)
1459 struct ast_custom_function *cur;
1461 if (!acf)
1462 return -1;
1464 AST_LIST_LOCK(&acf_root);
1465 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1466 if (cur == acf) {
1467 AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
1468 if (option_verbose > 1)
1469 ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
1470 break;
1473 AST_LIST_TRAVERSE_SAFE_END
1474 AST_LIST_UNLOCK(&acf_root);
1476 return acf ? 0 : -1;
1479 int ast_custom_function_register(struct ast_custom_function *acf)
1481 struct ast_custom_function *cur;
1483 if (!acf)
1484 return -1;
1486 AST_LIST_LOCK(&acf_root);
1488 if (ast_custom_function_find(acf->name)) {
1489 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
1490 AST_LIST_UNLOCK(&acf_root);
1491 return -1;
1494 /* Store in alphabetical order */
1495 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1496 if (strcasecmp(acf->name, cur->name) < 0) {
1497 AST_LIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
1498 break;
1501 AST_LIST_TRAVERSE_SAFE_END
1502 if (!cur)
1503 AST_LIST_INSERT_TAIL(&acf_root, acf, acflist);
1505 AST_LIST_UNLOCK(&acf_root);
1507 if (option_verbose > 1)
1508 ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
1510 return 0;
1513 /*! \brief return a pointer to the arguments of the function,
1514 * and terminates the function name with '\\0'
1516 static char *func_args(char *function)
1518 char *args = strchr(function, '(');
1520 if (!args)
1521 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1522 else {
1523 char *p;
1524 *args++ = '\0';
1525 if ((p = strrchr(args, ')')) )
1526 *p = '\0';
1527 else
1528 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1530 return args;
1533 int ast_func_read(struct ast_channel *chan, char *function, char *workspace, size_t len)
1535 char *args = func_args(function);
1536 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1538 if (acfptr == NULL)
1539 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1540 else if (!acfptr->read)
1541 ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
1542 else
1543 return acfptr->read(chan, function, args, workspace, len);
1544 return -1;
1547 int ast_func_write(struct ast_channel *chan, char *function, const char *value)
1549 char *args = func_args(function);
1550 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1552 if (acfptr == NULL)
1553 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1554 else if (!acfptr->write)
1555 ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
1556 else
1557 return acfptr->write(chan, function, args, value);
1559 return -1;
1562 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
1564 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
1565 zero-filled */
1566 char *cp4;
1567 const char *tmp, *whereweare;
1568 int length, offset, offset2, isfunction;
1569 char *workspace = NULL;
1570 char *ltmp = NULL, *var = NULL;
1571 char *nextvar, *nextexp, *nextthing;
1572 char *vars, *vare;
1573 int pos, brackets, needsub, len;
1575 whereweare=tmp=cp1;
1576 while (!ast_strlen_zero(whereweare) && count) {
1577 /* Assume we're copying the whole remaining string */
1578 pos = strlen(whereweare);
1579 nextvar = NULL;
1580 nextexp = NULL;
1581 nextthing = strchr(whereweare, '$');
1582 if (nextthing) {
1583 switch(nextthing[1]) {
1584 case '{':
1585 nextvar = nextthing;
1586 pos = nextvar - whereweare;
1587 break;
1588 case '[':
1589 nextexp = nextthing;
1590 pos = nextexp - whereweare;
1591 break;
1592 default:
1593 pos = 1;
1597 if (pos) {
1598 /* Can't copy more than 'count' bytes */
1599 if (pos > count)
1600 pos = count;
1602 /* Copy that many bytes */
1603 memcpy(cp2, whereweare, pos);
1605 count -= pos;
1606 cp2 += pos;
1607 whereweare += pos;
1610 if (nextvar) {
1611 /* We have a variable. Find the start and end, and determine
1612 if we are going to have to recursively call ourselves on the
1613 contents */
1614 vars = vare = nextvar + 2;
1615 brackets = 1;
1616 needsub = 0;
1618 /* Find the end of it */
1619 while (brackets && *vare) {
1620 if ((vare[0] == '$') && (vare[1] == '{')) {
1621 needsub++;
1622 } else if (vare[0] == '{') {
1623 brackets++;
1624 } else if (vare[0] == '}') {
1625 brackets--;
1626 } else if ((vare[0] == '$') && (vare[1] == '['))
1627 needsub++;
1628 vare++;
1630 if (brackets)
1631 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
1632 len = vare - vars - 1;
1634 /* Skip totally over variable string */
1635 whereweare += (len + 3);
1637 if (!var)
1638 var = alloca(VAR_BUF_SIZE);
1640 /* Store variable name (and truncate) */
1641 ast_copy_string(var, vars, len + 1);
1643 /* Substitute if necessary */
1644 if (needsub) {
1645 if (!ltmp)
1646 ltmp = alloca(VAR_BUF_SIZE);
1648 memset(ltmp, 0, VAR_BUF_SIZE);
1649 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1650 vars = ltmp;
1651 } else {
1652 vars = var;
1655 if (!workspace)
1656 workspace = alloca(VAR_BUF_SIZE);
1658 workspace[0] = '\0';
1660 parse_variable_name(vars, &offset, &offset2, &isfunction);
1661 if (isfunction) {
1662 /* Evaluate function */
1663 if (c || !headp)
1664 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1665 else {
1666 struct varshead old;
1667 struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
1668 if (c) {
1669 memcpy(&old, &c->varshead, sizeof(old));
1670 memcpy(&c->varshead, headp, sizeof(c->varshead));
1671 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1672 /* Don't deallocate the varshead that was passed in */
1673 memcpy(&c->varshead, &old, sizeof(c->varshead));
1674 ast_channel_free(c);
1675 } else
1676 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1679 if (option_debug)
1680 ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
1681 } else {
1682 /* Retrieve variable value */
1683 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
1685 if (cp4) {
1686 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
1688 length = strlen(cp4);
1689 if (length > count)
1690 length = count;
1691 memcpy(cp2, cp4, length);
1692 count -= length;
1693 cp2 += length;
1695 } else if (nextexp) {
1696 /* We have an expression. Find the start and end, and determine
1697 if we are going to have to recursively call ourselves on the
1698 contents */
1699 vars = vare = nextexp + 2;
1700 brackets = 1;
1701 needsub = 0;
1703 /* Find the end of it */
1704 while(brackets && *vare) {
1705 if ((vare[0] == '$') && (vare[1] == '[')) {
1706 needsub++;
1707 brackets++;
1708 vare++;
1709 } else if (vare[0] == '[') {
1710 brackets++;
1711 } else if (vare[0] == ']') {
1712 brackets--;
1713 } else if ((vare[0] == '$') && (vare[1] == '{')) {
1714 needsub++;
1715 vare++;
1717 vare++;
1719 if (brackets)
1720 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
1721 len = vare - vars - 1;
1723 /* Skip totally over expression */
1724 whereweare += (len + 3);
1726 if (!var)
1727 var = alloca(VAR_BUF_SIZE);
1729 /* Store variable name (and truncate) */
1730 ast_copy_string(var, vars, len + 1);
1732 /* Substitute if necessary */
1733 if (needsub) {
1734 if (!ltmp)
1735 ltmp = alloca(VAR_BUF_SIZE);
1737 memset(ltmp, 0, VAR_BUF_SIZE);
1738 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1739 vars = ltmp;
1740 } else {
1741 vars = var;
1744 length = ast_expr(vars, cp2, count);
1746 if (length) {
1747 if (option_debug)
1748 ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
1749 count -= length;
1750 cp2 += length;
1756 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
1758 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
1761 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
1763 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
1766 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
1768 memset(passdata, 0, datalen);
1770 /* No variables or expressions in e->data, so why scan it? */
1771 if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
1772 ast_copy_string(passdata, e->data, datalen);
1773 return;
1776 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
1779 /*!
1780 * \brief The return value depends on the action:
1782 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
1783 * and return 0 on failure, -1 on match;
1784 * E_FINDLABEL maps the label to a priority, and returns
1785 * the priority on success, ... XXX
1786 * E_SPAWN, spawn an application,
1787 * and return 0 on success, -1 on failure.
1789 * \note The channel is auto-serviced in this function, because doing an extension
1790 * match may block for a long time. For example, if the lookup has to use a network
1791 * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
1792 * auto-service code will queue up any important signalling frames to be processed
1793 * after this is done.
1795 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
1796 const char *context, const char *exten, int priority,
1797 const char *label, const char *callerid, enum ext_match_t action)
1799 struct ast_exten *e;
1800 struct ast_app *app;
1801 int res;
1802 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
1803 char passdata[EXT_DATA_SIZE];
1805 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
1807 ast_rdlock_contexts();
1808 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
1809 if (e) {
1810 if (matching_action) {
1811 ast_unlock_contexts();
1812 return -1; /* success, we found it */
1813 } else if (action == E_FINDLABEL) { /* map the label to a priority */
1814 res = e->priority;
1815 ast_unlock_contexts();
1816 return res; /* the priority we were looking for */
1817 } else { /* spawn */
1818 app = pbx_findapp(e->app);
1819 ast_unlock_contexts();
1820 if (!app) {
1821 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
1822 return -1;
1824 if (c->context != context)
1825 ast_copy_string(c->context, context, sizeof(c->context));
1826 if (c->exten != exten)
1827 ast_copy_string(c->exten, exten, sizeof(c->exten));
1828 c->priority = priority;
1829 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
1830 if (option_debug) {
1831 ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
1833 if (option_verbose > 2) {
1834 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
1835 ast_verbose( VERBOSE_PREFIX_3 "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
1836 exten, context, priority,
1837 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
1838 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
1839 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
1840 "in new stack");
1842 manager_event(EVENT_FLAG_CALL, "Newexten",
1843 "Channel: %s\r\n"
1844 "Context: %s\r\n"
1845 "Extension: %s\r\n"
1846 "Priority: %d\r\n"
1847 "Application: %s\r\n"
1848 "AppData: %s\r\n"
1849 "Uniqueid: %s\r\n",
1850 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
1851 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
1853 } else if (q.swo) { /* not found here, but in another switch */
1854 ast_unlock_contexts();
1855 if (matching_action) {
1856 return -1;
1857 } else {
1858 if (!q.swo->exec) {
1859 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
1860 res = -1;
1862 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
1864 } else { /* not found anywhere, see what happened */
1865 ast_unlock_contexts();
1866 switch (q.status) {
1867 case STATUS_NO_CONTEXT:
1868 if (!matching_action)
1869 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
1870 break;
1871 case STATUS_NO_EXTENSION:
1872 if (!matching_action)
1873 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
1874 break;
1875 case STATUS_NO_PRIORITY:
1876 if (!matching_action)
1877 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
1878 break;
1879 case STATUS_NO_LABEL:
1880 if (context)
1881 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
1882 break;
1883 default:
1884 if (option_debug)
1885 ast_log(LOG_DEBUG, "Shouldn't happen!\n");
1888 return (matching_action) ? 0 : -1;
1892 /*! \brief ast_hint_extension: Find hint for given extension in context */
1893 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
1895 struct ast_exten *e;
1896 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
1898 ast_rdlock_contexts();
1899 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
1900 ast_unlock_contexts();
1902 return e;
1905 /*! \brief ast_extensions_state2: Check state of extension by using hints */
1906 static int ast_extension_state2(struct ast_exten *e)
1908 char hint[AST_MAX_EXTENSION];
1909 char *cur, *rest;
1910 int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
1911 int busy = 0, inuse = 0, ring = 0;
1913 if (!e)
1914 return -1;
1916 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
1918 rest = hint; /* One or more devices separated with a & character */
1919 while ( (cur = strsep(&rest, "&")) ) {
1920 int res = ast_device_state(cur);
1921 switch (res) {
1922 case AST_DEVICE_NOT_INUSE:
1923 allunavailable = 0;
1924 allbusy = 0;
1925 allonhold = 0;
1926 break;
1927 case AST_DEVICE_INUSE:
1928 inuse = 1;
1929 allunavailable = 0;
1930 allfree = 0;
1931 allonhold = 0;
1932 break;
1933 case AST_DEVICE_RINGING:
1934 ring = 1;
1935 allunavailable = 0;
1936 allfree = 0;
1937 allonhold = 0;
1938 break;
1939 case AST_DEVICE_RINGINUSE:
1940 inuse = 1;
1941 ring = 1;
1942 allunavailable = 0;
1943 allfree = 0;
1944 allonhold = 0;
1945 break;
1946 case AST_DEVICE_ONHOLD:
1947 allunavailable = 0;
1948 allfree = 0;
1949 break;
1950 case AST_DEVICE_BUSY:
1951 allunavailable = 0;
1952 allfree = 0;
1953 allonhold = 0;
1954 busy = 1;
1955 break;
1956 case AST_DEVICE_UNAVAILABLE:
1957 case AST_DEVICE_INVALID:
1958 allbusy = 0;
1959 allfree = 0;
1960 allonhold = 0;
1961 break;
1962 default:
1963 allunavailable = 0;
1964 allbusy = 0;
1965 allfree = 0;
1966 allonhold = 0;
1970 if (!inuse && ring)
1971 return AST_EXTENSION_RINGING;
1972 if (inuse && ring)
1973 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
1974 if (inuse)
1975 return AST_EXTENSION_INUSE;
1976 if (allfree)
1977 return AST_EXTENSION_NOT_INUSE;
1978 if (allonhold)
1979 return AST_EXTENSION_ONHOLD;
1980 if (allbusy)
1981 return AST_EXTENSION_BUSY;
1982 if (allunavailable)
1983 return AST_EXTENSION_UNAVAILABLE;
1984 if (busy)
1985 return AST_EXTENSION_INUSE;
1987 return AST_EXTENSION_NOT_INUSE;
1990 /*! \brief ast_extension_state2str: Return extension_state as string */
1991 const char *ast_extension_state2str(int extension_state)
1993 int i;
1995 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
1996 if (extension_states[i].extension_state == extension_state)
1997 return extension_states[i].text;
1999 return "Unknown";
2002 /*! \brief ast_extension_state: Check extension state for an extension by using hint */
2003 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
2005 struct ast_exten *e;
2007 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
2008 if (!e)
2009 return -1; /* No hint, return -1 */
2011 return ast_extension_state2(e); /* Check all devices in the hint */
2014 void ast_hint_state_changed(const char *device)
2016 struct ast_hint *hint;
2018 AST_LIST_LOCK(&hints);
2020 AST_LIST_TRAVERSE(&hints, hint, list) {
2021 struct ast_state_cb *cblist;
2022 char buf[AST_MAX_EXTENSION];
2023 char *parse = buf;
2024 char *cur;
2025 int state;
2027 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
2028 while ( (cur = strsep(&parse, "&")) ) {
2029 if (!strcasecmp(cur, device))
2030 break;
2032 if (!cur)
2033 continue;
2035 /* Get device state for this hint */
2036 state = ast_extension_state2(hint->exten);
2038 if ((state == -1) || (state == hint->laststate))
2039 continue;
2041 /* Device state changed since last check - notify the watchers */
2043 /* For general callbacks */
2044 for (cblist = statecbs; cblist; cblist = cblist->next)
2045 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2047 /* For extension callbacks */
2048 for (cblist = hint->callbacks; cblist; cblist = cblist->next)
2049 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2051 hint->laststate = state; /* record we saw the change */
2054 AST_LIST_UNLOCK(&hints);
2057 /*! \brief ast_extension_state_add: Add watcher for extension states */
2058 int ast_extension_state_add(const char *context, const char *exten,
2059 ast_state_cb_type callback, void *data)
2061 struct ast_hint *hint;
2062 struct ast_state_cb *cblist;
2063 struct ast_exten *e;
2065 /* If there's no context and extension: add callback to statecbs list */
2066 if (!context && !exten) {
2067 AST_LIST_LOCK(&hints);
2069 for (cblist = statecbs; cblist; cblist = cblist->next) {
2070 if (cblist->callback == callback) {
2071 cblist->data = data;
2072 AST_LIST_UNLOCK(&hints);
2073 return 0;
2077 /* Now insert the callback */
2078 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2079 AST_LIST_UNLOCK(&hints);
2080 return -1;
2082 cblist->id = 0;
2083 cblist->callback = callback;
2084 cblist->data = data;
2086 cblist->next = statecbs;
2087 statecbs = cblist;
2089 AST_LIST_UNLOCK(&hints);
2090 return 0;
2093 if (!context || !exten)
2094 return -1;
2096 /* This callback type is for only one hint, so get the hint */
2097 e = ast_hint_extension(NULL, context, exten);
2098 if (!e) {
2099 return -1;
2102 /* Find the hint in the list of hints */
2103 AST_LIST_LOCK(&hints);
2105 AST_LIST_TRAVERSE(&hints, hint, list) {
2106 if (hint->exten == e)
2107 break;
2110 if (!hint) {
2111 /* We have no hint, sorry */
2112 AST_LIST_UNLOCK(&hints);
2113 return -1;
2116 /* Now insert the callback in the callback list */
2117 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2118 AST_LIST_UNLOCK(&hints);
2119 return -1;
2121 cblist->id = stateid++; /* Unique ID for this callback */
2122 cblist->callback = callback; /* Pointer to callback routine */
2123 cblist->data = data; /* Data for the callback */
2125 cblist->next = hint->callbacks;
2126 hint->callbacks = cblist;
2128 AST_LIST_UNLOCK(&hints);
2129 return cblist->id;
2132 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */
2133 int ast_extension_state_del(int id, ast_state_cb_type callback)
2135 struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
2136 int ret = -1;
2138 if (!id && !callback)
2139 return -1;
2141 AST_LIST_LOCK(&hints);
2143 if (!id) { /* id == 0 is a callback without extension */
2144 for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
2145 if ((*p_cur)->callback == callback)
2146 break;
2148 } else { /* callback with extension, find the callback based on ID */
2149 struct ast_hint *hint;
2150 AST_LIST_TRAVERSE(&hints, hint, list) {
2151 for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
2152 if ((*p_cur)->id == id)
2153 break;
2155 if (*p_cur) /* found in the inner loop */
2156 break;
2159 if (p_cur && *p_cur) {
2160 struct ast_state_cb *cur = *p_cur;
2161 *p_cur = cur->next;
2162 free(cur);
2163 ret = 0;
2165 AST_LIST_UNLOCK(&hints);
2166 return ret;
2169 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
2170 static int ast_add_hint(struct ast_exten *e)
2172 struct ast_hint *hint;
2174 if (!e)
2175 return -1;
2177 AST_LIST_LOCK(&hints);
2179 /* Search if hint exists, do nothing */
2180 AST_LIST_TRAVERSE(&hints, hint, list) {
2181 if (hint->exten == e) {
2182 AST_LIST_UNLOCK(&hints);
2183 if (option_debug > 1)
2184 ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2185 return -1;
2189 if (option_debug > 1)
2190 ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2192 if (!(hint = ast_calloc(1, sizeof(*hint)))) {
2193 AST_LIST_UNLOCK(&hints);
2194 return -1;
2196 /* Initialize and insert new item at the top */
2197 hint->exten = e;
2198 hint->laststate = ast_extension_state2(e);
2199 AST_LIST_INSERT_HEAD(&hints, hint, list);
2201 AST_LIST_UNLOCK(&hints);
2202 return 0;
2205 /*! \brief ast_change_hint: Change hint for an extension */
2206 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
2208 struct ast_hint *hint;
2209 int res = -1;
2211 AST_LIST_LOCK(&hints);
2212 AST_LIST_TRAVERSE(&hints, hint, list) {
2213 if (hint->exten == oe) {
2214 hint->exten = ne;
2215 res = 0;
2216 break;
2219 AST_LIST_UNLOCK(&hints);
2221 return res;
2224 /*! \brief ast_remove_hint: Remove hint from extension */
2225 static int ast_remove_hint(struct ast_exten *e)
2227 /* Cleanup the Notifys if hint is removed */
2228 struct ast_hint *hint;
2229 struct ast_state_cb *cblist, *cbprev;
2230 int res = -1;
2232 if (!e)
2233 return -1;
2235 AST_LIST_LOCK(&hints);
2236 AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
2237 if (hint->exten == e) {
2238 cbprev = NULL;
2239 cblist = hint->callbacks;
2240 while (cblist) {
2241 /* Notify with -1 and remove all callbacks */
2242 cbprev = cblist;
2243 cblist = cblist->next;
2244 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
2245 free(cbprev);
2247 hint->callbacks = NULL;
2248 AST_LIST_REMOVE_CURRENT(&hints, list);
2249 free(hint);
2250 res = 0;
2251 break;
2254 AST_LIST_TRAVERSE_SAFE_END
2255 AST_LIST_UNLOCK(&hints);
2257 return res;
2261 /*! \brief ast_get_hint: Get hint for channel */
2262 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
2264 struct ast_exten *e = ast_hint_extension(c, context, exten);
2266 if (e) {
2267 if (hint)
2268 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
2269 if (name) {
2270 const char *tmp = ast_get_extension_app_data(e);
2271 if (tmp)
2272 ast_copy_string(name, tmp, namesize);
2274 return -1;
2276 return 0;
2279 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2281 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH);
2284 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
2286 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL);
2289 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
2291 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
2294 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2296 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH);
2299 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2301 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE);
2304 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2306 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN);
2309 /* helper function to set extension and priority */
2310 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
2312 ast_channel_lock(c);
2313 ast_copy_string(c->exten, exten, sizeof(c->exten));
2314 c->priority = pri;
2315 ast_channel_unlock(c);
2319 * \brief collect digits from the channel into the buffer,
2320 * return -1 on error, 0 on timeout or done.
2322 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
2324 int digit;
2326 buf[pos] = '\0'; /* make sure it is properly terminated */
2327 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
2328 /* As long as we're willing to wait, and as long as it's not defined,
2329 keep reading digits until we can't possibly get a right answer anymore. */
2330 digit = ast_waitfordigit(c, waittime * 1000);
2331 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2332 c->_softhangup = 0;
2333 } else {
2334 if (!digit) /* No entry */
2335 break;
2336 if (digit < 0) /* Error, maybe a hangup */
2337 return -1;
2338 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
2339 buf[pos++] = digit;
2340 buf[pos] = '\0';
2342 waittime = c->pbx->dtimeout;
2345 return 0;
2348 static int __ast_pbx_run(struct ast_channel *c)
2350 int found = 0; /* set if we find at least one match */
2351 int res = 0;
2352 int autoloopflag;
2353 int error = 0; /* set an error conditions */
2355 /* A little initial setup here */
2356 if (c->pbx) {
2357 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
2358 /* XXX and now what ? */
2359 free(c->pbx);
2361 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
2362 return -1;
2363 if (c->amaflags) {
2364 if (!c->cdr) {
2365 c->cdr = ast_cdr_alloc();
2366 if (!c->cdr) {
2367 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
2368 free(c->pbx);
2369 return -1;
2371 ast_cdr_init(c->cdr, c);
2374 /* Set reasonable defaults */
2375 c->pbx->rtimeout = 10;
2376 c->pbx->dtimeout = 5;
2378 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
2379 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2381 /* Start by trying whatever the channel is set to */
2382 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2383 /* If not successful fall back to 's' */
2384 if (option_verbose > 1)
2385 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
2386 /* XXX the original code used the existing priority in the call to
2387 * ast_exists_extension(), and reset it to 1 afterwards.
2388 * I believe the correct thing is to set it to 1 immediately.
2390 set_ext_pri(c, "s", 1);
2391 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2392 /* JK02: And finally back to default if everything else failed */
2393 if (option_verbose > 1)
2394 ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
2395 ast_copy_string(c->context, "default", sizeof(c->context));
2398 if (c->cdr && ast_tvzero(c->cdr->start))
2399 ast_cdr_start(c->cdr);
2400 for (;;) {
2401 char dst_exten[256]; /* buffer to accumulate digits */
2402 int pos = 0; /* XXX should check bounds */
2403 int digit = 0;
2405 /* loop on priorities in this context/exten */
2406 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2407 found = 1;
2408 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2409 /* Something bad happened, or a hangup has been requested. */
2410 if (strchr("0123456789ABCDEF*#", res)) {
2411 if (option_debug)
2412 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
2413 pos = 0;
2414 dst_exten[pos++] = digit = res;
2415 dst_exten[pos] = '\0';
2416 break;
2418 if (res == AST_PBX_KEEPALIVE) {
2419 if (option_debug)
2420 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2421 if (option_verbose > 1)
2422 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2423 error = 1;
2424 break;
2426 if (option_debug)
2427 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2428 if (option_verbose > 1)
2429 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2430 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2431 c->_softhangup =0;
2432 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2433 /* atimeout, nothing bad */
2434 } else {
2435 if (c->cdr)
2436 ast_cdr_update(c);
2437 error = 1;
2438 break;
2441 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
2442 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
2443 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2444 c->whentohangup = 0;
2445 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2446 } else if (c->_softhangup) {
2447 if (option_debug)
2448 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
2449 c->exten, c->priority);
2450 error = 1;
2451 break;
2453 c->priority++;
2454 } /* end while - from here on we can use 'break' to go out */
2455 if (error)
2456 break;
2458 /* XXX we get here on non-existing extension or a keypress or hangup ? */
2460 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2461 /* If there is no match at priority 1, it is not a valid extension anymore.
2462 * Try to continue at "i", 1 or exit if the latter does not exist.
2464 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2465 if (option_verbose > 2)
2466 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2467 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2468 set_ext_pri(c, "i", 1);
2469 } else {
2470 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2471 c->name, c->exten, c->context);
2472 error = 1; /* we know what to do with it */
2473 break;
2475 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2476 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2477 c->_softhangup = 0;
2478 } else { /* keypress received, get more digits for a full extension */
2479 int waittime = 0;
2480 if (digit)
2481 waittime = c->pbx->dtimeout;
2482 else if (!autofallthrough)
2483 waittime = c->pbx->rtimeout;
2484 if (!waittime) {
2485 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2486 if (!status)
2487 status = "UNKNOWN";
2488 if (option_verbose > 2)
2489 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2490 if (!strcasecmp(status, "CONGESTION"))
2491 res = pbx_builtin_congestion(c, "10");
2492 else if (!strcasecmp(status, "CHANUNAVAIL"))
2493 res = pbx_builtin_congestion(c, "10");
2494 else if (!strcasecmp(status, "BUSY"))
2495 res = pbx_builtin_busy(c, "10");
2496 error = 1; /* XXX disable message */
2497 break; /* exit from the 'for' loop */
2500 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
2501 break;
2502 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
2503 set_ext_pri(c, dst_exten, 1);
2504 else {
2505 /* No such extension */
2506 if (!ast_strlen_zero(dst_exten)) {
2507 /* An invalid extension */
2508 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2509 if (option_verbose > 2)
2510 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
2511 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
2512 set_ext_pri(c, "i", 1);
2513 } else {
2514 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
2515 found = 1; /* XXX disable message */
2516 break;
2518 } else {
2519 /* A simple timeout */
2520 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2521 if (option_verbose > 2)
2522 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
2523 set_ext_pri(c, "t", 1);
2524 } else {
2525 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2526 found = 1; /* XXX disable message */
2527 break;
2531 if (c->cdr) {
2532 if (option_verbose > 2)
2533 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
2534 ast_cdr_update(c);
2538 if (!found && !error)
2539 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2540 if (res != AST_PBX_KEEPALIVE)
2541 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
2542 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2543 if (c->cdr && ast_opt_end_cdr_before_h_exten)
2544 ast_cdr_end(c->cdr);
2545 set_ext_pri(c, "h", 1);
2546 while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2547 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2548 /* Something bad happened, or a hangup has been requested. */
2549 if (option_debug)
2550 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2551 if (option_verbose > 1)
2552 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2553 break;
2555 c->priority++;
2558 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2560 pbx_destroy(c->pbx);
2561 c->pbx = NULL;
2562 if (res != AST_PBX_KEEPALIVE)
2563 ast_hangup(c);
2564 return 0;
2567 /* Returns 0 on success, non-zero if call limit was reached */
2568 static int increase_call_count(const struct ast_channel *c)
2570 int failed = 0;
2571 double curloadavg;
2572 ast_mutex_lock(&maxcalllock);
2573 if (option_maxcalls) {
2574 if (countcalls >= option_maxcalls) {
2575 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2576 failed = -1;
2579 if (option_maxload) {
2580 getloadavg(&curloadavg, 1);
2581 if (curloadavg >= option_maxload) {
2582 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2583 failed = -1;
2586 if (!failed)
2587 countcalls++;
2588 ast_mutex_unlock(&maxcalllock);
2590 return failed;
2593 static void decrease_call_count(void)
2595 ast_mutex_lock(&maxcalllock);
2596 if (countcalls > 0)
2597 countcalls--;
2598 ast_mutex_unlock(&maxcalllock);
2601 static void destroy_exten(struct ast_exten *e)
2603 if (e->priority == PRIORITY_HINT)
2604 ast_remove_hint(e);
2606 if (e->datad)
2607 e->datad(e->data);
2608 free(e);
2611 static void *pbx_thread(void *data)
2613 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2614 answer this channel and get it going.
2616 /* NOTE:
2617 The launcher of this function _MUST_ increment 'countcalls'
2618 before invoking the function; it will be decremented when the
2619 PBX has finished running on the channel
2621 struct ast_channel *c = data;
2623 __ast_pbx_run(c);
2624 decrease_call_count();
2626 pthread_exit(NULL);
2628 return NULL;
2631 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2633 pthread_t t;
2634 pthread_attr_t attr;
2636 if (!c) {
2637 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2638 return AST_PBX_FAILED;
2641 if (increase_call_count(c))
2642 return AST_PBX_CALL_LIMIT;
2644 /* Start a new thread, and get something handling this channel. */
2645 pthread_attr_init(&attr);
2646 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2647 if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
2648 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2649 pthread_attr_destroy(&attr);
2650 return AST_PBX_FAILED;
2652 pthread_attr_destroy(&attr);
2654 return AST_PBX_SUCCESS;
2657 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2659 enum ast_pbx_result res = AST_PBX_SUCCESS;
2661 if (increase_call_count(c))
2662 return AST_PBX_CALL_LIMIT;
2664 res = __ast_pbx_run(c);
2665 decrease_call_count();
2667 return res;
2670 int ast_active_calls(void)
2672 return countcalls;
2675 int pbx_set_autofallthrough(int newval)
2677 int oldval = autofallthrough;
2678 autofallthrough = newval;
2679 return oldval;
2682 /* lookup for a context with a given name,
2683 * return with conlock held if found, NULL if not found
2685 static struct ast_context *find_context_locked(const char *context)
2687 struct ast_context *c = NULL;
2689 ast_rdlock_contexts();
2690 while ( (c = ast_walk_contexts(c)) ) {
2691 if (!strcmp(ast_get_context_name(c), context))
2692 return c;
2694 ast_unlock_contexts();
2696 return NULL;
2700 * This function locks contexts list by &conlist, search for the right context
2701 * structure, leave context list locked and call ast_context_remove_include2
2702 * which removes include, unlock contexts list and return ...
2704 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2706 int ret = -1;
2707 struct ast_context *c = find_context_locked(context);
2709 if (c) {
2710 /* found, remove include from this context ... */
2711 ret = ast_context_remove_include2(c, include, registrar);
2712 ast_unlock_contexts();
2714 return ret;
2718 * When we call this function, &conlock lock must be locked, because when
2719 * we giving *con argument, some process can remove/change this context
2720 * and after that there can be segfault.
2722 * This function locks given context, removes include, unlock context and
2723 * return.
2725 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2727 struct ast_include *i, *pi = NULL;
2728 int ret = -1;
2730 ast_mutex_lock(&con->lock);
2732 /* find our include */
2733 for (i = con->includes; i; pi = i, i = i->next) {
2734 if (!strcmp(i->name, include) &&
2735 (!registrar || !strcmp(i->registrar, registrar))) {
2736 /* remove from list */
2737 if (pi)
2738 pi->next = i->next;
2739 else
2740 con->includes = i->next;
2741 /* free include and return */
2742 free(i);
2743 ret = 0;
2744 break;
2748 ast_mutex_unlock(&con->lock);
2749 return ret;
2753 * \note This function locks contexts list by &conlist, search for the rigt context
2754 * structure, leave context list locked and call ast_context_remove_switch2
2755 * which removes switch, unlock contexts list and return ...
2757 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2759 int ret = -1; /* default error return */
2760 struct ast_context *c = find_context_locked(context);
2762 if (c) {
2763 /* remove switch from this context ... */
2764 ret = ast_context_remove_switch2(c, sw, data, registrar);
2765 ast_unlock_contexts();
2767 return ret;
2771 * \brief This function locks given context, removes switch, unlock context and
2772 * return.
2773 * \note When we call this function, &conlock lock must be locked, because when
2774 * we giving *con argument, some process can remove/change this context
2775 * and after that there can be segfault.
2778 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2780 struct ast_sw *i;
2781 int ret = -1;
2783 ast_mutex_lock(&con->lock);
2785 /* walk switches */
2786 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
2787 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2788 (!registrar || !strcmp(i->registrar, registrar))) {
2789 /* found, remove from list */
2790 AST_LIST_REMOVE_CURRENT(&con->alts, list);
2791 free(i); /* free switch and return */
2792 ret = 0;
2793 break;
2796 AST_LIST_TRAVERSE_SAFE_END
2798 ast_mutex_unlock(&con->lock);
2800 return ret;
2804 * \note This functions lock contexts list, search for the right context,
2805 * call ast_context_remove_extension2, unlock contexts list and return.
2806 * In this function we are using
2808 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2810 int ret = -1; /* default error return */
2811 struct ast_context *c = find_context_locked(context);
2813 if (c) { /* ... remove extension ... */
2814 ret = ast_context_remove_extension2(c, extension, priority, registrar);
2815 ast_unlock_contexts();
2817 return ret;
2821 * \brief This functionc locks given context, search for the right extension and
2822 * fires out all peer in this extensions with given priority. If priority
2823 * is set to 0, all peers are removed. After that, unlock context and
2824 * return.
2825 * \note When do you want to call this function, make sure that &conlock is locked,
2826 * because some process can handle with your *con context before you lock
2827 * it.
2830 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2832 struct ast_exten *exten, *prev_exten = NULL;
2833 struct ast_exten *peer;
2835 ast_mutex_lock(&con->lock);
2837 /* scan the extension list to find matching extension-registrar */
2838 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
2839 if (!strcmp(exten->exten, extension) &&
2840 (!registrar || !strcmp(exten->registrar, registrar)))
2841 break;
2843 if (!exten) {
2844 /* we can't find right extension */
2845 ast_mutex_unlock(&con->lock);
2846 return -1;
2849 /* should we free all peers in this extension? (priority == 0)? */
2850 if (priority == 0) {
2851 /* remove this extension from context list */
2852 if (prev_exten)
2853 prev_exten->next = exten->next;
2854 else
2855 con->root = exten->next;
2857 /* fire out all peers */
2858 while ( (peer = exten) ) {
2859 exten = peer->peer; /* prepare for next entry */
2860 destroy_exten(peer);
2862 } else {
2863 /* scan the priority list to remove extension with exten->priority == priority */
2864 struct ast_exten *previous_peer = NULL;
2866 for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
2867 if (peer->priority == priority &&
2868 (!registrar || !strcmp(peer->registrar, registrar) ))
2869 break; /* found our priority */
2871 if (!peer) { /* not found */
2872 ast_mutex_unlock(&con->lock);
2873 return -1;
2875 /* we are first priority extension? */
2876 if (!previous_peer) {
2878 * We are first in the priority chain, so must update the extension chain.
2879 * The next node is either the next priority or the next extension
2881 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
2883 if (!prev_exten) /* change the root... */
2884 con->root = next_node;
2885 else
2886 prev_exten->next = next_node; /* unlink */
2887 if (peer->peer) /* XXX update the new head of the pri list */
2888 peer->peer->next = peer->next;
2889 } else { /* easy, we are not first priority in extension */
2890 previous_peer->peer = peer->peer;
2893 /* now, free whole priority extension */
2894 destroy_exten(peer);
2895 /* XXX should we return -1 ? */
2897 ast_mutex_unlock(&con->lock);
2898 return 0;
2903 * \note This function locks contexts list by &conlist, searches for the right context
2904 * structure, and locks the macrolock mutex in that context.
2905 * macrolock is used to limit a macro to be executed by one call at a time.
2907 int ast_context_lockmacro(const char *context)
2909 struct ast_context *c = NULL;
2910 int ret = -1;
2912 ast_rdlock_contexts();
2914 while ((c = ast_walk_contexts(c))) {
2915 if (!strcmp(ast_get_context_name(c), context)) {
2916 ret = 0;
2917 break;
2921 ast_unlock_contexts();
2923 /* if we found context, lock macrolock */
2924 if (ret == 0)
2925 ret = ast_mutex_lock(&c->macrolock);
2927 return ret;
2931 * \note This function locks contexts list by &conlist, searches for the right context
2932 * structure, and unlocks the macrolock mutex in that context.
2933 * macrolock is used to limit a macro to be executed by one call at a time.
2935 int ast_context_unlockmacro(const char *context)
2937 struct ast_context *c = NULL;
2938 int ret = -1;
2940 ast_rdlock_contexts();
2942 while ((c = ast_walk_contexts(c))) {
2943 if (!strcmp(ast_get_context_name(c), context)) {
2944 ret = 0;
2945 break;
2949 ast_unlock_contexts();
2951 /* if we found context, unlock macrolock */
2952 if (ret == 0)
2953 ret = ast_mutex_unlock(&c->macrolock);
2955 return ret;
2958 /*! \brief Dynamically register a new dial plan application */
2959 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
2961 struct ast_app *tmp, *cur = NULL;
2962 char tmps[80];
2963 int length;
2965 AST_LIST_LOCK(&apps);
2966 AST_LIST_TRAVERSE(&apps, tmp, list) {
2967 if (!strcasecmp(app, tmp->name)) {
2968 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2969 AST_LIST_UNLOCK(&apps);
2970 return -1;
2974 length = sizeof(*tmp) + strlen(app) + 1;
2976 if (!(tmp = ast_calloc(1, length))) {
2977 AST_LIST_UNLOCK(&apps);
2978 return -1;
2981 strcpy(tmp->name, app);
2982 tmp->execute = execute;
2983 tmp->synopsis = synopsis;
2984 tmp->description = description;
2986 /* Store in alphabetical order */
2987 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
2988 if (strcasecmp(tmp->name, cur->name) < 0) {
2989 AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
2990 break;
2993 AST_LIST_TRAVERSE_SAFE_END
2994 if (!cur)
2995 AST_LIST_INSERT_TAIL(&apps, tmp, list);
2997 if (option_verbose > 1)
2998 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
3000 AST_LIST_UNLOCK(&apps);
3002 return 0;
3006 * Append to the list. We don't have a tail pointer because we need
3007 * to scan the list anyways to check for duplicates during insertion.
3009 int ast_register_switch(struct ast_switch *sw)
3011 struct ast_switch *tmp;
3013 AST_LIST_LOCK(&switches);
3014 AST_LIST_TRAVERSE(&switches, tmp, list) {
3015 if (!strcasecmp(tmp->name, sw->name)) {
3016 AST_LIST_UNLOCK(&switches);
3017 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
3018 return -1;
3021 AST_LIST_INSERT_TAIL(&switches, sw, list);
3022 AST_LIST_UNLOCK(&switches);
3024 return 0;
3027 void ast_unregister_switch(struct ast_switch *sw)
3029 AST_LIST_LOCK(&switches);
3030 AST_LIST_REMOVE(&switches, sw, list);
3031 AST_LIST_UNLOCK(&switches);
3035 * Help for CLI commands ...
3037 static char show_applications_help[] =
3038 "Usage: core show applications [{like|describing} <text>]\n"
3039 " List applications which are currently available.\n"
3040 " If 'like', <text> will be a substring of the app name\n"
3041 " If 'describing', <text> will be a substring of the description\n";
3043 static char show_functions_help[] =
3044 "Usage: core show functions [like <text>]\n"
3045 " List builtin functions, optionally only those matching a given string\n";
3047 static char show_switches_help[] =
3048 "Usage: core show switches\n"
3049 " List registered switches\n";
3051 static char show_hints_help[] =
3052 "Usage: core show hints\n"
3053 " List registered hints\n";
3055 static char show_globals_help[] =
3056 "Usage: core show globals\n"
3057 " List current global dialplan variables and their values\n";
3059 static char show_application_help[] =
3060 "Usage: core show application <application> [<application> [<application> [...]]]\n"
3061 " Describes a particular application.\n";
3063 static char show_function_help[] =
3064 "Usage: core show function <function>\n"
3065 " Describe a particular dialplan function.\n";
3067 static char show_dialplan_help[] =
3068 "Usage: dialplan show [exten@][context]\n"
3069 " Show dialplan\n";
3071 static char set_global_help[] =
3072 "Usage: core set global <name> <value>\n"
3073 " Set global dialplan variable <name> to <value>\n";
3077 * \brief 'show application' CLI command implementation functions ...
3081 * There is a possibility to show informations about more than one
3082 * application at one time. You can type 'show application Dial Echo' and
3083 * you will see informations about these two applications ...
3085 static char *complete_show_application(const char *line, const char *word, int pos, int state)
3087 struct ast_app *a;
3088 char *ret = NULL;
3089 int which = 0;
3090 int wordlen = strlen(word);
3092 /* return the n-th [partial] matching entry */
3093 AST_LIST_LOCK(&apps);
3094 AST_LIST_TRAVERSE(&apps, a, list) {
3095 if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
3096 ret = strdup(a->name);
3097 break;
3100 AST_LIST_UNLOCK(&apps);
3102 return ret;
3105 static int handle_show_application_deprecated(int fd, int argc, char *argv[])
3107 struct ast_app *a;
3108 int app, no_registered_app = 1;
3110 if (argc < 3)
3111 return RESULT_SHOWUSAGE;
3113 /* ... go through all applications ... */
3114 AST_LIST_LOCK(&apps);
3115 AST_LIST_TRAVERSE(&apps, a, list) {
3116 /* ... compare this application name with all arguments given
3117 * to 'show application' command ... */
3118 for (app = 2; app < argc; app++) {
3119 if (!strcasecmp(a->name, argv[app])) {
3120 /* Maximum number of characters added by terminal coloring is 22 */
3121 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3122 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3123 int synopsis_size, description_size;
3125 no_registered_app = 0;
3127 if (a->synopsis)
3128 synopsis_size = strlen(a->synopsis) + 23;
3129 else
3130 synopsis_size = strlen("Not available") + 23;
3131 synopsis = alloca(synopsis_size);
3133 if (a->description)
3134 description_size = strlen(a->description) + 23;
3135 else
3136 description_size = strlen("Not available") + 23;
3137 description = alloca(description_size);
3139 if (synopsis && description) {
3140 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3141 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3142 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3143 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3144 term_color(synopsis,
3145 a->synopsis ? a->synopsis : "Not available",
3146 COLOR_CYAN, 0, synopsis_size);
3147 term_color(description,
3148 a->description ? a->description : "Not available",
3149 COLOR_CYAN, 0, description_size);
3151 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3152 } else {
3153 /* ... one of our applications, show info ...*/
3154 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3155 "[Synopsis]\n %s\n\n"
3156 "[Description]\n%s\n",
3157 a->name,
3158 a->synopsis ? a->synopsis : "Not available",
3159 a->description ? a->description : "Not available");
3164 AST_LIST_UNLOCK(&apps);
3166 /* we found at least one app? no? */
3167 if (no_registered_app) {
3168 ast_cli(fd, "Your application(s) is (are) not registered\n");
3169 return RESULT_FAILURE;
3172 return RESULT_SUCCESS;
3175 static int handle_show_application(int fd, int argc, char *argv[])
3177 struct ast_app *a;
3178 int app, no_registered_app = 1;
3180 if (argc < 4)
3181 return RESULT_SHOWUSAGE;
3183 /* ... go through all applications ... */
3184 AST_LIST_LOCK(&apps);
3185 AST_LIST_TRAVERSE(&apps, a, list) {
3186 /* ... compare this application name with all arguments given
3187 * to 'show application' command ... */
3188 for (app = 3; app < argc; app++) {
3189 if (!strcasecmp(a->name, argv[app])) {
3190 /* Maximum number of characters added by terminal coloring is 22 */
3191 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3192 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3193 int synopsis_size, description_size;
3195 no_registered_app = 0;
3197 if (a->synopsis)
3198 synopsis_size = strlen(a->synopsis) + 23;
3199 else
3200 synopsis_size = strlen("Not available") + 23;
3201 synopsis = alloca(synopsis_size);
3203 if (a->description)
3204 description_size = strlen(a->description) + 23;
3205 else
3206 description_size = strlen("Not available") + 23;
3207 description = alloca(description_size);
3209 if (synopsis && description) {
3210 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3211 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3212 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3213 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3214 term_color(synopsis,
3215 a->synopsis ? a->synopsis : "Not available",
3216 COLOR_CYAN, 0, synopsis_size);
3217 term_color(description,
3218 a->description ? a->description : "Not available",
3219 COLOR_CYAN, 0, description_size);
3221 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3222 } else {
3223 /* ... one of our applications, show info ...*/
3224 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3225 "[Synopsis]\n %s\n\n"
3226 "[Description]\n%s\n",
3227 a->name,
3228 a->synopsis ? a->synopsis : "Not available",
3229 a->description ? a->description : "Not available");
3234 AST_LIST_UNLOCK(&apps);
3236 /* we found at least one app? no? */
3237 if (no_registered_app) {
3238 ast_cli(fd, "Your application(s) is (are) not registered\n");
3239 return RESULT_FAILURE;
3242 return RESULT_SUCCESS;
3245 /*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
3246 static int handle_show_hints(int fd, int argc, char *argv[])
3248 struct ast_hint *hint;
3249 int num = 0;
3250 int watchers;
3251 struct ast_state_cb *watcher;
3253 if (AST_LIST_EMPTY(&hints)) {
3254 ast_cli(fd, "There are no registered dialplan hints\n");
3255 return RESULT_SUCCESS;
3257 /* ... we have hints ... */
3258 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
3259 AST_LIST_LOCK(&hints);
3260 AST_LIST_TRAVERSE(&hints, hint, list) {
3261 watchers = 0;
3262 for (watcher = hint->callbacks; watcher; watcher = watcher->next)
3263 watchers++;
3264 ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
3265 ast_get_extension_name(hint->exten),
3266 ast_get_context_name(ast_get_extension_context(hint->exten)),
3267 ast_get_extension_app(hint->exten),
3268 ast_extension_state2str(hint->laststate), watchers);
3269 num++;
3271 ast_cli(fd, "----------------\n");
3272 ast_cli(fd, "- %d hints registered\n", num);
3273 AST_LIST_UNLOCK(&hints);
3274 return RESULT_SUCCESS;
3277 /*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
3278 static int handle_show_switches(int fd, int argc, char *argv[])
3280 struct ast_switch *sw;
3282 AST_LIST_LOCK(&switches);
3284 if (AST_LIST_EMPTY(&switches)) {
3285 AST_LIST_UNLOCK(&switches);
3286 ast_cli(fd, "There are no registered alternative switches\n");
3287 return RESULT_SUCCESS;
3290 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
3291 AST_LIST_TRAVERSE(&switches, sw, list)
3292 ast_cli(fd, "%s: %s\n", sw->name, sw->description);
3294 AST_LIST_UNLOCK(&switches);
3296 return RESULT_SUCCESS;
3300 * 'show applications' CLI command implementation functions ...
3302 static int handle_show_applications_deprecated(int fd, int argc, char *argv[])
3304 struct ast_app *a;
3305 int like = 0, describing = 0;
3306 int total_match = 0; /* Number of matches in like clause */
3307 int total_apps = 0; /* Number of apps registered */
3309 AST_LIST_LOCK(&apps);
3311 if (AST_LIST_EMPTY(&apps)) {
3312 ast_cli(fd, "There are no registered applications\n");
3313 AST_LIST_UNLOCK(&apps);
3314 return -1;
3317 /* show applications like <keyword> */
3318 if ((argc == 4) && (!strcmp(argv[2], "like"))) {
3319 like = 1;
3320 } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
3321 describing = 1;
3324 /* show applications describing <keyword1> [<keyword2>] [...] */
3325 if ((!like) && (!describing)) {
3326 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3327 } else {
3328 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3331 AST_LIST_TRAVERSE(&apps, a, list) {
3332 int printapp = 0;
3333 total_apps++;
3334 if (like) {
3335 if (strcasestr(a->name, argv[3])) {
3336 printapp = 1;
3337 total_match++;
3339 } else if (describing) {
3340 if (a->description) {
3341 /* Match all words on command line */
3342 int i;
3343 printapp = 1;
3344 for (i = 3; i < argc; i++) {
3345 if (!strcasestr(a->description, argv[i])) {
3346 printapp = 0;
3347 } else {
3348 total_match++;
3352 } else {
3353 printapp = 1;
3356 if (printapp) {
3357 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3360 if ((!like) && (!describing)) {
3361 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3362 } else {
3363 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3366 AST_LIST_UNLOCK(&apps);
3368 return RESULT_SUCCESS;
3370 static int handle_show_applications(int fd, int argc, char *argv[])
3372 struct ast_app *a;
3373 int like = 0, describing = 0;
3374 int total_match = 0; /* Number of matches in like clause */
3375 int total_apps = 0; /* Number of apps registered */
3377 AST_LIST_LOCK(&apps);
3379 if (AST_LIST_EMPTY(&apps)) {
3380 ast_cli(fd, "There are no registered applications\n");
3381 AST_LIST_UNLOCK(&apps);
3382 return -1;
3385 /* core list applications like <keyword> */
3386 if ((argc == 5) && (!strcmp(argv[3], "like"))) {
3387 like = 1;
3388 } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) {
3389 describing = 1;
3392 /* core list applications describing <keyword1> [<keyword2>] [...] */
3393 if ((!like) && (!describing)) {
3394 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3395 } else {
3396 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3399 AST_LIST_TRAVERSE(&apps, a, list) {
3400 int printapp = 0;
3401 total_apps++;
3402 if (like) {
3403 if (strcasestr(a->name, argv[4])) {
3404 printapp = 1;
3405 total_match++;
3407 } else if (describing) {
3408 if (a->description) {
3409 /* Match all words on command line */
3410 int i;
3411 printapp = 1;
3412 for (i = 4; i < argc; i++) {
3413 if (!strcasestr(a->description, argv[i])) {
3414 printapp = 0;
3415 } else {
3416 total_match++;
3420 } else {
3421 printapp = 1;
3424 if (printapp) {
3425 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3428 if ((!like) && (!describing)) {
3429 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3430 } else {
3431 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3434 AST_LIST_UNLOCK(&apps);
3436 return RESULT_SUCCESS;
3439 static char *complete_show_applications_deprecated(const char *line, const char *word, int pos, int state)
3441 static char* choices[] = { "like", "describing", NULL };
3443 return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
3446 static char *complete_show_applications(const char *line, const char *word, int pos, int state)
3448 static char* choices[] = { "like", "describing", NULL };
3450 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
3454 * 'show dialplan' CLI command implementation functions ...
3456 static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
3457 int state)
3459 struct ast_context *c = NULL;
3460 char *ret = NULL;
3461 int which = 0;
3462 int wordlen;
3464 /* we are do completion of [exten@]context on second position only */
3465 if (pos != 2)
3466 return NULL;
3468 ast_rdlock_contexts();
3470 wordlen = strlen(word);
3472 /* walk through all contexts and return the n-th match */
3473 while ( (c = ast_walk_contexts(c)) ) {
3474 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
3475 ret = ast_strdup(ast_get_context_name(c));
3476 break;
3480 ast_unlock_contexts();
3482 return ret;
3485 struct dialplan_counters {
3486 int total_context;
3487 int total_exten;
3488 int total_prio;
3489 int context_existence;
3490 int extension_existence;
3493 /*! \brief helper function to print an extension */
3494 static void print_ext(struct ast_exten *e, char * buf, int buflen)
3496 int prio = ast_get_extension_priority(e);
3497 if (prio == PRIORITY_HINT) {
3498 snprintf(buf, buflen, "hint: %s",
3499 ast_get_extension_app(e));
3500 } else {
3501 snprintf(buf, buflen, "%d. %s(%s)",
3502 prio, ast_get_extension_app(e),
3503 (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
3507 /* XXX not verified */
3508 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[])
3510 struct ast_context *c = NULL;
3511 int res = 0, old_total_exten = dpc->total_exten;
3513 ast_rdlock_contexts();
3515 /* walk all contexts ... */
3516 while ( (c = ast_walk_contexts(c)) ) {
3517 struct ast_exten *e;
3518 struct ast_include *i;
3519 struct ast_ignorepat *ip;
3520 char buf[256], buf2[256];
3521 int context_info_printed = 0;
3523 if (context && strcmp(ast_get_context_name(c), context))
3524 continue; /* skip this one, name doesn't match */
3526 dpc->context_existence = 1;
3528 ast_lock_context(c);
3530 /* are we looking for exten too? if yes, we print context
3531 * only if we find our extension.
3532 * Otherwise print context even if empty ?
3533 * XXX i am not sure how the rinclude is handled.
3534 * I think it ought to go inside.
3536 if (!exten) {
3537 dpc->total_context++;
3538 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3539 ast_get_context_name(c), ast_get_context_registrar(c));
3540 context_info_printed = 1;
3543 /* walk extensions ... */
3544 e = NULL;
3545 while ( (e = ast_walk_context_extensions(c, e)) ) {
3546 struct ast_exten *p;
3548 if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
3549 continue; /* skip, extension match failed */
3551 dpc->extension_existence = 1;
3553 /* may we print context info? */
3554 if (!context_info_printed) {
3555 dpc->total_context++;
3556 if (rinclude) { /* TODO Print more info about rinclude */
3557 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
3558 ast_get_context_name(c), ast_get_context_registrar(c));
3559 } else {
3560 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3561 ast_get_context_name(c), ast_get_context_registrar(c));
3563 context_info_printed = 1;
3565 dpc->total_prio++;
3567 /* write extension name and first peer */
3568 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
3570 print_ext(e, buf2, sizeof(buf2));
3572 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
3573 ast_get_extension_registrar(e));
3575 dpc->total_exten++;
3576 /* walk next extension peers */
3577 p = e; /* skip the first one, we already got it */
3578 while ( (p = ast_walk_extension_priorities(e, p)) ) {
3579 const char *el = ast_get_extension_label(p);
3580 dpc->total_prio++;
3581 if (el)
3582 snprintf(buf, sizeof(buf), " [%s]", el);
3583 else
3584 buf[0] = '\0';
3585 print_ext(p, buf2, sizeof(buf2));
3587 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
3588 ast_get_extension_registrar(p));
3592 /* walk included and write info ... */
3593 i = NULL;
3594 while ( (i = ast_walk_context_includes(c, i)) ) {
3595 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
3596 if (exten) {
3597 /* Check all includes for the requested extension */
3598 if (includecount >= AST_PBX_MAX_STACK) {
3599 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
3600 } else {
3601 int dupe=0;
3602 int x;
3603 for (x=0;x<includecount;x++) {
3604 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
3605 dupe++;
3606 break;
3609 if (!dupe) {
3610 includes[includecount] = ast_get_include_name(i);
3611 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
3612 } else {
3613 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
3616 } else {
3617 ast_cli(fd, " Include => %-45s [%s]\n",
3618 buf, ast_get_include_registrar(i));
3622 /* walk ignore patterns and write info ... */
3623 ip = NULL;
3624 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
3625 const char *ipname = ast_get_ignorepat_name(ip);
3626 char ignorepat[AST_MAX_EXTENSION];
3627 snprintf(buf, sizeof(buf), "'%s'", ipname);
3628 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
3629 if (!exten || ast_extension_match(ignorepat, exten)) {
3630 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
3631 buf, ast_get_ignorepat_registrar(ip));
3634 if (!rinclude) {
3635 struct ast_sw *sw = NULL;
3636 while ( (sw = ast_walk_context_switches(c, sw)) ) {
3637 snprintf(buf, sizeof(buf), "'%s/%s'",
3638 ast_get_switch_name(sw),
3639 ast_get_switch_data(sw));
3640 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
3641 buf, ast_get_switch_registrar(sw));
3645 ast_unlock_context(c);
3647 /* if we print something in context, make an empty line */
3648 if (context_info_printed)
3649 ast_cli(fd, "\r\n");
3651 ast_unlock_contexts();
3653 return (dpc->total_exten == old_total_exten) ? -1 : res;
3656 static int handle_show_dialplan(int fd, int argc, char *argv[])
3658 char *exten = NULL, *context = NULL;
3659 /* Variables used for different counters */
3660 struct dialplan_counters counters;
3662 const char *incstack[AST_PBX_MAX_STACK];
3663 memset(&counters, 0, sizeof(counters));
3665 if (argc != 2 && argc != 3)
3666 return RESULT_SHOWUSAGE;
3668 /* we obtain [exten@]context? if yes, split them ... */
3669 if (argc == 3) {
3670 if (strchr(argv[2], '@')) { /* split into exten & context */
3671 context = ast_strdupa(argv[2]);
3672 exten = strsep(&context, "@");
3673 /* change empty strings to NULL */
3674 if (ast_strlen_zero(exten))
3675 exten = NULL;
3676 } else { /* no '@' char, only context given */
3677 context = argv[2];
3679 if (ast_strlen_zero(context))
3680 context = NULL;
3682 /* else Show complete dial plan, context and exten are NULL */
3683 show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
3685 /* check for input failure and throw some error messages */
3686 if (context && !counters.context_existence) {
3687 ast_cli(fd, "There is no existence of '%s' context\n", context);
3688 return RESULT_FAILURE;
3691 if (exten && !counters.extension_existence) {
3692 if (context)
3693 ast_cli(fd, "There is no existence of %s@%s extension\n",
3694 exten, context);
3695 else
3696 ast_cli(fd,
3697 "There is no existence of '%s' extension in all contexts\n",
3698 exten);
3699 return RESULT_FAILURE;
3702 ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
3703 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
3704 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
3705 counters.total_context, counters.total_context == 1 ? "context" : "contexts");
3707 /* everything ok */
3708 return RESULT_SUCCESS;
3711 /*! \brief CLI support for listing global variables in a parseable way */
3712 static int handle_show_globals(int fd, int argc, char *argv[])
3714 int i = 0;
3715 struct ast_var_t *newvariable;
3717 ast_mutex_lock(&globalslock);
3718 AST_LIST_TRAVERSE (&globals, newvariable, entries) {
3719 i++;
3720 ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
3722 ast_mutex_unlock(&globalslock);
3723 ast_cli(fd, "\n -- %d variables\n", i);
3725 return RESULT_SUCCESS;
3728 /*! \brief CLI support for setting global variables */
3729 static int handle_set_global_deprecated(int fd, int argc, char *argv[])
3731 if (argc != 4)
3732 return RESULT_SHOWUSAGE;
3734 pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
3735 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[2], argv[3]);
3737 return RESULT_SUCCESS;
3741 static int handle_set_global(int fd, int argc, char *argv[])
3743 if (argc != 5)
3744 return RESULT_SHOWUSAGE;
3746 pbx_builtin_setvar_helper(NULL, argv[3], argv[4]);
3747 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]);
3749 return RESULT_SUCCESS;
3755 * CLI entries for upper commands ...
3757 static struct ast_cli_entry cli_show_applications_deprecated = {
3758 { "show", "applications", NULL },
3759 handle_show_applications_deprecated, NULL,
3760 NULL, complete_show_applications_deprecated };
3762 static struct ast_cli_entry cli_show_functions_deprecated = {
3763 { "show", "functions", NULL },
3764 handle_show_functions_deprecated, NULL,
3765 NULL };
3767 static struct ast_cli_entry cli_show_switches_deprecated = {
3768 { "show", "switches", NULL },
3769 handle_show_switches, NULL,
3770 NULL };
3772 static struct ast_cli_entry cli_show_hints_deprecated = {
3773 { "show", "hints", NULL },
3774 handle_show_hints, NULL,
3775 NULL };
3777 static struct ast_cli_entry cli_show_globals_deprecated = {
3778 { "show", "globals", NULL },
3779 handle_show_globals, NULL,
3780 NULL };
3782 static struct ast_cli_entry cli_show_function_deprecated = {
3783 { "show" , "function", NULL },
3784 handle_show_function_deprecated, NULL,
3785 NULL, complete_show_function };
3787 static struct ast_cli_entry cli_show_application_deprecated = {
3788 { "show", "application", NULL },
3789 handle_show_application_deprecated, NULL,
3790 NULL, complete_show_application };
3792 static struct ast_cli_entry cli_show_dialplan_deprecated = {
3793 { "show", "dialplan", NULL },
3794 handle_show_dialplan, NULL,
3795 NULL, complete_show_dialplan_context };
3797 static struct ast_cli_entry cli_set_global_deprecated = {
3798 { "set", "global", NULL },
3799 handle_set_global_deprecated, NULL,
3800 NULL };
3802 static struct ast_cli_entry pbx_cli[] = {
3803 { { "core", "show", "applications", NULL },
3804 handle_show_applications, "Shows registered dialplan applications",
3805 show_applications_help, complete_show_applications, &cli_show_applications_deprecated },
3807 { { "core", "show", "functions", NULL },
3808 handle_show_functions, "Shows registered dialplan functions",
3809 show_functions_help, NULL, &cli_show_functions_deprecated },
3811 { { "core", "show", "switches", NULL },
3812 handle_show_switches, "Show alternative switches",
3813 show_switches_help, NULL, &cli_show_switches_deprecated },
3815 { { "core", "show", "hints", NULL },
3816 handle_show_hints, "Show dialplan hints",
3817 show_hints_help, NULL, &cli_show_hints_deprecated },
3819 { { "core", "show", "globals", NULL },
3820 handle_show_globals, "Show global dialplan variables",
3821 show_globals_help, NULL, &cli_show_globals_deprecated },
3823 { { "core", "show" , "function", NULL },
3824 handle_show_function, "Describe a specific dialplan function",
3825 show_function_help, complete_show_function, &cli_show_function_deprecated },
3827 { { "core", "show", "application", NULL },
3828 handle_show_application, "Describe a specific dialplan application",
3829 show_application_help, complete_show_application, &cli_show_application_deprecated },
3831 { { "core", "set", "global", NULL },
3832 handle_set_global, "Set global dialplan variable",
3833 set_global_help, NULL, &cli_set_global_deprecated },
3835 { { "dialplan", "show", NULL },
3836 handle_show_dialplan, "Show dialplan",
3837 show_dialplan_help, complete_show_dialplan_context, &cli_show_dialplan_deprecated },
3840 int ast_unregister_application(const char *app)
3842 struct ast_app *tmp;
3844 AST_LIST_LOCK(&apps);
3845 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
3846 if (!strcasecmp(app, tmp->name)) {
3847 AST_LIST_REMOVE_CURRENT(&apps, list);
3848 if (option_verbose > 1)
3849 ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
3850 free(tmp);
3851 break;
3854 AST_LIST_TRAVERSE_SAFE_END
3855 AST_LIST_UNLOCK(&apps);
3857 return tmp ? 0 : -1;
3860 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
3862 struct ast_context *tmp, **local_contexts;
3863 int length = sizeof(struct ast_context) + strlen(name) + 1;
3865 if (!extcontexts) {
3866 ast_rdlock_contexts();
3867 local_contexts = &contexts;
3868 } else
3869 local_contexts = extcontexts;
3871 for (tmp = *local_contexts; tmp; tmp = tmp->next) {
3872 if (!strcasecmp(tmp->name, name)) {
3873 if (!existsokay) {
3874 ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
3875 tmp = NULL;
3877 if (!extcontexts)
3878 ast_unlock_contexts();
3879 return tmp;
3883 if (!extcontexts)
3884 ast_unlock_contexts();
3886 if ((tmp = ast_calloc(1, length))) {
3887 ast_mutex_init(&tmp->lock);
3888 ast_mutex_init(&tmp->macrolock);
3889 strcpy(tmp->name, name);
3890 tmp->registrar = registrar;
3891 if (!extcontexts)
3892 ast_wrlock_contexts();
3893 tmp->next = *local_contexts;
3894 *local_contexts = tmp;
3895 if (!extcontexts)
3896 ast_unlock_contexts();
3897 if (option_debug)
3898 ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
3899 if (option_verbose > 2)
3900 ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
3903 return tmp;
3906 struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3908 return __ast_context_create(extcontexts, name, registrar, 0);
3911 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3913 return __ast_context_create(extcontexts, name, registrar, 1);
3915 void __ast_context_destroy(struct ast_context *con, const char *registrar);
3917 struct store_hint {
3918 char *context;
3919 char *exten;
3920 struct ast_state_cb *callbacks;
3921 int laststate;
3922 AST_LIST_ENTRY(store_hint) list;
3923 char data[1];
3926 AST_LIST_HEAD(store_hints, store_hint);
3928 /* XXX this does not check that multiple contexts are merged */
3929 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
3931 struct ast_context *tmp, *lasttmp = NULL;
3932 struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
3933 struct store_hint *this;
3934 struct ast_hint *hint;
3935 struct ast_exten *exten;
3936 int length;
3937 struct ast_state_cb *thiscb, *prevcb;
3939 /* it is very important that this function hold the hint list lock _and_ the conlock
3940 during its operation; not only do we need to ensure that the list of contexts
3941 and extensions does not change, but also that no hint callbacks (watchers) are
3942 added or removed during the merge/delete process
3944 in addition, the locks _must_ be taken in this order, because there are already
3945 other code paths that use this order
3947 ast_wrlock_contexts();
3948 AST_LIST_LOCK(&hints);
3950 /* preserve all watchers for hints associated with this registrar */
3951 AST_LIST_TRAVERSE(&hints, hint, list) {
3952 if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
3953 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
3954 if (!(this = ast_calloc(1, length)))
3955 continue;
3956 this->callbacks = hint->callbacks;
3957 hint->callbacks = NULL;
3958 this->laststate = hint->laststate;
3959 this->context = this->data;
3960 strcpy(this->data, hint->exten->parent->name);
3961 this->exten = this->data + strlen(this->context) + 1;
3962 strcpy(this->exten, hint->exten->exten);
3963 AST_LIST_INSERT_HEAD(&store, this, list);
3967 tmp = *extcontexts;
3968 if (registrar) {
3969 /* XXX remove previous contexts from same registrar */
3970 if (option_debug)
3971 ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
3972 __ast_context_destroy(NULL,registrar);
3973 while (tmp) {
3974 lasttmp = tmp;
3975 tmp = tmp->next;
3977 } else {
3978 /* XXX remove contexts with the same name */
3979 while (tmp) {
3980 ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
3981 __ast_context_destroy(tmp,tmp->registrar);
3982 lasttmp = tmp;
3983 tmp = tmp->next;
3986 if (lasttmp) {
3987 lasttmp->next = contexts;
3988 contexts = *extcontexts;
3989 *extcontexts = NULL;
3990 } else
3991 ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
3993 /* restore the watchers for hints that can be found; notify those that
3994 cannot be restored
3996 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
3997 struct pbx_find_info q = { .stacklen = 0 };
3998 exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
3999 /* Find the hint in the list of hints */
4000 AST_LIST_TRAVERSE(&hints, hint, list) {
4001 if (hint->exten == exten)
4002 break;
4004 if (!exten || !hint) {
4005 /* this hint has been removed, notify the watchers */
4006 prevcb = NULL;
4007 thiscb = this->callbacks;
4008 while (thiscb) {
4009 prevcb = thiscb;
4010 thiscb = thiscb->next;
4011 prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
4012 free(prevcb);
4014 } else {
4015 thiscb = this->callbacks;
4016 while (thiscb->next)
4017 thiscb = thiscb->next;
4018 thiscb->next = hint->callbacks;
4019 hint->callbacks = this->callbacks;
4020 hint->laststate = this->laststate;
4022 free(this);
4025 AST_LIST_UNLOCK(&hints);
4026 ast_unlock_contexts();
4028 return;
4032 * errno values
4033 * EBUSY - can't lock
4034 * ENOENT - no existence of context
4036 int ast_context_add_include(const char *context, const char *include, const char *registrar)
4038 int ret = -1;
4039 struct ast_context *c = find_context_locked(context);
4041 if (c) {
4042 ret = ast_context_add_include2(c, include, registrar);
4043 ast_unlock_contexts();
4045 return ret;
4048 /*! \brief Helper for get_range.
4049 * return the index of the matching entry, starting from 1.
4050 * If names is not supplied, try numeric values.
4052 static int lookup_name(const char *s, char *const names[], int max)
4054 int i;
4056 if (names) {
4057 for (i = 0; names[i]; i++) {
4058 if (!strcasecmp(s, names[i]))
4059 return i+1;
4061 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
4062 return i;
4064 return 0; /* error return */
4067 /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
4068 * names, if supplied, is an array of names that should be mapped to numbers.
4070 static unsigned get_range(char *src, int max, char *const names[], const char *msg)
4072 int s, e; /* start and ending position */
4073 unsigned int mask = 0;
4075 /* Check for whole range */
4076 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
4077 s = 0;
4078 e = max - 1;
4079 } else {
4080 /* Get start and ending position */
4081 char *c = strchr(src, '-');
4082 if (c)
4083 *c++ = '\0';
4084 /* Find the start */
4085 s = lookup_name(src, names, max);
4086 if (!s) {
4087 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
4088 return 0;
4090 s--;
4091 if (c) { /* find end of range */
4092 e = lookup_name(c, names, max);
4093 if (!e) {
4094 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
4095 return 0;
4097 e--;
4098 } else
4099 e = s;
4101 /* Fill the mask. Remember that ranges are cyclic */
4102 mask = 1 << e; /* initialize with last element */
4103 while (s != e) {
4104 if (s >= max) {
4105 s = 0;
4106 mask |= (1 << s);
4107 } else {
4108 mask |= (1 << s);
4109 s++;
4112 return mask;
4115 /*! \brief store a bitmask of valid times, one bit each 2 minute */
4116 static void get_timerange(struct ast_timing *i, char *times)
4118 char *e;
4119 int x;
4120 int s1, s2;
4121 int e1, e2;
4122 /* int cth, ctm; */
4124 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
4125 memset(i->minmask, 0, sizeof(i->minmask));
4127 /* 2-minutes per bit, since the mask has only 32 bits :( */
4128 /* Star is all times */
4129 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
4130 for (x=0; x<24; x++)
4131 i->minmask[x] = 0x3fffffff; /* 30 bits */
4132 return;
4134 /* Otherwise expect a range */
4135 e = strchr(times, '-');
4136 if (!e) {
4137 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
4138 return;
4140 *e++ = '\0';
4141 /* XXX why skip non digits ? */
4142 while (*e && !isdigit(*e))
4143 e++;
4144 if (!*e) {
4145 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
4146 return;
4148 if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
4149 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
4150 return;
4152 if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
4153 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
4154 return;
4156 /* XXX this needs to be optimized */
4157 #if 1
4158 s1 = s1 * 30 + s2/2;
4159 if ((s1 < 0) || (s1 >= 24*30)) {
4160 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
4161 return;
4163 e1 = e1 * 30 + e2/2;
4164 if ((e1 < 0) || (e1 >= 24*30)) {
4165 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
4166 return;
4168 /* Go through the time and enable each appropriate bit */
4169 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
4170 i->minmask[x/30] |= (1 << (x % 30));
4172 /* Do the last one */
4173 i->minmask[x/30] |= (1 << (x % 30));
4174 #else
4175 for (cth=0; cth<24; cth++) {
4176 /* Initialize masks to blank */
4177 i->minmask[cth] = 0;
4178 for (ctm=0; ctm<30; ctm++) {
4179 if (
4180 /* First hour with more than one hour */
4181 (((cth == s1) && (ctm >= s2)) &&
4182 ((cth < e1)))
4183 /* Only one hour */
4184 || (((cth == s1) && (ctm >= s2)) &&
4185 ((cth == e1) && (ctm <= e2)))
4186 /* In between first and last hours (more than 2 hours) */
4187 || ((cth > s1) &&
4188 (cth < e1))
4189 /* Last hour with more than one hour */
4190 || ((cth > s1) &&
4191 ((cth == e1) && (ctm <= e2)))
4193 i->minmask[cth] |= (1 << (ctm / 2));
4196 #endif
4197 /* All done */
4198 return;
4201 static char *days[] =
4203 "sun",
4204 "mon",
4205 "tue",
4206 "wed",
4207 "thu",
4208 "fri",
4209 "sat",
4210 NULL,
4213 static char *months[] =
4215 "jan",
4216 "feb",
4217 "mar",
4218 "apr",
4219 "may",
4220 "jun",
4221 "jul",
4222 "aug",
4223 "sep",
4224 "oct",
4225 "nov",
4226 "dec",
4227 NULL,
4230 int ast_build_timing(struct ast_timing *i, const char *info_in)
4232 char info_save[256];
4233 char *info;
4235 /* Check for empty just in case */
4236 if (ast_strlen_zero(info_in))
4237 return 0;
4238 /* make a copy just in case we were passed a static string */
4239 ast_copy_string(info_save, info_in, sizeof(info_save));
4240 info = info_save;
4241 /* Assume everything except time */
4242 i->monthmask = 0xfff; /* 12 bits */
4243 i->daymask = 0x7fffffffU; /* 31 bits */
4244 i->dowmask = 0x7f; /* 7 bits */
4245 /* on each call, use strsep() to move info to the next argument */
4246 get_timerange(i, strsep(&info, "|"));
4247 if (info)
4248 i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
4249 if (info)
4250 i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
4251 if (info)
4252 i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
4253 return 1;
4256 int ast_check_timing(const struct ast_timing *i)
4258 struct tm tm;
4259 time_t t = time(NULL);
4261 ast_localtime(&t, &tm, NULL);
4263 /* If it's not the right month, return */
4264 if (!(i->monthmask & (1 << tm.tm_mon)))
4265 return 0;
4267 /* If it's not that time of the month.... */
4268 /* Warning, tm_mday has range 1..31! */
4269 if (!(i->daymask & (1 << (tm.tm_mday-1))))
4270 return 0;
4272 /* If it's not the right day of the week */
4273 if (!(i->dowmask & (1 << tm.tm_wday)))
4274 return 0;
4276 /* Sanity check the hour just to be safe */
4277 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
4278 ast_log(LOG_WARNING, "Insane time...\n");
4279 return 0;
4282 /* Now the tough part, we calculate if it fits
4283 in the right time based on min/hour */
4284 if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
4285 return 0;
4287 /* If we got this far, then we're good */
4288 return 1;
4292 * errno values
4293 * ENOMEM - out of memory
4294 * EBUSY - can't lock
4295 * EEXIST - already included
4296 * EINVAL - there is no existence of context for inclusion
4298 int ast_context_add_include2(struct ast_context *con, const char *value,
4299 const char *registrar)
4301 struct ast_include *new_include;
4302 char *c;
4303 struct ast_include *i, *il = NULL; /* include, include_last */
4304 int length;
4305 char *p;
4307 length = sizeof(struct ast_include);
4308 length += 2 * (strlen(value) + 1);
4310 /* allocate new include structure ... */
4311 if (!(new_include = ast_calloc(1, length)))
4312 return -1;
4313 /* Fill in this structure. Use 'p' for assignments, as the fields
4314 * in the structure are 'const char *'
4316 p = new_include->stuff;
4317 new_include->name = p;
4318 strcpy(p, value);
4319 p += strlen(value) + 1;
4320 new_include->rname = p;
4321 strcpy(p, value);
4322 /* Strip off timing info, and process if it is there */
4323 if ( (c = strchr(p, '|')) ) {
4324 *c++ = '\0';
4325 new_include->hastime = ast_build_timing(&(new_include->timing), c);
4327 new_include->next = NULL;
4328 new_include->registrar = registrar;
4330 ast_mutex_lock(&con->lock);
4332 /* ... go to last include and check if context is already included too... */
4333 for (i = con->includes; i; i = i->next) {
4334 if (!strcasecmp(i->name, new_include->name)) {
4335 free(new_include);
4336 ast_mutex_unlock(&con->lock);
4337 errno = EEXIST;
4338 return -1;
4340 il = i;
4343 /* ... include new context into context list, unlock, return */
4344 if (il)
4345 il->next = new_include;
4346 else
4347 con->includes = new_include;
4348 if (option_verbose > 2)
4349 ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
4350 ast_mutex_unlock(&con->lock);
4352 return 0;
4356 * errno values
4357 * EBUSY - can't lock
4358 * ENOENT - no existence of context
4360 int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
4362 int ret = -1;
4363 struct ast_context *c = find_context_locked(context);
4365 if (c) { /* found, add switch to this context */
4366 ret = ast_context_add_switch2(c, sw, data, eval, registrar);
4367 ast_unlock_contexts();
4369 return ret;
4373 * errno values
4374 * ENOMEM - out of memory
4375 * EBUSY - can't lock
4376 * EEXIST - already included
4377 * EINVAL - there is no existence of context for inclusion
4379 int ast_context_add_switch2(struct ast_context *con, const char *value,
4380 const char *data, int eval, const char *registrar)
4382 struct ast_sw *new_sw;
4383 struct ast_sw *i;
4384 int length;
4385 char *p;
4387 length = sizeof(struct ast_sw);
4388 length += strlen(value) + 1;
4389 if (data)
4390 length += strlen(data);
4391 length++;
4392 if (eval) {
4393 /* Create buffer for evaluation of variables */
4394 length += SWITCH_DATA_LENGTH;
4395 length++;
4398 /* allocate new sw structure ... */
4399 if (!(new_sw = ast_calloc(1, length)))
4400 return -1;
4401 /* ... fill in this structure ... */
4402 p = new_sw->stuff;
4403 new_sw->name = p;
4404 strcpy(new_sw->name, value);
4405 p += strlen(value) + 1;
4406 new_sw->data = p;
4407 if (data) {
4408 strcpy(new_sw->data, data);
4409 p += strlen(data) + 1;
4410 } else {
4411 strcpy(new_sw->data, "");
4412 p++;
4414 if (eval)
4415 new_sw->tmpdata = p;
4416 new_sw->eval = eval;
4417 new_sw->registrar = registrar;
4419 /* ... try to lock this context ... */
4420 ast_mutex_lock(&con->lock);
4422 /* ... go to last sw and check if context is already swd too... */
4423 AST_LIST_TRAVERSE(&con->alts, i, list) {
4424 if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
4425 free(new_sw);
4426 ast_mutex_unlock(&con->lock);
4427 errno = EEXIST;
4428 return -1;
4432 /* ... sw new context into context list, unlock, return */
4433 AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
4435 if (option_verbose > 2)
4436 ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
4438 ast_mutex_unlock(&con->lock);
4440 return 0;
4444 * EBUSY - can't lock
4445 * ENOENT - there is not context existence
4447 int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
4449 int ret = -1;
4450 struct ast_context *c = find_context_locked(context);
4452 if (c) {
4453 ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
4454 ast_unlock_contexts();
4456 return ret;
4459 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
4461 struct ast_ignorepat *ip, *ipl = NULL;
4463 ast_mutex_lock(&con->lock);
4465 for (ip = con->ignorepats; ip; ip = ip->next) {
4466 if (!strcmp(ip->pattern, ignorepat) &&
4467 (!registrar || (registrar == ip->registrar))) {
4468 if (ipl) {
4469 ipl->next = ip->next;
4470 free(ip);
4471 } else {
4472 con->ignorepats = ip->next;
4473 free(ip);
4475 ast_mutex_unlock(&con->lock);
4476 return 0;
4478 ipl = ip;
4481 ast_mutex_unlock(&con->lock);
4482 errno = EINVAL;
4483 return -1;
4487 * EBUSY - can't lock
4488 * ENOENT - there is no existence of context
4490 int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
4492 int ret = -1;
4493 struct ast_context *c = find_context_locked(context);
4495 if (c) {
4496 ret = ast_context_add_ignorepat2(c, value, registrar);
4497 ast_unlock_contexts();
4499 return ret;
4502 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
4504 struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
4505 int length;
4506 length = sizeof(struct ast_ignorepat);
4507 length += strlen(value) + 1;
4508 if (!(ignorepat = ast_calloc(1, length)))
4509 return -1;
4510 /* The cast to char * is because we need to write the initial value.
4511 * The field is not supposed to be modified otherwise
4513 strcpy((char *)ignorepat->pattern, value);
4514 ignorepat->next = NULL;
4515 ignorepat->registrar = registrar;
4516 ast_mutex_lock(&con->lock);
4517 for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
4518 ignorepatl = ignorepatc;
4519 if (!strcasecmp(ignorepatc->pattern, value)) {
4520 /* Already there */
4521 ast_mutex_unlock(&con->lock);
4522 errno = EEXIST;
4523 return -1;
4526 if (ignorepatl)
4527 ignorepatl->next = ignorepat;
4528 else
4529 con->ignorepats = ignorepat;
4530 ast_mutex_unlock(&con->lock);
4531 return 0;
4535 int ast_ignore_pattern(const char *context, const char *pattern)
4537 struct ast_context *con = ast_context_find(context);
4538 if (con) {
4539 struct ast_ignorepat *pat;
4540 for (pat = con->ignorepats; pat; pat = pat->next) {
4541 if (ast_extension_match(pat->pattern, pattern))
4542 return 1;
4546 return 0;
4550 * EBUSY - can't lock
4551 * ENOENT - no existence of context
4554 int ast_add_extension(const char *context, int replace, const char *extension,
4555 int priority, const char *label, const char *callerid,
4556 const char *application, void *data, void (*datad)(void *), const char *registrar)
4558 int ret = -1;
4559 struct ast_context *c = find_context_locked(context);
4561 if (c) {
4562 ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
4563 application, data, datad, registrar);
4564 ast_unlock_contexts();
4566 return ret;
4569 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4571 if (!chan)
4572 return -1;
4574 ast_channel_lock(chan);
4576 if (!ast_strlen_zero(context))
4577 ast_copy_string(chan->context, context, sizeof(chan->context));
4578 if (!ast_strlen_zero(exten))
4579 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
4580 if (priority > -1) {
4581 chan->priority = priority;
4582 /* see flag description in channel.h for explanation */
4583 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
4584 chan->priority--;
4587 ast_channel_unlock(chan);
4589 return 0;
4592 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4594 int res = 0;
4596 ast_channel_lock(chan);
4598 if (chan->pbx) { /* This channel is currently in the PBX */
4599 ast_explicit_goto(chan, context, exten, priority);
4600 ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
4601 } else {
4602 /* In order to do it when the channel doesn't really exist within
4603 the PBX, we have to make a new channel, masquerade, and start the PBX
4604 at the new location */
4605 struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
4606 if (chan->cdr) {
4607 ast_cdr_discard(tmpchan->cdr);
4608 tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
4610 if (!tmpchan)
4611 res = -1;
4612 else {
4613 /* Make formats okay */
4614 tmpchan->readformat = chan->readformat;
4615 tmpchan->writeformat = chan->writeformat;
4616 /* Setup proper location */
4617 ast_explicit_goto(tmpchan,
4618 S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
4620 /* Masquerade into temp channel */
4621 ast_channel_masquerade(tmpchan, chan);
4623 /* Grab the locks and get going */
4624 ast_channel_lock(tmpchan);
4625 ast_do_masquerade(tmpchan);
4626 ast_channel_unlock(tmpchan);
4627 /* Start the PBX going on our stolen channel */
4628 if (ast_pbx_start(tmpchan)) {
4629 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
4630 ast_hangup(tmpchan);
4631 res = -1;
4635 ast_channel_unlock(chan);
4636 return res;
4639 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
4641 struct ast_channel *chan;
4642 int res = -1;
4644 chan = ast_get_channel_by_name_locked(channame);
4645 if (chan) {
4646 res = ast_async_goto(chan, context, exten, priority);
4647 ast_channel_unlock(chan);
4649 return res;
4652 /*! \brief copy a string skipping whitespace */
4653 static int ext_strncpy(char *dst, const char *src, int len)
4655 int count=0;
4657 while (*src && (count < len - 1)) {
4658 switch(*src) {
4659 case ' ':
4660 /* otherwise exten => [a-b],1,... doesn't work */
4661 /* case '-': */
4662 /* Ignore */
4663 break;
4664 default:
4665 *dst = *src;
4666 dst++;
4668 src++;
4669 count++;
4671 *dst = '\0';
4673 return count;
4676 /*! \brief add the extension in the priority chain.
4677 * returns 0 on success, -1 on failure
4679 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
4680 struct ast_exten *el, struct ast_exten *e, int replace)
4682 struct ast_exten *ep;
4684 for (ep = NULL; e ; ep = e, e = e->peer) {
4685 if (e->priority >= tmp->priority)
4686 break;
4688 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
4689 ep->peer = tmp;
4690 return 0; /* success */
4692 if (e->priority == tmp->priority) {
4693 /* Can't have something exactly the same. Is this a
4694 replacement? If so, replace, otherwise, bonk. */
4695 if (!replace) {
4696 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
4697 if (tmp->datad)
4698 tmp->datad(tmp->data);
4699 free(tmp);
4700 return -1;
4702 /* we are replacing e, so copy the link fields and then update
4703 * whoever pointed to e to point to us
4705 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
4706 tmp->peer = e->peer; /* always meaningful */
4707 if (ep) /* We're in the peer list, just insert ourselves */
4708 ep->peer = tmp;
4709 else if (el) /* We're the first extension. Take over e's functions */
4710 el->next = tmp;
4711 else /* We're the very first extension. */
4712 con->root = tmp;
4713 if (tmp->priority == PRIORITY_HINT)
4714 ast_change_hint(e,tmp);
4715 /* Destroy the old one */
4716 if (e->datad)
4717 e->datad(e->data);
4718 free(e);
4719 } else { /* Slip ourselves in just before e */
4720 tmp->peer = e;
4721 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
4722 if (ep) /* Easy enough, we're just in the peer list */
4723 ep->peer = tmp;
4724 else { /* we are the first in some peer list, so link in the ext list */
4725 if (el)
4726 el->next = tmp; /* in the middle... */
4727 else
4728 con->root = tmp; /* ... or at the head */
4729 e->next = NULL; /* e is no more at the head, so e->next must be reset */
4731 /* And immediately return success. */
4732 if (tmp->priority == PRIORITY_HINT)
4733 ast_add_hint(tmp);
4735 return 0;
4738 /*! \brief
4739 * Main interface to add extensions to the list for out context.
4741 * We sort extensions in order of matching preference, so that we can
4742 * stop the search as soon as we find a suitable match.
4743 * This ordering also takes care of wildcards such as '.' (meaning
4744 * "one or more of any character") and '!' (which is 'earlymatch',
4745 * meaning "zero or more of any character" but also impacts the
4746 * return value from CANMATCH and EARLYMATCH.
4748 * The extension match rules defined in the devmeeting 2006.05.05 are
4749 * quite simple: WE SELECT THE LONGEST MATCH.
4750 * In detail, "longest" means the number of matched characters in
4751 * the extension. In case of ties (e.g. _XXX and 333) in the length
4752 * of a pattern, we give priority to entries with the smallest cardinality
4753 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
4754 * while the latter has 7, etc.
4755 * In case of same cardinality, the first element in the range counts.
4756 * If we still have a tie, any final '!' will make this as a possibly
4757 * less specific pattern.
4759 * EBUSY - can't lock
4760 * EEXIST - extension with the same priority exist and no replace is set
4763 int ast_add_extension2(struct ast_context *con,
4764 int replace, const char *extension, int priority, const char *label, const char *callerid,
4765 const char *application, void *data, void (*datad)(void *),
4766 const char *registrar)
4769 * Sort extensions (or patterns) according to the rules indicated above.
4770 * These are implemented by the function ext_cmp()).
4771 * All priorities for the same ext/pattern/cid are kept in a list,
4772 * using the 'peer' field as a link field..
4774 struct ast_exten *tmp, *e, *el = NULL;
4775 int res;
4776 int length;
4777 char *p;
4778 char expand_buf[VAR_BUF_SIZE] = { 0, };
4780 /* if we are adding a hint, and there are global variables, and the hint
4781 contains variable references, then expand them
4783 ast_mutex_lock(&globalslock);
4784 if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
4785 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
4786 application = expand_buf;
4788 ast_mutex_unlock(&globalslock);
4790 length = sizeof(struct ast_exten);
4791 length += strlen(extension) + 1;
4792 length += strlen(application) + 1;
4793 if (label)
4794 length += strlen(label) + 1;
4795 if (callerid)
4796 length += strlen(callerid) + 1;
4797 else
4798 length ++; /* just the '\0' */
4800 /* Be optimistic: Build the extension structure first */
4801 if (!(tmp = ast_calloc(1, length)))
4802 return -1;
4804 /* use p as dst in assignments, as the fields are const char * */
4805 p = tmp->stuff;
4806 if (label) {
4807 tmp->label = p;
4808 strcpy(p, label);
4809 p += strlen(label) + 1;
4811 tmp->exten = p;
4812 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
4813 tmp->priority = priority;
4814 tmp->cidmatch = p; /* but use p for assignments below */
4815 if (callerid) {
4816 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
4817 tmp->matchcid = 1;
4818 } else {
4819 *p++ = '\0';
4820 tmp->matchcid = 0;
4822 tmp->app = p;
4823 strcpy(p, application);
4824 tmp->parent = con;
4825 tmp->data = data;
4826 tmp->datad = datad;
4827 tmp->registrar = registrar;
4829 ast_mutex_lock(&con->lock);
4830 res = 0; /* some compilers will think it is uninitialized otherwise */
4831 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
4832 res = ext_cmp(e->exten, extension);
4833 if (res == 0) { /* extension match, now look at cidmatch */
4834 if (!e->matchcid && !tmp->matchcid)
4835 res = 0;
4836 else if (tmp->matchcid && !e->matchcid)
4837 res = 1;
4838 else if (e->matchcid && !tmp->matchcid)
4839 res = -1;
4840 else
4841 res = strcasecmp(e->cidmatch, tmp->cidmatch);
4843 if (res >= 0)
4844 break;
4846 if (e && res == 0) { /* exact match, insert in the pri chain */
4847 res = add_pri(con, tmp, el, e, replace);
4848 ast_mutex_unlock(&con->lock);
4849 if (res < 0) {
4850 errno = EEXIST; /* XXX do we care ? */
4851 return 0; /* XXX should we return -1 maybe ? */
4853 } else {
4855 * not an exact match, this is the first entry with this pattern,
4856 * so insert in the main list right before 'e' (if any)
4858 tmp->next = e;
4859 if (el)
4860 el->next = tmp;
4861 else
4862 con->root = tmp;
4863 ast_mutex_unlock(&con->lock);
4864 if (tmp->priority == PRIORITY_HINT)
4865 ast_add_hint(tmp);
4867 if (option_debug) {
4868 if (tmp->matchcid) {
4869 if (option_debug)
4870 ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
4871 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4872 } else {
4873 if (option_debug)
4874 ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
4875 tmp->exten, tmp->priority, con->name);
4878 if (option_verbose > 2) {
4879 if (tmp->matchcid) {
4880 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
4881 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4882 } else {
4883 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
4884 tmp->exten, tmp->priority, con->name);
4887 return 0;
4890 struct async_stat {
4891 pthread_t p;
4892 struct ast_channel *chan;
4893 char context[AST_MAX_CONTEXT];
4894 char exten[AST_MAX_EXTENSION];
4895 int priority;
4896 int timeout;
4897 char app[AST_MAX_EXTENSION];
4898 char appdata[1024];
4901 static void *async_wait(void *data)
4903 struct async_stat *as = data;
4904 struct ast_channel *chan = as->chan;
4905 int timeout = as->timeout;
4906 int res;
4907 struct ast_frame *f;
4908 struct ast_app *app;
4910 while (timeout && (chan->_state != AST_STATE_UP)) {
4911 res = ast_waitfor(chan, timeout);
4912 if (res < 1)
4913 break;
4914 if (timeout > -1)
4915 timeout = res;
4916 f = ast_read(chan);
4917 if (!f)
4918 break;
4919 if (f->frametype == AST_FRAME_CONTROL) {
4920 if ((f->subclass == AST_CONTROL_BUSY) ||
4921 (f->subclass == AST_CONTROL_CONGESTION) ) {
4922 ast_frfree(f);
4923 break;
4926 ast_frfree(f);
4928 if (chan->_state == AST_STATE_UP) {
4929 if (!ast_strlen_zero(as->app)) {
4930 app = pbx_findapp(as->app);
4931 if (app) {
4932 if (option_verbose > 2)
4933 ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
4934 pbx_exec(chan, app, as->appdata);
4935 } else
4936 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
4937 } else {
4938 if (!ast_strlen_zero(as->context))
4939 ast_copy_string(chan->context, as->context, sizeof(chan->context));
4940 if (!ast_strlen_zero(as->exten))
4941 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
4942 if (as->priority > 0)
4943 chan->priority = as->priority;
4944 /* Run the PBX */
4945 if (ast_pbx_run(chan)) {
4946 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
4947 } else {
4948 /* PBX will have taken care of this */
4949 chan = NULL;
4953 free(as);
4954 if (chan)
4955 ast_hangup(chan);
4956 return NULL;
4959 /*! Function to post an empty cdr after a spool call fails.
4961 * This function posts an empty cdr for a failed spool call
4964 static int ast_pbx_outgoing_cdr_failed(void)
4966 /* allocate a channel */
4967 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
4969 if (!chan)
4970 return -1; /* failure */
4972 if (!chan->cdr) {
4973 /* allocation of the cdr failed */
4974 ast_channel_free(chan); /* free the channel */
4975 return -1; /* return failure */
4978 /* allocation of the cdr was successful */
4979 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
4980 ast_cdr_start(chan->cdr); /* record the start and stop time */
4981 ast_cdr_end(chan->cdr);
4982 ast_cdr_failed(chan->cdr); /* set the status to failed */
4983 ast_cdr_detach(chan->cdr); /* post and free the record */
4984 ast_channel_free(chan); /* free the channel */
4986 return 0; /* success */
4989 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)
4991 struct ast_channel *chan;
4992 struct async_stat *as;
4993 int res = -1, cdr_res = -1;
4994 struct outgoing_helper oh;
4995 pthread_attr_t attr;
4997 if (sync) {
4998 LOAD_OH(oh);
4999 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5000 if (channel) {
5001 *channel = chan;
5002 if (chan)
5003 ast_channel_lock(chan);
5005 if (chan) {
5006 if (chan->_state == AST_STATE_UP) {
5007 res = 0;
5008 if (option_verbose > 3)
5009 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5011 if (sync > 1) {
5012 if (channel)
5013 ast_channel_unlock(chan);
5014 if (ast_pbx_run(chan)) {
5015 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5016 if (channel)
5017 *channel = NULL;
5018 ast_hangup(chan);
5019 res = -1;
5021 } else {
5022 if (ast_pbx_start(chan)) {
5023 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
5024 if (channel) {
5025 *channel = NULL;
5026 ast_channel_unlock(chan);
5028 ast_hangup(chan);
5029 res = -1;
5032 } else {
5033 if (option_verbose > 3)
5034 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5036 if (chan->cdr) { /* update the cdr */
5037 /* here we update the status of the call, which sould be busy.
5038 * if that fails then we set the status to failed */
5039 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5040 ast_cdr_failed(chan->cdr);
5043 if (channel) {
5044 *channel = NULL;
5045 ast_channel_unlock(chan);
5047 ast_hangup(chan);
5051 if (res < 0) { /* the call failed for some reason */
5052 if (*reason == 0) { /* if the call failed (not busy or no answer)
5053 * update the cdr with the failed message */
5054 cdr_res = ast_pbx_outgoing_cdr_failed();
5055 if (cdr_res != 0) {
5056 res = cdr_res;
5057 goto outgoing_exten_cleanup;
5061 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
5062 /* check if "failed" exists */
5063 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
5064 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
5065 if (chan) {
5066 char failed_reason[4] = "";
5067 if (!ast_strlen_zero(context))
5068 ast_copy_string(chan->context, context, sizeof(chan->context));
5069 set_ext_pri(chan, "failed", 1);
5070 ast_set_variables(chan, vars);
5071 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
5072 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
5073 if (account)
5074 ast_cdr_setaccount(chan, account);
5075 ast_pbx_run(chan);
5079 } else {
5080 if (!(as = ast_calloc(1, sizeof(*as)))) {
5081 res = -1;
5082 goto outgoing_exten_cleanup;
5084 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
5085 if (channel) {
5086 *channel = chan;
5087 if (chan)
5088 ast_channel_lock(chan);
5090 if (!chan) {
5091 free(as);
5092 res = -1;
5093 goto outgoing_exten_cleanup;
5095 as->chan = chan;
5096 ast_copy_string(as->context, context, sizeof(as->context));
5097 set_ext_pri(as->chan, exten, priority);
5098 as->timeout = timeout;
5099 ast_set_variables(chan, vars);
5100 if (account)
5101 ast_cdr_setaccount(chan, account);
5102 pthread_attr_init(&attr);
5103 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5104 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5105 ast_log(LOG_WARNING, "Failed to start async wait\n");
5106 free(as);
5107 if (channel) {
5108 *channel = NULL;
5109 ast_channel_unlock(chan);
5111 ast_hangup(chan);
5112 res = -1;
5113 pthread_attr_destroy(&attr);
5114 goto outgoing_exten_cleanup;
5116 pthread_attr_destroy(&attr);
5117 res = 0;
5119 outgoing_exten_cleanup:
5120 ast_variables_destroy(vars);
5121 return res;
5124 struct app_tmp {
5125 char app[256];
5126 char data[256];
5127 struct ast_channel *chan;
5128 pthread_t t;
5131 /*! \brief run the application and free the descriptor once done */
5132 static void *ast_pbx_run_app(void *data)
5134 struct app_tmp *tmp = data;
5135 struct ast_app *app;
5136 app = pbx_findapp(tmp->app);
5137 if (app) {
5138 if (option_verbose > 3)
5139 ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
5140 pbx_exec(tmp->chan, app, tmp->data);
5141 } else
5142 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
5143 ast_hangup(tmp->chan);
5144 free(tmp);
5145 return NULL;
5148 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)
5150 struct ast_channel *chan;
5151 struct app_tmp *tmp;
5152 int res = -1, cdr_res = -1;
5153 struct outgoing_helper oh;
5154 pthread_attr_t attr;
5156 memset(&oh, 0, sizeof(oh));
5157 oh.vars = vars;
5158 oh.account = account;
5160 if (locked_channel)
5161 *locked_channel = NULL;
5162 if (ast_strlen_zero(app)) {
5163 res = -1;
5164 goto outgoing_app_cleanup;
5166 if (sync) {
5167 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5168 if (chan) {
5169 if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
5170 ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
5171 } else {
5172 chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
5173 if(!chan->cdr) {
5174 /* allocation of the cdr failed */
5175 free(chan->pbx);
5176 res = -1;
5177 goto outgoing_app_cleanup;
5179 /* allocation of the cdr was successful */
5180 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
5181 ast_cdr_start(chan->cdr);
5183 ast_set_variables(chan, vars);
5184 if (account)
5185 ast_cdr_setaccount(chan, account);
5186 if (chan->_state == AST_STATE_UP) {
5187 res = 0;
5188 if (option_verbose > 3)
5189 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5190 tmp = ast_calloc(1, sizeof(*tmp));
5191 if (!tmp)
5192 res = -1;
5193 else {
5194 ast_copy_string(tmp->app, app, sizeof(tmp->app));
5195 if (appdata)
5196 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
5197 tmp->chan = chan;
5198 if (sync > 1) {
5199 if (locked_channel)
5200 ast_channel_unlock(chan);
5201 ast_pbx_run_app(tmp);
5202 } else {
5203 pthread_attr_init(&attr);
5204 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5205 if (locked_channel)
5206 ast_channel_lock(chan);
5207 if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
5208 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
5209 free(tmp);
5210 if (locked_channel)
5211 ast_channel_unlock(chan);
5212 ast_hangup(chan);
5213 res = -1;
5214 } else {
5215 if (locked_channel)
5216 *locked_channel = chan;
5218 pthread_attr_destroy(&attr);
5221 } else {
5222 if (option_verbose > 3)
5223 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5224 if (chan->cdr) { /* update the cdr */
5225 /* here we update the status of the call, which sould be busy.
5226 * if that fails then we set the status to failed */
5227 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5228 ast_cdr_failed(chan->cdr);
5230 ast_hangup(chan);
5234 if (res < 0) { /* the call failed for some reason */
5235 if (*reason == 0) { /* if the call failed (not busy or no answer)
5236 * update the cdr with the failed message */
5237 cdr_res = ast_pbx_outgoing_cdr_failed();
5238 if (cdr_res != 0) {
5239 res = cdr_res;
5240 goto outgoing_app_cleanup;
5245 } else {
5246 struct async_stat *as;
5247 if (!(as = ast_calloc(1, sizeof(*as)))) {
5248 res = -1;
5249 goto outgoing_app_cleanup;
5251 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5252 if (!chan) {
5253 free(as);
5254 res = -1;
5255 goto outgoing_app_cleanup;
5257 as->chan = chan;
5258 ast_copy_string(as->app, app, sizeof(as->app));
5259 if (appdata)
5260 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
5261 as->timeout = timeout;
5262 ast_set_variables(chan, vars);
5263 if (account)
5264 ast_cdr_setaccount(chan, account);
5265 /* Start a new thread, and get something handling this channel. */
5266 pthread_attr_init(&attr);
5267 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5268 if (locked_channel)
5269 ast_channel_lock(chan);
5270 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5271 ast_log(LOG_WARNING, "Failed to start async wait\n");
5272 free(as);
5273 if (locked_channel)
5274 ast_channel_unlock(chan);
5275 ast_hangup(chan);
5276 res = -1;
5277 pthread_attr_destroy(&attr);
5278 goto outgoing_app_cleanup;
5279 } else {
5280 if (locked_channel)
5281 *locked_channel = chan;
5283 pthread_attr_destroy(&attr);
5284 res = 0;
5286 outgoing_app_cleanup:
5287 ast_variables_destroy(vars);
5288 return res;
5291 void __ast_context_destroy(struct ast_context *con, const char *registrar)
5293 struct ast_context *tmp, *tmpl=NULL;
5294 struct ast_include *tmpi;
5295 struct ast_sw *sw;
5296 struct ast_exten *e, *el, *en;
5297 struct ast_ignorepat *ipi;
5299 for (tmp = contexts; tmp; ) {
5300 struct ast_context *next; /* next starting point */
5301 for (; tmp; tmpl = tmp, tmp = tmp->next) {
5302 if (option_debug)
5303 ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
5304 if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
5305 (!con || !strcasecmp(tmp->name, con->name)) )
5306 break; /* found it */
5308 if (!tmp) /* not found, we are done */
5309 break;
5310 ast_mutex_lock(&tmp->lock);
5311 if (option_debug)
5312 ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
5313 next = tmp->next;
5314 if (tmpl)
5315 tmpl->next = next;
5316 else
5317 contexts = next;
5318 /* Okay, now we're safe to let it go -- in a sense, we were
5319 ready to let it go as soon as we locked it. */
5320 ast_mutex_unlock(&tmp->lock);
5321 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
5322 struct ast_include *tmpil = tmpi;
5323 tmpi = tmpi->next;
5324 free(tmpil);
5326 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
5327 struct ast_ignorepat *ipl = ipi;
5328 ipi = ipi->next;
5329 free(ipl);
5331 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
5332 free(sw);
5333 for (e = tmp->root; e;) {
5334 for (en = e->peer; en;) {
5335 el = en;
5336 en = en->peer;
5337 destroy_exten(el);
5339 el = e;
5340 e = e->next;
5341 destroy_exten(el);
5343 ast_mutex_destroy(&tmp->lock);
5344 free(tmp);
5345 /* if we have a specific match, we are done, otherwise continue */
5346 tmp = con ? NULL : next;
5350 void ast_context_destroy(struct ast_context *con, const char *registrar)
5352 ast_wrlock_contexts();
5353 __ast_context_destroy(con,registrar);
5354 ast_unlock_contexts();
5357 static void wait_for_hangup(struct ast_channel *chan, void *data)
5359 int res;
5360 struct ast_frame *f;
5361 int waittime;
5363 if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
5364 waittime = -1;
5365 if (waittime > -1) {
5366 ast_safe_sleep(chan, waittime * 1000);
5367 } else do {
5368 res = ast_waitfor(chan, -1);
5369 if (res < 0)
5370 return;
5371 f = ast_read(chan);
5372 if (f)
5373 ast_frfree(f);
5374 } while(f);
5378 * \ingroup applications
5380 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
5382 ast_indicate(chan, AST_CONTROL_PROGRESS);
5383 return 0;
5387 * \ingroup applications
5389 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
5391 ast_indicate(chan, AST_CONTROL_RINGING);
5392 return 0;
5396 * \ingroup applications
5398 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
5400 ast_indicate(chan, AST_CONTROL_BUSY);
5401 /* Don't change state of an UP channel, just indicate
5402 busy in audio */
5403 if (chan->_state != AST_STATE_UP)
5404 ast_setstate(chan, AST_STATE_BUSY);
5405 wait_for_hangup(chan, data);
5406 return -1;
5410 * \ingroup applications
5412 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
5414 ast_indicate(chan, AST_CONTROL_CONGESTION);
5415 /* Don't change state of an UP channel, just indicate
5416 congestion in audio */
5417 if (chan->_state != AST_STATE_UP)
5418 ast_setstate(chan, AST_STATE_BUSY);
5419 wait_for_hangup(chan, data);
5420 return -1;
5424 * \ingroup applications
5426 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
5428 int delay = 0;
5429 int res;
5431 if (chan->_state == AST_STATE_UP)
5432 delay = 0;
5433 else if (!ast_strlen_zero(data))
5434 delay = atoi(data);
5436 res = ast_answer(chan);
5437 if (res)
5438 return res;
5440 if (delay)
5441 res = ast_safe_sleep(chan, delay);
5443 return res;
5446 AST_APP_OPTIONS(resetcdr_opts, {
5447 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
5448 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
5449 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
5453 * \ingroup applications
5455 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
5457 char *args;
5458 struct ast_flags flags = { 0 };
5460 if (!ast_strlen_zero(data)) {
5461 args = ast_strdupa(data);
5462 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
5465 ast_cdr_reset(chan->cdr, &flags);
5467 return 0;
5471 * \ingroup applications
5473 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
5475 /* Copy the AMA Flags as specified */
5476 ast_cdr_setamaflags(chan, data ? data : "");
5477 return 0;
5481 * \ingroup applications
5483 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
5485 if (!ast_strlen_zero(data)) {
5486 int cause;
5487 char *endptr;
5489 if ((cause = ast_str2cause(data)) > -1) {
5490 chan->hangupcause = cause;
5491 return -1;
5494 cause = strtol((const char *) data, &endptr, 10);
5495 if (cause != 0 || (data != endptr)) {
5496 chan->hangupcause = cause;
5497 return -1;
5500 ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
5503 if (!chan->hangupcause) {
5504 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
5507 return -1;
5511 * \ingroup applications
5513 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
5515 int res=0;
5516 char *s, *ts;
5517 struct ast_timing timing;
5519 if (ast_strlen_zero(data)) {
5520 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
5521 return -1;
5524 ts = s = ast_strdupa(data);
5526 /* Separate the Goto path */
5527 strsep(&ts,"?");
5529 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
5530 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
5531 res = pbx_builtin_goto(chan, ts);
5533 return res;
5537 * \ingroup applications
5539 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
5541 char *s, *appname;
5542 struct ast_timing timing;
5543 struct ast_app *app;
5544 static const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
5546 if (ast_strlen_zero(data)) {
5547 ast_log(LOG_WARNING, "%s\n", usage);
5548 return -1;
5551 appname = ast_strdupa(data);
5553 s = strsep(&appname,"?"); /* Separate the timerange and application name/data */
5554 if (!appname) { /* missing application */
5555 ast_log(LOG_WARNING, "%s\n", usage);
5556 return -1;
5559 if (!ast_build_timing(&timing, s)) {
5560 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
5561 return -1;
5564 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
5565 return 0;
5567 /* now split appname|appargs */
5568 if ((s = strchr(appname, '|')))
5569 *s++ = '\0';
5571 if ((app = pbx_findapp(appname))) {
5572 return pbx_exec(chan, app, S_OR(s, ""));
5573 } else {
5574 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
5575 return -1;
5580 * \ingroup applications
5582 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
5584 double s;
5585 int ms;
5587 /* Wait for "n" seconds */
5588 if (data && (s = atof(data)) > 0) {
5589 ms = s * 1000.0;
5590 return ast_safe_sleep(chan, ms);
5592 return 0;
5596 * \ingroup applications
5598 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
5600 int ms, res;
5601 double sec;
5602 struct ast_flags flags = {0};
5603 char *opts[1] = { NULL };
5604 char *parse;
5605 AST_DECLARE_APP_ARGS(args,
5606 AST_APP_ARG(timeout);
5607 AST_APP_ARG(options);
5610 if (!ast_strlen_zero(data)) {
5611 parse = ast_strdupa(data);
5612 AST_STANDARD_APP_ARGS(args, parse);
5613 } else
5614 memset(&args, 0, sizeof(args));
5616 if (args.options)
5617 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
5619 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
5620 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
5621 } else if (ast_test_flag(&flags, WAITEXTEN_MOH))
5622 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
5624 /* Wait for "n" seconds */
5625 if (args.timeout && (sec = atof(args.timeout)) > 0.0)
5626 ms = 1000 * sec;
5627 else if (chan->pbx)
5628 ms = chan->pbx->rtimeout * 1000;
5629 else
5630 ms = 10000;
5631 res = ast_waitfordigit(chan, ms);
5632 if (!res) {
5633 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
5634 if (option_verbose > 2)
5635 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
5636 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
5637 if (option_verbose > 2)
5638 ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
5639 res = -1;
5640 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
5641 if (option_verbose > 2)
5642 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
5643 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
5644 } else {
5645 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
5646 res = -1;
5650 if (ast_test_flag(&flags, WAITEXTEN_MOH))
5651 ast_indicate(chan, AST_CONTROL_UNHOLD);
5653 return res;
5657 * \ingroup applications
5659 static int pbx_builtin_background(struct ast_channel *chan, void *data)
5661 int res = 0;
5662 struct ast_flags flags = {0};
5663 char *parse;
5664 AST_DECLARE_APP_ARGS(args,
5665 AST_APP_ARG(filename);
5666 AST_APP_ARG(options);
5667 AST_APP_ARG(lang);
5668 AST_APP_ARG(context);
5671 if (ast_strlen_zero(data)) {
5672 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
5673 return -1;
5676 parse = ast_strdupa(data);
5678 AST_STANDARD_APP_ARGS(args, parse);
5680 if (ast_strlen_zero(args.lang))
5681 args.lang = (char *)chan->language; /* XXX this is const */
5683 if (ast_strlen_zero(args.context))
5684 args.context = chan->context;
5686 if (args.options) {
5687 if (!strcasecmp(args.options, "skip"))
5688 flags.flags = BACKGROUND_SKIP;
5689 else if (!strcasecmp(args.options, "noanswer"))
5690 flags.flags = BACKGROUND_NOANSWER;
5691 else
5692 ast_app_parse_options(background_opts, &flags, NULL, args.options);
5695 /* Answer if need be */
5696 if (chan->_state != AST_STATE_UP) {
5697 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
5698 return 0;
5699 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
5700 res = ast_answer(chan);
5704 if (!res) {
5705 char *back = args.filename;
5706 char *front;
5707 ast_stopstream(chan); /* Stop anything playing */
5708 /* Stream the list of files */
5709 while (!res && (front = strsep(&back, "&")) ) {
5710 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
5711 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
5712 res = 0;
5713 break;
5715 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
5716 res = ast_waitstream(chan, "");
5717 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
5718 res = ast_waitstream_exten(chan, args.context);
5719 } else {
5720 res = ast_waitstream(chan, AST_DIGIT_ANY);
5722 ast_stopstream(chan);
5725 if (args.context != chan->context && res) {
5726 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
5727 ast_copy_string(chan->context, args.context, sizeof(chan->context));
5728 chan->priority = 0;
5729 res = 0;
5731 return res;
5734 /*! Goto
5735 * \ingroup applications
5737 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
5739 int res = ast_parseable_goto(chan, data);
5740 if (!res && (option_verbose > 2))
5741 ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
5742 return res;
5746 int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
5748 struct ast_var_t *variables;
5749 const char *var, *val;
5750 int total = 0;
5752 if (!chan)
5753 return 0;
5755 memset(buf, 0, size);
5757 ast_channel_lock(chan);
5759 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
5760 if ((var=ast_var_name(variables)) && (val=ast_var_value(variables))
5761 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
5763 if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
5764 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
5765 break;
5766 } else
5767 total++;
5768 } else
5769 break;
5772 ast_channel_unlock(chan);
5774 return total;
5777 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
5779 struct ast_var_t *variables;
5780 const char *ret = NULL;
5781 int i;
5782 struct varshead *places[2] = { NULL, &globals };
5784 if (!name)
5785 return NULL;
5787 if (chan) {
5788 ast_channel_lock(chan);
5789 places[0] = &chan->varshead;
5792 for (i = 0; i < 2; i++) {
5793 if (!places[i])
5794 continue;
5795 if (places[i] == &globals)
5796 ast_mutex_lock(&globalslock);
5797 AST_LIST_TRAVERSE(places[i], variables, entries) {
5798 if (!strcmp(name, ast_var_name(variables))) {
5799 ret = ast_var_value(variables);
5800 break;
5803 if (places[i] == &globals)
5804 ast_mutex_unlock(&globalslock);
5805 if (ret)
5806 break;
5809 if (chan)
5810 ast_channel_unlock(chan);
5812 return ret;
5815 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
5817 struct ast_var_t *newvariable;
5818 struct varshead *headp;
5820 if (name[strlen(name)-1] == ')') {
5821 char *function = ast_strdupa(name);
5823 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
5824 ast_func_write(chan, function, value);
5825 return;
5828 if (chan) {
5829 ast_channel_lock(chan);
5830 headp = &chan->varshead;
5831 } else {
5832 ast_mutex_lock(&globalslock);
5833 headp = &globals;
5836 if (value) {
5837 if ((option_verbose > 1) && (headp == &globals))
5838 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5839 newvariable = ast_var_assign(name, value);
5840 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5843 if (chan)
5844 ast_channel_unlock(chan);
5845 else
5846 ast_mutex_unlock(&globalslock);
5849 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
5851 struct ast_var_t *newvariable;
5852 struct varshead *headp;
5853 const char *nametail = name;
5855 if (name[strlen(name)-1] == ')') {
5856 char *function = ast_strdupa(name);
5858 ast_func_write(chan, function, value);
5859 return;
5862 if (chan) {
5863 ast_channel_lock(chan);
5864 headp = &chan->varshead;
5865 } else {
5866 ast_mutex_lock(&globalslock);
5867 headp = &globals;
5870 /* For comparison purposes, we have to strip leading underscores */
5871 if (*nametail == '_') {
5872 nametail++;
5873 if (*nametail == '_')
5874 nametail++;
5877 AST_LIST_TRAVERSE (headp, newvariable, entries) {
5878 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
5879 /* there is already such a variable, delete it */
5880 AST_LIST_REMOVE(headp, newvariable, entries);
5881 ast_var_delete(newvariable);
5882 break;
5886 if (value) {
5887 if ((option_verbose > 1) && (headp == &globals))
5888 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5889 newvariable = ast_var_assign(name, value);
5890 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5893 if (chan)
5894 ast_channel_unlock(chan);
5895 else
5896 ast_mutex_unlock(&globalslock);
5899 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
5901 char *name, *value, *mydata;
5902 int argc;
5903 char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
5904 int global = 0;
5905 int x;
5907 if (ast_strlen_zero(data)) {
5908 ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
5909 return 0;
5912 mydata = ast_strdupa(data);
5913 argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
5915 /* check for a trailing flags argument */
5916 if ((argc > 1) && !strchr(argv[argc-1], '=')) {
5917 argc--;
5918 if (strchr(argv[argc], 'g')) {
5919 ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
5920 global = 1;
5924 if (argc > 1)
5925 ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
5927 for (x = 0; x < argc; x++) {
5928 name = argv[x];
5929 if ((value = strchr(name, '='))) {
5930 *value++ = '\0';
5931 pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
5932 } else
5933 ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
5936 return(0);
5939 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
5941 char *name;
5942 char *value;
5943 char *channel;
5944 char tmp[VAR_BUF_SIZE]="";
5946 if (ast_strlen_zero(data)) {
5947 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5948 return 0;
5951 value = ast_strdupa(data);
5952 name = strsep(&value,"=");
5953 channel = strsep(&value,"|");
5954 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
5955 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
5956 if (chan2) {
5957 char *s = alloca(strlen(value) + 4);
5958 if (s) {
5959 sprintf(s, "${%s}", value);
5960 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
5962 ast_channel_unlock(chan2);
5964 pbx_builtin_setvar_helper(chan, name, tmp);
5967 return(0);
5970 /*! \todo XXX overwrites data ? */
5971 static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
5973 char *name;
5974 char *stringp = data;
5975 static int dep_warning = 0;
5977 if (ast_strlen_zero(data)) {
5978 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5979 return 0;
5982 name = strsep(&stringp, "=");
5984 if (!dep_warning) {
5985 dep_warning = 1;
5986 ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
5989 /*! \todo XXX watch out, leading whitespace ? */
5990 pbx_builtin_setvar_helper(NULL, name, stringp);
5992 return(0);
5995 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
5997 return 0;
6000 void pbx_builtin_clear_globals(void)
6002 struct ast_var_t *vardata;
6004 ast_mutex_lock(&globalslock);
6005 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
6006 ast_var_delete(vardata);
6007 ast_mutex_unlock(&globalslock);
6010 int pbx_checkcondition(const char *condition)
6012 if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
6013 return 0;
6014 else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
6015 return atoi(condition);
6016 else /* Strings are true */
6017 return 1;
6020 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
6022 char *condition, *branch1, *branch2, *branch;
6023 int rc;
6024 char *stringp;
6026 if (ast_strlen_zero(data)) {
6027 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
6028 return 0;
6031 stringp = ast_strdupa(data);
6032 condition = strsep(&stringp,"?");
6033 branch1 = strsep(&stringp,":");
6034 branch2 = strsep(&stringp,"");
6035 branch = pbx_checkcondition(condition) ? branch1 : branch2;
6037 if (ast_strlen_zero(branch)) {
6038 if (option_debug)
6039 ast_log(LOG_DEBUG, "Not taking any branch\n");
6040 return 0;
6043 rc = pbx_builtin_goto(chan, branch);
6045 return rc;
6048 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
6050 char tmp[256];
6051 char *number = tmp;
6052 char *options;
6054 if (ast_strlen_zero(data)) {
6055 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
6056 return -1;
6058 ast_copy_string(tmp, data, sizeof(tmp));
6059 strsep(&number, "|");
6060 options = strsep(&number, "|");
6061 if (options) {
6062 if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
6063 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
6064 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
6065 return -1;
6068 return ast_say_number(chan, atoi(tmp), "", chan->language, options);
6071 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
6073 int res = 0;
6075 if (data)
6076 res = ast_say_digit_str(chan, data, "", chan->language);
6077 return res;
6080 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
6082 int res = 0;
6084 if (data)
6085 res = ast_say_character_str(chan, data, "", chan->language);
6086 return res;
6089 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
6091 int res = 0;
6093 if (data)
6094 res = ast_say_phonetic_str(chan, data, "", chan->language);
6095 return res;
6098 int load_pbx(void)
6100 int x;
6102 /* Initialize the PBX */
6103 if (option_verbose) {
6104 ast_verbose( "Asterisk PBX Core Initializing\n");
6105 ast_verbose( "Registering builtin applications:\n");
6107 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
6109 /* Register builtin applications */
6110 for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
6111 if (option_verbose)
6112 ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
6113 if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
6114 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
6115 return -1;
6118 return 0;
6122 * Lock context list functions ...
6124 int ast_lock_contexts()
6126 return ast_rwlock_wrlock(&conlock);
6129 int ast_rdlock_contexts(void)
6131 return ast_rwlock_rdlock(&conlock);
6134 int ast_wrlock_contexts(void)
6136 return ast_rwlock_wrlock(&conlock);
6139 int ast_unlock_contexts()
6141 return ast_rwlock_unlock(&conlock);
6145 * Lock context ...
6147 int ast_lock_context(struct ast_context *con)
6149 return ast_mutex_lock(&con->lock);
6152 int ast_unlock_context(struct ast_context *con)
6154 return ast_mutex_unlock(&con->lock);
6158 * Name functions ...
6160 const char *ast_get_context_name(struct ast_context *con)
6162 return con ? con->name : NULL;
6165 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
6167 return exten ? exten->parent : NULL;
6170 const char *ast_get_extension_name(struct ast_exten *exten)
6172 return exten ? exten->exten : NULL;
6175 const char *ast_get_extension_label(struct ast_exten *exten)
6177 return exten ? exten->label : NULL;
6180 const char *ast_get_include_name(struct ast_include *inc)
6182 return inc ? inc->name : NULL;
6185 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
6187 return ip ? ip->pattern : NULL;
6190 int ast_get_extension_priority(struct ast_exten *exten)
6192 return exten ? exten->priority : -1;
6196 * Registrar info functions ...
6198 const char *ast_get_context_registrar(struct ast_context *c)
6200 return c ? c->registrar : NULL;
6203 const char *ast_get_extension_registrar(struct ast_exten *e)
6205 return e ? e->registrar : NULL;
6208 const char *ast_get_include_registrar(struct ast_include *i)
6210 return i ? i->registrar : NULL;
6213 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
6215 return ip ? ip->registrar : NULL;
6218 int ast_get_extension_matchcid(struct ast_exten *e)
6220 return e ? e->matchcid : 0;
6223 const char *ast_get_extension_cidmatch(struct ast_exten *e)
6225 return e ? e->cidmatch : NULL;
6228 const char *ast_get_extension_app(struct ast_exten *e)
6230 return e ? e->app : NULL;
6233 void *ast_get_extension_app_data(struct ast_exten *e)
6235 return e ? e->data : NULL;
6238 const char *ast_get_switch_name(struct ast_sw *sw)
6240 return sw ? sw->name : NULL;
6243 const char *ast_get_switch_data(struct ast_sw *sw)
6245 return sw ? sw->data : NULL;
6248 const char *ast_get_switch_registrar(struct ast_sw *sw)
6250 return sw ? sw->registrar : NULL;
6254 * Walking functions ...
6256 struct ast_context *ast_walk_contexts(struct ast_context *con)
6258 return con ? con->next : contexts;
6261 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
6262 struct ast_exten *exten)
6264 if (!exten)
6265 return con ? con->root : NULL;
6266 else
6267 return exten->next;
6270 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
6271 struct ast_sw *sw)
6273 if (!sw)
6274 return con ? AST_LIST_FIRST(&con->alts) : NULL;
6275 else
6276 return AST_LIST_NEXT(sw, list);
6279 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
6280 struct ast_exten *priority)
6282 return priority ? priority->peer : exten;
6285 struct ast_include *ast_walk_context_includes(struct ast_context *con,
6286 struct ast_include *inc)
6288 if (!inc)
6289 return con ? con->includes : NULL;
6290 else
6291 return inc->next;
6294 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
6295 struct ast_ignorepat *ip)
6297 if (!ip)
6298 return con ? con->ignorepats : NULL;
6299 else
6300 return ip->next;
6303 int ast_context_verify_includes(struct ast_context *con)
6305 struct ast_include *inc = NULL;
6306 int res = 0;
6308 while ( (inc = ast_walk_context_includes(con, inc)) )
6309 if (!ast_context_find(inc->rname)) {
6310 res = -1;
6311 ast_log(LOG_WARNING, "Context '%s' tries includes nonexistent context '%s'\n",
6312 ast_get_context_name(con), inc->rname);
6314 return res;
6318 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
6320 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
6322 if (!chan)
6323 return -2;
6325 if (context == NULL)
6326 context = chan->context;
6327 if (exten == NULL)
6328 exten = chan->exten;
6330 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
6331 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
6332 return goto_func(chan, context, exten, priority);
6333 else
6334 return -3;
6337 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
6339 return __ast_goto_if_exists(chan, context, exten, priority, 0);
6342 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
6344 return __ast_goto_if_exists(chan, context, exten, priority, 1);
6347 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
6349 char *exten, *pri, *context;
6350 char *stringp;
6351 int ipri;
6352 int mode = 0;
6354 if (ast_strlen_zero(goto_string)) {
6355 ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
6356 return -1;
6358 stringp = ast_strdupa(goto_string);
6359 context = strsep(&stringp, "|"); /* guaranteed non-null */
6360 exten = strsep(&stringp, "|");
6361 pri = strsep(&stringp, "|");
6362 if (!exten) { /* Only a priority in this one */
6363 pri = context;
6364 exten = NULL;
6365 context = NULL;
6366 } else if (!pri) { /* Only an extension and priority in this one */
6367 pri = exten;
6368 exten = context;
6369 context = NULL;
6371 if (*pri == '+') {
6372 mode = 1;
6373 pri++;
6374 } else if (*pri == '-') {
6375 mode = -1;
6376 pri++;
6378 if (sscanf(pri, "%d", &ipri) != 1) {
6379 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
6380 pri, chan->cid.cid_num)) < 1) {
6381 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
6382 return -1;
6383 } else
6384 mode = 0;
6386 /* At this point we have a priority and maybe an extension and a context */
6388 if (mode)
6389 ipri = chan->priority + (ipri * mode);
6391 ast_explicit_goto(chan, context, exten, ipri);
6392 ast_cdr_update(chan);
6393 return 0;