fix various other problems found by gcc 4.3
[asterisk-bristuff.git] / main / pbx.c
blob8d2aa108da9ade95ed1655cf88008a2168d1a607
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Core PBX routines.
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <sys/types.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <sys/time.h>
39 #include <limits.h>
41 #include "asterisk/lock.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/options.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/file.h"
48 #include "asterisk/callerid.h"
49 #include "asterisk/cdr.h"
50 #include "asterisk/config.h"
51 #include "asterisk/term.h"
52 #include "asterisk/manager.h"
53 #include "asterisk/ast_expr.h"
54 #include "asterisk/linkedlists.h"
55 #define SAY_STUBS /* generate declarations and stubs for say methods */
56 #include "asterisk/say.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/causes.h"
59 #include "asterisk/musiconhold.h"
60 #include "asterisk/app.h"
61 #include "asterisk/devicestate.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/threadstorage.h"
65 /*!
66 * \note I M P O R T A N T :
68 * The speed of extension handling will likely be among the most important
69 * aspects of this PBX. The switching scheme as it exists right now isn't
70 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
71 * of priorities, but a constant search time here would be great ;-)
75 #ifdef LOW_MEMORY
76 #define EXT_DATA_SIZE 256
77 #else
78 #define EXT_DATA_SIZE 8192
79 #endif
81 #define SWITCH_DATA_LENGTH 256
83 #define VAR_BUF_SIZE 4096
85 #define VAR_NORMAL 1
86 #define VAR_SOFTTRAN 2
87 #define VAR_HARDTRAN 3
89 #define BACKGROUND_SKIP (1 << 0)
90 #define BACKGROUND_NOANSWER (1 << 1)
91 #define BACKGROUND_MATCHEXTEN (1 << 2)
92 #define BACKGROUND_PLAYBACK (1 << 3)
94 AST_APP_OPTIONS(background_opts, {
95 AST_APP_OPTION('s', BACKGROUND_SKIP),
96 AST_APP_OPTION('n', BACKGROUND_NOANSWER),
97 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
98 AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
99 });
101 #define WAITEXTEN_MOH (1 << 0)
103 AST_APP_OPTIONS(waitexten_opts, {
104 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
107 struct ast_context;
109 AST_THREADSTORAGE(switch_data, switch_data_init);
112 \brief ast_exten: An extension
113 The dialplan is saved as a linked list with each context
114 having it's own linked list of extensions - one item per
115 priority.
117 struct ast_exten {
118 char *exten; /*!< Extension name */
119 int matchcid; /*!< Match caller id ? */
120 const char *cidmatch; /*!< Caller id to match for this extension */
121 int priority; /*!< Priority */
122 const char *label; /*!< Label */
123 struct ast_context *parent; /*!< The context this extension belongs to */
124 const char *app; /*!< Application to execute */
125 void *data; /*!< Data to use (arguments) */
126 void (*datad)(void *); /*!< Data destructor */
127 struct ast_exten *peer; /*!< Next higher priority with our extension */
128 const char *registrar; /*!< Registrar */
129 struct ast_exten *next; /*!< Extension with a greater ID */
130 char stuff[0];
133 /*! \brief ast_include: include= support in extensions.conf */
134 struct ast_include {
135 const char *name;
136 const char *rname; /*!< Context to include */
137 const char *registrar; /*!< Registrar */
138 int hastime; /*!< If time construct exists */
139 struct ast_timing timing; /*!< time construct */
140 struct ast_include *next; /*!< Link them together */
141 char stuff[0];
144 /*! \brief ast_sw: Switch statement in extensions.conf */
145 struct ast_sw {
146 char *name;
147 const char *registrar; /*!< Registrar */
148 char *data; /*!< Data load */
149 int eval;
150 AST_LIST_ENTRY(ast_sw) list;
151 char stuff[0];
154 /*! \brief ast_ignorepat: Ignore patterns in dial plan */
155 struct ast_ignorepat {
156 const char *registrar;
157 struct ast_ignorepat *next;
158 const char pattern[0];
161 /*! \brief ast_context: An extension context */
162 struct ast_context {
163 ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
164 struct ast_exten *root; /*!< The root of the list of extensions */
165 struct ast_context *next; /*!< Link them together */
166 struct ast_include *includes; /*!< Include other contexts */
167 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
168 const char *registrar; /*!< Registrar */
169 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
170 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
171 char name[0]; /*!< Name of the context */
175 /*! \brief ast_app: A registered application */
176 struct ast_app {
177 int (*execute)(struct ast_channel *chan, void *data);
178 const char *synopsis; /*!< Synopsis text for 'show applications' */
179 const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
180 AST_LIST_ENTRY(ast_app) list; /*!< Next app in list */
181 struct module *module; /*!< Module this app belongs to */
182 char name[0]; /*!< Name of the application */
185 /*! \brief ast_state_cb: An extension state notify register item */
186 struct ast_state_cb {
187 int id;
188 void *data;
189 ast_state_cb_type callback;
190 struct ast_state_cb *next;
193 /*! \brief Structure for dial plan hints
195 \note Hints are pointers from an extension in the dialplan to one or
196 more devices (tech/name) */
197 struct ast_hint {
198 struct ast_exten *exten; /*!< Extension */
199 int laststate; /*!< Last known state */
200 struct ast_state_cb *callbacks; /*!< Callback list for this extension */
201 AST_LIST_ENTRY(ast_hint) list; /*!< Pointer to next hint in list */
204 static const struct cfextension_states {
205 int extension_state;
206 const char * const text;
207 } extension_states[] = {
208 { AST_EXTENSION_NOT_INUSE, "Idle" },
209 { AST_EXTENSION_INUSE, "InUse" },
210 { AST_EXTENSION_BUSY, "Busy" },
211 { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
212 { AST_EXTENSION_RINGING, "Ringing" },
213 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
214 { AST_EXTENSION_ONHOLD, "Hold" },
215 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
218 static int pbx_builtin_answer(struct ast_channel *, void *);
219 static int pbx_builtin_goto(struct ast_channel *, void *);
220 static int pbx_builtin_hangup(struct ast_channel *, void *);
221 static int pbx_builtin_background(struct ast_channel *, void *);
222 static int pbx_builtin_wait(struct ast_channel *, void *);
223 static int pbx_builtin_waitexten(struct ast_channel *, void *);
224 static int pbx_builtin_resetcdr(struct ast_channel *, void *);
225 static int pbx_builtin_setamaflags(struct ast_channel *, void *);
226 static int pbx_builtin_ringing(struct ast_channel *, void *);
227 static int pbx_builtin_progress(struct ast_channel *, void *);
228 static int pbx_builtin_congestion(struct ast_channel *, void *);
229 static int pbx_builtin_busy(struct ast_channel *, void *);
230 static int pbx_builtin_setglobalvar(struct ast_channel *, void *);
231 static int pbx_builtin_noop(struct ast_channel *, void *);
232 static int pbx_builtin_gotoif(struct ast_channel *, void *);
233 static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
234 static int pbx_builtin_execiftime(struct ast_channel *, void *);
235 static int pbx_builtin_saynumber(struct ast_channel *, void *);
236 static int pbx_builtin_saydigits(struct ast_channel *, void *);
237 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
238 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
239 int pbx_builtin_setvar(struct ast_channel *, void *);
240 static int pbx_builtin_importvar(struct ast_channel *, void *);
242 AST_MUTEX_DEFINE_STATIC(globalslock);
243 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
245 static int autofallthrough = 1;
247 AST_MUTEX_DEFINE_STATIC(maxcalllock);
248 static int countcalls;
250 static AST_LIST_HEAD_STATIC(acf_root, ast_custom_function);
252 /*! \brief Declaration of builtin applications */
253 static struct pbx_builtin {
254 char name[AST_MAX_APP];
255 int (*execute)(struct ast_channel *chan, void *data);
256 char *synopsis;
257 char *description;
258 } builtins[] =
260 /* These applications are built into the PBX core and do not
261 need separate modules */
263 { "Answer", pbx_builtin_answer,
264 "Answer a channel if ringing",
265 " Answer([delay]): If the call has not been answered, this application will\n"
266 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
267 "Asterisk will wait this number of milliseconds before returning to\n"
268 "the dialplan after answering the call.\n"
271 { "BackGround", pbx_builtin_background,
272 "Play an audio file while waiting for digits of an extension to go to.",
273 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n"
274 "This application will play the given list of files (do not put extension)\n"
275 "while waiting for an extension to be dialed by the calling channel. To\n"
276 "continue waiting for digits after this application has finished playing\n"
277 "files, the WaitExten application should be used. The 'langoverride' option\n"
278 "explicitly specifies which language to attempt to use for the requested sound\n"
279 "files. If a 'context' is specified, this is the dialplan context that this\n"
280 "application will use when exiting to a dialed extension."
281 " If one of the requested sound files does not exist, call processing will be\n"
282 "terminated.\n"
283 " Options:\n"
284 " s - Causes the playback of the message to be skipped\n"
285 " if the channel is not in the 'up' state (i.e. it\n"
286 " hasn't been answered yet). If this happens, the\n"
287 " application will return immediately.\n"
288 " n - Don't answer the channel before playing the files.\n"
289 " m - Only break if a digit hit matches a one digit\n"
290 " extension in the destination context.\n"
293 { "Busy", pbx_builtin_busy,
294 "Indicate the Busy condition",
295 " Busy([timeout]): This application will indicate the busy condition to\n"
296 "the calling channel. If the optional timeout is specified, the calling channel\n"
297 "will be hung up after the specified number of seconds. Otherwise, this\n"
298 "application will wait until the calling channel hangs up.\n"
301 { "Congestion", pbx_builtin_congestion,
302 "Indicate the Congestion condition",
303 " Congestion([timeout]): This application will indicate the congestion\n"
304 "condition to the calling channel. If the optional timeout is specified, the\n"
305 "calling channel will be hung up after the specified number of seconds.\n"
306 "Otherwise, this application will wait until the calling channel hangs up.\n"
309 { "Goto", pbx_builtin_goto,
310 "Jump to a particular priority, extension, or context",
311 " Goto([[context|]extension|]priority): This application will set the current\n"
312 "context, extension, and priority in the channel structure. After it completes, the\n"
313 "pbx engine will continue dialplan execution at the specified location.\n"
314 "If no specific extension, or extension and context, are specified, then this\n"
315 "application will just set the specified priority of the current extension.\n"
316 " At least a priority is required as an argument, or the goto will return a -1,\n"
317 "and the channel and call will be terminated.\n"
318 " If the location that is put into the channel information is bogus, and asterisk cannot\n"
319 "find that location in the dialplan,\n"
320 "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
321 "extension in the current context. If that does not exist, it will try to execute the\n"
322 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
323 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
324 "What this means is that, for example, you specify a context that does not exist, then\n"
325 "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
328 { "GotoIf", pbx_builtin_gotoif,
329 "Conditional goto",
330 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
331 "context, extension, and priority in the channel structure based on the evaluation of\n"
332 "the given condition. After this application completes, the\n"
333 "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
334 "The channel will continue at\n"
335 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
336 "false. The labels are specified with the same syntax as used within the Goto\n"
337 "application. If the label chosen by the condition is omitted, no jump is\n"
338 "performed, and the execution passes to the next instruction.\n"
339 "If the target location is bogus, and does not exist, the execution engine will try \n"
340 "to find and execute the code in the 'i' (invalid)\n"
341 "extension in the current context. If that does not exist, it will try to execute the\n"
342 "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
343 "channel is hung up, and the execution of instructions on the channel is terminated.\n"
344 "Remember that this command can set the current context, and if the context specified\n"
345 "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
346 "the channel and call will both be terminated!\n"
349 { "GotoIfTime", pbx_builtin_gotoiftime,
350 "Conditional Goto based on the current time",
351 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n"
352 "This application will set the context, extension, and priority in the channel structure\n"
353 "if the current time matches the given time specification. Otherwise, nothing is done.\n"
354 "Further information on the time specification can be found in examples\n"
355 "illustrating how to do time-based context includes in the dialplan.\n"
356 "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
359 { "ExecIfTime", pbx_builtin_execiftime,
360 "Conditional application execution based on the current time",
361 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n"
362 "This application will execute the specified dialplan application, with optional\n"
363 "arguments, if the current time matches the given time specification.\n"
366 { "Hangup", pbx_builtin_hangup,
367 "Hang up the calling channel",
368 " Hangup([causecode]): This application will hang up the calling channel.\n"
369 "If a causecode is given the channel's hangup cause will be set to the given\n"
370 "value.\n"
373 { "NoOp", pbx_builtin_noop,
374 "Do Nothing",
375 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n"
376 "purposes. Any text that is provided as arguments to this application can be\n"
377 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
378 "variables or functions without having any effect."
381 { "Progress", pbx_builtin_progress,
382 "Indicate progress",
383 " Progress(): This application will request that in-band progress information\n"
384 "be provided to the calling channel.\n"
387 { "ResetCDR", pbx_builtin_resetcdr,
388 "Resets the Call Data Record",
389 " ResetCDR([options]): This application causes the Call Data Record to be\n"
390 "reset.\n"
391 " Options:\n"
392 " w -- Store the current CDR record before resetting it.\n"
393 " a -- Store any stacked records.\n"
394 " v -- Save CDR variables.\n"
397 { "Ringing", pbx_builtin_ringing,
398 "Indicate ringing tone",
399 " Ringing(): This application will request that the channel indicate a ringing\n"
400 "tone to the user.\n"
403 { "SayNumber", pbx_builtin_saynumber,
404 "Say Number",
405 " SayNumber(digits[,gender]): This application will play the sounds that\n"
406 "correspond to the given number. Optionally, a gender may be specified.\n"
407 "This will use the language that is currently set for the channel. See the\n"
408 "LANGUAGE function for more information on setting the language for the channel.\n"
411 { "SayDigits", pbx_builtin_saydigits,
412 "Say Digits",
413 " SayDigits(digits): This application will play the sounds that correspond\n"
414 "to the digits of the given number. This will use the language that is currently\n"
415 "set for the channel. See the LANGUAGE function for more information on setting\n"
416 "the language for the channel.\n"
419 { "SayAlpha", pbx_builtin_saycharacters,
420 "Say Alpha",
421 " SayAlpha(string): This application will play the sounds that correspond to\n"
422 "the letters of the given string.\n"
425 { "SayPhonetic", pbx_builtin_sayphonetic,
426 "Say Phonetic",
427 " SayPhonetic(string): This application will play the sounds from the phonetic\n"
428 "alphabet that correspond to the letters in the given string.\n"
431 { "SetAMAFlags", pbx_builtin_setamaflags,
432 "Set the AMA Flags",
433 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
434 " billing purposes.\n"
437 { "SetGlobalVar", pbx_builtin_setglobalvar,
438 "Set a global variable to a given value",
439 " SetGlobalVar(variable=value): This application sets a given global variable to\n"
440 "the specified value.\n"
441 "\n\nThis application is deprecated in favor of Set(GLOBAL(var)=value)\n"
444 { "Set", pbx_builtin_setvar,
445 "Set channel variable(s) or function value(s)",
446 " Set(name1=value1|name2=value2|..[|options])\n"
447 "This function can be used to set the value of channel variables or dialplan\n"
448 "functions. It will accept up to 24 name/value pairs. When setting variables,\n"
449 "if the variable name is prefixed with _, the variable will be inherited into\n"
450 "channels created from the current channel. If the variable name is prefixed\n"
451 "with __, the variable will be inherited into channels created from the current\n"
452 "channel and all children channels.\n"
453 " Options:\n"
454 " g - Set variable globally instead of on the channel\n"
455 " (applies only to variables, not functions)\n"
456 "\n\nThe use of Set to set multiple variables at once and the g flag have both\n"
457 "been deprecated. Please use multiple Set calls and the GLOBAL() dialplan\n"
458 "function instead.\n"
461 { "ImportVar", pbx_builtin_importvar,
462 "Import a variable from a channel into a new variable",
463 " ImportVar(newvar=channelname|variable): This application imports a variable\n"
464 "from the specified channel (as opposed to the current one) and stores it as\n"
465 "a variable in the current channel (the channel that is calling this\n"
466 "application). Variables created by this application have the same inheritance\n"
467 "properties as those created with the Set application. See the documentation for\n"
468 "Set for more information.\n"
471 { "Wait", pbx_builtin_wait,
472 "Waits for some time",
473 " Wait(seconds): This application waits for a specified number of seconds.\n"
474 "Then, dialplan execution will continue at the next priority.\n"
475 " Note that the seconds can be passed with fractions of a second. For example,\n"
476 "'1.5' will ask the application to wait for 1.5 seconds.\n"
479 { "WaitExten", pbx_builtin_waitexten,
480 "Waits for an extension to be entered",
481 " WaitExten([seconds][|options]): This application waits for the user to enter\n"
482 "a new extension for a specified number of seconds.\n"
483 " Note that the seconds can be passed with fractions of a second. For example,\n"
484 "'1.5' will ask the application to wait for 1.5 seconds.\n"
485 " Options:\n"
486 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
487 " Optionally, specify the class for music on hold within parenthesis.\n"
492 static struct ast_context *contexts;
493 AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
495 static AST_LIST_HEAD_STATIC(apps, ast_app);
497 static AST_LIST_HEAD_STATIC(switches, ast_switch);
499 static int stateid = 1;
500 /* WARNING:
501 When holding this list's lock, do _not_ do anything that will cause conlock
502 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
503 function will take the locks in conlock/hints order, so any other
504 paths that require both locks must also take them in that order.
506 static AST_LIST_HEAD_STATIC(hints, ast_hint);
507 struct ast_state_cb *statecbs;
510 \note This function is special. It saves the stack so that no matter
511 how many times it is called, it returns to the same place */
512 int pbx_exec(struct ast_channel *c, /*!< Channel */
513 struct ast_app *app, /*!< Application */
514 void *data) /*!< Data for execution */
516 int res;
518 const char *saved_c_appl;
519 const char *saved_c_data;
521 if (c->cdr && !ast_check_hangup(c))
522 ast_cdr_setapp(c->cdr, app->name, data);
524 /* save channel values */
525 saved_c_appl= c->appl;
526 saved_c_data= c->data;
528 c->appl = app->name;
529 c->data = data;
530 /* XXX remember what to to when we have linked apps to modules */
531 if (app->module) {
532 /* XXX LOCAL_USER_ADD(app->module) */
534 res = app->execute(c, S_OR(data, ""));
535 if (app->module) {
536 /* XXX LOCAL_USER_REMOVE(app->module) */
538 /* restore channel values */
539 c->appl = saved_c_appl;
540 c->data = saved_c_data;
541 return res;
545 /*! Go no deeper than this through includes (not counting loops) */
546 #define AST_PBX_MAX_STACK 128
548 /*! \brief Find application handle in linked list
550 struct ast_app *pbx_findapp(const char *app)
552 struct ast_app *tmp;
554 AST_LIST_LOCK(&apps);
555 AST_LIST_TRAVERSE(&apps, tmp, list) {
556 if (!strcasecmp(tmp->name, app))
557 break;
559 AST_LIST_UNLOCK(&apps);
561 return tmp;
564 static struct ast_switch *pbx_findswitch(const char *sw)
566 struct ast_switch *asw;
568 AST_LIST_LOCK(&switches);
569 AST_LIST_TRAVERSE(&switches, asw, list) {
570 if (!strcasecmp(asw->name, sw))
571 break;
573 AST_LIST_UNLOCK(&switches);
575 return asw;
578 static inline int include_valid(struct ast_include *i)
580 if (!i->hastime)
581 return 1;
583 return ast_check_timing(&(i->timing));
586 static void pbx_destroy(struct ast_pbx *p)
588 free(p);
592 * Special characters used in patterns:
593 * '_' underscore is the leading character of a pattern.
594 * In other position it is treated as a regular char.
595 * ' ' '-' space and '-' are separator and ignored.
596 * . one or more of any character. Only allowed at the end of
597 * a pattern.
598 * ! zero or more of anything. Also impacts the result of CANMATCH
599 * and MATCHMORE. Only allowed at the end of a pattern.
600 * In the core routine, ! causes a match with a return code of 2.
601 * In turn, depending on the search mode: (XXX check if it is implemented)
602 * - E_MATCH retuns 1 (does match)
603 * - E_MATCHMORE returns 0 (no match)
604 * - E_CANMATCH returns 1 (does match)
606 * / should not appear as it is considered the separator of the CID info.
607 * XXX at the moment we may stop on this char.
609 * X Z N match ranges 0-9, 1-9, 2-9 respectively.
610 * [ denotes the start of a set of character. Everything inside
611 * is considered literally. We can have ranges a-d and individual
612 * characters. A '[' and '-' can be considered literally if they
613 * are just before ']'.
614 * XXX currently there is no way to specify ']' in a range, nor \ is
615 * considered specially.
617 * When we compare a pattern with a specific extension, all characters in the extension
618 * itself are considered literally with the only exception of '-' which is considered
619 * as a separator and thus ignored.
620 * XXX do we want to consider space as a separator as well ?
621 * XXX do we want to consider the separators in non-patterns as well ?
625 * \brief helper functions to sort extensions and patterns in the desired way,
626 * so that more specific patterns appear first.
628 * ext_cmp1 compares individual characters (or sets of), returning
629 * an int where bits 0-7 are the ASCII code of the first char in the set,
630 * while bit 8-15 are the cardinality of the set minus 1.
631 * This way more specific patterns (smaller cardinality) appear first.
632 * Wildcards have a special value, so that we can directly compare them to
633 * sets by subtracting the two values. In particular:
634 * 0x000xx one character, xx
635 * 0x0yyxx yy character set starting with xx
636 * 0x10000 '.' (one or more of anything)
637 * 0x20000 '!' (zero or more of anything)
638 * 0x30000 NUL (end of string)
639 * 0x40000 error in set.
640 * The pointer to the string is advanced according to needs.
641 * NOTES:
642 * 1. the empty set is equivalent to NUL.
643 * 2. given that a full set has always 0 as the first element,
644 * we could encode the special cases as 0xffXX where XX
645 * is 1, 2, 3, 4 as used above.
647 static int ext_cmp1(const char **p)
649 uint32_t chars[8];
650 int c, cmin = 0xff, count = 0;
651 const char *end;
653 /* load, sign extend and advance pointer until we find
654 * a valid character.
656 while ( (c = *(*p)++) && (c == ' ' || c == '-') )
657 ; /* ignore some characters */
659 /* always return unless we have a set of chars */
660 switch (c) {
661 default: /* ordinary character */
662 return 0x0000 | (c & 0xff);
664 case 'N': /* 2..9 */
665 return 0x0700 | '2' ;
667 case 'X': /* 0..9 */
668 return 0x0900 | '0';
670 case 'Z': /* 1..9 */
671 return 0x0800 | '1';
673 case '.': /* wildcard */
674 return 0x10000;
676 case '!': /* earlymatch */
677 return 0x20000; /* less specific than NULL */
679 case '\0': /* empty string */
680 *p = NULL;
681 return 0x30000;
683 case '[': /* pattern */
684 break;
686 /* locate end of set */
687 end = strchr(*p, ']');
689 if (end == NULL) {
690 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
691 return 0x40000; /* XXX make this entry go last... */
694 bzero(chars, sizeof(chars)); /* clear all chars in the set */
695 for (; *p < end ; (*p)++) {
696 unsigned char c1, c2; /* first-last char in range */
697 c1 = (unsigned char)((*p)[0]);
698 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
699 c2 = (unsigned char)((*p)[2]);
700 *p += 2; /* skip a total of 3 chars */
701 } else /* individual character */
702 c2 = c1;
703 if (c1 < cmin)
704 cmin = c1;
705 for (; c1 <= c2; c1++) {
706 uint32_t mask = 1 << (c1 % 32);
707 if ( (chars[ c1 / 32 ] & mask) == 0)
708 count += 0x100;
709 chars[ c1 / 32 ] |= mask;
712 (*p)++;
713 return count == 0 ? 0x30000 : (count | cmin);
717 * \brief the full routine to compare extensions in rules.
719 static int ext_cmp(const char *a, const char *b)
721 /* make sure non-patterns come first.
722 * If a is not a pattern, it either comes first or
723 * we use strcmp to compare the strings.
725 int ret = 0;
727 if (a[0] != '_')
728 return (b[0] == '_') ? -1 : strcmp(a, b);
730 /* Now we know a is a pattern; if b is not, a comes first */
731 if (b[0] != '_')
732 return 1;
733 #if 0 /* old mode for ext matching */
734 return strcmp(a, b);
735 #endif
736 /* ok we need full pattern sorting routine */
737 while (!ret && a && b)
738 ret = ext_cmp1(&a) - ext_cmp1(&b);
739 if (ret == 0)
740 return 0;
741 else
742 return (ret > 0) ? 1 : -1;
746 * When looking up extensions, we can have different requests
747 * identified by the 'action' argument, as follows.
748 * Note that the coding is such that the low 4 bits are the
749 * third argument to extension_match_core.
751 enum ext_match_t {
752 E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */
753 E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */
754 E_MATCH = 0x02, /* extension is an exact match */
755 E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */
756 E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */
757 E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */
761 * Internal function for ast_extension_{match|close}
762 * return 0 on no-match, 1 on match, 2 on early match.
763 * mode is as follows:
764 * E_MATCH success only on exact match
765 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
766 * E_CANMATCH either of the above.
769 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
771 mode &= E_MATCH_MASK; /* only consider the relevant bits */
773 if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
774 return 1;
776 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
777 int ld = strlen(data), lp = strlen(pattern);
779 if (lp < ld) /* pattern too short, cannot match */
780 return 0;
781 /* depending on the mode, accept full or partial match or both */
782 if (mode == E_MATCH)
783 return !strcmp(pattern, data); /* 1 on match, 0 on fail */
784 if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
785 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
786 else
787 return 0;
789 pattern++; /* skip leading _ */
791 * XXX below we stop at '/' which is a separator for the CID info. However we should
792 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
794 while (*data && *pattern && *pattern != '/') {
795 const char *end;
797 if (*data == '-') { /* skip '-' in data (just a separator) */
798 data++;
799 continue;
801 switch (toupper(*pattern)) {
802 case '[': /* a range */
803 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
804 if (end == NULL) {
805 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
806 return 0; /* unconditional failure */
808 for (pattern++; pattern != end; pattern++) {
809 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
810 if (*data >= pattern[0] && *data <= pattern[2])
811 break; /* match found */
812 else {
813 pattern += 2; /* skip a total of 3 chars */
814 continue;
816 } else if (*data == pattern[0])
817 break; /* match found */
819 if (pattern == end)
820 return 0;
821 pattern = end; /* skip and continue */
822 break;
823 case 'N':
824 if (*data < '2' || *data > '9')
825 return 0;
826 break;
827 case 'X':
828 if (*data < '0' || *data > '9')
829 return 0;
830 break;
831 case 'Z':
832 if (*data < '1' || *data > '9')
833 return 0;
834 break;
835 case '.': /* Must match, even with more digits */
836 return 1;
837 case '!': /* Early match */
838 return 2;
839 case ' ':
840 case '-': /* Ignore these in patterns */
841 data--; /* compensate the final data++ */
842 break;
843 default:
844 if (*data != *pattern)
845 return 0;
847 data++;
848 pattern++;
850 if (*data) /* data longer than pattern, no match */
851 return 0;
853 * match so far, but ran off the end of the data.
854 * Depending on what is next, determine match or not.
856 if (*pattern == '\0' || *pattern == '/') /* exact match */
857 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
858 else if (*pattern == '!') /* early match */
859 return 2;
860 else /* partial match */
861 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
865 * Wrapper around _extension_match_core() to do performance measurement
866 * using the profiling code.
868 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
870 int i;
871 static int prof_id = -2; /* marker for 'unallocated' id */
872 if (prof_id == -2)
873 prof_id = ast_add_profile("ext_match", 0);
874 ast_mark(prof_id, 1);
875 i = _extension_match_core(pattern, data, mode);
876 ast_mark(prof_id, 0);
877 return i;
880 int ast_extension_match(const char *pattern, const char *data)
882 return extension_match_core(pattern, data, E_MATCH);
885 int ast_extension_close(const char *pattern, const char *data, int needmore)
887 if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
888 ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
889 return extension_match_core(pattern, data, needmore);
892 struct ast_context *ast_context_find(const char *name)
894 struct ast_context *tmp = NULL;
896 ast_rdlock_contexts();
898 while ( (tmp = ast_walk_contexts(tmp)) ) {
899 if (!name || !strcasecmp(name, tmp->name))
900 break;
903 ast_unlock_contexts();
905 return tmp;
908 #define STATUS_NO_CONTEXT 1
909 #define STATUS_NO_EXTENSION 2
910 #define STATUS_NO_PRIORITY 3
911 #define STATUS_NO_LABEL 4
912 #define STATUS_SUCCESS 5
914 static int matchcid(const char *cidpattern, const char *callerid)
916 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
917 failing to get a number should count as a match, otherwise not */
919 if (ast_strlen_zero(callerid))
920 return ast_strlen_zero(cidpattern) ? 1 : 0;
922 return ast_extension_match(cidpattern, callerid);
925 /* request and result for pbx_find_extension */
926 struct pbx_find_info {
927 #if 0
928 const char *context;
929 const char *exten;
930 int priority;
931 #endif
933 char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */
934 int stacklen; /* modified during the search */
935 int status; /* set on return */
936 struct ast_switch *swo; /* set on return */
937 const char *data; /* set on return */
938 const char *foundcontext; /* set on return */
941 static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
942 struct ast_context *bypass, struct pbx_find_info *q,
943 const char *context, const char *exten, int priority,
944 const char *label, const char *callerid, enum ext_match_t action)
946 int x, res;
947 struct ast_context *tmp;
948 struct ast_exten *e, *eroot;
949 struct ast_include *i;
950 struct ast_sw *sw;
951 char *tmpdata = NULL;
953 /* Initialize status if appropriate */
954 if (q->stacklen == 0) {
955 q->status = STATUS_NO_CONTEXT;
956 q->swo = NULL;
957 q->data = NULL;
958 q->foundcontext = NULL;
960 /* Check for stack overflow */
961 if (q->stacklen >= AST_PBX_MAX_STACK) {
962 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
963 return NULL;
965 /* Check first to see if we've already been checked */
966 for (x = 0; x < q->stacklen; x++) {
967 if (!strcasecmp(q->incstack[x], context))
968 return NULL;
970 if (bypass) /* bypass means we only look there */
971 tmp = bypass;
972 else { /* look in contexts */
973 tmp = NULL;
974 while ((tmp = ast_walk_contexts(tmp)) ) {
975 if (!strcmp(tmp->name, context))
976 break;
978 if (!tmp)
979 return NULL;
981 if (q->status < STATUS_NO_EXTENSION)
982 q->status = STATUS_NO_EXTENSION;
984 /* scan the list trying to match extension and CID */
985 eroot = NULL;
986 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
987 int match = extension_match_core(eroot->exten, exten, action);
988 /* 0 on fail, 1 on match, 2 on earlymatch */
990 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
991 continue; /* keep trying */
992 if (match == 2 && action == E_MATCHMORE) {
993 /* We match an extension ending in '!'.
994 * The decision in this case is final and is NULL (no match).
996 return NULL;
998 /* found entry, now look for the right priority */
999 if (q->status < STATUS_NO_PRIORITY)
1000 q->status = STATUS_NO_PRIORITY;
1001 e = NULL;
1002 while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
1003 /* Match label or priority */
1004 if (action == E_FINDLABEL) {
1005 if (q->status < STATUS_NO_LABEL)
1006 q->status = STATUS_NO_LABEL;
1007 if (label && e->label && !strcmp(label, e->label))
1008 break; /* found it */
1009 } else if (e->priority == priority) {
1010 break; /* found it */
1011 } /* else keep searching */
1013 if (e) { /* found a valid match */
1014 q->status = STATUS_SUCCESS;
1015 q->foundcontext = context;
1016 return e;
1019 /* Check alternative switches */
1020 AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
1021 struct ast_switch *asw = pbx_findswitch(sw->name);
1022 ast_switch_f *aswf = NULL;
1023 char *datap;
1025 if (!asw) {
1026 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
1027 continue;
1029 /* Substitute variables now */
1030 if (sw->eval) {
1031 if (!(tmpdata = ast_threadstorage_get(&switch_data, 512))) {
1032 ast_log(LOG_WARNING, "Can't evaluate switch?!");
1033 continue;
1035 pbx_substitute_variables_helper(chan, sw->data, tmpdata, 512);
1038 /* equivalent of extension_match_core() at the switch level */
1039 if (action == E_CANMATCH)
1040 aswf = asw->canmatch;
1041 else if (action == E_MATCHMORE)
1042 aswf = asw->matchmore;
1043 else /* action == E_MATCH */
1044 aswf = asw->exists;
1045 datap = sw->eval ? tmpdata : sw->data;
1046 if (!aswf)
1047 res = 0;
1048 else {
1049 if (chan)
1050 ast_autoservice_start(chan);
1051 res = aswf(chan, context, exten, priority, callerid, datap);
1052 if (chan)
1053 ast_autoservice_stop(chan);
1055 if (res) { /* Got a match */
1056 q->swo = asw;
1057 q->data = datap;
1058 q->foundcontext = context;
1059 /* XXX keep status = STATUS_NO_CONTEXT ? */
1060 return NULL;
1063 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
1064 /* Now try any includes we have in this context */
1065 for (i = tmp->includes; i; i = i->next) {
1066 if (include_valid(i)) {
1067 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
1068 return e;
1069 if (q->swo)
1070 return NULL;
1073 return NULL;
1076 /*! \brief extract offset:length from variable name.
1077 * Returns 1 if there is a offset:length part, which is
1078 * trimmed off (values go into variables)
1080 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
1082 int parens=0;
1084 *offset = 0;
1085 *length = INT_MAX;
1086 *isfunc = 0;
1087 for (; *var; var++) {
1088 if (*var == '(') {
1089 (*isfunc)++;
1090 parens++;
1091 } else if (*var == ')') {
1092 parens--;
1093 } else if (*var == ':' && parens == 0) {
1094 *var++ = '\0';
1095 sscanf(var, "%d:%d", offset, length);
1096 return 1; /* offset:length valid */
1099 return 0;
1102 /*! \brief takes a substring. It is ok to call with value == workspace.
1104 * offset < 0 means start from the end of the string and set the beginning
1105 * to be that many characters back.
1106 * length is the length of the substring. A value less than 0 means to leave
1107 * that many off the end.
1108 * Always return a copy in workspace.
1110 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
1112 char *ret = workspace;
1113 int lr; /* length of the input string after the copy */
1115 ast_copy_string(workspace, value, workspace_len); /* always make a copy */
1117 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
1119 /* Quick check if no need to do anything */
1120 if (offset == 0 && length >= lr) /* take the whole string */
1121 return ret;
1123 if (offset < 0) { /* translate negative offset into positive ones */
1124 offset = lr + offset;
1125 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
1126 offset = 0;
1129 /* too large offset result in empty string so we know what to return */
1130 if (offset >= lr)
1131 return ret + lr; /* the final '\0' */
1133 ret += offset; /* move to the start position */
1134 if (length >= 0 && length < lr - offset) /* truncate if necessary */
1135 ret[length] = '\0';
1136 else if (length < 0) {
1137 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
1138 ret[lr + length - offset] = '\0';
1139 else
1140 ret[0] = '\0';
1143 return ret;
1146 /*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables
1147 ---*/
1148 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
1150 const char not_found = '\0';
1151 char *tmpvar;
1152 const char *s; /* the result */
1153 int offset, length;
1154 int i, need_substring;
1155 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
1157 if (c) {
1158 ast_channel_lock(c);
1159 places[0] = &c->varshead;
1162 * Make a copy of var because parse_variable_name() modifies the string.
1163 * Then if called directly, we might need to run substring() on the result;
1164 * remember this for later in 'need_substring', 'offset' and 'length'
1166 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
1167 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
1170 * Look first into predefined variables, then into variable lists.
1171 * Variable 's' points to the result, according to the following rules:
1172 * s == &not_found (set at the beginning) means that we did not find a
1173 * matching variable and need to look into more places.
1174 * If s != &not_found, s is a valid result string as follows:
1175 * s = NULL if the variable does not have a value;
1176 * you typically do this when looking for an unset predefined variable.
1177 * s = workspace if the result has been assembled there;
1178 * typically done when the result is built e.g. with an snprintf(),
1179 * so we don't need to do an additional copy.
1180 * s != workspace in case we have a string, that needs to be copied
1181 * (the ast_copy_string is done once for all at the end).
1182 * Typically done when the result is already available in some string.
1184 s = &not_found; /* default value */
1185 if (c) { /* This group requires a valid channel */
1186 /* Names with common parts are looked up a piece at a time using strncmp. */
1187 if (!strncmp(var, "CALL", 4)) {
1188 if (!strncmp(var + 4, "ING", 3)) {
1189 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
1190 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
1191 s = workspace;
1192 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
1193 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
1194 s = workspace;
1195 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
1196 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
1197 s = workspace;
1198 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
1199 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
1200 s = workspace;
1203 } else if (!strcmp(var, "HINT")) {
1204 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
1205 } else if (!strcmp(var, "HINTNAME")) {
1206 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
1207 } else if (!strcmp(var, "EXTEN")) {
1208 s = c->exten;
1209 } else if (!strcmp(var, "CONTEXT")) {
1210 s = c->context;
1211 } else if (!strcmp(var, "PRIORITY")) {
1212 snprintf(workspace, workspacelen, "%d", c->priority);
1213 s = workspace;
1214 } else if (!strcmp(var, "CHANNEL")) {
1215 s = c->name;
1216 } else if (!strcmp(var, "UNIQUEID")) {
1217 s = c->uniqueid;
1218 } else if (!strcmp(var, "HANGUPCAUSE")) {
1219 snprintf(workspace, workspacelen, "%d", c->hangupcause);
1220 s = workspace;
1223 if (s == &not_found) { /* look for more */
1224 if (!strcmp(var, "EPOCH")) {
1225 snprintf(workspace, workspacelen, "%u",(int)time(NULL));
1226 s = workspace;
1227 } else if (!strcmp(var, "SYSTEMNAME")) {
1228 s = ast_config_AST_SYSTEM_NAME;
1231 /* if not found, look into chanvars or global vars */
1232 for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
1233 struct ast_var_t *variables;
1234 if (!places[i])
1235 continue;
1236 if (places[i] == &globals)
1237 ast_mutex_lock(&globalslock);
1238 AST_LIST_TRAVERSE(places[i], variables, entries) {
1239 if (strcasecmp(ast_var_name(variables), var)==0) {
1240 s = ast_var_value(variables);
1241 break;
1244 if (places[i] == &globals)
1245 ast_mutex_unlock(&globalslock);
1247 if (s == &not_found || s == NULL)
1248 *ret = NULL;
1249 else {
1250 if (s != workspace)
1251 ast_copy_string(workspace, s, workspacelen);
1252 *ret = workspace;
1253 if (need_substring)
1254 *ret = substring(*ret, offset, length, workspace, workspacelen);
1257 if (c)
1258 ast_channel_unlock(c);
1261 /*! \brief CLI function to show installed custom functions
1262 \addtogroup CLI_functions
1264 static int handle_show_functions_deprecated(int fd, int argc, char *argv[])
1266 struct ast_custom_function *acf;
1267 int count_acf = 0;
1268 int like = 0;
1270 if (argc == 4 && (!strcmp(argv[2], "like")) ) {
1271 like = 1;
1272 } else if (argc != 2) {
1273 return RESULT_SHOWUSAGE;
1276 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1278 AST_LIST_LOCK(&acf_root);
1279 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1280 if (!like || strstr(acf->name, argv[3])) {
1281 count_acf++;
1282 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1285 AST_LIST_UNLOCK(&acf_root);
1287 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1289 return RESULT_SUCCESS;
1291 static int handle_show_functions(int fd, int argc, char *argv[])
1293 struct ast_custom_function *acf;
1294 int count_acf = 0;
1295 int like = 0;
1297 if (argc == 5 && (!strcmp(argv[3], "like")) ) {
1298 like = 1;
1299 } else if (argc != 3) {
1300 return RESULT_SHOWUSAGE;
1303 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
1305 AST_LIST_LOCK(&acf_root);
1306 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1307 if (!like || strstr(acf->name, argv[4])) {
1308 count_acf++;
1309 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
1312 AST_LIST_UNLOCK(&acf_root);
1314 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
1316 return RESULT_SUCCESS;
1319 static int handle_show_function_deprecated(int fd, int argc, char *argv[])
1321 struct ast_custom_function *acf;
1322 /* Maximum number of characters added by terminal coloring is 22 */
1323 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1324 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1325 char stxtitle[40], *syntax = NULL;
1326 int synopsis_size, description_size, syntax_size;
1328 if (argc < 3)
1329 return RESULT_SHOWUSAGE;
1331 if (!(acf = ast_custom_function_find(argv[2]))) {
1332 ast_cli(fd, "No function by that name registered.\n");
1333 return RESULT_FAILURE;
1337 if (acf->synopsis)
1338 synopsis_size = strlen(acf->synopsis) + 23;
1339 else
1340 synopsis_size = strlen("Not available") + 23;
1341 synopsis = alloca(synopsis_size);
1343 if (acf->desc)
1344 description_size = strlen(acf->desc) + 23;
1345 else
1346 description_size = strlen("Not available") + 23;
1347 description = alloca(description_size);
1349 if (acf->syntax)
1350 syntax_size = strlen(acf->syntax) + 23;
1351 else
1352 syntax_size = strlen("Not available") + 23;
1353 syntax = alloca(syntax_size);
1355 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1356 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1357 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1358 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1359 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1360 term_color(syntax,
1361 acf->syntax ? acf->syntax : "Not available",
1362 COLOR_CYAN, 0, syntax_size);
1363 term_color(synopsis,
1364 acf->synopsis ? acf->synopsis : "Not available",
1365 COLOR_CYAN, 0, synopsis_size);
1366 term_color(description,
1367 acf->desc ? acf->desc : "Not available",
1368 COLOR_CYAN, 0, description_size);
1370 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1372 return RESULT_SUCCESS;
1375 static int handle_show_function(int fd, int argc, char *argv[])
1377 struct ast_custom_function *acf;
1378 /* Maximum number of characters added by terminal coloring is 22 */
1379 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
1380 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
1381 char stxtitle[40], *syntax = NULL;
1382 int synopsis_size, description_size, syntax_size;
1384 if (argc < 4)
1385 return RESULT_SHOWUSAGE;
1387 if (!(acf = ast_custom_function_find(argv[3]))) {
1388 ast_cli(fd, "No function by that name registered.\n");
1389 return RESULT_FAILURE;
1393 if (acf->synopsis)
1394 synopsis_size = strlen(acf->synopsis) + 23;
1395 else
1396 synopsis_size = strlen("Not available") + 23;
1397 synopsis = alloca(synopsis_size);
1399 if (acf->desc)
1400 description_size = strlen(acf->desc) + 23;
1401 else
1402 description_size = strlen("Not available") + 23;
1403 description = alloca(description_size);
1405 if (acf->syntax)
1406 syntax_size = strlen(acf->syntax) + 23;
1407 else
1408 syntax_size = strlen("Not available") + 23;
1409 syntax = alloca(syntax_size);
1411 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
1412 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
1413 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1414 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1415 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
1416 term_color(syntax,
1417 acf->syntax ? acf->syntax : "Not available",
1418 COLOR_CYAN, 0, syntax_size);
1419 term_color(synopsis,
1420 acf->synopsis ? acf->synopsis : "Not available",
1421 COLOR_CYAN, 0, synopsis_size);
1422 term_color(description,
1423 acf->desc ? acf->desc : "Not available",
1424 COLOR_CYAN, 0, description_size);
1426 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
1428 return RESULT_SUCCESS;
1431 static char *complete_show_function(const char *line, const char *word, int pos, int state)
1433 struct ast_custom_function *acf;
1434 char *ret = NULL;
1435 int which = 0;
1436 int wordlen = strlen(word);
1438 /* case-insensitive for convenience in this 'complete' function */
1439 AST_LIST_LOCK(&acf_root);
1440 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1441 if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
1442 ret = strdup(acf->name);
1443 break;
1446 AST_LIST_UNLOCK(&acf_root);
1448 return ret;
1451 struct ast_custom_function *ast_custom_function_find(const char *name)
1453 struct ast_custom_function *acf = NULL;
1455 AST_LIST_LOCK(&acf_root);
1456 AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
1457 if (!strcmp(name, acf->name))
1458 break;
1460 AST_LIST_UNLOCK(&acf_root);
1462 return acf;
1465 int ast_custom_function_unregister(struct ast_custom_function *acf)
1467 struct ast_custom_function *cur;
1469 if (!acf)
1470 return -1;
1472 AST_LIST_LOCK(&acf_root);
1473 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1474 if (cur == acf) {
1475 AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
1476 if (option_verbose > 1)
1477 ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
1478 break;
1481 AST_LIST_TRAVERSE_SAFE_END
1482 AST_LIST_UNLOCK(&acf_root);
1484 return acf ? 0 : -1;
1487 int ast_custom_function_register(struct ast_custom_function *acf)
1489 struct ast_custom_function *cur;
1491 if (!acf)
1492 return -1;
1494 AST_LIST_LOCK(&acf_root);
1496 if (ast_custom_function_find(acf->name)) {
1497 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
1498 AST_LIST_UNLOCK(&acf_root);
1499 return -1;
1502 /* Store in alphabetical order */
1503 AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
1504 if (strcasecmp(acf->name, cur->name) < 0) {
1505 AST_LIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
1506 break;
1509 AST_LIST_TRAVERSE_SAFE_END
1510 if (!cur)
1511 AST_LIST_INSERT_TAIL(&acf_root, acf, acflist);
1513 AST_LIST_UNLOCK(&acf_root);
1515 if (option_verbose > 1)
1516 ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
1518 return 0;
1521 /*! \brief return a pointer to the arguments of the function,
1522 * and terminates the function name with '\\0'
1524 static char *func_args(char *function)
1526 char *args = strchr(function, '(');
1528 if (!args)
1529 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
1530 else {
1531 char *p;
1532 *args++ = '\0';
1533 if ((p = strrchr(args, ')')) )
1534 *p = '\0';
1535 else
1536 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
1538 return args;
1541 int ast_func_read(struct ast_channel *chan, char *function, char *workspace, size_t len)
1543 char *args = func_args(function);
1544 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1546 if (acfptr == NULL)
1547 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1548 else if (!acfptr->read)
1549 ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
1550 else
1551 return acfptr->read(chan, function, args, workspace, len);
1552 return -1;
1555 int ast_func_write(struct ast_channel *chan, char *function, const char *value)
1557 char *args = func_args(function);
1558 struct ast_custom_function *acfptr = ast_custom_function_find(function);
1560 if (acfptr == NULL)
1561 ast_log(LOG_ERROR, "Function %s not registered\n", function);
1562 else if (!acfptr->write)
1563 ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
1564 else
1565 return acfptr->write(chan, function, args, value);
1567 return -1;
1570 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
1572 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
1573 zero-filled */
1574 char *cp4;
1575 const char *tmp, *whereweare;
1576 int length, offset, offset2, isfunction;
1577 char *workspace = NULL;
1578 char *ltmp = NULL, *var = NULL;
1579 char *nextvar, *nextexp, *nextthing;
1580 char *vars, *vare;
1581 int pos, brackets, needsub, len;
1583 whereweare=tmp=cp1;
1584 while (!ast_strlen_zero(whereweare) && count) {
1585 /* Assume we're copying the whole remaining string */
1586 pos = strlen(whereweare);
1587 nextvar = NULL;
1588 nextexp = NULL;
1589 nextthing = strchr(whereweare, '$');
1590 if (nextthing) {
1591 switch(nextthing[1]) {
1592 case '{':
1593 nextvar = nextthing;
1594 pos = nextvar - whereweare;
1595 break;
1596 case '[':
1597 nextexp = nextthing;
1598 pos = nextexp - whereweare;
1599 break;
1600 default:
1601 pos = 1;
1605 if (pos) {
1606 /* Can't copy more than 'count' bytes */
1607 if (pos > count)
1608 pos = count;
1610 /* Copy that many bytes */
1611 memcpy(cp2, whereweare, pos);
1613 count -= pos;
1614 cp2 += pos;
1615 whereweare += pos;
1618 if (nextvar) {
1619 /* We have a variable. Find the start and end, and determine
1620 if we are going to have to recursively call ourselves on the
1621 contents */
1622 vars = vare = nextvar + 2;
1623 brackets = 1;
1624 needsub = 0;
1626 /* Find the end of it */
1627 while (brackets && *vare) {
1628 if ((vare[0] == '$') && (vare[1] == '{')) {
1629 needsub++;
1630 } else if (vare[0] == '{') {
1631 brackets++;
1632 } else if (vare[0] == '}') {
1633 brackets--;
1634 } else if ((vare[0] == '$') && (vare[1] == '['))
1635 needsub++;
1636 vare++;
1638 if (brackets)
1639 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
1640 len = vare - vars - 1;
1642 /* Skip totally over variable string */
1643 whereweare += (len + 3);
1645 if (!var)
1646 var = alloca(VAR_BUF_SIZE);
1648 /* Store variable name (and truncate) */
1649 ast_copy_string(var, vars, len + 1);
1651 /* Substitute if necessary */
1652 if (needsub) {
1653 if (!ltmp)
1654 ltmp = alloca(VAR_BUF_SIZE);
1656 memset(ltmp, 0, VAR_BUF_SIZE);
1657 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1658 vars = ltmp;
1659 } else {
1660 vars = var;
1663 if (!workspace)
1664 workspace = alloca(VAR_BUF_SIZE);
1666 workspace[0] = '\0';
1668 parse_variable_name(vars, &offset, &offset2, &isfunction);
1669 if (isfunction) {
1670 /* Evaluate function */
1671 if (c || !headp)
1672 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1673 else {
1674 struct varshead old;
1675 struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
1676 if (c) {
1677 memcpy(&old, &c->varshead, sizeof(old));
1678 memcpy(&c->varshead, headp, sizeof(c->varshead));
1679 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
1680 /* Don't deallocate the varshead that was passed in */
1681 memcpy(&c->varshead, &old, sizeof(c->varshead));
1682 ast_channel_free(c);
1683 } else
1684 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
1687 if (option_debug)
1688 ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
1689 } else {
1690 /* Retrieve variable value */
1691 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
1693 if (cp4) {
1694 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
1696 length = strlen(cp4);
1697 if (length > count)
1698 length = count;
1699 memcpy(cp2, cp4, length);
1700 count -= length;
1701 cp2 += length;
1703 } else if (nextexp) {
1704 /* We have an expression. Find the start and end, and determine
1705 if we are going to have to recursively call ourselves on the
1706 contents */
1707 vars = vare = nextexp + 2;
1708 brackets = 1;
1709 needsub = 0;
1711 /* Find the end of it */
1712 while(brackets && *vare) {
1713 if ((vare[0] == '$') && (vare[1] == '[')) {
1714 needsub++;
1715 brackets++;
1716 vare++;
1717 } else if (vare[0] == '[') {
1718 brackets++;
1719 } else if (vare[0] == ']') {
1720 brackets--;
1721 } else if ((vare[0] == '$') && (vare[1] == '{')) {
1722 needsub++;
1723 vare++;
1725 vare++;
1727 if (brackets)
1728 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
1729 len = vare - vars - 1;
1731 /* Skip totally over expression */
1732 whereweare += (len + 3);
1734 if (!var)
1735 var = alloca(VAR_BUF_SIZE);
1737 /* Store variable name (and truncate) */
1738 ast_copy_string(var, vars, len + 1);
1740 /* Substitute if necessary */
1741 if (needsub) {
1742 if (!ltmp)
1743 ltmp = alloca(VAR_BUF_SIZE);
1745 memset(ltmp, 0, VAR_BUF_SIZE);
1746 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
1747 vars = ltmp;
1748 } else {
1749 vars = var;
1752 length = ast_expr(vars, cp2, count);
1754 if (length) {
1755 if (option_debug)
1756 ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
1757 count -= length;
1758 cp2 += length;
1764 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
1766 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
1769 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
1771 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
1774 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
1776 memset(passdata, 0, datalen);
1778 /* No variables or expressions in e->data, so why scan it? */
1779 if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
1780 ast_copy_string(passdata, e->data, datalen);
1781 return;
1784 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
1787 /*!
1788 * \brief The return value depends on the action:
1790 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
1791 * and return 0 on failure, -1 on match;
1792 * E_FINDLABEL maps the label to a priority, and returns
1793 * the priority on success, ... XXX
1794 * E_SPAWN, spawn an application,
1795 * and return 0 on success, -1 on failure.
1797 * \note The channel is auto-serviced in this function, because doing an extension
1798 * match may block for a long time. For example, if the lookup has to use a network
1799 * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
1800 * auto-service code will queue up any important signalling frames to be processed
1801 * after this is done.
1803 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
1804 const char *context, const char *exten, int priority,
1805 const char *label, const char *callerid, enum ext_match_t action)
1807 struct ast_exten *e;
1808 struct ast_app *app;
1809 int res;
1810 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
1811 char passdata[EXT_DATA_SIZE];
1813 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
1815 ast_rdlock_contexts();
1816 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
1817 if (e) {
1818 if (matching_action) {
1819 ast_unlock_contexts();
1820 return -1; /* success, we found it */
1821 } else if (action == E_FINDLABEL) { /* map the label to a priority */
1822 res = e->priority;
1823 ast_unlock_contexts();
1824 return res; /* the priority we were looking for */
1825 } else { /* spawn */
1826 app = pbx_findapp(e->app);
1827 ast_unlock_contexts();
1828 if (!app) {
1829 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
1830 return -1;
1832 if (c->context != context)
1833 ast_copy_string(c->context, context, sizeof(c->context));
1834 if (c->exten != exten)
1835 ast_copy_string(c->exten, exten, sizeof(c->exten));
1836 c->priority = priority;
1837 pbx_substitute_variables(passdata, sizeof(passdata), c, e);
1838 if (option_debug) {
1839 ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
1841 if (option_verbose > 2) {
1842 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
1843 ast_verbose( VERBOSE_PREFIX_3 "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
1844 exten, context, priority,
1845 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
1846 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
1847 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
1848 "in new stack");
1850 manager_event(EVENT_FLAG_CALL, "Newexten",
1851 "Channel: %s\r\n"
1852 "Context: %s\r\n"
1853 "Extension: %s\r\n"
1854 "Priority: %d\r\n"
1855 "Application: %s\r\n"
1856 "AppData: %s\r\n"
1857 "Uniqueid: %s\r\n",
1858 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
1859 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
1861 } else if (q.swo) { /* not found here, but in another switch */
1862 ast_unlock_contexts();
1863 if (matching_action) {
1864 return -1;
1865 } else {
1866 if (!q.swo->exec) {
1867 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
1868 res = -1;
1870 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
1872 } else { /* not found anywhere, see what happened */
1873 ast_unlock_contexts();
1874 switch (q.status) {
1875 case STATUS_NO_CONTEXT:
1876 if (!matching_action)
1877 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
1878 break;
1879 case STATUS_NO_EXTENSION:
1880 if (!matching_action)
1881 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
1882 break;
1883 case STATUS_NO_PRIORITY:
1884 if (!matching_action)
1885 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
1886 break;
1887 case STATUS_NO_LABEL:
1888 if (context)
1889 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
1890 break;
1891 default:
1892 if (option_debug)
1893 ast_log(LOG_DEBUG, "Shouldn't happen!\n");
1896 return (matching_action) ? 0 : -1;
1900 /*! \brief ast_hint_extension: Find hint for given extension in context */
1901 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
1903 struct ast_exten *e;
1904 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
1906 ast_rdlock_contexts();
1907 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
1908 ast_unlock_contexts();
1910 return e;
1913 /*! \brief ast_extensions_state2: Check state of extension by using hints */
1914 static int ast_extension_state2(struct ast_exten *e)
1916 char hint[AST_MAX_EXTENSION];
1917 char *cur, *rest;
1918 int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
1919 int busy = 0, inuse = 0, ring = 0;
1921 if (!e)
1922 return -1;
1924 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
1926 rest = hint; /* One or more devices separated with a & character */
1927 while ( (cur = strsep(&rest, "&")) ) {
1928 int res = ast_device_state(cur);
1929 switch (res) {
1930 case AST_DEVICE_NOT_INUSE:
1931 allunavailable = 0;
1932 allbusy = 0;
1933 allonhold = 0;
1934 break;
1935 case AST_DEVICE_INUSE:
1936 inuse = 1;
1937 allunavailable = 0;
1938 allfree = 0;
1939 allonhold = 0;
1940 break;
1941 case AST_DEVICE_RINGING:
1942 ring = 1;
1943 allunavailable = 0;
1944 allfree = 0;
1945 allonhold = 0;
1946 break;
1947 case AST_DEVICE_RINGINUSE:
1948 inuse = 1;
1949 ring = 1;
1950 allunavailable = 0;
1951 allfree = 0;
1952 allonhold = 0;
1953 break;
1954 case AST_DEVICE_ONHOLD:
1955 allunavailable = 0;
1956 allfree = 0;
1957 break;
1958 case AST_DEVICE_BUSY:
1959 allunavailable = 0;
1960 allfree = 0;
1961 allonhold = 0;
1962 busy = 1;
1963 break;
1964 case AST_DEVICE_UNAVAILABLE:
1965 case AST_DEVICE_INVALID:
1966 allbusy = 0;
1967 allfree = 0;
1968 allonhold = 0;
1969 break;
1970 default:
1971 allunavailable = 0;
1972 allbusy = 0;
1973 allfree = 0;
1974 allonhold = 0;
1978 if (!inuse && ring)
1979 return AST_EXTENSION_RINGING;
1980 if (inuse && ring)
1981 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
1982 if (inuse)
1983 return AST_EXTENSION_INUSE;
1984 if (allfree)
1985 return AST_EXTENSION_NOT_INUSE;
1986 if (allonhold)
1987 return AST_EXTENSION_ONHOLD;
1988 if (allbusy)
1989 return AST_EXTENSION_BUSY;
1990 if (allunavailable)
1991 return AST_EXTENSION_UNAVAILABLE;
1992 if (busy)
1993 return AST_EXTENSION_INUSE;
1995 return AST_EXTENSION_NOT_INUSE;
1998 /*! \brief ast_extension_state2str: Return extension_state as string */
1999 const char *ast_extension_state2str(int extension_state)
2001 int i;
2003 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
2004 if (extension_states[i].extension_state == extension_state)
2005 return extension_states[i].text;
2007 return "Unknown";
2010 /*! \brief ast_extension_state: Check extension state for an extension by using hint */
2011 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
2013 struct ast_exten *e;
2015 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
2016 if (!e)
2017 return -1; /* No hint, return -1 */
2019 return ast_extension_state2(e); /* Check all devices in the hint */
2022 void ast_hint_state_changed(const char *device)
2024 struct ast_hint *hint;
2026 AST_LIST_LOCK(&hints);
2028 AST_LIST_TRAVERSE(&hints, hint, list) {
2029 struct ast_state_cb *cblist;
2030 char buf[AST_MAX_EXTENSION];
2031 char *parse = buf;
2032 char *cur;
2033 int state;
2035 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
2036 while ( (cur = strsep(&parse, "&")) ) {
2037 if (!strcasecmp(cur, device))
2038 break;
2040 if (!cur)
2041 continue;
2043 /* Get device state for this hint */
2044 state = ast_extension_state2(hint->exten);
2046 if ((state == -1) || (state == hint->laststate))
2047 continue;
2049 /* Device state changed since last check - notify the watchers */
2051 /* For general callbacks */
2052 for (cblist = statecbs; cblist; cblist = cblist->next)
2053 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2055 /* For extension callbacks */
2056 for (cblist = hint->callbacks; cblist; cblist = cblist->next)
2057 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
2059 hint->laststate = state; /* record we saw the change */
2062 AST_LIST_UNLOCK(&hints);
2065 /*! \brief ast_extension_state_add: Add watcher for extension states */
2066 int ast_extension_state_add(const char *context, const char *exten,
2067 ast_state_cb_type callback, void *data)
2069 struct ast_hint *hint;
2070 struct ast_state_cb *cblist;
2071 struct ast_exten *e;
2073 /* If there's no context and extension: add callback to statecbs list */
2074 if (!context && !exten) {
2075 AST_LIST_LOCK(&hints);
2077 for (cblist = statecbs; cblist; cblist = cblist->next) {
2078 if (cblist->callback == callback) {
2079 cblist->data = data;
2080 AST_LIST_UNLOCK(&hints);
2081 return 0;
2085 /* Now insert the callback */
2086 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2087 AST_LIST_UNLOCK(&hints);
2088 return -1;
2090 cblist->id = 0;
2091 cblist->callback = callback;
2092 cblist->data = data;
2094 cblist->next = statecbs;
2095 statecbs = cblist;
2097 AST_LIST_UNLOCK(&hints);
2098 return 0;
2101 if (!context || !exten)
2102 return -1;
2104 /* This callback type is for only one hint, so get the hint */
2105 e = ast_hint_extension(NULL, context, exten);
2106 if (!e) {
2107 return -1;
2110 /* Find the hint in the list of hints */
2111 AST_LIST_LOCK(&hints);
2113 AST_LIST_TRAVERSE(&hints, hint, list) {
2114 if (hint->exten == e)
2115 break;
2118 if (!hint) {
2119 /* We have no hint, sorry */
2120 AST_LIST_UNLOCK(&hints);
2121 return -1;
2124 /* Now insert the callback in the callback list */
2125 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
2126 AST_LIST_UNLOCK(&hints);
2127 return -1;
2129 cblist->id = stateid++; /* Unique ID for this callback */
2130 cblist->callback = callback; /* Pointer to callback routine */
2131 cblist->data = data; /* Data for the callback */
2133 cblist->next = hint->callbacks;
2134 hint->callbacks = cblist;
2136 AST_LIST_UNLOCK(&hints);
2137 return cblist->id;
2140 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */
2141 int ast_extension_state_del(int id, ast_state_cb_type callback)
2143 struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
2144 int ret = -1;
2146 if (!id && !callback)
2147 return -1;
2149 AST_LIST_LOCK(&hints);
2151 if (!id) { /* id == 0 is a callback without extension */
2152 for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
2153 if ((*p_cur)->callback == callback)
2154 break;
2156 } else { /* callback with extension, find the callback based on ID */
2157 struct ast_hint *hint;
2158 AST_LIST_TRAVERSE(&hints, hint, list) {
2159 for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
2160 if ((*p_cur)->id == id)
2161 break;
2163 if (*p_cur) /* found in the inner loop */
2164 break;
2167 if (p_cur && *p_cur) {
2168 struct ast_state_cb *cur = *p_cur;
2169 *p_cur = cur->next;
2170 free(cur);
2171 ret = 0;
2173 AST_LIST_UNLOCK(&hints);
2174 return ret;
2177 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
2178 static int ast_add_hint(struct ast_exten *e)
2180 struct ast_hint *hint;
2182 if (!e)
2183 return -1;
2185 AST_LIST_LOCK(&hints);
2187 /* Search if hint exists, do nothing */
2188 AST_LIST_TRAVERSE(&hints, hint, list) {
2189 if (hint->exten == e) {
2190 AST_LIST_UNLOCK(&hints);
2191 if (option_debug > 1)
2192 ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2193 return -1;
2197 if (option_debug > 1)
2198 ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
2200 if (!(hint = ast_calloc(1, sizeof(*hint)))) {
2201 AST_LIST_UNLOCK(&hints);
2202 return -1;
2204 /* Initialize and insert new item at the top */
2205 hint->exten = e;
2206 hint->laststate = ast_extension_state2(e);
2207 AST_LIST_INSERT_HEAD(&hints, hint, list);
2209 AST_LIST_UNLOCK(&hints);
2210 return 0;
2213 /*! \brief ast_change_hint: Change hint for an extension */
2214 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
2216 struct ast_hint *hint;
2217 int res = -1;
2219 AST_LIST_LOCK(&hints);
2220 AST_LIST_TRAVERSE(&hints, hint, list) {
2221 if (hint->exten == oe) {
2222 hint->exten = ne;
2223 res = 0;
2224 break;
2227 AST_LIST_UNLOCK(&hints);
2229 return res;
2232 /*! \brief ast_remove_hint: Remove hint from extension */
2233 static int ast_remove_hint(struct ast_exten *e)
2235 /* Cleanup the Notifys if hint is removed */
2236 struct ast_hint *hint;
2237 struct ast_state_cb *cblist, *cbprev;
2238 int res = -1;
2240 if (!e)
2241 return -1;
2243 AST_LIST_LOCK(&hints);
2244 AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
2245 if (hint->exten == e) {
2246 cbprev = NULL;
2247 cblist = hint->callbacks;
2248 while (cblist) {
2249 /* Notify with -1 and remove all callbacks */
2250 cbprev = cblist;
2251 cblist = cblist->next;
2252 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
2253 free(cbprev);
2255 hint->callbacks = NULL;
2256 AST_LIST_REMOVE_CURRENT(&hints, list);
2257 free(hint);
2258 res = 0;
2259 break;
2262 AST_LIST_TRAVERSE_SAFE_END
2263 AST_LIST_UNLOCK(&hints);
2265 return res;
2269 /*! \brief ast_get_hint: Get hint for channel */
2270 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
2272 struct ast_exten *e = ast_hint_extension(c, context, exten);
2274 if (e) {
2275 if (hint)
2276 ast_copy_string(hint, ast_get_extension_app(e), hintsize);
2277 if (name) {
2278 const char *tmp = ast_get_extension_app_data(e);
2279 if (tmp)
2280 ast_copy_string(name, tmp, namesize);
2282 return -1;
2284 return 0;
2287 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2289 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH);
2292 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
2294 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL);
2297 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
2299 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
2302 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2304 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH);
2307 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2309 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE);
2312 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
2314 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN);
2317 /* helper function to set extension and priority */
2318 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
2320 ast_channel_lock(c);
2321 ast_copy_string(c->exten, exten, sizeof(c->exten));
2322 c->priority = pri;
2323 ast_channel_unlock(c);
2327 * \brief collect digits from the channel into the buffer,
2328 * return -1 on error, 0 on timeout or done.
2330 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
2332 int digit;
2334 buf[pos] = '\0'; /* make sure it is properly terminated */
2335 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
2336 /* As long as we're willing to wait, and as long as it's not defined,
2337 keep reading digits until we can't possibly get a right answer anymore. */
2338 digit = ast_waitfordigit(c, waittime * 1000);
2339 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2340 c->_softhangup = 0;
2341 } else {
2342 if (!digit) /* No entry */
2343 break;
2344 if (digit < 0) /* Error, maybe a hangup */
2345 return -1;
2346 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
2347 buf[pos++] = digit;
2348 buf[pos] = '\0';
2350 waittime = c->pbx->dtimeout;
2353 return 0;
2356 static int __ast_pbx_run(struct ast_channel *c)
2358 int found = 0; /* set if we find at least one match */
2359 int res = 0;
2360 int autoloopflag;
2361 int error = 0; /* set an error conditions */
2363 /* A little initial setup here */
2364 if (c->pbx) {
2365 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
2366 /* XXX and now what ? */
2367 free(c->pbx);
2369 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
2370 return -1;
2371 if (c->amaflags) {
2372 if (!c->cdr) {
2373 c->cdr = ast_cdr_alloc();
2374 if (!c->cdr) {
2375 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
2376 free(c->pbx);
2377 return -1;
2379 ast_cdr_init(c->cdr, c);
2382 /* Set reasonable defaults */
2383 c->pbx->rtimeout = 10;
2384 c->pbx->dtimeout = 5;
2386 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
2387 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
2389 /* Start by trying whatever the channel is set to */
2390 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2391 /* If not successful fall back to 's' */
2392 if (option_verbose > 1)
2393 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);
2394 /* XXX the original code used the existing priority in the call to
2395 * ast_exists_extension(), and reset it to 1 afterwards.
2396 * I believe the correct thing is to set it to 1 immediately.
2398 set_ext_pri(c, "s", 1);
2399 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2400 /* JK02: And finally back to default if everything else failed */
2401 if (option_verbose > 1)
2402 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);
2403 ast_copy_string(c->context, "default", sizeof(c->context));
2406 if (c->cdr && ast_tvzero(c->cdr->start))
2407 ast_cdr_start(c->cdr);
2408 for (;;) {
2409 char dst_exten[256]; /* buffer to accumulate digits */
2410 int pos = 0; /* XXX should check bounds */
2411 int digit = 0;
2413 /* loop on priorities in this context/exten */
2414 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2415 found = 1;
2416 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2417 /* Something bad happened, or a hangup has been requested. */
2418 if (strchr("0123456789ABCDEF*#", res)) {
2419 if (option_debug)
2420 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
2421 pos = 0;
2422 dst_exten[pos++] = digit = res;
2423 dst_exten[pos] = '\0';
2424 break;
2426 if (res == AST_PBX_KEEPALIVE) {
2427 if (option_debug)
2428 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2429 if (option_verbose > 1)
2430 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
2431 error = 1;
2432 break;
2434 if (option_debug)
2435 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2436 if (option_verbose > 1)
2437 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2438 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
2439 c->_softhangup =0;
2440 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2441 /* atimeout, nothing bad */
2442 } else {
2443 if (c->cdr)
2444 ast_cdr_update(c);
2445 error = 1;
2446 break;
2449 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
2450 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
2451 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
2452 c->whentohangup = 0;
2453 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
2454 } else if (c->_softhangup) {
2455 if (option_debug)
2456 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
2457 c->exten, c->priority);
2458 error = 1;
2459 break;
2461 c->priority++;
2462 } /* end while - from here on we can use 'break' to go out */
2463 if (error)
2464 break;
2466 /* XXX we get here on non-existing extension or a keypress or hangup ? */
2468 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
2469 /* If there is no match at priority 1, it is not a valid extension anymore.
2470 * Try to continue at "i", 1 or exit if the latter does not exist.
2472 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2473 if (option_verbose > 2)
2474 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
2475 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
2476 set_ext_pri(c, "i", 1);
2477 } else {
2478 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
2479 c->name, c->exten, c->context);
2480 error = 1; /* we know what to do with it */
2481 break;
2483 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
2484 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
2485 c->_softhangup = 0;
2486 } else { /* keypress received, get more digits for a full extension */
2487 int waittime = 0;
2488 if (digit)
2489 waittime = c->pbx->dtimeout;
2490 else if (!autofallthrough)
2491 waittime = c->pbx->rtimeout;
2492 if (!waittime) {
2493 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
2494 if (!status)
2495 status = "UNKNOWN";
2496 if (option_verbose > 2)
2497 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
2498 if (!strcasecmp(status, "CONGESTION"))
2499 res = pbx_builtin_congestion(c, "10");
2500 else if (!strcasecmp(status, "CHANUNAVAIL"))
2501 res = pbx_builtin_congestion(c, "10");
2502 else if (!strcasecmp(status, "BUSY"))
2503 res = pbx_builtin_busy(c, "10");
2504 error = 1; /* XXX disable message */
2505 break; /* exit from the 'for' loop */
2508 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
2509 break;
2510 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
2511 set_ext_pri(c, dst_exten, 1);
2512 else {
2513 /* No such extension */
2514 if (!ast_strlen_zero(dst_exten)) {
2515 /* An invalid extension */
2516 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
2517 if (option_verbose > 2)
2518 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
2519 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
2520 set_ext_pri(c, "i", 1);
2521 } else {
2522 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
2523 found = 1; /* XXX disable message */
2524 break;
2526 } else {
2527 /* A simple timeout */
2528 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
2529 if (option_verbose > 2)
2530 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
2531 set_ext_pri(c, "t", 1);
2532 } else {
2533 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
2534 found = 1; /* XXX disable message */
2535 break;
2539 if (c->cdr) {
2540 if (option_verbose > 2)
2541 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
2542 ast_cdr_update(c);
2546 if (!found && !error)
2547 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
2548 if (res != AST_PBX_KEEPALIVE)
2549 ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
2550 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
2551 if (c->cdr && ast_opt_end_cdr_before_h_exten)
2552 ast_cdr_end(c->cdr);
2553 set_ext_pri(c, "h", 1);
2554 while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
2555 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
2556 /* Something bad happened, or a hangup has been requested. */
2557 if (option_debug)
2558 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2559 if (option_verbose > 1)
2560 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
2561 break;
2563 c->priority++;
2566 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
2568 pbx_destroy(c->pbx);
2569 c->pbx = NULL;
2570 if (res != AST_PBX_KEEPALIVE)
2571 ast_hangup(c);
2572 return 0;
2575 /* Returns 0 on success, non-zero if call limit was reached */
2576 static int increase_call_count(const struct ast_channel *c)
2578 int failed = 0;
2579 double curloadavg;
2580 ast_mutex_lock(&maxcalllock);
2581 if (option_maxcalls) {
2582 if (countcalls >= option_maxcalls) {
2583 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
2584 failed = -1;
2587 if (option_maxload) {
2588 getloadavg(&curloadavg, 1);
2589 if (curloadavg >= option_maxload) {
2590 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
2591 failed = -1;
2594 if (!failed)
2595 countcalls++;
2596 ast_mutex_unlock(&maxcalllock);
2598 return failed;
2601 static void decrease_call_count(void)
2603 ast_mutex_lock(&maxcalllock);
2604 if (countcalls > 0)
2605 countcalls--;
2606 ast_mutex_unlock(&maxcalllock);
2609 static void destroy_exten(struct ast_exten *e)
2611 if (e->priority == PRIORITY_HINT)
2612 ast_remove_hint(e);
2614 if (e->datad)
2615 e->datad(e->data);
2616 free(e);
2619 static void *pbx_thread(void *data)
2621 /* Oh joyeous kernel, we're a new thread, with nothing to do but
2622 answer this channel and get it going.
2624 /* NOTE:
2625 The launcher of this function _MUST_ increment 'countcalls'
2626 before invoking the function; it will be decremented when the
2627 PBX has finished running on the channel
2629 struct ast_channel *c = data;
2631 __ast_pbx_run(c);
2632 decrease_call_count();
2634 pthread_exit(NULL);
2636 return NULL;
2639 enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
2641 pthread_t t;
2642 pthread_attr_t attr;
2644 if (!c) {
2645 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
2646 return AST_PBX_FAILED;
2649 if (increase_call_count(c))
2650 return AST_PBX_CALL_LIMIT;
2652 /* Start a new thread, and get something handling this channel. */
2653 pthread_attr_init(&attr);
2654 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2655 if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
2656 ast_log(LOG_WARNING, "Failed to create new channel thread\n");
2657 pthread_attr_destroy(&attr);
2658 return AST_PBX_FAILED;
2660 pthread_attr_destroy(&attr);
2662 return AST_PBX_SUCCESS;
2665 enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
2667 enum ast_pbx_result res = AST_PBX_SUCCESS;
2669 if (increase_call_count(c))
2670 return AST_PBX_CALL_LIMIT;
2672 res = __ast_pbx_run(c);
2673 decrease_call_count();
2675 return res;
2678 int ast_active_calls(void)
2680 return countcalls;
2683 int pbx_set_autofallthrough(int newval)
2685 int oldval = autofallthrough;
2686 autofallthrough = newval;
2687 return oldval;
2690 /* lookup for a context with a given name,
2691 * return with conlock held if found, NULL if not found
2693 static struct ast_context *find_context_locked(const char *context)
2695 struct ast_context *c = NULL;
2697 ast_rdlock_contexts();
2698 while ( (c = ast_walk_contexts(c)) ) {
2699 if (!strcmp(ast_get_context_name(c), context))
2700 return c;
2702 ast_unlock_contexts();
2704 return NULL;
2708 * This function locks contexts list by &conlist, search for the right context
2709 * structure, leave context list locked and call ast_context_remove_include2
2710 * which removes include, unlock contexts list and return ...
2712 int ast_context_remove_include(const char *context, const char *include, const char *registrar)
2714 int ret = -1;
2715 struct ast_context *c = find_context_locked(context);
2717 if (c) {
2718 /* found, remove include from this context ... */
2719 ret = ast_context_remove_include2(c, include, registrar);
2720 ast_unlock_contexts();
2722 return ret;
2726 * When we call this function, &conlock lock must be locked, because when
2727 * we giving *con argument, some process can remove/change this context
2728 * and after that there can be segfault.
2730 * This function locks given context, removes include, unlock context and
2731 * return.
2733 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
2735 struct ast_include *i, *pi = NULL;
2736 int ret = -1;
2738 ast_mutex_lock(&con->lock);
2740 /* find our include */
2741 for (i = con->includes; i; pi = i, i = i->next) {
2742 if (!strcmp(i->name, include) &&
2743 (!registrar || !strcmp(i->registrar, registrar))) {
2744 /* remove from list */
2745 if (pi)
2746 pi->next = i->next;
2747 else
2748 con->includes = i->next;
2749 /* free include and return */
2750 free(i);
2751 ret = 0;
2752 break;
2756 ast_mutex_unlock(&con->lock);
2757 return ret;
2761 * \note This function locks contexts list by &conlist, search for the rigt context
2762 * structure, leave context list locked and call ast_context_remove_switch2
2763 * which removes switch, unlock contexts list and return ...
2765 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
2767 int ret = -1; /* default error return */
2768 struct ast_context *c = find_context_locked(context);
2770 if (c) {
2771 /* remove switch from this context ... */
2772 ret = ast_context_remove_switch2(c, sw, data, registrar);
2773 ast_unlock_contexts();
2775 return ret;
2779 * \brief This function locks given context, removes switch, unlock context and
2780 * return.
2781 * \note When we call this function, &conlock lock must be locked, because when
2782 * we giving *con argument, some process can remove/change this context
2783 * and after that there can be segfault.
2786 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
2788 struct ast_sw *i;
2789 int ret = -1;
2791 ast_mutex_lock(&con->lock);
2793 /* walk switches */
2794 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
2795 if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
2796 (!registrar || !strcmp(i->registrar, registrar))) {
2797 /* found, remove from list */
2798 AST_LIST_REMOVE_CURRENT(&con->alts, list);
2799 free(i); /* free switch and return */
2800 ret = 0;
2801 break;
2804 AST_LIST_TRAVERSE_SAFE_END
2806 ast_mutex_unlock(&con->lock);
2808 return ret;
2812 * \note This functions lock contexts list, search for the right context,
2813 * call ast_context_remove_extension2, unlock contexts list and return.
2814 * In this function we are using
2816 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
2818 int ret = -1; /* default error return */
2819 struct ast_context *c = find_context_locked(context);
2821 if (c) { /* ... remove extension ... */
2822 ret = ast_context_remove_extension2(c, extension, priority, registrar);
2823 ast_unlock_contexts();
2825 return ret;
2829 * \brief This functionc locks given context, search for the right extension and
2830 * fires out all peer in this extensions with given priority. If priority
2831 * is set to 0, all peers are removed. After that, unlock context and
2832 * return.
2833 * \note When do you want to call this function, make sure that &conlock is locked,
2834 * because some process can handle with your *con context before you lock
2835 * it.
2838 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
2840 struct ast_exten *exten, *prev_exten = NULL;
2841 struct ast_exten *peer;
2843 ast_mutex_lock(&con->lock);
2845 /* scan the extension list to find matching extension-registrar */
2846 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
2847 if (!strcmp(exten->exten, extension) &&
2848 (!registrar || !strcmp(exten->registrar, registrar)))
2849 break;
2851 if (!exten) {
2852 /* we can't find right extension */
2853 ast_mutex_unlock(&con->lock);
2854 return -1;
2857 /* should we free all peers in this extension? (priority == 0)? */
2858 if (priority == 0) {
2859 /* remove this extension from context list */
2860 if (prev_exten)
2861 prev_exten->next = exten->next;
2862 else
2863 con->root = exten->next;
2865 /* fire out all peers */
2866 while ( (peer = exten) ) {
2867 exten = peer->peer; /* prepare for next entry */
2868 destroy_exten(peer);
2870 } else {
2871 /* scan the priority list to remove extension with exten->priority == priority */
2872 struct ast_exten *previous_peer = NULL;
2874 for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
2875 if (peer->priority == priority &&
2876 (!registrar || !strcmp(peer->registrar, registrar) ))
2877 break; /* found our priority */
2879 if (!peer) { /* not found */
2880 ast_mutex_unlock(&con->lock);
2881 return -1;
2883 /* we are first priority extension? */
2884 if (!previous_peer) {
2886 * We are first in the priority chain, so must update the extension chain.
2887 * The next node is either the next priority or the next extension
2889 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
2891 if (!prev_exten) /* change the root... */
2892 con->root = next_node;
2893 else
2894 prev_exten->next = next_node; /* unlink */
2895 if (peer->peer) /* XXX update the new head of the pri list */
2896 peer->peer->next = peer->next;
2897 } else { /* easy, we are not first priority in extension */
2898 previous_peer->peer = peer->peer;
2901 /* now, free whole priority extension */
2902 destroy_exten(peer);
2903 /* XXX should we return -1 ? */
2905 ast_mutex_unlock(&con->lock);
2906 return 0;
2911 * \note This function locks contexts list by &conlist, searches for the right context
2912 * structure, and locks the macrolock mutex in that context.
2913 * macrolock is used to limit a macro to be executed by one call at a time.
2915 int ast_context_lockmacro(const char *context)
2917 struct ast_context *c = NULL;
2918 int ret = -1;
2920 ast_rdlock_contexts();
2922 while ((c = ast_walk_contexts(c))) {
2923 if (!strcmp(ast_get_context_name(c), context)) {
2924 ret = 0;
2925 break;
2929 ast_unlock_contexts();
2931 /* if we found context, lock macrolock */
2932 if (ret == 0)
2933 ret = ast_mutex_lock(&c->macrolock);
2935 return ret;
2939 * \note This function locks contexts list by &conlist, searches for the right context
2940 * structure, and unlocks the macrolock mutex in that context.
2941 * macrolock is used to limit a macro to be executed by one call at a time.
2943 int ast_context_unlockmacro(const char *context)
2945 struct ast_context *c = NULL;
2946 int ret = -1;
2948 ast_rdlock_contexts();
2950 while ((c = ast_walk_contexts(c))) {
2951 if (!strcmp(ast_get_context_name(c), context)) {
2952 ret = 0;
2953 break;
2957 ast_unlock_contexts();
2959 /* if we found context, unlock macrolock */
2960 if (ret == 0)
2961 ret = ast_mutex_unlock(&c->macrolock);
2963 return ret;
2966 /*! \brief Dynamically register a new dial plan application */
2967 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
2969 struct ast_app *tmp, *cur = NULL;
2970 char tmps[80];
2971 int length;
2973 AST_LIST_LOCK(&apps);
2974 AST_LIST_TRAVERSE(&apps, tmp, list) {
2975 if (!strcasecmp(app, tmp->name)) {
2976 ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
2977 AST_LIST_UNLOCK(&apps);
2978 return -1;
2982 length = sizeof(*tmp) + strlen(app) + 1;
2984 if (!(tmp = ast_calloc(1, length))) {
2985 AST_LIST_UNLOCK(&apps);
2986 return -1;
2989 strcpy(tmp->name, app);
2990 tmp->execute = execute;
2991 tmp->synopsis = synopsis;
2992 tmp->description = description;
2994 /* Store in alphabetical order */
2995 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
2996 if (strcasecmp(tmp->name, cur->name) < 0) {
2997 AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
2998 break;
3001 AST_LIST_TRAVERSE_SAFE_END
3002 if (!cur)
3003 AST_LIST_INSERT_TAIL(&apps, tmp, list);
3005 if (option_verbose > 1)
3006 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
3008 AST_LIST_UNLOCK(&apps);
3010 return 0;
3014 * Append to the list. We don't have a tail pointer because we need
3015 * to scan the list anyways to check for duplicates during insertion.
3017 int ast_register_switch(struct ast_switch *sw)
3019 struct ast_switch *tmp;
3021 AST_LIST_LOCK(&switches);
3022 AST_LIST_TRAVERSE(&switches, tmp, list) {
3023 if (!strcasecmp(tmp->name, sw->name)) {
3024 AST_LIST_UNLOCK(&switches);
3025 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
3026 return -1;
3029 AST_LIST_INSERT_TAIL(&switches, sw, list);
3030 AST_LIST_UNLOCK(&switches);
3032 return 0;
3035 void ast_unregister_switch(struct ast_switch *sw)
3037 AST_LIST_LOCK(&switches);
3038 AST_LIST_REMOVE(&switches, sw, list);
3039 AST_LIST_UNLOCK(&switches);
3043 * Help for CLI commands ...
3045 static char show_applications_help[] =
3046 "Usage: core show applications [{like|describing} <text>]\n"
3047 " List applications which are currently available.\n"
3048 " If 'like', <text> will be a substring of the app name\n"
3049 " If 'describing', <text> will be a substring of the description\n";
3051 static char show_functions_help[] =
3052 "Usage: core show functions [like <text>]\n"
3053 " List builtin functions, optionally only those matching a given string\n";
3055 static char show_switches_help[] =
3056 "Usage: core show switches\n"
3057 " List registered switches\n";
3059 static char show_hints_help[] =
3060 "Usage: core show hints\n"
3061 " List registered hints\n";
3063 static char show_globals_help[] =
3064 "Usage: core show globals\n"
3065 " List current global dialplan variables and their values\n";
3067 static char show_application_help[] =
3068 "Usage: core show application <application> [<application> [<application> [...]]]\n"
3069 " Describes a particular application.\n";
3071 static char show_function_help[] =
3072 "Usage: core show function <function>\n"
3073 " Describe a particular dialplan function.\n";
3075 static char show_dialplan_help[] =
3076 "Usage: dialplan show [exten@][context]\n"
3077 " Show dialplan\n";
3079 static char set_global_help[] =
3080 "Usage: core set global <name> <value>\n"
3081 " Set global dialplan variable <name> to <value>\n";
3085 * \brief 'show application' CLI command implementation functions ...
3089 * There is a possibility to show informations about more than one
3090 * application at one time. You can type 'show application Dial Echo' and
3091 * you will see informations about these two applications ...
3093 static char *complete_show_application(const char *line, const char *word, int pos, int state)
3095 struct ast_app *a;
3096 char *ret = NULL;
3097 int which = 0;
3098 int wordlen = strlen(word);
3100 /* return the n-th [partial] matching entry */
3101 AST_LIST_LOCK(&apps);
3102 AST_LIST_TRAVERSE(&apps, a, list) {
3103 if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
3104 ret = strdup(a->name);
3105 break;
3108 AST_LIST_UNLOCK(&apps);
3110 return ret;
3113 static int handle_show_application_deprecated(int fd, int argc, char *argv[])
3115 struct ast_app *a;
3116 int app, no_registered_app = 1;
3118 if (argc < 3)
3119 return RESULT_SHOWUSAGE;
3121 /* ... go through all applications ... */
3122 AST_LIST_LOCK(&apps);
3123 AST_LIST_TRAVERSE(&apps, a, list) {
3124 /* ... compare this application name with all arguments given
3125 * to 'show application' command ... */
3126 for (app = 2; app < argc; app++) {
3127 if (!strcasecmp(a->name, argv[app])) {
3128 /* Maximum number of characters added by terminal coloring is 22 */
3129 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3130 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3131 int synopsis_size, description_size;
3133 no_registered_app = 0;
3135 if (a->synopsis)
3136 synopsis_size = strlen(a->synopsis) + 23;
3137 else
3138 synopsis_size = strlen("Not available") + 23;
3139 synopsis = alloca(synopsis_size);
3141 if (a->description)
3142 description_size = strlen(a->description) + 23;
3143 else
3144 description_size = strlen("Not available") + 23;
3145 description = alloca(description_size);
3147 if (synopsis && description) {
3148 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3149 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3150 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3151 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3152 term_color(synopsis,
3153 a->synopsis ? a->synopsis : "Not available",
3154 COLOR_CYAN, 0, synopsis_size);
3155 term_color(description,
3156 a->description ? a->description : "Not available",
3157 COLOR_CYAN, 0, description_size);
3159 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3160 } else {
3161 /* ... one of our applications, show info ...*/
3162 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3163 "[Synopsis]\n %s\n\n"
3164 "[Description]\n%s\n",
3165 a->name,
3166 a->synopsis ? a->synopsis : "Not available",
3167 a->description ? a->description : "Not available");
3172 AST_LIST_UNLOCK(&apps);
3174 /* we found at least one app? no? */
3175 if (no_registered_app) {
3176 ast_cli(fd, "Your application(s) is (are) not registered\n");
3177 return RESULT_FAILURE;
3180 return RESULT_SUCCESS;
3183 static int handle_show_application(int fd, int argc, char *argv[])
3185 struct ast_app *a;
3186 int app, no_registered_app = 1;
3188 if (argc < 4)
3189 return RESULT_SHOWUSAGE;
3191 /* ... go through all applications ... */
3192 AST_LIST_LOCK(&apps);
3193 AST_LIST_TRAVERSE(&apps, a, list) {
3194 /* ... compare this application name with all arguments given
3195 * to 'show application' command ... */
3196 for (app = 3; app < argc; app++) {
3197 if (!strcasecmp(a->name, argv[app])) {
3198 /* Maximum number of characters added by terminal coloring is 22 */
3199 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
3200 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
3201 int synopsis_size, description_size;
3203 no_registered_app = 0;
3205 if (a->synopsis)
3206 synopsis_size = strlen(a->synopsis) + 23;
3207 else
3208 synopsis_size = strlen("Not available") + 23;
3209 synopsis = alloca(synopsis_size);
3211 if (a->description)
3212 description_size = strlen(a->description) + 23;
3213 else
3214 description_size = strlen("Not available") + 23;
3215 description = alloca(description_size);
3217 if (synopsis && description) {
3218 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name);
3219 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
3220 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
3221 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
3222 term_color(synopsis,
3223 a->synopsis ? a->synopsis : "Not available",
3224 COLOR_CYAN, 0, synopsis_size);
3225 term_color(description,
3226 a->description ? a->description : "Not available",
3227 COLOR_CYAN, 0, description_size);
3229 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
3230 } else {
3231 /* ... one of our applications, show info ...*/
3232 ast_cli(fd,"\n -= Info about application '%s' =- \n\n"
3233 "[Synopsis]\n %s\n\n"
3234 "[Description]\n%s\n",
3235 a->name,
3236 a->synopsis ? a->synopsis : "Not available",
3237 a->description ? a->description : "Not available");
3242 AST_LIST_UNLOCK(&apps);
3244 /* we found at least one app? no? */
3245 if (no_registered_app) {
3246 ast_cli(fd, "Your application(s) is (are) not registered\n");
3247 return RESULT_FAILURE;
3250 return RESULT_SUCCESS;
3253 /*! \brief handle_show_hints: CLI support for listing registred dial plan hints */
3254 static int handle_show_hints(int fd, int argc, char *argv[])
3256 struct ast_hint *hint;
3257 int num = 0;
3258 int watchers;
3259 struct ast_state_cb *watcher;
3261 if (AST_LIST_EMPTY(&hints)) {
3262 ast_cli(fd, "There are no registered dialplan hints\n");
3263 return RESULT_SUCCESS;
3265 /* ... we have hints ... */
3266 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
3267 AST_LIST_LOCK(&hints);
3268 AST_LIST_TRAVERSE(&hints, hint, list) {
3269 watchers = 0;
3270 for (watcher = hint->callbacks; watcher; watcher = watcher->next)
3271 watchers++;
3272 ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
3273 ast_get_extension_name(hint->exten),
3274 ast_get_context_name(ast_get_extension_context(hint->exten)),
3275 ast_get_extension_app(hint->exten),
3276 ast_extension_state2str(hint->laststate), watchers);
3277 num++;
3279 ast_cli(fd, "----------------\n");
3280 ast_cli(fd, "- %d hints registered\n", num);
3281 AST_LIST_UNLOCK(&hints);
3282 return RESULT_SUCCESS;
3285 /*! \brief handle_show_switches: CLI support for listing registred dial plan switches */
3286 static int handle_show_switches(int fd, int argc, char *argv[])
3288 struct ast_switch *sw;
3290 AST_LIST_LOCK(&switches);
3292 if (AST_LIST_EMPTY(&switches)) {
3293 AST_LIST_UNLOCK(&switches);
3294 ast_cli(fd, "There are no registered alternative switches\n");
3295 return RESULT_SUCCESS;
3298 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n");
3299 AST_LIST_TRAVERSE(&switches, sw, list)
3300 ast_cli(fd, "%s: %s\n", sw->name, sw->description);
3302 AST_LIST_UNLOCK(&switches);
3304 return RESULT_SUCCESS;
3308 * 'show applications' CLI command implementation functions ...
3310 static int handle_show_applications_deprecated(int fd, int argc, char *argv[])
3312 struct ast_app *a;
3313 int like = 0, describing = 0;
3314 int total_match = 0; /* Number of matches in like clause */
3315 int total_apps = 0; /* Number of apps registered */
3317 AST_LIST_LOCK(&apps);
3319 if (AST_LIST_EMPTY(&apps)) {
3320 ast_cli(fd, "There are no registered applications\n");
3321 AST_LIST_UNLOCK(&apps);
3322 return -1;
3325 /* show applications like <keyword> */
3326 if ((argc == 4) && (!strcmp(argv[2], "like"))) {
3327 like = 1;
3328 } else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
3329 describing = 1;
3332 /* show applications describing <keyword1> [<keyword2>] [...] */
3333 if ((!like) && (!describing)) {
3334 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3335 } else {
3336 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3339 AST_LIST_TRAVERSE(&apps, a, list) {
3340 int printapp = 0;
3341 total_apps++;
3342 if (like) {
3343 if (strcasestr(a->name, argv[3])) {
3344 printapp = 1;
3345 total_match++;
3347 } else if (describing) {
3348 if (a->description) {
3349 /* Match all words on command line */
3350 int i;
3351 printapp = 1;
3352 for (i = 3; i < argc; i++) {
3353 if (!strcasestr(a->description, argv[i])) {
3354 printapp = 0;
3355 } else {
3356 total_match++;
3360 } else {
3361 printapp = 1;
3364 if (printapp) {
3365 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3368 if ((!like) && (!describing)) {
3369 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3370 } else {
3371 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3374 AST_LIST_UNLOCK(&apps);
3376 return RESULT_SUCCESS;
3378 static int handle_show_applications(int fd, int argc, char *argv[])
3380 struct ast_app *a;
3381 int like = 0, describing = 0;
3382 int total_match = 0; /* Number of matches in like clause */
3383 int total_apps = 0; /* Number of apps registered */
3385 AST_LIST_LOCK(&apps);
3387 if (AST_LIST_EMPTY(&apps)) {
3388 ast_cli(fd, "There are no registered applications\n");
3389 AST_LIST_UNLOCK(&apps);
3390 return -1;
3393 /* core list applications like <keyword> */
3394 if ((argc == 5) && (!strcmp(argv[3], "like"))) {
3395 like = 1;
3396 } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) {
3397 describing = 1;
3400 /* core list applications describing <keyword1> [<keyword2>] [...] */
3401 if ((!like) && (!describing)) {
3402 ast_cli(fd, " -= Registered Asterisk Applications =-\n");
3403 } else {
3404 ast_cli(fd, " -= Matching Asterisk Applications =-\n");
3407 AST_LIST_TRAVERSE(&apps, a, list) {
3408 int printapp = 0;
3409 total_apps++;
3410 if (like) {
3411 if (strcasestr(a->name, argv[4])) {
3412 printapp = 1;
3413 total_match++;
3415 } else if (describing) {
3416 if (a->description) {
3417 /* Match all words on command line */
3418 int i;
3419 printapp = 1;
3420 for (i = 4; i < argc; i++) {
3421 if (!strcasestr(a->description, argv[i])) {
3422 printapp = 0;
3423 } else {
3424 total_match++;
3428 } else {
3429 printapp = 1;
3432 if (printapp) {
3433 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
3436 if ((!like) && (!describing)) {
3437 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps);
3438 } else {
3439 ast_cli(fd, " -= %d Applications Matching =-\n",total_match);
3442 AST_LIST_UNLOCK(&apps);
3444 return RESULT_SUCCESS;
3447 static char *complete_show_applications_deprecated(const char *line, const char *word, int pos, int state)
3449 static char* choices[] = { "like", "describing", NULL };
3451 return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
3454 static char *complete_show_applications(const char *line, const char *word, int pos, int state)
3456 static char* choices[] = { "like", "describing", NULL };
3458 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
3462 * 'show dialplan' CLI command implementation functions ...
3464 static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
3465 int state)
3467 struct ast_context *c = NULL;
3468 char *ret = NULL;
3469 int which = 0;
3470 int wordlen;
3472 /* we are do completion of [exten@]context on second position only */
3473 if (pos != 2)
3474 return NULL;
3476 ast_rdlock_contexts();
3478 wordlen = strlen(word);
3480 /* walk through all contexts and return the n-th match */
3481 while ( (c = ast_walk_contexts(c)) ) {
3482 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
3483 ret = ast_strdup(ast_get_context_name(c));
3484 break;
3488 ast_unlock_contexts();
3490 return ret;
3493 struct dialplan_counters {
3494 int total_context;
3495 int total_exten;
3496 int total_prio;
3497 int context_existence;
3498 int extension_existence;
3501 /*! \brief helper function to print an extension */
3502 static void print_ext(struct ast_exten *e, char * buf, int buflen)
3504 int prio = ast_get_extension_priority(e);
3505 if (prio == PRIORITY_HINT) {
3506 snprintf(buf, buflen, "hint: %s",
3507 ast_get_extension_app(e));
3508 } else {
3509 snprintf(buf, buflen, "%d. %s(%s)",
3510 prio, ast_get_extension_app(e),
3511 (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
3515 /* XXX not verified */
3516 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[])
3518 struct ast_context *c = NULL;
3519 int res = 0, old_total_exten = dpc->total_exten;
3521 ast_rdlock_contexts();
3523 /* walk all contexts ... */
3524 while ( (c = ast_walk_contexts(c)) ) {
3525 struct ast_exten *e;
3526 struct ast_include *i;
3527 struct ast_ignorepat *ip;
3528 char buf[256], buf2[256];
3529 int context_info_printed = 0;
3531 if (context && strcmp(ast_get_context_name(c), context))
3532 continue; /* skip this one, name doesn't match */
3534 dpc->context_existence = 1;
3536 ast_lock_context(c);
3538 /* are we looking for exten too? if yes, we print context
3539 * only if we find our extension.
3540 * Otherwise print context even if empty ?
3541 * XXX i am not sure how the rinclude is handled.
3542 * I think it ought to go inside.
3544 if (!exten) {
3545 dpc->total_context++;
3546 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3547 ast_get_context_name(c), ast_get_context_registrar(c));
3548 context_info_printed = 1;
3551 /* walk extensions ... */
3552 e = NULL;
3553 while ( (e = ast_walk_context_extensions(c, e)) ) {
3554 struct ast_exten *p;
3556 if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
3557 continue; /* skip, extension match failed */
3559 dpc->extension_existence = 1;
3561 /* may we print context info? */
3562 if (!context_info_printed) {
3563 dpc->total_context++;
3564 if (rinclude) { /* TODO Print more info about rinclude */
3565 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
3566 ast_get_context_name(c), ast_get_context_registrar(c));
3567 } else {
3568 ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
3569 ast_get_context_name(c), ast_get_context_registrar(c));
3571 context_info_printed = 1;
3573 dpc->total_prio++;
3575 /* write extension name and first peer */
3576 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
3578 print_ext(e, buf2, sizeof(buf2));
3580 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
3581 ast_get_extension_registrar(e));
3583 dpc->total_exten++;
3584 /* walk next extension peers */
3585 p = e; /* skip the first one, we already got it */
3586 while ( (p = ast_walk_extension_priorities(e, p)) ) {
3587 const char *el = ast_get_extension_label(p);
3588 dpc->total_prio++;
3589 if (el)
3590 snprintf(buf, sizeof(buf), " [%s]", el);
3591 else
3592 buf[0] = '\0';
3593 print_ext(p, buf2, sizeof(buf2));
3595 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
3596 ast_get_extension_registrar(p));
3600 /* walk included and write info ... */
3601 i = NULL;
3602 while ( (i = ast_walk_context_includes(c, i)) ) {
3603 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
3604 if (exten) {
3605 /* Check all includes for the requested extension */
3606 if (includecount >= AST_PBX_MAX_STACK) {
3607 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
3608 } else {
3609 int dupe=0;
3610 int x;
3611 for (x=0;x<includecount;x++) {
3612 if (!strcasecmp(includes[x], ast_get_include_name(i))) {
3613 dupe++;
3614 break;
3617 if (!dupe) {
3618 includes[includecount] = ast_get_include_name(i);
3619 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
3620 } else {
3621 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
3624 } else {
3625 ast_cli(fd, " Include => %-45s [%s]\n",
3626 buf, ast_get_include_registrar(i));
3630 /* walk ignore patterns and write info ... */
3631 ip = NULL;
3632 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
3633 const char *ipname = ast_get_ignorepat_name(ip);
3634 char ignorepat[AST_MAX_EXTENSION];
3635 snprintf(buf, sizeof(buf), "'%s'", ipname);
3636 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
3637 if (!exten || ast_extension_match(ignorepat, exten)) {
3638 ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
3639 buf, ast_get_ignorepat_registrar(ip));
3642 if (!rinclude) {
3643 struct ast_sw *sw = NULL;
3644 while ( (sw = ast_walk_context_switches(c, sw)) ) {
3645 snprintf(buf, sizeof(buf), "'%s/%s'",
3646 ast_get_switch_name(sw),
3647 ast_get_switch_data(sw));
3648 ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
3649 buf, ast_get_switch_registrar(sw));
3653 ast_unlock_context(c);
3655 /* if we print something in context, make an empty line */
3656 if (context_info_printed)
3657 ast_cli(fd, "\r\n");
3659 ast_unlock_contexts();
3661 return (dpc->total_exten == old_total_exten) ? -1 : res;
3664 static int handle_show_dialplan(int fd, int argc, char *argv[])
3666 char *exten = NULL, *context = NULL;
3667 /* Variables used for different counters */
3668 struct dialplan_counters counters;
3670 const char *incstack[AST_PBX_MAX_STACK];
3671 memset(&counters, 0, sizeof(counters));
3673 if (argc != 2 && argc != 3)
3674 return RESULT_SHOWUSAGE;
3676 /* we obtain [exten@]context? if yes, split them ... */
3677 if (argc == 3) {
3678 if (strchr(argv[2], '@')) { /* split into exten & context */
3679 context = ast_strdupa(argv[2]);
3680 exten = strsep(&context, "@");
3681 /* change empty strings to NULL */
3682 if (ast_strlen_zero(exten))
3683 exten = NULL;
3684 } else { /* no '@' char, only context given */
3685 context = argv[2];
3687 if (ast_strlen_zero(context))
3688 context = NULL;
3690 /* else Show complete dial plan, context and exten are NULL */
3691 show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
3693 /* check for input failure and throw some error messages */
3694 if (context && !counters.context_existence) {
3695 ast_cli(fd, "There is no existence of '%s' context\n", context);
3696 return RESULT_FAILURE;
3699 if (exten && !counters.extension_existence) {
3700 if (context)
3701 ast_cli(fd, "There is no existence of %s@%s extension\n",
3702 exten, context);
3703 else
3704 ast_cli(fd,
3705 "There is no existence of '%s' extension in all contexts\n",
3706 exten);
3707 return RESULT_FAILURE;
3710 ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
3711 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
3712 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
3713 counters.total_context, counters.total_context == 1 ? "context" : "contexts");
3715 /* everything ok */
3716 return RESULT_SUCCESS;
3719 /*! \brief CLI support for listing global variables in a parseable way */
3720 static int handle_show_globals(int fd, int argc, char *argv[])
3722 int i = 0;
3723 struct ast_var_t *newvariable;
3725 ast_mutex_lock(&globalslock);
3726 AST_LIST_TRAVERSE (&globals, newvariable, entries) {
3727 i++;
3728 ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
3730 ast_mutex_unlock(&globalslock);
3731 ast_cli(fd, "\n -- %d variables\n", i);
3733 return RESULT_SUCCESS;
3736 /*! \brief CLI support for setting global variables */
3737 static int handle_set_global_deprecated(int fd, int argc, char *argv[])
3739 if (argc != 4)
3740 return RESULT_SHOWUSAGE;
3742 pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
3743 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[2], argv[3]);
3745 return RESULT_SUCCESS;
3749 static int handle_set_global(int fd, int argc, char *argv[])
3751 if (argc != 5)
3752 return RESULT_SHOWUSAGE;
3754 pbx_builtin_setvar_helper(NULL, argv[3], argv[4]);
3755 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]);
3757 return RESULT_SUCCESS;
3763 * CLI entries for upper commands ...
3765 static struct ast_cli_entry cli_show_applications_deprecated = {
3766 { "show", "applications", NULL },
3767 handle_show_applications_deprecated, NULL,
3768 NULL, complete_show_applications_deprecated };
3770 static struct ast_cli_entry cli_show_functions_deprecated = {
3771 { "show", "functions", NULL },
3772 handle_show_functions_deprecated, NULL,
3773 NULL };
3775 static struct ast_cli_entry cli_show_switches_deprecated = {
3776 { "show", "switches", NULL },
3777 handle_show_switches, NULL,
3778 NULL };
3780 static struct ast_cli_entry cli_show_hints_deprecated = {
3781 { "show", "hints", NULL },
3782 handle_show_hints, NULL,
3783 NULL };
3785 static struct ast_cli_entry cli_show_globals_deprecated = {
3786 { "show", "globals", NULL },
3787 handle_show_globals, NULL,
3788 NULL };
3790 static struct ast_cli_entry cli_show_function_deprecated = {
3791 { "show" , "function", NULL },
3792 handle_show_function_deprecated, NULL,
3793 NULL, complete_show_function };
3795 static struct ast_cli_entry cli_show_application_deprecated = {
3796 { "show", "application", NULL },
3797 handle_show_application_deprecated, NULL,
3798 NULL, complete_show_application };
3800 static struct ast_cli_entry cli_show_dialplan_deprecated = {
3801 { "show", "dialplan", NULL },
3802 handle_show_dialplan, NULL,
3803 NULL, complete_show_dialplan_context };
3805 static struct ast_cli_entry cli_set_global_deprecated = {
3806 { "set", "global", NULL },
3807 handle_set_global_deprecated, NULL,
3808 NULL };
3810 static struct ast_cli_entry pbx_cli[] = {
3811 { { "core", "show", "applications", NULL },
3812 handle_show_applications, "Shows registered dialplan applications",
3813 show_applications_help, complete_show_applications, &cli_show_applications_deprecated },
3815 { { "core", "show", "functions", NULL },
3816 handle_show_functions, "Shows registered dialplan functions",
3817 show_functions_help, NULL, &cli_show_functions_deprecated },
3819 { { "core", "show", "switches", NULL },
3820 handle_show_switches, "Show alternative switches",
3821 show_switches_help, NULL, &cli_show_switches_deprecated },
3823 { { "core", "show", "hints", NULL },
3824 handle_show_hints, "Show dialplan hints",
3825 show_hints_help, NULL, &cli_show_hints_deprecated },
3827 { { "core", "show", "globals", NULL },
3828 handle_show_globals, "Show global dialplan variables",
3829 show_globals_help, NULL, &cli_show_globals_deprecated },
3831 { { "core", "show" , "function", NULL },
3832 handle_show_function, "Describe a specific dialplan function",
3833 show_function_help, complete_show_function, &cli_show_function_deprecated },
3835 { { "core", "show", "application", NULL },
3836 handle_show_application, "Describe a specific dialplan application",
3837 show_application_help, complete_show_application, &cli_show_application_deprecated },
3839 { { "core", "set", "global", NULL },
3840 handle_set_global, "Set global dialplan variable",
3841 set_global_help, NULL, &cli_set_global_deprecated },
3843 { { "dialplan", "show", NULL },
3844 handle_show_dialplan, "Show dialplan",
3845 show_dialplan_help, complete_show_dialplan_context, &cli_show_dialplan_deprecated },
3848 int ast_unregister_application(const char *app)
3850 struct ast_app *tmp;
3852 AST_LIST_LOCK(&apps);
3853 AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
3854 if (!strcasecmp(app, tmp->name)) {
3855 AST_LIST_REMOVE_CURRENT(&apps, list);
3856 if (option_verbose > 1)
3857 ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
3858 free(tmp);
3859 break;
3862 AST_LIST_TRAVERSE_SAFE_END
3863 AST_LIST_UNLOCK(&apps);
3865 return tmp ? 0 : -1;
3868 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
3870 struct ast_context *tmp, **local_contexts;
3871 int length = sizeof(struct ast_context) + strlen(name) + 1;
3873 if (!extcontexts) {
3874 ast_rdlock_contexts();
3875 local_contexts = &contexts;
3876 } else
3877 local_contexts = extcontexts;
3879 for (tmp = *local_contexts; tmp; tmp = tmp->next) {
3880 if (!strcasecmp(tmp->name, name)) {
3881 if (!existsokay) {
3882 ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
3883 tmp = NULL;
3885 if (!extcontexts)
3886 ast_unlock_contexts();
3887 return tmp;
3891 if (!extcontexts)
3892 ast_unlock_contexts();
3894 if ((tmp = ast_calloc(1, length))) {
3895 ast_mutex_init(&tmp->lock);
3896 ast_mutex_init(&tmp->macrolock);
3897 strcpy(tmp->name, name);
3898 tmp->registrar = registrar;
3899 if (!extcontexts)
3900 ast_wrlock_contexts();
3901 tmp->next = *local_contexts;
3902 *local_contexts = tmp;
3903 if (!extcontexts)
3904 ast_unlock_contexts();
3905 if (option_debug)
3906 ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
3907 if (option_verbose > 2)
3908 ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
3911 return tmp;
3914 struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3916 return __ast_context_create(extcontexts, name, registrar, 0);
3919 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
3921 return __ast_context_create(extcontexts, name, registrar, 1);
3923 void __ast_context_destroy(struct ast_context *con, const char *registrar);
3925 struct store_hint {
3926 char *context;
3927 char *exten;
3928 struct ast_state_cb *callbacks;
3929 int laststate;
3930 AST_LIST_ENTRY(store_hint) list;
3931 char data[1];
3934 AST_LIST_HEAD(store_hints, store_hint);
3936 /* XXX this does not check that multiple contexts are merged */
3937 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
3939 struct ast_context *tmp, *lasttmp = NULL;
3940 struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
3941 struct store_hint *this;
3942 struct ast_hint *hint;
3943 struct ast_exten *exten;
3944 int length;
3945 struct ast_state_cb *thiscb, *prevcb;
3947 /* it is very important that this function hold the hint list lock _and_ the conlock
3948 during its operation; not only do we need to ensure that the list of contexts
3949 and extensions does not change, but also that no hint callbacks (watchers) are
3950 added or removed during the merge/delete process
3952 in addition, the locks _must_ be taken in this order, because there are already
3953 other code paths that use this order
3955 ast_wrlock_contexts();
3956 AST_LIST_LOCK(&hints);
3958 /* preserve all watchers for hints associated with this registrar */
3959 AST_LIST_TRAVERSE(&hints, hint, list) {
3960 if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
3961 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
3962 if (!(this = ast_calloc(1, length)))
3963 continue;
3964 this->callbacks = hint->callbacks;
3965 hint->callbacks = NULL;
3966 this->laststate = hint->laststate;
3967 this->context = this->data;
3968 strcpy(this->data, hint->exten->parent->name);
3969 this->exten = this->data + strlen(this->context) + 1;
3970 strcpy(this->exten, hint->exten->exten);
3971 AST_LIST_INSERT_HEAD(&store, this, list);
3975 tmp = *extcontexts;
3976 if (registrar) {
3977 /* XXX remove previous contexts from same registrar */
3978 if (option_debug)
3979 ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
3980 __ast_context_destroy(NULL,registrar);
3981 while (tmp) {
3982 lasttmp = tmp;
3983 tmp = tmp->next;
3985 } else {
3986 /* XXX remove contexts with the same name */
3987 while (tmp) {
3988 ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
3989 __ast_context_destroy(tmp,tmp->registrar);
3990 lasttmp = tmp;
3991 tmp = tmp->next;
3994 if (lasttmp) {
3995 lasttmp->next = contexts;
3996 contexts = *extcontexts;
3997 *extcontexts = NULL;
3998 } else
3999 ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
4001 /* restore the watchers for hints that can be found; notify those that
4002 cannot be restored
4004 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
4005 struct pbx_find_info q = { .stacklen = 0 };
4006 exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
4007 /* Find the hint in the list of hints */
4008 AST_LIST_TRAVERSE(&hints, hint, list) {
4009 if (hint->exten == exten)
4010 break;
4012 if (!exten || !hint) {
4013 /* this hint has been removed, notify the watchers */
4014 prevcb = NULL;
4015 thiscb = this->callbacks;
4016 while (thiscb) {
4017 prevcb = thiscb;
4018 thiscb = thiscb->next;
4019 prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
4020 free(prevcb);
4022 } else {
4023 thiscb = this->callbacks;
4024 while (thiscb->next)
4025 thiscb = thiscb->next;
4026 thiscb->next = hint->callbacks;
4027 hint->callbacks = this->callbacks;
4028 hint->laststate = this->laststate;
4030 free(this);
4033 AST_LIST_UNLOCK(&hints);
4034 ast_unlock_contexts();
4036 return;
4040 * errno values
4041 * EBUSY - can't lock
4042 * ENOENT - no existence of context
4044 int ast_context_add_include(const char *context, const char *include, const char *registrar)
4046 int ret = -1;
4047 struct ast_context *c = find_context_locked(context);
4049 if (c) {
4050 ret = ast_context_add_include2(c, include, registrar);
4051 ast_unlock_contexts();
4053 return ret;
4056 /*! \brief Helper for get_range.
4057 * return the index of the matching entry, starting from 1.
4058 * If names is not supplied, try numeric values.
4060 static int lookup_name(const char *s, char *const names[], int max)
4062 int i;
4064 if (names) {
4065 for (i = 0; names[i]; i++) {
4066 if (!strcasecmp(s, names[i]))
4067 return i+1;
4069 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
4070 return i;
4072 return 0; /* error return */
4075 /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
4076 * names, if supplied, is an array of names that should be mapped to numbers.
4078 static unsigned get_range(char *src, int max, char *const names[], const char *msg)
4080 int s, e; /* start and ending position */
4081 unsigned int mask = 0;
4083 /* Check for whole range */
4084 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
4085 s = 0;
4086 e = max - 1;
4087 } else {
4088 /* Get start and ending position */
4089 char *c = strchr(src, '-');
4090 if (c)
4091 *c++ = '\0';
4092 /* Find the start */
4093 s = lookup_name(src, names, max);
4094 if (!s) {
4095 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
4096 return 0;
4098 s--;
4099 if (c) { /* find end of range */
4100 e = lookup_name(c, names, max);
4101 if (!e) {
4102 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
4103 return 0;
4105 e--;
4106 } else
4107 e = s;
4109 /* Fill the mask. Remember that ranges are cyclic */
4110 mask = 1 << e; /* initialize with last element */
4111 while (s != e) {
4112 if (s >= max) {
4113 s = 0;
4114 mask |= (1 << s);
4115 } else {
4116 mask |= (1 << s);
4117 s++;
4120 return mask;
4123 /*! \brief store a bitmask of valid times, one bit each 2 minute */
4124 static void get_timerange(struct ast_timing *i, char *times)
4126 char *e;
4127 int x;
4128 int s1, s2;
4129 int e1, e2;
4130 /* int cth, ctm; */
4132 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
4133 memset(i->minmask, 0, sizeof(i->minmask));
4135 /* 2-minutes per bit, since the mask has only 32 bits :( */
4136 /* Star is all times */
4137 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
4138 for (x=0; x<24; x++)
4139 i->minmask[x] = 0x3fffffff; /* 30 bits */
4140 return;
4142 /* Otherwise expect a range */
4143 e = strchr(times, '-');
4144 if (!e) {
4145 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
4146 return;
4148 *e++ = '\0';
4149 /* XXX why skip non digits ? */
4150 while (*e && !isdigit(*e))
4151 e++;
4152 if (!*e) {
4153 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
4154 return;
4156 if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
4157 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
4158 return;
4160 if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
4161 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
4162 return;
4164 /* XXX this needs to be optimized */
4165 #if 1
4166 s1 = s1 * 30 + s2/2;
4167 if ((s1 < 0) || (s1 >= 24*30)) {
4168 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
4169 return;
4171 e1 = e1 * 30 + e2/2;
4172 if ((e1 < 0) || (e1 >= 24*30)) {
4173 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
4174 return;
4176 /* Go through the time and enable each appropriate bit */
4177 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
4178 i->minmask[x/30] |= (1 << (x % 30));
4180 /* Do the last one */
4181 i->minmask[x/30] |= (1 << (x % 30));
4182 #else
4183 for (cth=0; cth<24; cth++) {
4184 /* Initialize masks to blank */
4185 i->minmask[cth] = 0;
4186 for (ctm=0; ctm<30; ctm++) {
4187 if (
4188 /* First hour with more than one hour */
4189 (((cth == s1) && (ctm >= s2)) &&
4190 ((cth < e1)))
4191 /* Only one hour */
4192 || (((cth == s1) && (ctm >= s2)) &&
4193 ((cth == e1) && (ctm <= e2)))
4194 /* In between first and last hours (more than 2 hours) */
4195 || ((cth > s1) &&
4196 (cth < e1))
4197 /* Last hour with more than one hour */
4198 || ((cth > s1) &&
4199 ((cth == e1) && (ctm <= e2)))
4201 i->minmask[cth] |= (1 << (ctm / 2));
4204 #endif
4205 /* All done */
4206 return;
4209 static char *days[] =
4211 "sun",
4212 "mon",
4213 "tue",
4214 "wed",
4215 "thu",
4216 "fri",
4217 "sat",
4218 NULL,
4221 static char *months[] =
4223 "jan",
4224 "feb",
4225 "mar",
4226 "apr",
4227 "may",
4228 "jun",
4229 "jul",
4230 "aug",
4231 "sep",
4232 "oct",
4233 "nov",
4234 "dec",
4235 NULL,
4238 int ast_build_timing(struct ast_timing *i, const char *info_in)
4240 char info_save[256];
4241 char *info;
4243 /* Check for empty just in case */
4244 if (ast_strlen_zero(info_in))
4245 return 0;
4246 /* make a copy just in case we were passed a static string */
4247 ast_copy_string(info_save, info_in, sizeof(info_save));
4248 info = info_save;
4249 /* Assume everything except time */
4250 i->monthmask = 0xfff; /* 12 bits */
4251 i->daymask = 0x7fffffffU; /* 31 bits */
4252 i->dowmask = 0x7f; /* 7 bits */
4253 /* on each call, use strsep() to move info to the next argument */
4254 get_timerange(i, strsep(&info, "|"));
4255 if (info)
4256 i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
4257 if (info)
4258 i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
4259 if (info)
4260 i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
4261 return 1;
4264 int ast_check_timing(const struct ast_timing *i)
4266 struct tm tm;
4267 time_t t = time(NULL);
4269 ast_localtime(&t, &tm, NULL);
4271 /* If it's not the right month, return */
4272 if (!(i->monthmask & (1 << tm.tm_mon)))
4273 return 0;
4275 /* If it's not that time of the month.... */
4276 /* Warning, tm_mday has range 1..31! */
4277 if (!(i->daymask & (1 << (tm.tm_mday-1))))
4278 return 0;
4280 /* If it's not the right day of the week */
4281 if (!(i->dowmask & (1 << tm.tm_wday)))
4282 return 0;
4284 /* Sanity check the hour just to be safe */
4285 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
4286 ast_log(LOG_WARNING, "Insane time...\n");
4287 return 0;
4290 /* Now the tough part, we calculate if it fits
4291 in the right time based on min/hour */
4292 if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
4293 return 0;
4295 /* If we got this far, then we're good */
4296 return 1;
4300 * errno values
4301 * ENOMEM - out of memory
4302 * EBUSY - can't lock
4303 * EEXIST - already included
4304 * EINVAL - there is no existence of context for inclusion
4306 int ast_context_add_include2(struct ast_context *con, const char *value,
4307 const char *registrar)
4309 struct ast_include *new_include;
4310 char *c;
4311 struct ast_include *i, *il = NULL; /* include, include_last */
4312 int length;
4313 char *p;
4315 length = sizeof(struct ast_include);
4316 length += 2 * (strlen(value) + 1);
4318 /* allocate new include structure ... */
4319 if (!(new_include = ast_calloc(1, length)))
4320 return -1;
4321 /* Fill in this structure. Use 'p' for assignments, as the fields
4322 * in the structure are 'const char *'
4324 p = new_include->stuff;
4325 new_include->name = p;
4326 strcpy(p, value);
4327 p += strlen(value) + 1;
4328 new_include->rname = p;
4329 strcpy(p, value);
4330 /* Strip off timing info, and process if it is there */
4331 if ( (c = strchr(p, '|')) ) {
4332 *c++ = '\0';
4333 new_include->hastime = ast_build_timing(&(new_include->timing), c);
4335 new_include->next = NULL;
4336 new_include->registrar = registrar;
4338 ast_mutex_lock(&con->lock);
4340 /* ... go to last include and check if context is already included too... */
4341 for (i = con->includes; i; i = i->next) {
4342 if (!strcasecmp(i->name, new_include->name)) {
4343 free(new_include);
4344 ast_mutex_unlock(&con->lock);
4345 errno = EEXIST;
4346 return -1;
4348 il = i;
4351 /* ... include new context into context list, unlock, return */
4352 if (il)
4353 il->next = new_include;
4354 else
4355 con->includes = new_include;
4356 if (option_verbose > 2)
4357 ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
4358 ast_mutex_unlock(&con->lock);
4360 return 0;
4364 * errno values
4365 * EBUSY - can't lock
4366 * ENOENT - no existence of context
4368 int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
4370 int ret = -1;
4371 struct ast_context *c = find_context_locked(context);
4373 if (c) { /* found, add switch to this context */
4374 ret = ast_context_add_switch2(c, sw, data, eval, registrar);
4375 ast_unlock_contexts();
4377 return ret;
4381 * errno values
4382 * ENOMEM - out of memory
4383 * EBUSY - can't lock
4384 * EEXIST - already included
4385 * EINVAL - there is no existence of context for inclusion
4387 int ast_context_add_switch2(struct ast_context *con, const char *value,
4388 const char *data, int eval, const char *registrar)
4390 struct ast_sw *new_sw;
4391 struct ast_sw *i;
4392 int length;
4393 char *p;
4395 length = sizeof(struct ast_sw);
4396 length += strlen(value) + 1;
4397 if (data)
4398 length += strlen(data);
4399 length++;
4401 /* allocate new sw structure ... */
4402 if (!(new_sw = ast_calloc(1, length)))
4403 return -1;
4404 /* ... fill in this structure ... */
4405 p = new_sw->stuff;
4406 new_sw->name = p;
4407 strcpy(new_sw->name, value);
4408 p += strlen(value) + 1;
4409 new_sw->data = p;
4410 if (data) {
4411 strcpy(new_sw->data, data);
4412 p += strlen(data) + 1;
4413 } else {
4414 strcpy(new_sw->data, "");
4415 p++;
4417 new_sw->eval = eval;
4418 new_sw->registrar = registrar;
4420 /* ... try to lock this context ... */
4421 ast_mutex_lock(&con->lock);
4423 /* ... go to last sw and check if context is already swd too... */
4424 AST_LIST_TRAVERSE(&con->alts, i, list) {
4425 if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
4426 free(new_sw);
4427 ast_mutex_unlock(&con->lock);
4428 errno = EEXIST;
4429 return -1;
4433 /* ... sw new context into context list, unlock, return */
4434 AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
4436 if (option_verbose > 2)
4437 ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
4439 ast_mutex_unlock(&con->lock);
4441 return 0;
4445 * EBUSY - can't lock
4446 * ENOENT - there is not context existence
4448 int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
4450 int ret = -1;
4451 struct ast_context *c = find_context_locked(context);
4453 if (c) {
4454 ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
4455 ast_unlock_contexts();
4457 return ret;
4460 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
4462 struct ast_ignorepat *ip, *ipl = NULL;
4464 ast_mutex_lock(&con->lock);
4466 for (ip = con->ignorepats; ip; ip = ip->next) {
4467 if (!strcmp(ip->pattern, ignorepat) &&
4468 (!registrar || (registrar == ip->registrar))) {
4469 if (ipl) {
4470 ipl->next = ip->next;
4471 free(ip);
4472 } else {
4473 con->ignorepats = ip->next;
4474 free(ip);
4476 ast_mutex_unlock(&con->lock);
4477 return 0;
4479 ipl = ip;
4482 ast_mutex_unlock(&con->lock);
4483 errno = EINVAL;
4484 return -1;
4488 * EBUSY - can't lock
4489 * ENOENT - there is no existence of context
4491 int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
4493 int ret = -1;
4494 struct ast_context *c = find_context_locked(context);
4496 if (c) {
4497 ret = ast_context_add_ignorepat2(c, value, registrar);
4498 ast_unlock_contexts();
4500 return ret;
4503 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
4505 struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
4506 int length;
4507 length = sizeof(struct ast_ignorepat);
4508 length += strlen(value) + 1;
4509 if (!(ignorepat = ast_calloc(1, length)))
4510 return -1;
4511 /* The cast to char * is because we need to write the initial value.
4512 * The field is not supposed to be modified otherwise
4514 strcpy((char *)ignorepat->pattern, value);
4515 ignorepat->next = NULL;
4516 ignorepat->registrar = registrar;
4517 ast_mutex_lock(&con->lock);
4518 for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
4519 ignorepatl = ignorepatc;
4520 if (!strcasecmp(ignorepatc->pattern, value)) {
4521 /* Already there */
4522 ast_mutex_unlock(&con->lock);
4523 errno = EEXIST;
4524 return -1;
4527 if (ignorepatl)
4528 ignorepatl->next = ignorepat;
4529 else
4530 con->ignorepats = ignorepat;
4531 ast_mutex_unlock(&con->lock);
4532 return 0;
4536 int ast_ignore_pattern(const char *context, const char *pattern)
4538 struct ast_context *con = ast_context_find(context);
4539 if (con) {
4540 struct ast_ignorepat *pat;
4541 for (pat = con->ignorepats; pat; pat = pat->next) {
4542 if (ast_extension_match(pat->pattern, pattern))
4543 return 1;
4547 return 0;
4551 * EBUSY - can't lock
4552 * ENOENT - no existence of context
4555 int ast_add_extension(const char *context, int replace, const char *extension,
4556 int priority, const char *label, const char *callerid,
4557 const char *application, void *data, void (*datad)(void *), const char *registrar)
4559 int ret = -1;
4560 struct ast_context *c = find_context_locked(context);
4562 if (c) {
4563 ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
4564 application, data, datad, registrar);
4565 ast_unlock_contexts();
4567 return ret;
4570 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4572 if (!chan)
4573 return -1;
4575 ast_channel_lock(chan);
4577 if (!ast_strlen_zero(context))
4578 ast_copy_string(chan->context, context, sizeof(chan->context));
4579 if (!ast_strlen_zero(exten))
4580 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
4581 if (priority > -1) {
4582 chan->priority = priority;
4583 /* see flag description in channel.h for explanation */
4584 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
4585 chan->priority--;
4588 ast_channel_unlock(chan);
4590 return 0;
4593 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
4595 int res = 0;
4597 ast_channel_lock(chan);
4599 if (chan->pbx) { /* This channel is currently in the PBX */
4600 ast_explicit_goto(chan, context, exten, priority);
4601 ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
4602 } else {
4603 /* In order to do it when the channel doesn't really exist within
4604 the PBX, we have to make a new channel, masquerade, and start the PBX
4605 at the new location */
4606 struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
4607 if (chan->cdr) {
4608 ast_cdr_discard(tmpchan->cdr);
4609 tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
4611 if (!tmpchan)
4612 res = -1;
4613 else {
4614 /* Make formats okay */
4615 tmpchan->readformat = chan->readformat;
4616 tmpchan->writeformat = chan->writeformat;
4617 /* Setup proper location */
4618 ast_explicit_goto(tmpchan,
4619 S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
4621 /* Masquerade into temp channel */
4622 ast_channel_masquerade(tmpchan, chan);
4624 /* Grab the locks and get going */
4625 ast_channel_lock(tmpchan);
4626 ast_do_masquerade(tmpchan);
4627 ast_channel_unlock(tmpchan);
4628 /* Start the PBX going on our stolen channel */
4629 if (ast_pbx_start(tmpchan)) {
4630 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
4631 ast_hangup(tmpchan);
4632 res = -1;
4636 ast_channel_unlock(chan);
4637 return res;
4640 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
4642 struct ast_channel *chan;
4643 int res = -1;
4645 chan = ast_get_channel_by_name_locked(channame);
4646 if (chan) {
4647 res = ast_async_goto(chan, context, exten, priority);
4648 ast_channel_unlock(chan);
4650 return res;
4653 /*! \brief copy a string skipping whitespace */
4654 static int ext_strncpy(char *dst, const char *src, int len)
4656 int count=0;
4658 while (*src && (count < len - 1)) {
4659 switch(*src) {
4660 case ' ':
4661 /* otherwise exten => [a-b],1,... doesn't work */
4662 /* case '-': */
4663 /* Ignore */
4664 break;
4665 default:
4666 *dst = *src;
4667 dst++;
4669 src++;
4670 count++;
4672 *dst = '\0';
4674 return count;
4677 /*! \brief add the extension in the priority chain.
4678 * returns 0 on success, -1 on failure
4680 static int add_pri(struct ast_context *con, struct ast_exten *tmp,
4681 struct ast_exten *el, struct ast_exten *e, int replace)
4683 struct ast_exten *ep;
4685 for (ep = NULL; e ; ep = e, e = e->peer) {
4686 if (e->priority >= tmp->priority)
4687 break;
4689 if (!e) { /* go at the end, and ep is surely set because the list is not empty */
4690 ep->peer = tmp;
4691 return 0; /* success */
4693 if (e->priority == tmp->priority) {
4694 /* Can't have something exactly the same. Is this a
4695 replacement? If so, replace, otherwise, bonk. */
4696 if (!replace) {
4697 ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
4698 if (tmp->datad)
4699 tmp->datad(tmp->data);
4700 free(tmp);
4701 return -1;
4703 /* we are replacing e, so copy the link fields and then update
4704 * whoever pointed to e to point to us
4706 tmp->next = e->next; /* not meaningful if we are not first in the peer list */
4707 tmp->peer = e->peer; /* always meaningful */
4708 if (ep) /* We're in the peer list, just insert ourselves */
4709 ep->peer = tmp;
4710 else if (el) /* We're the first extension. Take over e's functions */
4711 el->next = tmp;
4712 else /* We're the very first extension. */
4713 con->root = tmp;
4714 if (tmp->priority == PRIORITY_HINT)
4715 ast_change_hint(e,tmp);
4716 /* Destroy the old one */
4717 if (e->datad)
4718 e->datad(e->data);
4719 free(e);
4720 } else { /* Slip ourselves in just before e */
4721 tmp->peer = e;
4722 tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
4723 if (ep) /* Easy enough, we're just in the peer list */
4724 ep->peer = tmp;
4725 else { /* we are the first in some peer list, so link in the ext list */
4726 if (el)
4727 el->next = tmp; /* in the middle... */
4728 else
4729 con->root = tmp; /* ... or at the head */
4730 e->next = NULL; /* e is no more at the head, so e->next must be reset */
4732 /* And immediately return success. */
4733 if (tmp->priority == PRIORITY_HINT)
4734 ast_add_hint(tmp);
4736 return 0;
4739 /*! \brief
4740 * Main interface to add extensions to the list for out context.
4742 * We sort extensions in order of matching preference, so that we can
4743 * stop the search as soon as we find a suitable match.
4744 * This ordering also takes care of wildcards such as '.' (meaning
4745 * "one or more of any character") and '!' (which is 'earlymatch',
4746 * meaning "zero or more of any character" but also impacts the
4747 * return value from CANMATCH and EARLYMATCH.
4749 * The extension match rules defined in the devmeeting 2006.05.05 are
4750 * quite simple: WE SELECT THE LONGEST MATCH.
4751 * In detail, "longest" means the number of matched characters in
4752 * the extension. In case of ties (e.g. _XXX and 333) in the length
4753 * of a pattern, we give priority to entries with the smallest cardinality
4754 * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
4755 * while the latter has 7, etc.
4756 * In case of same cardinality, the first element in the range counts.
4757 * If we still have a tie, any final '!' will make this as a possibly
4758 * less specific pattern.
4760 * EBUSY - can't lock
4761 * EEXIST - extension with the same priority exist and no replace is set
4764 int ast_add_extension2(struct ast_context *con,
4765 int replace, const char *extension, int priority, const char *label, const char *callerid,
4766 const char *application, void *data, void (*datad)(void *),
4767 const char *registrar)
4770 * Sort extensions (or patterns) according to the rules indicated above.
4771 * These are implemented by the function ext_cmp()).
4772 * All priorities for the same ext/pattern/cid are kept in a list,
4773 * using the 'peer' field as a link field..
4775 struct ast_exten *tmp, *e, *el = NULL;
4776 int res;
4777 int length;
4778 char *p;
4779 char expand_buf[VAR_BUF_SIZE] = { 0, };
4781 /* if we are adding a hint, and there are global variables, and the hint
4782 contains variable references, then expand them
4784 ast_mutex_lock(&globalslock);
4785 if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
4786 pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
4787 application = expand_buf;
4789 ast_mutex_unlock(&globalslock);
4791 length = sizeof(struct ast_exten);
4792 length += strlen(extension) + 1;
4793 length += strlen(application) + 1;
4794 if (label)
4795 length += strlen(label) + 1;
4796 if (callerid)
4797 length += strlen(callerid) + 1;
4798 else
4799 length ++; /* just the '\0' */
4801 /* Be optimistic: Build the extension structure first */
4802 if (!(tmp = ast_calloc(1, length)))
4803 return -1;
4805 /* use p as dst in assignments, as the fields are const char * */
4806 p = tmp->stuff;
4807 if (label) {
4808 tmp->label = p;
4809 strcpy(p, label);
4810 p += strlen(label) + 1;
4812 tmp->exten = p;
4813 p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
4814 tmp->priority = priority;
4815 tmp->cidmatch = p; /* but use p for assignments below */
4816 if (callerid) {
4817 p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
4818 tmp->matchcid = 1;
4819 } else {
4820 *p++ = '\0';
4821 tmp->matchcid = 0;
4823 tmp->app = p;
4824 strcpy(p, application);
4825 tmp->parent = con;
4826 tmp->data = data;
4827 tmp->datad = datad;
4828 tmp->registrar = registrar;
4830 ast_mutex_lock(&con->lock);
4831 res = 0; /* some compilers will think it is uninitialized otherwise */
4832 for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
4833 res = ext_cmp(e->exten, extension);
4834 if (res == 0) { /* extension match, now look at cidmatch */
4835 if (!e->matchcid && !tmp->matchcid)
4836 res = 0;
4837 else if (tmp->matchcid && !e->matchcid)
4838 res = 1;
4839 else if (e->matchcid && !tmp->matchcid)
4840 res = -1;
4841 else
4842 res = strcasecmp(e->cidmatch, tmp->cidmatch);
4844 if (res >= 0)
4845 break;
4847 if (e && res == 0) { /* exact match, insert in the pri chain */
4848 res = add_pri(con, tmp, el, e, replace);
4849 ast_mutex_unlock(&con->lock);
4850 if (res < 0) {
4851 errno = EEXIST; /* XXX do we care ? */
4852 return 0; /* XXX should we return -1 maybe ? */
4854 } else {
4856 * not an exact match, this is the first entry with this pattern,
4857 * so insert in the main list right before 'e' (if any)
4859 tmp->next = e;
4860 if (el)
4861 el->next = tmp;
4862 else
4863 con->root = tmp;
4864 ast_mutex_unlock(&con->lock);
4865 if (tmp->priority == PRIORITY_HINT)
4866 ast_add_hint(tmp);
4868 if (option_debug) {
4869 if (tmp->matchcid) {
4870 if (option_debug)
4871 ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
4872 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4873 } else {
4874 if (option_debug)
4875 ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
4876 tmp->exten, tmp->priority, con->name);
4879 if (option_verbose > 2) {
4880 if (tmp->matchcid) {
4881 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
4882 tmp->exten, tmp->priority, tmp->cidmatch, con->name);
4883 } else {
4884 ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
4885 tmp->exten, tmp->priority, con->name);
4888 return 0;
4891 struct async_stat {
4892 pthread_t p;
4893 struct ast_channel *chan;
4894 char context[AST_MAX_CONTEXT];
4895 char exten[AST_MAX_EXTENSION];
4896 int priority;
4897 int timeout;
4898 char app[AST_MAX_EXTENSION];
4899 char appdata[1024];
4902 static void *async_wait(void *data)
4904 struct async_stat *as = data;
4905 struct ast_channel *chan = as->chan;
4906 int timeout = as->timeout;
4907 int res;
4908 struct ast_frame *f;
4909 struct ast_app *app;
4911 while (timeout && (chan->_state != AST_STATE_UP)) {
4912 res = ast_waitfor(chan, timeout);
4913 if (res < 1)
4914 break;
4915 if (timeout > -1)
4916 timeout = res;
4917 f = ast_read(chan);
4918 if (!f)
4919 break;
4920 if (f->frametype == AST_FRAME_CONTROL) {
4921 if ((f->subclass == AST_CONTROL_BUSY) ||
4922 (f->subclass == AST_CONTROL_CONGESTION) ) {
4923 ast_frfree(f);
4924 break;
4927 ast_frfree(f);
4929 if (chan->_state == AST_STATE_UP) {
4930 if (!ast_strlen_zero(as->app)) {
4931 app = pbx_findapp(as->app);
4932 if (app) {
4933 if (option_verbose > 2)
4934 ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
4935 pbx_exec(chan, app, as->appdata);
4936 } else
4937 ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
4938 } else {
4939 if (!ast_strlen_zero(as->context))
4940 ast_copy_string(chan->context, as->context, sizeof(chan->context));
4941 if (!ast_strlen_zero(as->exten))
4942 ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
4943 if (as->priority > 0)
4944 chan->priority = as->priority;
4945 /* Run the PBX */
4946 if (ast_pbx_run(chan)) {
4947 ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
4948 } else {
4949 /* PBX will have taken care of this */
4950 chan = NULL;
4954 free(as);
4955 if (chan)
4956 ast_hangup(chan);
4957 return NULL;
4960 /*! Function to post an empty cdr after a spool call fails.
4962 * This function posts an empty cdr for a failed spool call
4965 static int ast_pbx_outgoing_cdr_failed(void)
4967 /* allocate a channel */
4968 struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
4970 if (!chan)
4971 return -1; /* failure */
4973 if (!chan->cdr) {
4974 /* allocation of the cdr failed */
4975 ast_channel_free(chan); /* free the channel */
4976 return -1; /* return failure */
4979 /* allocation of the cdr was successful */
4980 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
4981 ast_cdr_start(chan->cdr); /* record the start and stop time */
4982 ast_cdr_end(chan->cdr);
4983 ast_cdr_failed(chan->cdr); /* set the status to failed */
4984 ast_cdr_detach(chan->cdr); /* post and free the record */
4985 ast_channel_free(chan); /* free the channel */
4987 return 0; /* success */
4990 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)
4992 struct ast_channel *chan;
4993 struct async_stat *as;
4994 int res = -1, cdr_res = -1;
4995 struct outgoing_helper oh;
4996 pthread_attr_t attr;
4998 if (sync) {
4999 LOAD_OH(oh);
5000 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5001 if (channel) {
5002 *channel = chan;
5003 if (chan)
5004 ast_channel_lock(chan);
5006 if (chan) {
5007 if (chan->_state == AST_STATE_UP) {
5008 res = 0;
5009 if (option_verbose > 3)
5010 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5012 if (sync > 1) {
5013 if (channel)
5014 ast_channel_unlock(chan);
5015 if (ast_pbx_run(chan)) {
5016 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5017 if (channel)
5018 *channel = NULL;
5019 ast_hangup(chan);
5020 chan = NULL;
5021 res = -1;
5023 } else {
5024 if (ast_pbx_start(chan)) {
5025 ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
5026 if (channel) {
5027 *channel = NULL;
5028 ast_channel_unlock(chan);
5030 ast_hangup(chan);
5031 res = -1;
5033 chan = NULL;
5035 } else {
5036 if (option_verbose > 3)
5037 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5039 if (chan->cdr) { /* update the cdr */
5040 /* here we update the status of the call, which sould be busy.
5041 * if that fails then we set the status to failed */
5042 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5043 ast_cdr_failed(chan->cdr);
5046 if (channel) {
5047 *channel = NULL;
5048 ast_channel_unlock(chan);
5050 ast_hangup(chan);
5051 chan = NULL;
5055 if (res < 0) { /* the call failed for some reason */
5056 if (*reason == 0) { /* if the call failed (not busy or no answer)
5057 * update the cdr with the failed message */
5058 cdr_res = ast_pbx_outgoing_cdr_failed();
5059 if (cdr_res != 0) {
5060 res = cdr_res;
5061 goto outgoing_exten_cleanup;
5065 /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
5066 /* check if "failed" exists */
5067 if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
5068 chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
5069 if (chan) {
5070 char failed_reason[4] = "";
5071 if (!ast_strlen_zero(context))
5072 ast_copy_string(chan->context, context, sizeof(chan->context));
5073 set_ext_pri(chan, "failed", 1);
5074 ast_set_variables(chan, vars);
5075 snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
5076 pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
5077 if (account)
5078 ast_cdr_setaccount(chan, account);
5079 if (ast_pbx_run(chan)) {
5080 ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
5081 ast_hangup(chan);
5083 chan = NULL;
5087 } else {
5088 if (!(as = ast_calloc(1, sizeof(*as)))) {
5089 res = -1;
5090 goto outgoing_exten_cleanup;
5092 chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
5093 if (channel) {
5094 *channel = chan;
5095 if (chan)
5096 ast_channel_lock(chan);
5098 if (!chan) {
5099 free(as);
5100 res = -1;
5101 goto outgoing_exten_cleanup;
5103 as->chan = chan;
5104 ast_copy_string(as->context, context, sizeof(as->context));
5105 set_ext_pri(as->chan, exten, priority);
5106 as->timeout = timeout;
5107 ast_set_variables(chan, vars);
5108 if (account)
5109 ast_cdr_setaccount(chan, account);
5110 pthread_attr_init(&attr);
5111 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5112 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5113 ast_log(LOG_WARNING, "Failed to start async wait\n");
5114 free(as);
5115 if (channel) {
5116 *channel = NULL;
5117 ast_channel_unlock(chan);
5119 ast_hangup(chan);
5120 res = -1;
5121 pthread_attr_destroy(&attr);
5122 goto outgoing_exten_cleanup;
5124 pthread_attr_destroy(&attr);
5125 res = 0;
5127 outgoing_exten_cleanup:
5128 ast_variables_destroy(vars);
5129 return res;
5132 struct app_tmp {
5133 char app[256];
5134 char data[256];
5135 struct ast_channel *chan;
5136 pthread_t t;
5139 /*! \brief run the application and free the descriptor once done */
5140 static void *ast_pbx_run_app(void *data)
5142 struct app_tmp *tmp = data;
5143 struct ast_app *app;
5144 app = pbx_findapp(tmp->app);
5145 if (app) {
5146 if (option_verbose > 3)
5147 ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
5148 pbx_exec(tmp->chan, app, tmp->data);
5149 } else
5150 ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
5151 ast_hangup(tmp->chan);
5152 free(tmp);
5153 return NULL;
5156 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)
5158 struct ast_channel *chan;
5159 struct app_tmp *tmp;
5160 int res = -1, cdr_res = -1;
5161 struct outgoing_helper oh;
5162 pthread_attr_t attr;
5164 memset(&oh, 0, sizeof(oh));
5165 oh.vars = vars;
5166 oh.account = account;
5168 if (locked_channel)
5169 *locked_channel = NULL;
5170 if (ast_strlen_zero(app)) {
5171 res = -1;
5172 goto outgoing_app_cleanup;
5174 if (sync) {
5175 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5176 if (chan) {
5177 if (!chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
5178 chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
5179 if(!chan->cdr) {
5180 /* allocation of the cdr failed */
5181 free(chan->pbx);
5182 res = -1;
5183 goto outgoing_app_cleanup;
5185 /* allocation of the cdr was successful */
5186 ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
5187 ast_cdr_start(chan->cdr);
5189 ast_set_variables(chan, vars);
5190 if (account)
5191 ast_cdr_setaccount(chan, account);
5192 if (chan->_state == AST_STATE_UP) {
5193 res = 0;
5194 if (option_verbose > 3)
5195 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
5196 tmp = ast_calloc(1, sizeof(*tmp));
5197 if (!tmp)
5198 res = -1;
5199 else {
5200 ast_copy_string(tmp->app, app, sizeof(tmp->app));
5201 if (appdata)
5202 ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
5203 tmp->chan = chan;
5204 if (sync > 1) {
5205 if (locked_channel)
5206 ast_channel_unlock(chan);
5207 ast_pbx_run_app(tmp);
5208 } else {
5209 pthread_attr_init(&attr);
5210 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5211 if (locked_channel)
5212 ast_channel_lock(chan);
5213 if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
5214 ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
5215 free(tmp);
5216 if (locked_channel)
5217 ast_channel_unlock(chan);
5218 ast_hangup(chan);
5219 res = -1;
5220 } else {
5221 if (locked_channel)
5222 *locked_channel = chan;
5224 pthread_attr_destroy(&attr);
5227 } else {
5228 if (option_verbose > 3)
5229 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
5230 if (chan->cdr) { /* update the cdr */
5231 /* here we update the status of the call, which sould be busy.
5232 * if that fails then we set the status to failed */
5233 if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
5234 ast_cdr_failed(chan->cdr);
5236 ast_hangup(chan);
5240 if (res < 0) { /* the call failed for some reason */
5241 if (*reason == 0) { /* if the call failed (not busy or no answer)
5242 * update the cdr with the failed message */
5243 cdr_res = ast_pbx_outgoing_cdr_failed();
5244 if (cdr_res != 0) {
5245 res = cdr_res;
5246 goto outgoing_app_cleanup;
5251 } else {
5252 struct async_stat *as;
5253 if (!(as = ast_calloc(1, sizeof(*as)))) {
5254 res = -1;
5255 goto outgoing_app_cleanup;
5257 chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
5258 if (!chan) {
5259 free(as);
5260 res = -1;
5261 goto outgoing_app_cleanup;
5263 as->chan = chan;
5264 ast_copy_string(as->app, app, sizeof(as->app));
5265 if (appdata)
5266 ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
5267 as->timeout = timeout;
5268 ast_set_variables(chan, vars);
5269 if (account)
5270 ast_cdr_setaccount(chan, account);
5271 /* Start a new thread, and get something handling this channel. */
5272 pthread_attr_init(&attr);
5273 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5274 if (locked_channel)
5275 ast_channel_lock(chan);
5276 if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
5277 ast_log(LOG_WARNING, "Failed to start async wait\n");
5278 free(as);
5279 if (locked_channel)
5280 ast_channel_unlock(chan);
5281 ast_hangup(chan);
5282 res = -1;
5283 pthread_attr_destroy(&attr);
5284 goto outgoing_app_cleanup;
5285 } else {
5286 if (locked_channel)
5287 *locked_channel = chan;
5289 pthread_attr_destroy(&attr);
5290 res = 0;
5292 outgoing_app_cleanup:
5293 ast_variables_destroy(vars);
5294 return res;
5297 void __ast_context_destroy(struct ast_context *con, const char *registrar)
5299 struct ast_context *tmp, *tmpl=NULL;
5300 struct ast_include *tmpi;
5301 struct ast_sw *sw;
5302 struct ast_exten *e, *el, *en;
5303 struct ast_ignorepat *ipi;
5305 for (tmp = contexts; tmp; ) {
5306 struct ast_context *next; /* next starting point */
5307 for (; tmp; tmpl = tmp, tmp = tmp->next) {
5308 if (option_debug)
5309 ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
5310 if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
5311 (!con || !strcasecmp(tmp->name, con->name)) )
5312 break; /* found it */
5314 if (!tmp) /* not found, we are done */
5315 break;
5316 ast_mutex_lock(&tmp->lock);
5317 if (option_debug)
5318 ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
5319 next = tmp->next;
5320 if (tmpl)
5321 tmpl->next = next;
5322 else
5323 contexts = next;
5324 /* Okay, now we're safe to let it go -- in a sense, we were
5325 ready to let it go as soon as we locked it. */
5326 ast_mutex_unlock(&tmp->lock);
5327 for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
5328 struct ast_include *tmpil = tmpi;
5329 tmpi = tmpi->next;
5330 free(tmpil);
5332 for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
5333 struct ast_ignorepat *ipl = ipi;
5334 ipi = ipi->next;
5335 free(ipl);
5337 while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
5338 free(sw);
5339 for (e = tmp->root; e;) {
5340 for (en = e->peer; en;) {
5341 el = en;
5342 en = en->peer;
5343 destroy_exten(el);
5345 el = e;
5346 e = e->next;
5347 destroy_exten(el);
5349 ast_mutex_destroy(&tmp->lock);
5350 free(tmp);
5351 /* if we have a specific match, we are done, otherwise continue */
5352 tmp = con ? NULL : next;
5356 void ast_context_destroy(struct ast_context *con, const char *registrar)
5358 ast_wrlock_contexts();
5359 __ast_context_destroy(con,registrar);
5360 ast_unlock_contexts();
5363 static void wait_for_hangup(struct ast_channel *chan, void *data)
5365 int res;
5366 struct ast_frame *f;
5367 int waittime;
5369 if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
5370 waittime = -1;
5371 if (waittime > -1) {
5372 ast_safe_sleep(chan, waittime * 1000);
5373 } else do {
5374 res = ast_waitfor(chan, -1);
5375 if (res < 0)
5376 return;
5377 f = ast_read(chan);
5378 if (f)
5379 ast_frfree(f);
5380 } while(f);
5384 * \ingroup applications
5386 static int pbx_builtin_progress(struct ast_channel *chan, void *data)
5388 ast_indicate(chan, AST_CONTROL_PROGRESS);
5389 return 0;
5393 * \ingroup applications
5395 static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
5397 ast_indicate(chan, AST_CONTROL_RINGING);
5398 return 0;
5402 * \ingroup applications
5404 static int pbx_builtin_busy(struct ast_channel *chan, void *data)
5406 ast_indicate(chan, AST_CONTROL_BUSY);
5407 /* Don't change state of an UP channel, just indicate
5408 busy in audio */
5409 if (chan->_state != AST_STATE_UP)
5410 ast_setstate(chan, AST_STATE_BUSY);
5411 wait_for_hangup(chan, data);
5412 return -1;
5416 * \ingroup applications
5418 static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
5420 ast_indicate(chan, AST_CONTROL_CONGESTION);
5421 /* Don't change state of an UP channel, just indicate
5422 congestion in audio */
5423 if (chan->_state != AST_STATE_UP)
5424 ast_setstate(chan, AST_STATE_BUSY);
5425 wait_for_hangup(chan, data);
5426 return -1;
5430 * \ingroup applications
5432 static int pbx_builtin_answer(struct ast_channel *chan, void *data)
5434 int delay = 0;
5435 int res;
5437 if (chan->_state == AST_STATE_UP)
5438 delay = 0;
5439 else if (!ast_strlen_zero(data))
5440 delay = atoi(data);
5442 res = ast_answer(chan);
5443 if (res)
5444 return res;
5446 if (delay)
5447 res = ast_safe_sleep(chan, delay);
5449 return res;
5452 AST_APP_OPTIONS(resetcdr_opts, {
5453 AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
5454 AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
5455 AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
5459 * \ingroup applications
5461 static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
5463 char *args;
5464 struct ast_flags flags = { 0 };
5466 if (!ast_strlen_zero(data)) {
5467 args = ast_strdupa(data);
5468 ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
5471 ast_cdr_reset(chan->cdr, &flags);
5473 return 0;
5477 * \ingroup applications
5479 static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
5481 /* Copy the AMA Flags as specified */
5482 ast_cdr_setamaflags(chan, data ? data : "");
5483 return 0;
5487 * \ingroup applications
5489 static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
5491 if (!ast_strlen_zero(data)) {
5492 int cause;
5493 char *endptr;
5495 if ((cause = ast_str2cause(data)) > -1) {
5496 chan->hangupcause = cause;
5497 return -1;
5500 cause = strtol((const char *) data, &endptr, 10);
5501 if (cause != 0 || (data != endptr)) {
5502 chan->hangupcause = cause;
5503 return -1;
5506 ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
5509 if (!chan->hangupcause) {
5510 chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
5513 return -1;
5517 * \ingroup applications
5519 static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
5521 int res=0;
5522 char *s, *ts;
5523 struct ast_timing timing;
5525 if (ast_strlen_zero(data)) {
5526 ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
5527 return -1;
5530 ts = s = ast_strdupa(data);
5532 /* Separate the Goto path */
5533 strsep(&ts,"?");
5535 /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
5536 if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
5537 res = pbx_builtin_goto(chan, ts);
5539 return res;
5543 * \ingroup applications
5545 static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
5547 char *s, *appname;
5548 struct ast_timing timing;
5549 struct ast_app *app;
5550 static const char *usage = "ExecIfTime requires an argument:\n <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
5552 if (ast_strlen_zero(data)) {
5553 ast_log(LOG_WARNING, "%s\n", usage);
5554 return -1;
5557 appname = ast_strdupa(data);
5559 s = strsep(&appname,"?"); /* Separate the timerange and application name/data */
5560 if (!appname) { /* missing application */
5561 ast_log(LOG_WARNING, "%s\n", usage);
5562 return -1;
5565 if (!ast_build_timing(&timing, s)) {
5566 ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
5567 return -1;
5570 if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
5571 return 0;
5573 /* now split appname|appargs */
5574 if ((s = strchr(appname, '|')))
5575 *s++ = '\0';
5577 if ((app = pbx_findapp(appname))) {
5578 return pbx_exec(chan, app, S_OR(s, ""));
5579 } else {
5580 ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
5581 return -1;
5586 * \ingroup applications
5588 static int pbx_builtin_wait(struct ast_channel *chan, void *data)
5590 double s;
5591 int ms;
5593 /* Wait for "n" seconds */
5594 if (data && (s = atof(data)) > 0) {
5595 ms = s * 1000.0;
5596 return ast_safe_sleep(chan, ms);
5598 return 0;
5602 * \ingroup applications
5604 static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
5606 int ms, res;
5607 double sec;
5608 struct ast_flags flags = {0};
5609 char *opts[1] = { NULL };
5610 char *parse;
5611 AST_DECLARE_APP_ARGS(args,
5612 AST_APP_ARG(timeout);
5613 AST_APP_ARG(options);
5616 if (!ast_strlen_zero(data)) {
5617 parse = ast_strdupa(data);
5618 AST_STANDARD_APP_ARGS(args, parse);
5619 } else
5620 memset(&args, 0, sizeof(args));
5622 if (args.options)
5623 ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
5625 if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
5626 ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
5627 } else if (ast_test_flag(&flags, WAITEXTEN_MOH))
5628 ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
5630 /* Wait for "n" seconds */
5631 if (args.timeout && (sec = atof(args.timeout)) > 0.0)
5632 ms = 1000 * sec;
5633 else if (chan->pbx)
5634 ms = chan->pbx->rtimeout * 1000;
5635 else
5636 ms = 10000;
5637 res = ast_waitfordigit(chan, ms);
5638 if (!res) {
5639 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
5640 if (option_verbose > 2)
5641 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
5642 } else if (chan->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
5643 if (option_verbose > 2)
5644 ast_verbose(VERBOSE_PREFIX_3 "Call timeout on %s, checking for 'T'\n", chan->name);
5645 res = -1;
5646 } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
5647 if (option_verbose > 2)
5648 ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, going to 't'\n", chan->name);
5649 set_ext_pri(chan, "t", 0); /* 0 will become 1, next time through the loop */
5650 } else {
5651 ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
5652 res = -1;
5656 if (ast_test_flag(&flags, WAITEXTEN_MOH))
5657 ast_indicate(chan, AST_CONTROL_UNHOLD);
5659 return res;
5663 * \ingroup applications
5665 static int pbx_builtin_background(struct ast_channel *chan, void *data)
5667 int res = 0;
5668 struct ast_flags flags = {0};
5669 char *parse;
5670 AST_DECLARE_APP_ARGS(args,
5671 AST_APP_ARG(filename);
5672 AST_APP_ARG(options);
5673 AST_APP_ARG(lang);
5674 AST_APP_ARG(context);
5677 if (ast_strlen_zero(data)) {
5678 ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
5679 return -1;
5682 parse = ast_strdupa(data);
5684 AST_STANDARD_APP_ARGS(args, parse);
5686 if (ast_strlen_zero(args.lang))
5687 args.lang = (char *)chan->language; /* XXX this is const */
5689 if (ast_strlen_zero(args.context))
5690 args.context = chan->context;
5692 if (args.options) {
5693 if (!strcasecmp(args.options, "skip"))
5694 flags.flags = BACKGROUND_SKIP;
5695 else if (!strcasecmp(args.options, "noanswer"))
5696 flags.flags = BACKGROUND_NOANSWER;
5697 else
5698 ast_app_parse_options(background_opts, &flags, NULL, args.options);
5701 /* Answer if need be */
5702 if (chan->_state != AST_STATE_UP) {
5703 if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
5704 return 0;
5705 } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
5706 res = ast_answer(chan);
5710 if (!res) {
5711 char *back = args.filename;
5712 char *front;
5713 ast_stopstream(chan); /* Stop anything playing */
5714 /* Stream the list of files */
5715 while (!res && (front = strsep(&back, "&")) ) {
5716 if ( (res = ast_streamfile(chan, front, args.lang)) ) {
5717 ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
5718 res = 0;
5719 break;
5721 if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
5722 res = ast_waitstream(chan, "");
5723 } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
5724 res = ast_waitstream_exten(chan, args.context);
5725 } else {
5726 res = ast_waitstream(chan, AST_DIGIT_ANY);
5728 ast_stopstream(chan);
5731 if (args.context != chan->context && res) {
5732 snprintf(chan->exten, sizeof(chan->exten), "%c", res);
5733 ast_copy_string(chan->context, args.context, sizeof(chan->context));
5734 chan->priority = 0;
5735 res = 0;
5737 return res;
5740 /*! Goto
5741 * \ingroup applications
5743 static int pbx_builtin_goto(struct ast_channel *chan, void *data)
5745 int res = ast_parseable_goto(chan, data);
5746 if (!res && (option_verbose > 2))
5747 ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
5748 return res;
5752 int pbx_builtin_serialize_variables(struct ast_channel *chan, char *buf, size_t size)
5754 struct ast_var_t *variables;
5755 const char *var, *val;
5756 int total = 0;
5758 if (!chan)
5759 return 0;
5761 memset(buf, 0, size);
5763 ast_channel_lock(chan);
5765 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
5766 if ((var=ast_var_name(variables)) && (val=ast_var_value(variables))
5767 /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
5769 if (ast_build_string(&buf, &size, "%s=%s\n", var, val)) {
5770 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
5771 break;
5772 } else
5773 total++;
5774 } else
5775 break;
5778 ast_channel_unlock(chan);
5780 return total;
5783 const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
5785 struct ast_var_t *variables;
5786 const char *ret = NULL;
5787 int i;
5788 struct varshead *places[2] = { NULL, &globals };
5790 if (!name)
5791 return NULL;
5793 if (chan) {
5794 ast_channel_lock(chan);
5795 places[0] = &chan->varshead;
5798 for (i = 0; i < 2; i++) {
5799 if (!places[i])
5800 continue;
5801 if (places[i] == &globals)
5802 ast_mutex_lock(&globalslock);
5803 AST_LIST_TRAVERSE(places[i], variables, entries) {
5804 if (!strcmp(name, ast_var_name(variables))) {
5805 ret = ast_var_value(variables);
5806 break;
5809 if (places[i] == &globals)
5810 ast_mutex_unlock(&globalslock);
5811 if (ret)
5812 break;
5815 if (chan)
5816 ast_channel_unlock(chan);
5818 return ret;
5821 void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
5823 struct ast_var_t *newvariable;
5824 struct varshead *headp;
5826 if (name[strlen(name)-1] == ')') {
5827 char *function = ast_strdupa(name);
5829 ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
5830 ast_func_write(chan, function, value);
5831 return;
5834 if (chan) {
5835 ast_channel_lock(chan);
5836 headp = &chan->varshead;
5837 } else {
5838 ast_mutex_lock(&globalslock);
5839 headp = &globals;
5842 if (value) {
5843 if ((option_verbose > 1) && (headp == &globals))
5844 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5845 newvariable = ast_var_assign(name, value);
5846 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5849 if (chan)
5850 ast_channel_unlock(chan);
5851 else
5852 ast_mutex_unlock(&globalslock);
5855 void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
5857 struct ast_var_t *newvariable;
5858 struct varshead *headp;
5859 const char *nametail = name;
5861 if (name[strlen(name)-1] == ')') {
5862 char *function = ast_strdupa(name);
5864 ast_func_write(chan, function, value);
5865 return;
5868 if (chan) {
5869 ast_channel_lock(chan);
5870 headp = &chan->varshead;
5871 } else {
5872 ast_mutex_lock(&globalslock);
5873 headp = &globals;
5876 /* For comparison purposes, we have to strip leading underscores */
5877 if (*nametail == '_') {
5878 nametail++;
5879 if (*nametail == '_')
5880 nametail++;
5883 AST_LIST_TRAVERSE (headp, newvariable, entries) {
5884 if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
5885 /* there is already such a variable, delete it */
5886 AST_LIST_REMOVE(headp, newvariable, entries);
5887 ast_var_delete(newvariable);
5888 break;
5892 if (value) {
5893 if ((option_verbose > 1) && (headp == &globals))
5894 ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
5895 newvariable = ast_var_assign(name, value);
5896 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
5899 if (chan)
5900 ast_channel_unlock(chan);
5901 else
5902 ast_mutex_unlock(&globalslock);
5905 int pbx_builtin_setvar(struct ast_channel *chan, void *data)
5907 char *name, *value, *mydata;
5908 int argc;
5909 char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */
5910 int global = 0;
5911 int x;
5913 if (ast_strlen_zero(data)) {
5914 ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
5915 return 0;
5918 mydata = ast_strdupa(data);
5919 argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
5921 /* check for a trailing flags argument */
5922 if ((argc > 1) && !strchr(argv[argc-1], '=')) {
5923 argc--;
5924 if (strchr(argv[argc], 'g')) {
5925 ast_log(LOG_WARNING, "The use of the 'g' flag is deprecated. Please use Set(GLOBAL(foo)=bar) instead\n");
5926 global = 1;
5930 if (argc > 1)
5931 ast_log(LOG_WARNING, "Setting multiple variables at once within Set is deprecated. Please separate each name/value pair into its own line.\n");
5933 for (x = 0; x < argc; x++) {
5934 name = argv[x];
5935 if ((value = strchr(name, '='))) {
5936 *value++ = '\0';
5937 pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
5938 } else
5939 ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
5942 return(0);
5945 int pbx_builtin_importvar(struct ast_channel *chan, void *data)
5947 char *name;
5948 char *value;
5949 char *channel;
5950 char tmp[VAR_BUF_SIZE]="";
5952 if (ast_strlen_zero(data)) {
5953 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5954 return 0;
5957 value = ast_strdupa(data);
5958 name = strsep(&value,"=");
5959 channel = strsep(&value,"|");
5960 if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
5961 struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
5962 if (chan2) {
5963 char *s = alloca(strlen(value) + 4);
5964 if (s) {
5965 sprintf(s, "${%s}", value);
5966 pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
5968 ast_channel_unlock(chan2);
5970 pbx_builtin_setvar_helper(chan, name, tmp);
5973 return(0);
5976 /*! \todo XXX overwrites data ? */
5977 static int pbx_builtin_setglobalvar(struct ast_channel *chan, void *data)
5979 char *name;
5980 char *stringp = data;
5981 static int dep_warning = 0;
5983 if (ast_strlen_zero(data)) {
5984 ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
5985 return 0;
5988 name = strsep(&stringp, "=");
5990 if (!dep_warning) {
5991 dep_warning = 1;
5992 ast_log(LOG_WARNING, "SetGlobalVar is deprecated. Please use Set(GLOBAL(%s)=%s) instead.\n", name, stringp);
5995 /*! \todo XXX watch out, leading whitespace ? */
5996 pbx_builtin_setvar_helper(NULL, name, stringp);
5998 return(0);
6001 static int pbx_builtin_noop(struct ast_channel *chan, void *data)
6003 return 0;
6006 void pbx_builtin_clear_globals(void)
6008 struct ast_var_t *vardata;
6010 ast_mutex_lock(&globalslock);
6011 while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
6012 ast_var_delete(vardata);
6013 ast_mutex_unlock(&globalslock);
6016 int pbx_checkcondition(const char *condition)
6018 if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
6019 return 0;
6020 else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
6021 return atoi(condition);
6022 else /* Strings are true */
6023 return 1;
6026 static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
6028 char *condition, *branch1, *branch2, *branch;
6029 int rc;
6030 char *stringp;
6032 if (ast_strlen_zero(data)) {
6033 ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
6034 return 0;
6037 stringp = ast_strdupa(data);
6038 condition = strsep(&stringp,"?");
6039 branch1 = strsep(&stringp,":");
6040 branch2 = strsep(&stringp,"");
6041 branch = pbx_checkcondition(condition) ? branch1 : branch2;
6043 if (ast_strlen_zero(branch)) {
6044 if (option_debug)
6045 ast_log(LOG_DEBUG, "Not taking any branch\n");
6046 return 0;
6049 rc = pbx_builtin_goto(chan, branch);
6051 return rc;
6054 static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
6056 char tmp[256];
6057 char *number = tmp;
6058 char *options;
6060 if (ast_strlen_zero(data)) {
6061 ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
6062 return -1;
6064 ast_copy_string(tmp, data, sizeof(tmp));
6065 strsep(&number, "|");
6066 options = strsep(&number, "|");
6067 if (options) {
6068 if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
6069 strcasecmp(options, "c") && strcasecmp(options, "n") ) {
6070 ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
6071 return -1;
6074 return ast_say_number(chan, atoi(tmp), "", chan->language, options);
6077 static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
6079 int res = 0;
6081 if (data)
6082 res = ast_say_digit_str(chan, data, "", chan->language);
6083 return res;
6086 static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
6088 int res = 0;
6090 if (data)
6091 res = ast_say_character_str(chan, data, "", chan->language);
6092 return res;
6095 static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
6097 int res = 0;
6099 if (data)
6100 res = ast_say_phonetic_str(chan, data, "", chan->language);
6101 return res;
6104 int load_pbx(void)
6106 int x;
6108 /* Initialize the PBX */
6109 if (option_verbose) {
6110 ast_verbose( "Asterisk PBX Core Initializing\n");
6111 ast_verbose( "Registering builtin applications:\n");
6113 ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
6115 /* Register builtin applications */
6116 for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
6117 if (option_verbose)
6118 ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
6119 if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
6120 ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
6121 return -1;
6124 return 0;
6128 * Lock context list functions ...
6130 int ast_lock_contexts()
6132 return ast_rwlock_wrlock(&conlock);
6135 int ast_rdlock_contexts(void)
6137 return ast_rwlock_rdlock(&conlock);
6140 int ast_wrlock_contexts(void)
6142 return ast_rwlock_wrlock(&conlock);
6145 int ast_unlock_contexts()
6147 return ast_rwlock_unlock(&conlock);
6151 * Lock context ...
6153 int ast_lock_context(struct ast_context *con)
6155 return ast_mutex_lock(&con->lock);
6158 int ast_unlock_context(struct ast_context *con)
6160 return ast_mutex_unlock(&con->lock);
6164 * Name functions ...
6166 const char *ast_get_context_name(struct ast_context *con)
6168 return con ? con->name : NULL;
6171 struct ast_context *ast_get_extension_context(struct ast_exten *exten)
6173 return exten ? exten->parent : NULL;
6176 const char *ast_get_extension_name(struct ast_exten *exten)
6178 return exten ? exten->exten : NULL;
6181 const char *ast_get_extension_label(struct ast_exten *exten)
6183 return exten ? exten->label : NULL;
6186 const char *ast_get_include_name(struct ast_include *inc)
6188 return inc ? inc->name : NULL;
6191 const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
6193 return ip ? ip->pattern : NULL;
6196 int ast_get_extension_priority(struct ast_exten *exten)
6198 return exten ? exten->priority : -1;
6202 * Registrar info functions ...
6204 const char *ast_get_context_registrar(struct ast_context *c)
6206 return c ? c->registrar : NULL;
6209 const char *ast_get_extension_registrar(struct ast_exten *e)
6211 return e ? e->registrar : NULL;
6214 const char *ast_get_include_registrar(struct ast_include *i)
6216 return i ? i->registrar : NULL;
6219 const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
6221 return ip ? ip->registrar : NULL;
6224 int ast_get_extension_matchcid(struct ast_exten *e)
6226 return e ? e->matchcid : 0;
6229 const char *ast_get_extension_cidmatch(struct ast_exten *e)
6231 return e ? e->cidmatch : NULL;
6234 const char *ast_get_extension_app(struct ast_exten *e)
6236 return e ? e->app : NULL;
6239 void *ast_get_extension_app_data(struct ast_exten *e)
6241 return e ? e->data : NULL;
6244 const char *ast_get_switch_name(struct ast_sw *sw)
6246 return sw ? sw->name : NULL;
6249 const char *ast_get_switch_data(struct ast_sw *sw)
6251 return sw ? sw->data : NULL;
6254 const char *ast_get_switch_registrar(struct ast_sw *sw)
6256 return sw ? sw->registrar : NULL;
6260 * Walking functions ...
6262 struct ast_context *ast_walk_contexts(struct ast_context *con)
6264 return con ? con->next : contexts;
6267 struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
6268 struct ast_exten *exten)
6270 if (!exten)
6271 return con ? con->root : NULL;
6272 else
6273 return exten->next;
6276 struct ast_sw *ast_walk_context_switches(struct ast_context *con,
6277 struct ast_sw *sw)
6279 if (!sw)
6280 return con ? AST_LIST_FIRST(&con->alts) : NULL;
6281 else
6282 return AST_LIST_NEXT(sw, list);
6285 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
6286 struct ast_exten *priority)
6288 return priority ? priority->peer : exten;
6291 struct ast_include *ast_walk_context_includes(struct ast_context *con,
6292 struct ast_include *inc)
6294 if (!inc)
6295 return con ? con->includes : NULL;
6296 else
6297 return inc->next;
6300 struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
6301 struct ast_ignorepat *ip)
6303 if (!ip)
6304 return con ? con->ignorepats : NULL;
6305 else
6306 return ip->next;
6309 int ast_context_verify_includes(struct ast_context *con)
6311 struct ast_include *inc = NULL;
6312 int res = 0;
6314 while ( (inc = ast_walk_context_includes(con, inc)) ) {
6315 if (ast_context_find(inc->rname))
6316 continue;
6318 res = -1;
6319 ast_log(LOG_WARNING, "Context '%s' tries includes nonexistent context '%s'\n",
6320 ast_get_context_name(con), inc->rname);
6321 break;
6324 return res;
6328 static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
6330 int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
6332 if (!chan)
6333 return -2;
6335 if (context == NULL)
6336 context = chan->context;
6337 if (exten == NULL)
6338 exten = chan->exten;
6340 goto_func = (async) ? ast_async_goto : ast_explicit_goto;
6341 if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
6342 return goto_func(chan, context, exten, priority);
6343 else
6344 return -3;
6347 int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
6349 return __ast_goto_if_exists(chan, context, exten, priority, 0);
6352 int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
6354 return __ast_goto_if_exists(chan, context, exten, priority, 1);
6357 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
6359 char *exten, *pri, *context;
6360 char *stringp;
6361 int ipri;
6362 int mode = 0;
6364 if (ast_strlen_zero(goto_string)) {
6365 ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
6366 return -1;
6368 stringp = ast_strdupa(goto_string);
6369 context = strsep(&stringp, "|"); /* guaranteed non-null */
6370 exten = strsep(&stringp, "|");
6371 pri = strsep(&stringp, "|");
6372 if (!exten) { /* Only a priority in this one */
6373 pri = context;
6374 exten = NULL;
6375 context = NULL;
6376 } else if (!pri) { /* Only an extension and priority in this one */
6377 pri = exten;
6378 exten = context;
6379 context = NULL;
6381 if (*pri == '+') {
6382 mode = 1;
6383 pri++;
6384 } else if (*pri == '-') {
6385 mode = -1;
6386 pri++;
6388 if (sscanf(pri, "%d", &ipri) != 1) {
6389 if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
6390 pri, chan->cid.cid_num)) < 1) {
6391 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
6392 return -1;
6393 } else
6394 mode = 0;
6396 /* At this point we have a priority and maybe an extension and a context */
6398 if (mode)
6399 ipri = chan->priority + (ipri * mode);
6401 ast_explicit_goto(chan, context, exten, ipri);
6402 ast_cdr_update(chan);
6403 return 0;