![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
pbx.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Core PBX routines. 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53128 $") 00029 00030 #include <sys/types.h> 00031 #include <string.h> 00032 #include <unistd.h> 00033 #include <stdlib.h> 00034 #include <stdio.h> 00035 #include <ctype.h> 00036 #include <errno.h> 00037 #include <time.h> 00038 #include <sys/time.h> 00039 #include <limits.h> 00040 00041 #include "asterisk/lock.h" 00042 #include "asterisk/cli.h" 00043 #include "asterisk/pbx.h" 00044 #include "asterisk/channel.h" 00045 #include "asterisk/options.h" 00046 #include "asterisk/logger.h" 00047 #include "asterisk/file.h" 00048 #include "asterisk/callerid.h" 00049 #include "asterisk/cdr.h" 00050 #include "asterisk/config.h" 00051 #include "asterisk/term.h" 00052 #include "asterisk/manager.h" 00053 #include "asterisk/ast_expr.h" 00054 #include "asterisk/linkedlists.h" 00055 #define SAY_STUBS /* generate declarations and stubs for say methods */ 00056 #include "asterisk/say.h" 00057 #include "asterisk/utils.h" 00058 #include "asterisk/causes.h" 00059 #include "asterisk/musiconhold.h" 00060 #include "asterisk/app.h" 00061 #include "asterisk/devicestate.h" 00062 #include "asterisk/stringfields.h" 00063 00064 /*! 00065 * \note I M P O R T A N T : 00066 * 00067 * The speed of extension handling will likely be among the most important 00068 * aspects of this PBX. The switching scheme as it exists right now isn't 00069 * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg # 00070 * of priorities, but a constant search time here would be great ;-) 00071 * 00072 */ 00073 00074 #ifdef LOW_MEMORY 00075 #define EXT_DATA_SIZE 256 00076 #else 00077 #define EXT_DATA_SIZE 8192 00078 #endif 00079 00080 #define SWITCH_DATA_LENGTH 256 00081 00082 #define VAR_BUF_SIZE 4096 00083 00084 #define VAR_NORMAL 1 00085 #define VAR_SOFTTRAN 2 00086 #define VAR_HARDTRAN 3 00087 00088 #define BACKGROUND_SKIP (1 << 0) 00089 #define BACKGROUND_NOANSWER (1 << 1) 00090 #define BACKGROUND_MATCHEXTEN (1 << 2) 00091 #define BACKGROUND_PLAYBACK (1 << 3) 00092 00093 AST_APP_OPTIONS(background_opts, { 00094 AST_APP_OPTION('s', BACKGROUND_SKIP), 00095 AST_APP_OPTION('n', BACKGROUND_NOANSWER), 00096 AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN), 00097 AST_APP_OPTION('p', BACKGROUND_PLAYBACK), 00098 }); 00099 00100 #define WAITEXTEN_MOH (1 << 0) 00101 00102 AST_APP_OPTIONS(waitexten_opts, { 00103 AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0), 00104 }); 00105 00106 struct ast_context; 00107 struct ast_app; 00108 00109 /*! 00110 \brief ast_exten: An extension 00111 The dialplan is saved as a linked list with each context 00112 having it's own linked list of extensions - one item per 00113 priority. 00114 */ 00115 struct ast_exten { 00116 char *exten; /*!< Extension name */ 00117 int matchcid; /*!< Match caller id ? */ 00118 const char *cidmatch; /*!< Caller id to match for this extension */ 00119 int priority; /*!< Priority */ 00120 const char *label; /*!< Label */ 00121 struct ast_context *parent; /*!< The context this extension belongs to */ 00122 const char *app; /*!< Application to execute */ 00123 struct ast_app *cached_app; /*!< Cached location of application */ 00124 void *data; /*!< Data to use (arguments) */ 00125 void (*datad)(void *); /*!< Data destructor */ 00126 struct ast_exten *peer; /*!< Next higher priority with our extension */ 00127 const char *registrar; /*!< Registrar */ 00128 struct ast_exten *next; /*!< Extension with a greater ID */ 00129 char stuff[0]; 00130 }; 00131 00132 /*! \brief ast_include: include= support in extensions.conf */ 00133 struct ast_include { 00134 const char *name; 00135 const char *rname; /*!< Context to include */ 00136 const char *registrar; /*!< Registrar */ 00137 int hastime; /*!< If time construct exists */ 00138 struct ast_timing timing; /*!< time construct */ 00139 struct ast_include *next; /*!< Link them together */ 00140 char stuff[0]; 00141 }; 00142 00143 /*! \brief ast_sw: Switch statement in extensions.conf */ 00144 struct ast_sw { 00145 char *name; 00146 const char *registrar; /*!< Registrar */ 00147 char *data; /*!< Data load */ 00148 int eval; 00149 AST_LIST_ENTRY(ast_sw) list; 00150 char *tmpdata; 00151 char stuff[0]; 00152 }; 00153 00154 /*! \brief ast_ignorepat: Ignore patterns in dial plan */ 00155 struct ast_ignorepat { 00156 const char *registrar; 00157 struct ast_ignorepat *next; 00158 const char pattern[0]; 00159 }; 00160 00161 /*! \brief ast_context: An extension context */ 00162 struct ast_context { 00163 ast_mutex_t lock; /*!< A lock to prevent multiple threads from clobbering the context */ 00164 struct ast_exten *root; /*!< The root of the list of extensions */ 00165 struct ast_context *next; /*!< Link them together */ 00166 struct ast_include *includes; /*!< Include other contexts */ 00167 struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */ 00168 const char *registrar; /*!< Registrar */ 00169 AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */ 00170 ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */ 00171 char name[0]; /*!< Name of the context */ 00172 }; 00173 00174 00175 /*! \brief ast_app: A registered application */ 00176 struct ast_app { 00177 int (*execute)(struct ast_channel *chan, void *data); 00178 const char *synopsis; /*!< Synopsis text for 'show applications' */ 00179 const char *description; /*!< Description (help text) for 'show application <name>' */ 00180 AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */ 00181 struct module *module; /*!< Module this app belongs to */ 00182 char name[0]; /*!< Name of the application */ 00183 }; 00184 00185 /*! \brief ast_state_cb: An extension state notify register item */ 00186 struct ast_state_cb { 00187 int id; 00188 void *data; 00189 ast_state_cb_type callback; 00190 struct ast_state_cb *next; 00191 }; 00192 00193 /*! \brief Structure for dial plan hints 00194 00195 \note Hints are pointers from an extension in the dialplan to one or 00196 more devices (tech/name) 00197 - See \ref AstExtState 00198 */ 00199 struct ast_hint { 00200 struct ast_exten *exten; /*!< Extension */ 00201 int laststate; /*!< Last known state */ 00202 struct ast_state_cb *callbacks; /*!< Callback list for this extension */ 00203 AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */ 00204 }; 00205 00206 static const struct cfextension_states { 00207 int extension_state; 00208 const char * const text; 00209 } extension_states[] = { 00210 { AST_EXTENSION_NOT_INUSE, "Idle" }, 00211 { AST_EXTENSION_INUSE, "InUse" }, 00212 { AST_EXTENSION_BUSY, "Busy" }, 00213 { AST_EXTENSION_UNAVAILABLE, "Unavailable" }, 00214 { AST_EXTENSION_RINGING, "Ringing" }, 00215 { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" }, 00216 { AST_EXTENSION_ONHOLD, "Hold" }, 00217 { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" } 00218 }; 00219 00220 static int pbx_builtin_answer(struct ast_channel *, void *); 00221 static int pbx_builtin_goto(struct ast_channel *, void *); 00222 static int pbx_builtin_hangup(struct ast_channel *, void *); 00223 static int pbx_builtin_background(struct ast_channel *, void *); 00224 static int pbx_builtin_wait(struct ast_channel *, void *); 00225 static int pbx_builtin_waitexten(struct ast_channel *, void *); 00226 static int pbx_builtin_resetcdr(struct ast_channel *, void *); 00227 static int pbx_builtin_setamaflags(struct ast_channel *, void *); 00228 static int pbx_builtin_ringing(struct ast_channel *, void *); 00229 static int pbx_builtin_progress(struct ast_channel *, void *); 00230 static int pbx_builtin_congestion(struct ast_channel *, void *); 00231 static int pbx_builtin_busy(struct ast_channel *, void *); 00232 static int pbx_builtin_noop(struct ast_channel *, void *); 00233 static int pbx_builtin_gotoif(struct ast_channel *, void *); 00234 static int pbx_builtin_gotoiftime(struct ast_channel *, void *); 00235 static int pbx_builtin_execiftime(struct ast_channel *, void *); 00236 static int pbx_builtin_saynumber(struct ast_channel *, void *); 00237 static int pbx_builtin_saydigits(struct ast_channel *, void *); 00238 static int pbx_builtin_saycharacters(struct ast_channel *, void *); 00239 static int pbx_builtin_sayphonetic(struct ast_channel *, void *); 00240 int pbx_builtin_setvar(struct ast_channel *, void *); 00241 static int pbx_builtin_importvar(struct ast_channel *, void *); 00242 00243 AST_RWLOCK_DEFINE_STATIC(globalslock); 00244 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE; 00245 00246 static int autofallthrough = 1; 00247 00248 AST_MUTEX_DEFINE_STATIC(maxcalllock); 00249 static int countcalls; 00250 00251 static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function); 00252 00253 /*! \brief Declaration of builtin applications */ 00254 static struct pbx_builtin { 00255 char name[AST_MAX_APP]; 00256 int (*execute)(struct ast_channel *chan, void *data); 00257 char *synopsis; 00258 char *description; 00259 } builtins[] = 00260 { 00261 /* These applications are built into the PBX core and do not 00262 need separate modules */ 00263 00264 { "Answer", pbx_builtin_answer, 00265 "Answer a channel if ringing", 00266 " Answer([delay]): If the call has not been answered, this application will\n" 00267 "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n" 00268 "Asterisk will wait this number of milliseconds before returning to\n" 00269 "the dialplan after answering the call.\n" 00270 }, 00271 00272 { "BackGround", pbx_builtin_background, 00273 "Play an audio file while waiting for digits of an extension to go to.", 00274 " Background(filename1[&filename2...][|options[|langoverride][|context]]):\n" 00275 "This application will play the given list of files while waiting for an\n" 00276 "extension to be dialed by the calling channel. To continue waiting for digits\n" 00277 "after this application has finished playing files, the WaitExten application\n" 00278 "should be used. The 'langoverride' option explicitly specifies which language\n" 00279 "to attempt to use for the requested sound files. If a 'context' is specified,\n" 00280 "this is the dialplan context that this application will use when exiting to a\n" 00281 "dialed extension." 00282 " If one of the requested sound files does not exist, call processing will be\n" 00283 "terminated.\n" 00284 " Options:\n" 00285 " s - Causes the playback of the message to be skipped\n" 00286 " if the channel is not in the 'up' state (i.e. it\n" 00287 " hasn't been answered yet). If this happens, the\n" 00288 " application will return immediately.\n" 00289 " n - Don't answer the channel before playing the files.\n" 00290 " m - Only break if a digit hit matches a one digit\n" 00291 " extension in the destination context.\n" 00292 "This application sets the following channel variable upon completion:\n" 00293 " BACKGROUNDSTATUS The status of the background attempt as a text string, one of\n" 00294 " SUCCESS | FAILED\n" 00295 }, 00296 00297 { "Busy", pbx_builtin_busy, 00298 "Indicate the Busy condition", 00299 " Busy([timeout]): This application will indicate the busy condition to\n" 00300 "the calling channel. If the optional timeout is specified, the calling channel\n" 00301 "will be hung up after the specified number of seconds. Otherwise, this\n" 00302 "application will wait until the calling channel hangs up.\n" 00303 }, 00304 00305 { "Congestion", pbx_builtin_congestion, 00306 "Indicate the Congestion condition", 00307 " Congestion([timeout]): This application will indicate the congestion\n" 00308 "condition to the calling channel. If the optional timeout is specified, the\n" 00309 "calling channel will be hung up after the specified number of seconds.\n" 00310 "Otherwise, this application will wait until the calling channel hangs up.\n" 00311 }, 00312 00313 { "ExecIfTime", pbx_builtin_execiftime, 00314 "Conditional application execution based on the current time", 00315 " ExecIfTime(<times>|<weekdays>|<mdays>|<months>?appname[|appargs]):\n" 00316 "This application will execute the specified dialplan application, with optional\n" 00317 "arguments, if the current time matches the given time specification.\n" 00318 }, 00319 00320 { "Goto", pbx_builtin_goto, 00321 "Jump to a particular priority, extension, or context", 00322 " Goto([[context|]extension|]priority): This application will cause the\n" 00323 "calling channel to continue dialplan execution at the specified priority.\n" 00324 "If no specific extension, or extension and context, are specified, then this\n" 00325 "application will jump to the specified priority of the current extension.\n" 00326 " If the attempt to jump to another location in the dialplan is not successful,\n" 00327 "then the channel will continue at the next priority of the current extension.\n" 00328 }, 00329 00330 { "GotoIf", pbx_builtin_gotoif, 00331 "Conditional goto", 00332 " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will cause\n" 00333 "the calling channel to jump to the specified location in the dialplan based on\n" 00334 "the evaluation of the given condition. The channel will continue at\n" 00335 "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n" 00336 "false. The labels are specified with the same syntax as used within the Goto\n" 00337 "application. If the label chosen by the condition is omitted, no jump is\n" 00338 "performed, but execution continues with the next priority in the dialplan.\n" 00339 }, 00340 00341 { "GotoIfTime", pbx_builtin_gotoiftime, 00342 "Conditional Goto based on the current time", 00343 " GotoIfTime(<times>|<weekdays>|<mdays>|<months>?[[context|]exten|]priority):\n" 00344 "This application will have the calling channel jump to the specified location\n" 00345 "in the dialplan if the current time matches the given time specification.\n" 00346 }, 00347 00348 { "ImportVar", pbx_builtin_importvar, 00349 "Import a variable from a channel into a new variable", 00350 " ImportVar(newvar=channelname|variable): This application imports a variable\n" 00351 "from the specified channel (as opposed to the current one) and stores it as\n" 00352 "a variable in the current channel (the channel that is calling this\n" 00353 "application). Variables created by this application have the same inheritance\n" 00354 "properties as those created with the Set application. See the documentation for\n" 00355 "Set for more information.\n" 00356 }, 00357 00358 { "Hangup", pbx_builtin_hangup, 00359 "Hang up the calling channel", 00360 " Hangup([causecode]): This application will hang up the calling channel.\n" 00361 "If a causecode is given the channel's hangup cause will be set to the given\n" 00362 "value.\n" 00363 }, 00364 00365 { "NoOp", pbx_builtin_noop, 00366 "Do Nothing", 00367 " NoOp(): This applicatiion does nothing. However, it is useful for debugging\n" 00368 "purposes. Any text that is provided as arguments to this application can be\n" 00369 "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n" 00370 "variables or functions without having any effect." 00371 }, 00372 00373 { "Progress", pbx_builtin_progress, 00374 "Indicate progress", 00375 " Progress(): This application will request that in-band progress information\n" 00376 "be provided to the calling channel.\n" 00377 }, 00378 00379 { "ResetCDR", pbx_builtin_resetcdr, 00380 "Resets the Call Data Record", 00381 " ResetCDR([options]): This application causes the Call Data Record to be\n" 00382 "reset.\n" 00383 " Options:\n" 00384 " w -- Store the current CDR record before resetting it.\n" 00385 " a -- Store any stacked records.\n" 00386 " v -- Save CDR variables.\n" 00387 }, 00388 00389 { "Ringing", pbx_builtin_ringing, 00390 "Indicate ringing tone", 00391 " Ringing(): This application will request that the channel indicate a ringing\n" 00392 "tone to the user.\n" 00393 }, 00394 00395 { "SayAlpha", pbx_builtin_saycharacters, 00396 "Say Alpha", 00397 " SayAlpha(string): This application will play the sounds that correspond to\n" 00398 "the letters of the given string.\n" 00399 }, 00400 00401 { "SayDigits", pbx_builtin_saydigits, 00402 "Say Digits", 00403 " SayDigits(digits): This application will play the sounds that correspond\n" 00404 "to the digits of the given number. This will use the language that is currently\n" 00405 "set for the channel. See the LANGUAGE function for more information on setting\n" 00406 "the language for the channel.\n" 00407 }, 00408 00409 { "SayNumber", pbx_builtin_saynumber, 00410 "Say Number", 00411 " SayNumber(digits[,gender]): This application will play the sounds that\n" 00412 "correspond to the given number. Optionally, a gender may be specified.\n" 00413 "This will use the language that is currently set for the channel. See the\n" 00414 "LANGUAGE function for more information on setting the language for the channel.\n" 00415 }, 00416 00417 { "SayPhonetic", pbx_builtin_sayphonetic, 00418 "Say Phonetic", 00419 " SayPhonetic(string): This application will play the sounds from the phonetic\n" 00420 "alphabet that correspond to the letters in the given string.\n" 00421 }, 00422 00423 { "Set", pbx_builtin_setvar, 00424 "Set channel variable(s) or function value(s)", 00425 " Set(name1=value1|name2=value2|..[|options])\n" 00426 "This function can be used to set the value of channel variables or dialplan\n" 00427 "functions. It will accept up to 24 name/value pairs. When setting variables,\n" 00428 "if the variable name is prefixed with _, the variable will be inherited into\n" 00429 "channels created from the current channel. If the variable name is prefixed\n" 00430 "with __, the variable will be inherited into channels created from the current\n" 00431 "channel and all children channels.\n" 00432 " Options:\n" 00433 " g - Set variable globally instead of on the channel\n" 00434 " (applies only to variables, not functions)\n" 00435 }, 00436 00437 { "SetAMAFlags", pbx_builtin_setamaflags, 00438 "Set the AMA Flags", 00439 " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n" 00440 " billing purposes.\n" 00441 }, 00442 00443 { "Wait", pbx_builtin_wait, 00444 "Waits for some time", 00445 " Wait(seconds): This application waits for a specified number of seconds.\n" 00446 "Then, dialplan execution will continue at the next priority.\n" 00447 " Note that the seconds can be passed with fractions of a second. For example,\n" 00448 "'1.5' will ask the application to wait for 1.5 seconds.\n" 00449 }, 00450 00451 { "WaitExten", pbx_builtin_waitexten, 00452 "Waits for an extension to be entered", 00453 " WaitExten([seconds][|options]): This application waits for the user to enter\n" 00454 "a new extension for a specified number of seconds.\n" 00455 " Note that the seconds can be passed with fractions of a second. For example,\n" 00456 "'1.5' will ask the application to wait for 1.5 seconds.\n" 00457 " Options:\n" 00458 " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n" 00459 " Optionally, specify the class for music on hold within parenthesis.\n" 00460 }, 00461 00462 }; 00463 00464 static struct ast_context *contexts; 00465 AST_MUTEX_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */ 00466 00467 static AST_RWLIST_HEAD_STATIC(apps, ast_app); 00468 00469 static AST_RWLIST_HEAD_STATIC(switches, ast_switch); 00470 00471 static int stateid = 1; 00472 /* WARNING: 00473 When holding this list's lock, do _not_ do anything that will cause conlock 00474 to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete 00475 function will take the locks in conlock/hints order, so any other 00476 paths that require both locks must also take them in that order. 00477 */ 00478 static AST_RWLIST_HEAD_STATIC(hints, ast_hint); 00479 struct ast_state_cb *statecbs; 00480 00481 /* 00482 \note This function is special. It saves the stack so that no matter 00483 how many times it is called, it returns to the same place */ 00484 int pbx_exec(struct ast_channel *c, /*!< Channel */ 00485 struct ast_app *app, /*!< Application */ 00486 void *data) /*!< Data for execution */ 00487 { 00488 int res; 00489 00490 const char *saved_c_appl; 00491 const char *saved_c_data; 00492 00493 if (c->cdr) 00494 ast_cdr_setapp(c->cdr, app->name, data); 00495 00496 /* save channel values */ 00497 saved_c_appl= c->appl; 00498 saved_c_data= c->data; 00499 00500 c->appl = app->name; 00501 c->data = data; 00502 /* XXX remember what to to when we have linked apps to modules */ 00503 if (app->module) { 00504 /* XXX LOCAL_USER_ADD(app->module) */ 00505 } 00506 res = app->execute(c, data); 00507 if (app->module) { 00508 /* XXX LOCAL_USER_REMOVE(app->module) */ 00509 } 00510 /* restore channel values */ 00511 c->appl = saved_c_appl; 00512 c->data = saved_c_data; 00513 return res; 00514 } 00515 00516 00517 /*! Go no deeper than this through includes (not counting loops) */ 00518 #define AST_PBX_MAX_STACK 128 00519 00520 /*! \brief Find application handle in linked list 00521 */ 00522 struct ast_app *pbx_findapp(const char *app) 00523 { 00524 struct ast_app *tmp; 00525 00526 AST_RWLIST_RDLOCK(&apps); 00527 AST_RWLIST_TRAVERSE(&apps, tmp, list) { 00528 if (!strcasecmp(tmp->name, app)) 00529 break; 00530 } 00531 AST_RWLIST_UNLOCK(&apps); 00532 00533 return tmp; 00534 } 00535 00536 static struct ast_switch *pbx_findswitch(const char *sw) 00537 { 00538 struct ast_switch *asw; 00539 00540 AST_RWLIST_RDLOCK(&switches); 00541 AST_RWLIST_TRAVERSE(&switches, asw, list) { 00542 if (!strcasecmp(asw->name, sw)) 00543 break; 00544 } 00545 AST_RWLIST_UNLOCK(&switches); 00546 00547 return asw; 00548 } 00549 00550 static inline int include_valid(struct ast_include *i) 00551 { 00552 if (!i->hastime) 00553 return 1; 00554 00555 return ast_check_timing(&(i->timing)); 00556 } 00557 00558 static void pbx_destroy(struct ast_pbx *p) 00559 { 00560 free(p); 00561 } 00562 00563 /* 00564 * Special characters used in patterns: 00565 * '_' underscore is the leading character of a pattern. 00566 * In other position it is treated as a regular char. 00567 * ' ' '-' space and '-' are separator and ignored. 00568 * . one or more of any character. Only allowed at the end of 00569 * a pattern. 00570 * ! zero or more of anything. Also impacts the result of CANMATCH 00571 * and MATCHMORE. Only allowed at the end of a pattern. 00572 * In the core routine, ! causes a match with a return code of 2. 00573 * In turn, depending on the search mode: (XXX check if it is implemented) 00574 * - E_MATCH retuns 1 (does match) 00575 * - E_MATCHMORE returns 0 (no match) 00576 * - E_CANMATCH returns 1 (does match) 00577 * 00578 * / should not appear as it is considered the separator of the CID info. 00579 * XXX at the moment we may stop on this char. 00580 * 00581 * X Z N match ranges 0-9, 1-9, 2-9 respectively. 00582 * [ denotes the start of a set of character. Everything inside 00583 * is considered literally. We can have ranges a-d and individual 00584 * characters. A '[' and '-' can be considered literally if they 00585 * are just before ']'. 00586 * XXX currently there is no way to specify ']' in a range, nor \ is 00587 * considered specially. 00588 * 00589 * When we compare a pattern with a specific extension, all characters in the extension 00590 * itself are considered literally with the only exception of '-' which is considered 00591 * as a separator and thus ignored. 00592 * XXX do we want to consider space as a separator as well ? 00593 * XXX do we want to consider the separators in non-patterns as well ? 00594 */ 00595 00596 /*! 00597 * \brief helper functions to sort extensions and patterns in the desired way, 00598 * so that more specific patterns appear first. 00599 * 00600 * ext_cmp1 compares individual characters (or sets of), returning 00601 * an int where bits 0-7 are the ASCII code of the first char in the set, 00602 * while bit 8-15 are the cardinality of the set minus 1. 00603 * This way more specific patterns (smaller cardinality) appear first. 00604 * Wildcards have a special value, so that we can directly compare them to 00605 * sets by subtracting the two values. In particular: 00606 * 0x000xx one character, xx 00607 * 0x0yyxx yy character set starting with xx 00608 * 0x10000 '.' (one or more of anything) 00609 * 0x20000 '!' (zero or more of anything) 00610 * 0x30000 NUL (end of string) 00611 * 0x40000 error in set. 00612 * The pointer to the string is advanced according to needs. 00613 * NOTES: 00614 * 1. the empty set is equivalent to NUL. 00615 * 2. given that a full set has always 0 as the first element, 00616 * we could encode the special cases as 0xffXX where XX 00617 * is 1, 2, 3, 4 as used above. 00618 */ 00619 static int ext_cmp1(const char **p) 00620 { 00621 uint32_t chars[8]; 00622 int c, cmin = 0xff, count = 0; 00623 const char *end; 00624 00625 /* load, sign extend and advance pointer until we find 00626 * a valid character. 00627 */ 00628 while ( (c = *(*p)++) && (c == ' ' || c == '-') ) 00629 ; /* ignore some characters */ 00630 00631 /* always return unless we have a set of chars */ 00632 switch (c) { 00633 default: /* ordinary character */ 00634 return 0x0000 | (c & 0xff); 00635 00636 case 'N': /* 2..9 */ 00637 return 0x0700 | '2' ; 00638 00639 case 'X': /* 0..9 */ 00640 return 0x0900 | '0'; 00641 00642 case 'Z': /* 1..9 */ 00643 return 0x0800 | '1'; 00644 00645 case '.': /* wildcard */ 00646 return 0x10000; 00647 00648 case '!': /* earlymatch */ 00649 return 0x20000; /* less specific than NULL */ 00650 00651 case '\0': /* empty string */ 00652 *p = NULL; 00653 return 0x30000; 00654 00655 case '[': /* pattern */ 00656 break; 00657 } 00658 /* locate end of set */ 00659 end = strchr(*p, ']'); 00660 00661 if (end == NULL) { 00662 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n"); 00663 return 0x40000; /* XXX make this entry go last... */ 00664 } 00665 00666 bzero(chars, sizeof(chars)); /* clear all chars in the set */ 00667 for (; *p < end ; (*p)++) { 00668 unsigned char c1, c2; /* first-last char in range */ 00669 c1 = (unsigned char)((*p)[0]); 00670 if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */ 00671 c2 = (unsigned char)((*p)[2]); 00672 *p += 2; /* skip a total of 3 chars */ 00673 } else /* individual character */ 00674 c2 = c1; 00675 if (c1 < cmin) 00676 cmin = c1; 00677 for (; c1 <= c2; c1++) { 00678 uint32_t mask = 1 << (c1 % 32); 00679 if ( (chars[ c1 / 32 ] & mask) == 0) 00680 count += 0x100; 00681 chars[ c1 / 32 ] |= mask; 00682 } 00683 } 00684 (*p)++; 00685 return count == 0 ? 0x30000 : (count | cmin); 00686 } 00687 00688 /*! 00689 * \brief the full routine to compare extensions in rules. 00690 */ 00691 static int ext_cmp(const char *a, const char *b) 00692 { 00693 /* make sure non-patterns come first. 00694 * If a is not a pattern, it either comes first or 00695 * we use strcmp to compare the strings. 00696 */ 00697 int ret = 0; 00698 00699 if (a[0] != '_') 00700 return (b[0] == '_') ? -1 : strcmp(a, b); 00701 00702 /* Now we know a is a pattern; if b is not, a comes first */ 00703 if (b[0] != '_') 00704 return 1; 00705 #if 0 /* old mode for ext matching */ 00706 return strcmp(a, b); 00707 #endif 00708 /* ok we need full pattern sorting routine */ 00709 while (!ret && a && b) 00710 ret = ext_cmp1(&a) - ext_cmp1(&b); 00711 if (ret == 0) 00712 return 0; 00713 else 00714 return (ret > 0) ? 1 : -1; 00715 } 00716 00717 /*! 00718 * When looking up extensions, we can have different requests 00719 * identified by the 'action' argument, as follows. 00720 * Note that the coding is such that the low 4 bits are the 00721 * third argument to extension_match_core. 00722 */ 00723 enum ext_match_t { 00724 E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */ 00725 E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */ 00726 E_MATCH = 0x02, /* extension is an exact match */ 00727 E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */ 00728 E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */ 00729 E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */ 00730 }; 00731 00732 /* 00733 * Internal function for ast_extension_{match|close} 00734 * return 0 on no-match, 1 on match, 2 on early match. 00735 * mode is as follows: 00736 * E_MATCH success only on exact match 00737 * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern) 00738 * E_CANMATCH either of the above. 00739 */ 00740 00741 static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode) 00742 { 00743 mode &= E_MATCH_MASK; /* only consider the relevant bits */ 00744 00745 if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */ 00746 return 1; 00747 00748 if (pattern[0] != '_') { /* not a pattern, try exact or partial match */ 00749 int ld = strlen(data), lp = strlen(pattern); 00750 00751 if (lp < ld) /* pattern too short, cannot match */ 00752 return 0; 00753 /* depending on the mode, accept full or partial match or both */ 00754 if (mode == E_MATCH) 00755 return !strcmp(pattern, data); /* 1 on match, 0 on fail */ 00756 if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */ 00757 return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */ 00758 else 00759 return 0; 00760 } 00761 pattern++; /* skip leading _ */ 00762 /* 00763 * XXX below we stop at '/' which is a separator for the CID info. However we should 00764 * not store '/' in the pattern at all. When we insure it, we can remove the checks. 00765 */ 00766 while (*data && *pattern && *pattern != '/') { 00767 const char *end; 00768 00769 if (*data == '-') { /* skip '-' in data (just a separator) */ 00770 data++; 00771 continue; 00772 } 00773 switch (toupper(*pattern)) { 00774 case '[': /* a range */ 00775 end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */ 00776 if (end == NULL) { 00777 ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n"); 00778 return 0; /* unconditional failure */ 00779 } 00780 for (pattern++; pattern != end; pattern++) { 00781 if (pattern+2 < end && pattern[1] == '-') { /* this is a range */ 00782 if (*data >= pattern[0] && *data <= pattern[2]) 00783 break; /* match found */ 00784 else { 00785 pattern += 2; /* skip a total of 3 chars */ 00786 continue; 00787 } 00788 } else if (*data == pattern[0]) 00789 break; /* match found */ 00790 } 00791 if (pattern == end) 00792 return 0; 00793 pattern = end; /* skip and continue */ 00794 break; 00795 case 'N': 00796 if (*data < '2' || *data > '9') 00797 return 0; 00798 break; 00799 case 'X': 00800 if (*data < '0' || *data > '9') 00801 return 0; 00802 break; 00803 case 'Z': 00804 if (*data < '1' || *data > '9') 00805 return 0; 00806 break; 00807 case '.': /* Must match, even with more digits */ 00808 return 1; 00809 case '!': /* Early match */ 00810 return 2; 00811 case ' ': 00812 case '-': /* Ignore these in patterns */ 00813 data--; /* compensate the final data++ */ 00814 break; 00815 default: 00816 if (*data != *pattern) 00817 return 0; 00818 } 00819 data++; 00820 pattern++; 00821 } 00822 if (*data) /* data longer than pattern, no match */ 00823 return 0; 00824 /* 00825 * match so far, but ran off the end of the data. 00826 * Depending on what is next, determine match or not. 00827 */ 00828 if (*pattern == '\0' || *pattern == '/') /* exact match */ 00829 return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */ 00830 else if (*pattern == '!') /* early match */ 00831 return 2; 00832 else /* partial match */ 00833 return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */ 00834 } 00835 00836 /* 00837 * Wrapper around _extension_match_core() to do performance measurement 00838 * using the profiling code. 00839 */ 00840 static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode) 00841 { 00842 int i; 00843 static int prof_id = -2; /* marker for 'unallocated' id */ 00844 if (prof_id == -2) 00845 prof_id = ast_add_profile("ext_match", 0); 00846 ast_mark(prof_id, 1); 00847 i = _extension_match_core(pattern, data, mode); 00848 ast_mark(prof_id, 0); 00849 return i; 00850 } 00851 00852 int ast_extension_match(const char *pattern, const char *data) 00853 { 00854 return extension_match_core(pattern, data, E_MATCH); 00855 } 00856 00857 int ast_extension_close(const char *pattern, const char *data, int needmore) 00858 { 00859 if (needmore != E_MATCHMORE && needmore != E_CANMATCH) 00860 ast_log(LOG_WARNING, "invalid argument %d\n", needmore); 00861 return extension_match_core(pattern, data, needmore); 00862 } 00863 00864 struct ast_context *ast_context_find(const char *name) 00865 { 00866 struct ast_context *tmp = NULL; 00867 ast_mutex_lock(&conlock); 00868 while ( (tmp = ast_walk_contexts(tmp)) ) { 00869 if (!name || !strcasecmp(name, tmp->name)) 00870 break; 00871 } 00872 ast_mutex_unlock(&conlock); 00873 return tmp; 00874 } 00875 00876 #define STATUS_NO_CONTEXT 1 00877 #define STATUS_NO_EXTENSION 2 00878 #define STATUS_NO_PRIORITY 3 00879 #define STATUS_NO_LABEL 4 00880 #define STATUS_SUCCESS 5 00881 00882 static int matchcid(const char *cidpattern, const char *callerid) 00883 { 00884 /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so 00885 failing to get a number should count as a match, otherwise not */ 00886 00887 if (ast_strlen_zero(callerid)) 00888 return ast_strlen_zero(cidpattern) ? 1 : 0; 00889 00890 return ast_extension_match(cidpattern, callerid); 00891 } 00892 00893 /* request and result for pbx_find_extension */ 00894 struct pbx_find_info { 00895 #if 0 00896 const char *context; 00897 const char *exten; 00898 int priority; 00899 #endif 00900 00901 char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */ 00902 int stacklen; /* modified during the search */ 00903 int status; /* set on return */ 00904 struct ast_switch *swo; /* set on return */ 00905 const char *data; /* set on return */ 00906 const char *foundcontext; /* set on return */ 00907 }; 00908 00909 static struct ast_exten *pbx_find_extension(struct ast_channel *chan, 00910 struct ast_context *bypass, struct pbx_find_info *q, 00911 const char *context, const char *exten, int priority, 00912 const char *label, const char *callerid, enum ext_match_t action) 00913 { 00914 int x, res; 00915 struct ast_context *tmp; 00916 struct ast_exten *e, *eroot; 00917 struct ast_include *i; 00918 struct ast_sw *sw; 00919 00920 /* Initialize status if appropriate */ 00921 if (q->stacklen == 0) { 00922 q->status = STATUS_NO_CONTEXT; 00923 q->swo = NULL; 00924 q->data = NULL; 00925 q->foundcontext = NULL; 00926 } 00927 /* Check for stack overflow */ 00928 if (q->stacklen >= AST_PBX_MAX_STACK) { 00929 ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n"); 00930 return NULL; 00931 } 00932 /* Check first to see if we've already been checked */ 00933 for (x = 0; x < q->stacklen; x++) { 00934 if (!strcasecmp(q->incstack[x], context)) 00935 return NULL; 00936 } 00937 if (bypass) /* bypass means we only look there */ 00938 tmp = bypass; 00939 else { /* look in contexts */ 00940 tmp = NULL; 00941 while ((tmp = ast_walk_contexts(tmp)) ) { 00942 if (!strcmp(tmp->name, context)) 00943 break; 00944 } 00945 if (!tmp) 00946 return NULL; 00947 } 00948 if (q->status < STATUS_NO_EXTENSION) 00949 q->status = STATUS_NO_EXTENSION; 00950 00951 /* scan the list trying to match extension and CID */ 00952 eroot = NULL; 00953 while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) { 00954 int match = extension_match_core(eroot->exten, exten, action); 00955 /* 0 on fail, 1 on match, 2 on earlymatch */ 00956 00957 if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid))) 00958 continue; /* keep trying */ 00959 if (match == 2 && action == E_MATCHMORE) { 00960 /* We match an extension ending in '!'. 00961 * The decision in this case is final and is NULL (no match). 00962 */ 00963 return NULL; 00964 } 00965 /* found entry, now look for the right priority */ 00966 if (q->status < STATUS_NO_PRIORITY) 00967 q->status = STATUS_NO_PRIORITY; 00968 e = NULL; 00969 while ( (e = ast_walk_extension_priorities(eroot, e)) ) { 00970 /* Match label or priority */ 00971 if (action == E_FINDLABEL) { 00972 if (q->status < STATUS_NO_LABEL) 00973 q->status = STATUS_NO_LABEL; 00974 if (label && e->label && !strcmp(label, e->label)) 00975 break; /* found it */ 00976 } else if (e->priority == priority) { 00977 break; /* found it */ 00978 } /* else keep searching */ 00979 } 00980 if (e) { /* found a valid match */ 00981 q->status = STATUS_SUCCESS; 00982 q->foundcontext = context; 00983 return e; 00984 } 00985 } 00986 /* Check alternative switches */ 00987 AST_LIST_TRAVERSE(&tmp->alts, sw, list) { 00988 struct ast_switch *asw = pbx_findswitch(sw->name); 00989 ast_switch_f *aswf = NULL; 00990 char *datap; 00991 00992 if (!asw) { 00993 ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name); 00994 continue; 00995 } 00996 /* Substitute variables now */ 00997 if (sw->eval) 00998 pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1); 00999 01000 /* equivalent of extension_match_core() at the switch level */ 01001 if (action == E_CANMATCH) 01002 aswf = asw->canmatch; 01003 else if (action == E_MATCHMORE) 01004 aswf = asw->matchmore; 01005 else /* action == E_MATCH */ 01006 aswf = asw->exists; 01007 datap = sw->eval ? sw->tmpdata : sw->data; 01008 res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap); 01009 if (res) { /* Got a match */ 01010 q->swo = asw; 01011 q->data = datap; 01012 q->foundcontext = context; 01013 /* XXX keep status = STATUS_NO_CONTEXT ? */ 01014 return NULL; 01015 } 01016 } 01017 q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */ 01018 /* Now try any includes we have in this context */ 01019 for (i = tmp->includes; i; i = i->next) { 01020 if (include_valid(i)) { 01021 if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) 01022 return e; 01023 if (q->swo) 01024 return NULL; 01025 } 01026 } 01027 return NULL; 01028 } 01029 01030 /*! \brief extract offset:length from variable name. 01031 * Returns 1 if there is a offset:length part, which is 01032 * trimmed off (values go into variables) 01033 */ 01034 static int parse_variable_name(char *var, int *offset, int *length, int *isfunc) 01035 { 01036 int parens=0; 01037 01038 *offset = 0; 01039 *length = INT_MAX; 01040 *isfunc = 0; 01041 for (; *var; var++) { 01042 if (*var == '(') { 01043 (*isfunc)++; 01044 parens++; 01045 } else if (*var == ')') { 01046 parens--; 01047 } else if (*var == ':' && parens == 0) { 01048 *var++ = '\0'; 01049 sscanf(var, "%d:%d", offset, length); 01050 return 1; /* offset:length valid */ 01051 } 01052 } 01053 return 0; 01054 } 01055 01056 /*! \brief takes a substring. It is ok to call with value == workspace. 01057 * 01058 * offset < 0 means start from the end of the string and set the beginning 01059 * to be that many characters back. 01060 * length is the length of the substring. A value less than 0 means to leave 01061 * that many off the end. 01062 * Always return a copy in workspace. 01063 */ 01064 static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len) 01065 { 01066 char *ret = workspace; 01067 int lr; /* length of the input string after the copy */ 01068 01069 ast_copy_string(workspace, value, workspace_len); /* always make a copy */ 01070 01071 lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */ 01072 01073 /* Quick check if no need to do anything */ 01074 if (offset == 0 && length >= lr) /* take the whole string */ 01075 return ret; 01076 01077 if (offset < 0) { /* translate negative offset into positive ones */ 01078 offset = lr + offset; 01079 if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */ 01080 offset = 0; 01081 } 01082 01083 /* too large offset result in empty string so we know what to return */ 01084 if (offset >= lr) 01085 return ret + lr; /* the final '\0' */ 01086 01087 ret += offset; /* move to the start position */ 01088 if (length >= 0 && length < lr - offset) /* truncate if necessary */ 01089 ret[length] = '\0'; 01090 else if (length < 0) { 01091 if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */ 01092 ret[lr + length - offset] = '\0'; 01093 else 01094 ret[0] = '\0'; 01095 } 01096 01097 return ret; 01098 } 01099 01100 /*! \brief Support for Asterisk built-in variables in the dialplan 01101 01102 \note See also 01103 - \ref AstVar Channel variables 01104 - \ref AstCauses The HANGUPCAUSE variable 01105 */ 01106 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp) 01107 { 01108 const char not_found = '\0'; 01109 char *tmpvar; 01110 const char *s; /* the result */ 01111 int offset, length; 01112 int i, need_substring; 01113 struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */ 01114 01115 if (c) { 01116 places[0] = &c->varshead; 01117 } 01118 /* 01119 * Make a copy of var because parse_variable_name() modifies the string. 01120 * Then if called directly, we might need to run substring() on the result; 01121 * remember this for later in 'need_substring', 'offset' and 'length' 01122 */ 01123 tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */ 01124 need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */); 01125 01126 /* 01127 * Look first into predefined variables, then into variable lists. 01128 * Variable 's' points to the result, according to the following rules: 01129 * s == ¬_found (set at the beginning) means that we did not find a 01130 * matching variable and need to look into more places. 01131 * If s != ¬_found, s is a valid result string as follows: 01132 * s = NULL if the variable does not have a value; 01133 * you typically do this when looking for an unset predefined variable. 01134 * s = workspace if the result has been assembled there; 01135 * typically done when the result is built e.g. with an snprintf(), 01136 * so we don't need to do an additional copy. 01137 * s != workspace in case we have a string, that needs to be copied 01138 * (the ast_copy_string is done once for all at the end). 01139 * Typically done when the result is already available in some string. 01140 */ 01141 s = ¬_found; /* default value */ 01142 if (c) { /* This group requires a valid channel */ 01143 /* Names with common parts are looked up a piece at a time using strncmp. */ 01144 if (!strncmp(var, "CALL", 4)) { 01145 if (!strncmp(var + 4, "ING", 3)) { 01146 if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */ 01147 snprintf(workspace, workspacelen, "%d", c->cid.cid_pres); 01148 s = workspace; 01149 } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */ 01150 snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2); 01151 s = workspace; 01152 } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */ 01153 snprintf(workspace, workspacelen, "%d", c->cid.cid_ton); 01154 s = workspace; 01155 } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */ 01156 snprintf(workspace, workspacelen, "%d", c->cid.cid_tns); 01157 s = workspace; 01158 } 01159 } 01160 } else if (!strcmp(var, "HINT")) { 01161 s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL; 01162 } else if (!strcmp(var, "HINTNAME")) { 01163 s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL; 01164 } else if (!strcmp(var, "EXTEN")) { 01165 s = c->exten; 01166 } else if (!strcmp(var, "CONTEXT")) { 01167 s = c->context; 01168 } else if (!strcmp(var, "PRIORITY")) { 01169 snprintf(workspace, workspacelen, "%d", c->priority); 01170 s = workspace; 01171 } else if (!strcmp(var, "CHANNEL")) { 01172 s = c->name; 01173 } else if (!strcmp(var, "UNIQUEID")) { 01174 s = c->uniqueid; 01175 } else if (!strcmp(var, "HANGUPCAUSE")) { 01176 snprintf(workspace, workspacelen, "%d", c->hangupcause); 01177 s = workspace; 01178 } 01179 } 01180 if (s == ¬_found) { /* look for more */ 01181 if (!strcmp(var, "EPOCH")) { 01182 snprintf(workspace, workspacelen, "%u",(int)time(NULL)); 01183 s = workspace; 01184 } else if (!strcmp(var, "SYSTEMNAME")) { 01185 s = ast_config_AST_SYSTEM_NAME; 01186 } 01187 } 01188 /* if not found, look into chanvars or global vars */ 01189 for (i = 0; s == ¬_found && i < (sizeof(places) / sizeof(places[0])); i++) { 01190 struct ast_var_t *variables; 01191 if (!places[i]) 01192 continue; 01193 if (places[i] == &globals) 01194 ast_rwlock_rdlock(&globalslock); 01195 AST_LIST_TRAVERSE(places[i], variables, entries) { 01196 if (strcasecmp(ast_var_name(variables), var)==0) { 01197 s = ast_var_value(variables); 01198 break; 01199 } 01200 } 01201 if (places[i] == &globals) 01202 ast_rwlock_unlock(&globalslock); 01203 } 01204 if (s == ¬_found || s == NULL) 01205 *ret = NULL; 01206 else { 01207 if (s != workspace) 01208 ast_copy_string(workspace, s, workspacelen); 01209 *ret = workspace; 01210 if (need_substring) 01211 *ret = substring(*ret, offset, length, workspace, workspacelen); 01212 } 01213 } 01214 01215 static int handle_show_functions(int fd, int argc, char *argv[]) 01216 { 01217 struct ast_custom_function *acf; 01218 int count_acf = 0; 01219 int like = 0; 01220 01221 if (argc == 5 && (!strcmp(argv[3], "like")) ) { 01222 like = 1; 01223 } else if (argc != 3) { 01224 return RESULT_SHOWUSAGE; 01225 } 01226 01227 ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed"); 01228 01229 AST_RWLIST_RDLOCK(&acf_root); 01230 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) { 01231 if (!like || strstr(acf->name, argv[4])) { 01232 count_acf++; 01233 ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis); 01234 } 01235 } 01236 AST_RWLIST_UNLOCK(&acf_root); 01237 01238 ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : ""); 01239 01240 return RESULT_SUCCESS; 01241 } 01242 01243 static int handle_show_function(int fd, int argc, char *argv[]) 01244 { 01245 struct ast_custom_function *acf; 01246 /* Maximum number of characters added by terminal coloring is 22 */ 01247 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40]; 01248 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL; 01249 char stxtitle[40], *syntax = NULL; 01250 int synopsis_size, description_size, syntax_size; 01251 01252 if (argc < 4) 01253 return RESULT_SHOWUSAGE; 01254 01255 if (!(acf = ast_custom_function_find(argv[3]))) { 01256 ast_cli(fd, "No function by that name registered.\n"); 01257 return RESULT_FAILURE; 01258 01259 } 01260 01261 if (acf->synopsis) 01262 synopsis_size = strlen(acf->synopsis) + 23; 01263 else 01264 synopsis_size = strlen("Not available") + 23; 01265 synopsis = alloca(synopsis_size); 01266 01267 if (acf->desc) 01268 description_size = strlen(acf->desc) + 23; 01269 else 01270 description_size = strlen("Not available") + 23; 01271 description = alloca(description_size); 01272 01273 if (acf->syntax) 01274 syntax_size = strlen(acf->syntax) + 23; 01275 else 01276 syntax_size = strlen("Not available") + 23; 01277 syntax = alloca(syntax_size); 01278 01279 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name); 01280 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22); 01281 term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40); 01282 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40); 01283 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40); 01284 term_color(syntax, 01285 acf->syntax ? acf->syntax : "Not available", 01286 COLOR_CYAN, 0, syntax_size); 01287 term_color(synopsis, 01288 acf->synopsis ? acf->synopsis : "Not available", 01289 COLOR_CYAN, 0, synopsis_size); 01290 term_color(description, 01291 acf->desc ? acf->desc : "Not available", 01292 COLOR_CYAN, 0, description_size); 01293 01294 ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description); 01295 01296 return RESULT_SUCCESS; 01297 } 01298 01299 static char *complete_show_function(const char *line, const char *word, int pos, int state) 01300 { 01301 struct ast_custom_function *acf; 01302 char *ret = NULL; 01303 int which = 0; 01304 int wordlen = strlen(word); 01305 01306 /* case-insensitive for convenience in this 'complete' function */ 01307 AST_RWLIST_RDLOCK(&acf_root); 01308 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) { 01309 if (!strncasecmp(word, acf->name, wordlen) && ++which > state) { 01310 ret = strdup(acf->name); 01311 break; 01312 } 01313 } 01314 AST_RWLIST_UNLOCK(&acf_root); 01315 01316 return ret; 01317 } 01318 01319 struct ast_custom_function *ast_custom_function_find(const char *name) 01320 { 01321 struct ast_custom_function *acf = NULL; 01322 01323 AST_RWLIST_RDLOCK(&acf_root); 01324 AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) { 01325 if (!strcmp(name, acf->name)) 01326 break; 01327 } 01328 AST_RWLIST_UNLOCK(&acf_root); 01329 01330 return acf; 01331 } 01332 01333 int ast_custom_function_unregister(struct ast_custom_function *acf) 01334 { 01335 struct ast_custom_function *cur; 01336 01337 if (!acf) 01338 return -1; 01339 01340 AST_RWLIST_WRLOCK(&acf_root); 01341 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) { 01342 if (cur == acf) { 01343 AST_RWLIST_REMOVE_CURRENT(&acf_root, acflist); 01344 if (option_verbose > 1) 01345 ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name); 01346 break; 01347 } 01348 } 01349 AST_RWLIST_TRAVERSE_SAFE_END 01350 AST_RWLIST_UNLOCK(&acf_root); 01351 01352 return acf ? 0 : -1; 01353 } 01354 01355 int ast_custom_function_register(struct ast_custom_function *acf) 01356 { 01357 struct ast_custom_function *cur; 01358 01359 if (!acf) 01360 return -1; 01361 01362 AST_RWLIST_WRLOCK(&acf_root); 01363 01364 AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) { 01365 if (!strcmp(acf->name, cur->name)) { 01366 ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name); 01367 AST_RWLIST_UNLOCK(&acf_root); 01368 return -1; 01369 } 01370 } 01371 01372 /* Store in alphabetical order */ 01373 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) { 01374 if (strcasecmp(acf->name, cur->name) < 0) { 01375 AST_RWLIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist); 01376 break; 01377 } 01378 } 01379 AST_RWLIST_TRAVERSE_SAFE_END 01380 if (!cur) 01381 AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist); 01382 01383 AST_RWLIST_UNLOCK(&acf_root); 01384 01385 if (option_verbose > 1) 01386 ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name); 01387 01388 return 0; 01389 } 01390 01391 /*! \brief return a pointer to the arguments of the function, 01392 * and terminates the function name with '\\0' 01393 */ 01394 static char *func_args(char *function) 01395 { 01396 char *args = strchr(function, '('); 01397 01398 if (!args) 01399 ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n"); 01400 else { 01401 char *p; 01402 *args++ = '\0'; 01403 if ((p = strrchr(args, ')')) ) 01404 *p = '\0'; 01405 else 01406 ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n"); 01407 } 01408 return args; 01409 } 01410 01411 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len) 01412 { 01413 char *copy = ast_strdupa(function); 01414 char *args = func_args(copy); 01415 struct ast_custom_function *acfptr = ast_custom_function_find(copy); 01416 01417 if (acfptr == NULL) 01418 ast_log(LOG_ERROR, "Function %s not registered\n", copy); 01419 else if (!acfptr->read) 01420 ast_log(LOG_ERROR, "Function %s cannot be read\n", copy); 01421 else 01422 return acfptr->read(chan, copy, args, workspace, len); 01423 return -1; 01424 } 01425 01426 int ast_func_write(struct ast_channel *chan, const char *function, const char *value) 01427 { 01428 char *copy = ast_strdupa(function); 01429 char *args = func_args(copy); 01430 struct ast_custom_function *acfptr = ast_custom_function_find(copy); 01431 01432 if (acfptr == NULL) 01433 ast_log(LOG_ERROR, "Function %s not registered\n", copy); 01434 else if (!acfptr->write) 01435 ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy); 01436 else 01437 return acfptr->write(chan, copy, args, value); 01438 01439 return -1; 01440 } 01441 01442 static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count) 01443 { 01444 /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be 01445 zero-filled */ 01446 char *cp4; 01447 const char *tmp, *whereweare; 01448 int length, offset, offset2, isfunction; 01449 char *workspace = NULL; 01450 char *ltmp = NULL, *var = NULL; 01451 char *nextvar, *nextexp, *nextthing; 01452 char *vars, *vare; 01453 int pos, brackets, needsub, len; 01454 01455 whereweare=tmp=cp1; 01456 while (!ast_strlen_zero(whereweare) && count) { 01457 /* Assume we're copying the whole remaining string */ 01458 pos = strlen(whereweare); 01459 nextvar = NULL; 01460 nextexp = NULL; 01461 nextthing = strchr(whereweare, '$'); 01462 if (nextthing) { 01463 switch (nextthing[1]) { 01464 case '{': 01465 nextvar = nextthing; 01466 pos = nextvar - whereweare; 01467 break; 01468 case '[': 01469 nextexp = nextthing; 01470 pos = nextexp - whereweare; 01471 break; 01472 } 01473 } 01474 01475 if (pos) { 01476 /* Can't copy more than 'count' bytes */ 01477 if (pos > count) 01478 pos = count; 01479 01480 /* Copy that many bytes */ 01481 memcpy(cp2, whereweare, pos); 01482 01483 count -= pos; 01484 cp2 += pos; 01485 whereweare += pos; 01486 } 01487 01488 if (nextvar) { 01489 /* We have a variable. Find the start and end, and determine 01490 if we are going to have to recursively call ourselves on the 01491 contents */ 01492 vars = vare = nextvar + 2; 01493 brackets = 1; 01494 needsub = 0; 01495 01496 /* Find the end of it */ 01497 while (brackets && *vare) { 01498 if ((vare[0] == '$') && (vare[1] == '{')) { 01499 needsub++; 01500 } else if (vare[0] == '{') { 01501 brackets++; 01502 } else if (vare[0] == '}') { 01503 brackets--; 01504 } else if ((vare[0] == '$') && (vare[1] == '[')) 01505 needsub++; 01506 vare++; 01507 } 01508 if (brackets) 01509 ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n"); 01510 len = vare - vars - 1; 01511 01512 /* Skip totally over variable string */ 01513 whereweare += (len + 3); 01514 01515 if (!var) 01516 var = alloca(VAR_BUF_SIZE); 01517 01518 /* Store variable name (and truncate) */ 01519 ast_copy_string(var, vars, len + 1); 01520 01521 /* Substitute if necessary */ 01522 if (needsub) { 01523 if (!ltmp) 01524 ltmp = alloca(VAR_BUF_SIZE); 01525 01526 memset(ltmp, 0, VAR_BUF_SIZE); 01527 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1); 01528 vars = ltmp; 01529 } else { 01530 vars = var; 01531 } 01532 01533 if (!workspace) 01534 workspace = alloca(VAR_BUF_SIZE); 01535 01536 workspace[0] = '\0'; 01537 01538 parse_variable_name(vars, &offset, &offset2, &isfunction); 01539 if (isfunction) { 01540 /* Evaluate function */ 01541 cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace; 01542 if (option_debug) 01543 ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)"); 01544 } else { 01545 /* Retrieve variable value */ 01546 pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp); 01547 } 01548 if (cp4) { 01549 cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE); 01550 01551 length = strlen(cp4); 01552 if (length > count) 01553 length = count; 01554 memcpy(cp2, cp4, length); 01555 count -= length; 01556 cp2 += length; 01557 } 01558 } else if (nextexp) { 01559 /* We have an expression. Find the start and end, and determine 01560 if we are going to have to recursively call ourselves on the 01561 contents */ 01562 vars = vare = nextexp + 2; 01563 brackets = 1; 01564 needsub = 0; 01565 01566 /* Find the end of it */ 01567 while (brackets && *vare) { 01568 if ((vare[0] == '$') && (vare[1] == '[')) { 01569 needsub++; 01570 brackets++; 01571 vare++; 01572 } else if (vare[0] == '[') { 01573 brackets++; 01574 } else if (vare[0] == ']') { 01575 brackets--; 01576 } else if ((vare[0] == '$') && (vare[1] == '{')) { 01577 needsub++; 01578 vare++; 01579 } 01580 vare++; 01581 } 01582 if (brackets) 01583 ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n"); 01584 len = vare - vars - 1; 01585 01586 /* Skip totally over expression */ 01587 whereweare += (len + 3); 01588 01589 if (!var) 01590 var = alloca(VAR_BUF_SIZE); 01591 01592 /* Store variable name (and truncate) */ 01593 ast_copy_string(var, vars, len + 1); 01594 01595 /* Substitute if necessary */ 01596 if (needsub) { 01597 if (!ltmp) 01598 ltmp = alloca(VAR_BUF_SIZE); 01599 01600 memset(ltmp, 0, VAR_BUF_SIZE); 01601 pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1); 01602 vars = ltmp; 01603 } else { 01604 vars = var; 01605 } 01606 01607 length = ast_expr(vars, cp2, count); 01608 01609 if (length) { 01610 if (option_debug) 01611 ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2); 01612 count -= length; 01613 cp2 += length; 01614 } 01615 } else 01616 break; 01617 } 01618 } 01619 01620 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count) 01621 { 01622 pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count); 01623 } 01624 01625 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count) 01626 { 01627 pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count); 01628 } 01629 01630 static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e) 01631 { 01632 memset(passdata, 0, datalen); 01633 01634 /* No variables or expressions in e->data, so why scan it? */ 01635 if (e->data && !strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) { 01636 ast_copy_string(passdata, e->data, datalen); 01637 return; 01638 } 01639 01640 pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1); 01641 } 01642 01643 /*! \brief The return value depends on the action: 01644 * 01645 * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match, 01646 * and return 0 on failure, -1 on match; 01647 * E_FINDLABEL maps the label to a priority, and returns 01648 * the priority on success, ... XXX 01649 * E_SPAWN, spawn an application, 01650 * and return 0 on success, -1 on failure. 01651 */ 01652 static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, 01653 const char *context, const char *exten, int priority, 01654 const char *label, const char *callerid, enum ext_match_t action) 01655 { 01656 struct ast_exten *e; 01657 struct ast_app *app; 01658 int res; 01659 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ 01660 char passdata[EXT_DATA_SIZE]; 01661 01662 int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE); 01663 01664 ast_mutex_lock(&conlock); 01665 e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action); 01666 if (e) { 01667 if (matching_action) { 01668 ast_mutex_unlock(&conlock); 01669 return -1; /* success, we found it */ 01670 } else if (action == E_FINDLABEL) { /* map the label to a priority */ 01671 res = e->priority; 01672 ast_mutex_unlock(&conlock); 01673 return res; /* the priority we were looking for */ 01674 } else { /* spawn */ 01675 if (!e->cached_app) 01676 e->cached_app = pbx_findapp(e->app); 01677 app = e->cached_app; 01678 ast_mutex_unlock(&conlock); 01679 if (!app) { 01680 ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority); 01681 return -1; 01682 } 01683 if (c->context != context) 01684 ast_copy_string(c->context, context, sizeof(c->context)); 01685 if (c->exten != exten) 01686 ast_copy_string(c->exten, exten, sizeof(c->exten)); 01687 c->priority = priority; 01688 pbx_substitute_variables(passdata, sizeof(passdata), c, e); 01689 if (option_debug) { 01690 char atmp[80]; 01691 char atmp2[EXT_DATA_SIZE+100]; 01692 if (option_debug) 01693 ast_log(LOG_DEBUG, "Launching '%s'\n", app->name); 01694 snprintf(atmp, sizeof(atmp), "STACK-%s-%s-%d", context, exten, priority); 01695 snprintf(atmp2, sizeof(atmp2), "%s(\"%s\", \"%s\") %s", 01696 app->name, c->name, passdata, "in new stack"); 01697 pbx_builtin_setvar_helper(c, atmp, atmp2); 01698 } 01699 if (option_verbose > 2) { 01700 char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE]; 01701 ast_verbose( VERBOSE_PREFIX_3 "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n", 01702 exten, context, priority, 01703 term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)), 01704 term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)), 01705 term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)), 01706 "in new stack"); 01707 } 01708 manager_event(EVENT_FLAG_CALL, "Newexten", 01709 "Channel: %s\r\n" 01710 "Context: %s\r\n" 01711 "Extension: %s\r\n" 01712 "Priority: %d\r\n" 01713 "Application: %s\r\n" 01714 "AppData: %s\r\n" 01715 "Uniqueid: %s\r\n", 01716 c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid); 01717 return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */ 01718 } 01719 } else if (q.swo) { /* not found here, but in another switch */ 01720 ast_mutex_unlock(&conlock); 01721 if (matching_action) 01722 return -1; 01723 else { 01724 if (!q.swo->exec) { 01725 ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name); 01726 res = -1; 01727 } 01728 return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data); 01729 } 01730 } else { /* not found anywhere, see what happened */ 01731 ast_mutex_unlock(&conlock); 01732 switch (q.status) { 01733 case STATUS_NO_CONTEXT: 01734 if (!matching_action) 01735 ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context); 01736 break; 01737 case STATUS_NO_EXTENSION: 01738 if (!matching_action) 01739 ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context); 01740 break; 01741 case STATUS_NO_PRIORITY: 01742 if (!matching_action) 01743 ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context); 01744 break; 01745 case STATUS_NO_LABEL: 01746 if (context) 01747 ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context); 01748 break; 01749 default: 01750 if (option_debug) 01751 ast_log(LOG_DEBUG, "Shouldn't happen!\n"); 01752 } 01753 01754 return (matching_action) ? 0 : -1; 01755 } 01756 } 01757 01758 /*! \brief ast_hint_extension: Find hint for given extension in context */ 01759 static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten) 01760 { 01761 struct ast_exten *e; 01762 struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */ 01763 01764 ast_mutex_lock(&conlock); 01765 e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH); 01766 ast_mutex_unlock(&conlock); 01767 01768 return e; 01769 } 01770 01771 /*! \brief ast_extensions_state2: Check state of extension by using hints */ 01772 static int ast_extension_state2(struct ast_exten *e) 01773 { 01774 char hint[AST_MAX_EXTENSION]; 01775 char *cur, *rest; 01776 int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1; 01777 int busy = 0, inuse = 0, ring = 0; 01778 01779 if (!e) 01780 return -1; 01781 01782 ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint)); 01783 01784 rest = hint; /* One or more devices separated with a & character */ 01785 while ( (cur = strsep(&rest, "&")) ) { 01786 int res = ast_device_state(cur); 01787 switch (res) { 01788 case AST_DEVICE_NOT_INUSE: 01789 allunavailable = 0; 01790 allbusy = 0; 01791 allonhold = 0; 01792 break; 01793 case AST_DEVICE_INUSE: 01794 inuse = 1; 01795 allunavailable = 0; 01796 allfree = 0; 01797 allonhold = 0; 01798 break; 01799 case AST_DEVICE_RINGING: 01800 ring = 1; 01801 allunavailable = 0; 01802 allfree = 0; 01803 allonhold = 0; 01804 break; 01805 case AST_DEVICE_RINGINUSE: 01806 inuse = 1; 01807 ring = 1; 01808 allunavailable = 0; 01809 allfree = 0; 01810 allonhold = 0; 01811 break; 01812 case AST_DEVICE_ONHOLD: 01813 allunavailable = 0; 01814 allfree = 0; 01815 break; 01816 case AST_DEVICE_BUSY: 01817 allunavailable = 0; 01818 allfree = 0; 01819 allonhold = 0; 01820 busy = 1; 01821 break; 01822 case AST_DEVICE_UNAVAILABLE: 01823 case AST_DEVICE_INVALID: 01824 allbusy = 0; 01825 allfree = 0; 01826 allonhold = 0; 01827 break; 01828 default: 01829 allunavailable = 0; 01830 allbusy = 0; 01831 allfree = 0; 01832 allonhold = 0; 01833 } 01834 } 01835 01836 if (!inuse && ring) 01837 return AST_EXTENSION_RINGING; 01838 if (inuse && ring) 01839 return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING); 01840 if (inuse) 01841 return AST_EXTENSION_INUSE; 01842 if (allfree) 01843 return AST_EXTENSION_NOT_INUSE; 01844 if (allonhold) 01845 return AST_EXTENSION_ONHOLD; 01846 if (allbusy) 01847 return AST_EXTENSION_BUSY; 01848 if (allunavailable) 01849 return AST_EXTENSION_UNAVAILABLE; 01850 if (busy) 01851 return AST_EXTENSION_INUSE; 01852 01853 return AST_EXTENSION_NOT_INUSE; 01854 } 01855 01856 /*! \brief ast_extension_state2str: Return extension_state as string */ 01857 const char *ast_extension_state2str(int extension_state) 01858 { 01859 int i; 01860 01861 for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) { 01862 if (extension_states[i].extension_state == extension_state) 01863 return extension_states[i].text; 01864 } 01865 return "Unknown"; 01866 } 01867 01868 /*! \brief ast_extension_state: Check extension state for an extension by using hint */ 01869 int ast_extension_state(struct ast_channel *c, const char *context, const char *exten) 01870 { 01871 struct ast_exten *e; 01872 01873 e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */ 01874 if (!e) 01875 return -1; /* No hint, return -1 */ 01876 01877 return ast_extension_state2(e); /* Check all devices in the hint */ 01878 } 01879 01880 void ast_hint_state_changed(const char *device) 01881 { 01882 struct ast_hint *hint; 01883 01884 AST_RWLIST_RDLOCK(&hints); 01885 01886 AST_RWLIST_TRAVERSE(&hints, hint, list) { 01887 struct ast_state_cb *cblist; 01888 char buf[AST_MAX_EXTENSION]; 01889 char *parse = buf; 01890 char *cur; 01891 int state; 01892 01893 ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf)); 01894 while ( (cur = strsep(&parse, "&")) ) { 01895 if (!strcasecmp(cur, device)) 01896 break; 01897 } 01898 if (!cur) 01899 continue; 01900 01901 /* Get device state for this hint */ 01902 state = ast_extension_state2(hint->exten); 01903 01904 if ((state == -1) || (state == hint->laststate)) 01905 continue; 01906 01907 /* Device state changed since last check - notify the watchers */ 01908 01909 /* For general callbacks */ 01910 for (cblist = statecbs; cblist; cblist = cblist->next) 01911 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); 01912 01913 /* For extension callbacks */ 01914 for (cblist = hint->callbacks; cblist; cblist = cblist->next) 01915 cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); 01916 01917 hint->laststate = state; /* record we saw the change */ 01918 } 01919 01920 AST_RWLIST_UNLOCK(&hints); 01921 } 01922 01923 /*! \brief ast_extension_state_add: Add watcher for extension states */ 01924 int ast_extension_state_add(const char *context, const char *exten, 01925 ast_state_cb_type callback, void *data) 01926 { 01927 struct ast_hint *hint; 01928 struct ast_state_cb *cblist; 01929 struct ast_exten *e; 01930 01931 /* If there's no context and extension: add callback to statecbs list */ 01932 if (!context && !exten) { 01933 AST_RWLIST_WRLOCK(&hints); 01934 01935 for (cblist = statecbs; cblist; cblist = cblist->next) { 01936 if (cblist->callback == callback) { 01937 cblist->data = data; 01938 AST_RWLIST_UNLOCK(&hints); 01939 return 0; 01940 } 01941 } 01942 01943 /* Now insert the callback */ 01944 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { 01945 AST_RWLIST_UNLOCK(&hints); 01946 return -1; 01947 } 01948 cblist->id = 0; 01949 cblist->callback = callback; 01950 cblist->data = data; 01951 01952 cblist->next = statecbs; 01953 statecbs = cblist; 01954 01955 AST_RWLIST_UNLOCK(&hints); 01956 return 0; 01957 } 01958 01959 if (!context || !exten) 01960 return -1; 01961 01962 /* This callback type is for only one hint, so get the hint */ 01963 e = ast_hint_extension(NULL, context, exten); 01964 if (!e) { 01965 return -1; 01966 } 01967 01968 /* Find the hint in the list of hints */ 01969 AST_RWLIST_WRLOCK(&hints); 01970 01971 AST_RWLIST_TRAVERSE(&hints, hint, list) { 01972 if (hint->exten == e) 01973 break; 01974 } 01975 01976 if (!hint) { 01977 /* We have no hint, sorry */ 01978 AST_RWLIST_UNLOCK(&hints); 01979 return -1; 01980 } 01981 01982 /* Now insert the callback in the callback list */ 01983 if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { 01984 AST_RWLIST_UNLOCK(&hints); 01985 return -1; 01986 } 01987 cblist->id = stateid++; /* Unique ID for this callback */ 01988 cblist->callback = callback; /* Pointer to callback routine */ 01989 cblist->data = data; /* Data for the callback */ 01990 01991 cblist->next = hint->callbacks; 01992 hint->callbacks = cblist; 01993 01994 AST_RWLIST_UNLOCK(&hints); 01995 return cblist->id; 01996 } 01997 01998 /*! \brief ast_extension_state_del: Remove a watcher from the callback list */ 01999 int ast_extension_state_del(int id, ast_state_cb_type callback) 02000 { 02001 struct ast_state_cb **p_cur = NULL; /* address of pointer to us */ 02002 int ret = -1; 02003 02004 if (!id && !callback) 02005 return -1; 02006 02007 AST_RWLIST_WRLOCK(&hints); 02008 02009 if (!id) { /* id == 0 is a callback without extension */ 02010 for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) { 02011 if ((*p_cur)->callback == callback) 02012 break; 02013 } 02014 } else { /* callback with extension, find the callback based on ID */ 02015 struct ast_hint *hint; 02016 AST_RWLIST_TRAVERSE(&hints, hint, list) { 02017 for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) { 02018 if ((*p_cur)->id == id) 02019 break; 02020 } 02021 if (*p_cur) /* found in the inner loop */ 02022 break; 02023 } 02024 } 02025 if (p_cur && *p_cur) { 02026 struct ast_state_cb *cur = *p_cur; 02027 *p_cur = cur->next; 02028 free(cur); 02029 ret = 0; 02030 } 02031 AST_RWLIST_UNLOCK(&hints); 02032 return ret; 02033 } 02034 02035 /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */ 02036 static int ast_add_hint(struct ast_exten *e) 02037 { 02038 struct ast_hint *hint; 02039 02040 if (!e) 02041 return -1; 02042 02043 AST_RWLIST_WRLOCK(&hints); 02044 02045 /* Search if hint exists, do nothing */ 02046 AST_RWLIST_TRAVERSE(&hints, hint, list) { 02047 if (hint->exten == e) { 02048 AST_RWLIST_UNLOCK(&hints); 02049 if (option_debug > 1) 02050 ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); 02051 return -1; 02052 } 02053 } 02054 02055 if (option_debug > 1) 02056 ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); 02057 02058 if (!(hint = ast_calloc(1, sizeof(*hint)))) { 02059 AST_RWLIST_UNLOCK(&hints); 02060 return -1; 02061 } 02062 /* Initialize and insert new item at the top */ 02063 hint->exten = e; 02064 hint->laststate = ast_extension_state2(e); 02065 AST_RWLIST_INSERT_HEAD(&hints, hint, list); 02066 02067 AST_RWLIST_UNLOCK(&hints); 02068 return 0; 02069 } 02070 02071 /*! \brief ast_change_hint: Change hint for an extension */ 02072 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne) 02073 { 02074 struct ast_hint *hint; 02075 int res = -1; 02076 02077 AST_RWLIST_WRLOCK(&hints); 02078 AST_RWLIST_TRAVERSE(&hints, hint, list) { 02079 if (hint->exten == oe) { 02080 hint->exten = ne; 02081 res = 0; 02082 break; 02083 } 02084 } 02085 AST_RWLIST_UNLOCK(&hints); 02086 02087 return res; 02088 } 02089 02090 /*! \brief ast_remove_hint: Remove hint from extension */ 02091 static int ast_remove_hint(struct ast_exten *e) 02092 { 02093 /* Cleanup the Notifys if hint is removed */ 02094 struct ast_hint *hint; 02095 struct ast_state_cb *cblist, *cbprev; 02096 int res = -1; 02097 02098 if (!e) 02099 return -1; 02100 02101 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) { 02102 if (hint->exten == e) { 02103 cbprev = NULL; 02104 cblist = hint->callbacks; 02105 while (cblist) { 02106 /* Notify with -1 and remove all callbacks */ 02107 cbprev = cblist; 02108 cblist = cblist->next; 02109 cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data); 02110 free(cbprev); 02111 } 02112 hint->callbacks = NULL; 02113 AST_RWLIST_REMOVE_CURRENT(&hints, list); 02114 free(hint); 02115 res = 0; 02116 break; 02117 } 02118 } 02119 AST_RWLIST_TRAVERSE_SAFE_END 02120 02121 return res; 02122 } 02123 02124 02125 /*! \brief ast_get_hint: Get hint for channel */ 02126 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten) 02127 { 02128 struct ast_exten *e = ast_hint_extension(c, context, exten); 02129 02130 if (e) { 02131 if (hint) 02132 ast_copy_string(hint, ast_get_extension_app(e), hintsize); 02133 if (name) { 02134 const char *tmp = ast_get_extension_app_data(e); 02135 if (tmp) 02136 ast_copy_string(name, tmp, namesize); 02137 } 02138 return -1; 02139 } 02140 return 0; 02141 } 02142 02143 int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 02144 { 02145 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH); 02146 } 02147 02148 int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid) 02149 { 02150 return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL); 02151 } 02152 02153 int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid) 02154 { 02155 return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL); 02156 } 02157 02158 int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 02159 { 02160 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH); 02161 } 02162 02163 int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 02164 { 02165 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE); 02166 } 02167 02168 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 02169 { 02170 return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN); 02171 } 02172 02173 /* helper function to set extension and priority */ 02174 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri) 02175 { 02176 ast_copy_string(c->exten, exten, sizeof(c->exten)); 02177 c->priority = pri; 02178 } 02179 02180 /*! 02181 * \brief collect digits from the channel into the buffer, 02182 * return -1 on error, 0 on timeout or done. 02183 */ 02184 static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos) 02185 { 02186 int digit; 02187 02188 buf[pos] = '\0'; /* make sure it is properly terminated */ 02189 while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) { 02190 /* As long as we're willing to wait, and as long as it's not defined, 02191 keep reading digits until we can't possibly get a right answer anymore. */ 02192 digit = ast_waitfordigit(c, waittime * 1000); 02193 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) { 02194 c->_softhangup = 0; 02195 } else { 02196 if (!digit) /* No entry */ 02197 break; 02198 if (digit < 0) /* Error, maybe a hangup */ 02199 return -1; 02200 if (pos < buflen - 1) { /* XXX maybe error otherwise ? */ 02201 buf[pos++] = digit; 02202 buf[pos] = '\0'; 02203 } 02204 waittime = c->pbx->dtimeout; 02205 } 02206 } 02207 return 0; 02208 } 02209 02210 static int __ast_pbx_run(struct ast_channel *c) 02211 { 02212 int found = 0; /* set if we find at least one match */ 02213 int res = 0; 02214 int autoloopflag; 02215 int error = 0; /* set an error conditions */ 02216 02217 /* A little initial setup here */ 02218 if (c->pbx) { 02219 ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name); 02220 /* XXX and now what ? */ 02221 free(c->pbx); 02222 } 02223 if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx)))) 02224 return -1; 02225 if (c->amaflags) { 02226 if (!c->cdr) { 02227 c->cdr = ast_cdr_alloc(); 02228 if (!c->cdr) { 02229 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); 02230 free(c->pbx); 02231 return -1; 02232 } 02233 ast_cdr_init(c->cdr, c); 02234 } 02235 } 02236 /* Set reasonable defaults */ 02237 c->pbx->rtimeout = 10; 02238 c->pbx->dtimeout = 5; 02239 02240 autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */ 02241 ast_set_flag(c, AST_FLAG_IN_AUTOLOOP); 02242 02243 /* Start by trying whatever the channel is set to */ 02244 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) { 02245 /* If not successful fall back to 's' */ 02246 if (option_verbose > 1) 02247 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); 02248 /* XXX the original code used the existing priority in the call to 02249 * ast_exists_extension(), and reset it to 1 afterwards. 02250 * I believe the correct thing is to set it to 1 immediately. 02251 */ 02252 set_ext_pri(c, "s", 1); 02253 if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) { 02254 /* JK02: And finally back to default if everything else failed */ 02255 if (option_verbose > 1) 02256 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); 02257 ast_copy_string(c->context, "default", sizeof(c->context)); 02258 } 02259 } 02260 if (c->cdr && ast_tvzero(c->cdr->start)) 02261 ast_cdr_start(c->cdr); 02262 for (;;) { 02263 char dst_exten[256]; /* buffer to accumulate digits */ 02264 int pos = 0; /* XXX should check bounds */ 02265 int digit = 0; 02266 02267 /* loop on priorities in this context/exten */ 02268 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) { 02269 found = 1; 02270 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) { 02271 /* Something bad happened, or a hangup has been requested. */ 02272 if (strchr("0123456789ABCDEF*#", res)) { 02273 if (option_debug) 02274 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res); 02275 pos = 0; 02276 dst_exten[pos++] = digit = res; 02277 dst_exten[pos] = '\0'; 02278 break; 02279 } 02280 if (res == AST_PBX_KEEPALIVE) { 02281 if (option_debug) 02282 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name); 02283 if (option_verbose > 1) 02284 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name); 02285 error = 1; 02286 break; 02287 } 02288 if (option_debug) 02289 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); 02290 if (option_verbose > 1) 02291 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); 02292 if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) { 02293 c->_softhangup =0; 02294 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) { 02295 /* atimeout, nothing bad */ 02296 } else { 02297 if (c->cdr) 02298 ast_cdr_update(c); 02299 error = 1; 02300 break; 02301 } 02302 } 02303 if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) { 02304 set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */ 02305 /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */ 02306 c->whentohangup = 0; 02307 c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT; 02308 } else if (c->_softhangup) { 02309 if (option_debug) 02310 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n", 02311 c->exten, c->priority); 02312 error = 1; 02313 break; 02314 } 02315 c->priority++; 02316 } /* end while - from here on we can use 'break' to go out */ 02317 if (error) 02318 break; 02319 02320 /* XXX we get here on non-existing extension or a keypress or hangup ? */ 02321 02322 if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) { 02323 /* If there is no match at priority 1, it is not a valid extension anymore. 02324 * Try to continue at "i", 1 or exit if the latter does not exist. 02325 */ 02326 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) { 02327 if (option_verbose > 2) 02328 ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name); 02329 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten); 02330 set_ext_pri(c, "i", 1); 02331 } else { 02332 ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n", 02333 c->name, c->exten, c->context); 02334 error = 1; /* we know what to do with it */ 02335 break; 02336 } 02337 } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) { 02338 /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */ 02339 c->_softhangup = 0; 02340 } else { /* keypress received, get more digits for a full extension */ 02341 int waittime = 0; 02342 if (digit) 02343 waittime = c->pbx->dtimeout; 02344 else if (!autofallthrough) 02345 waittime = c->pbx->rtimeout; 02346 if (!waittime) { 02347 const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS"); 02348 if (!status) 02349 status = "UNKNOWN"; 02350 if (option_verbose > 2) 02351 ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status); 02352 if (!strcasecmp(status, "CONGESTION")) 02353 res = pbx_builtin_congestion(c, "10"); 02354 else if (!strcasecmp(status, "CHANUNAVAIL")) 02355 res = pbx_builtin_congestion(c, "10"); 02356 else if (!strcasecmp(status, "BUSY")) 02357 res = pbx_builtin_busy(c, "10"); 02358 error = 1; /* XXX disable message */ 02359 break; /* exit from the 'for' loop */ 02360 } 02361 02362 if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos)) 02363 break; 02364 if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */ 02365 set_ext_pri(c, dst_exten, 1); 02366 else { 02367 /* No such extension */ 02368 if (!ast_strlen_zero(dst_exten)) { 02369 /* An invalid extension */ 02370 if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) { 02371 if (option_verbose > 2) 02372 ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name); 02373 pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten); 02374 set_ext_pri(c, "i", 1); 02375 } else { 02376 ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context); 02377 found = 1; /* XXX disable message */ 02378 break; 02379 } 02380 } else { 02381 /* A simple timeout */ 02382 if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) { 02383 if (option_verbose > 2) 02384 ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name); 02385 set_ext_pri(c, "t", 1); 02386 } else { 02387 ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context); 02388 found = 1; /* XXX disable message */ 02389 break; 02390 } 02391 } 02392 } 02393 if (c->cdr) { 02394 if (option_verbose > 2) 02395 ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name); 02396 ast_cdr_update(c); 02397 } 02398 } 02399 } 02400 if (!found && !error) 02401 ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name); 02402 if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) { 02403 if (c->cdr && ast_opt_end_cdr_before_h_exten) 02404 ast_cdr_end(c->cdr); 02405 set_ext_pri(c, "h", 1); 02406 while (ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) { 02407 if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) { 02408 /* Something bad happened, or a hangup has been requested. */ 02409 if (option_debug) 02410 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); 02411 if (option_verbose > 1) 02412 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name); 02413 break; 02414 } 02415 c->priority++; 02416 } 02417 } 02418 ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP); 02419 02420 pbx_destroy(c->pbx); 02421 c->pbx = NULL; 02422 if (res != AST_PBX_KEEPALIVE) 02423 ast_hangup(c); 02424 return 0; 02425 } 02426 02427 /* Returns 0 on success, non-zero if call limit was reached */ 02428 static int increase_call_count(const struct ast_channel *c) 02429 { 02430 int failed = 0; 02431 double curloadavg; 02432 ast_mutex_lock(&maxcalllock); 02433 if (option_maxcalls) { 02434 if (countcalls >= option_maxcalls) { 02435 ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name); 02436 failed = -1; 02437 } 02438 } 02439 if (option_maxload) { 02440 getloadavg(&curloadavg, 1); 02441 if (curloadavg >= option_maxload) { 02442 ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg); 02443 failed = -1; 02444 } 02445 } 02446 if (!failed) 02447 countcalls++; 02448 ast_mutex_unlock(&maxcalllock); 02449 02450 return failed; 02451 } 02452 02453 static void decrease_call_count(void) 02454 { 02455 ast_mutex_lock(&maxcalllock); 02456 if (countcalls > 0) 02457 countcalls--; 02458 ast_mutex_unlock(&maxcalllock); 02459 } 02460 02461 static void destroy_exten(struct ast_exten *e) 02462 { 02463 if (e->priority == PRIORITY_HINT) 02464 ast_remove_hint(e); 02465 02466 if (e->datad) 02467 e->datad(e->data); 02468 free(e); 02469 } 02470 02471 static void *pbx_thread(void *data) 02472 { 02473 /* Oh joyeous kernel, we're a new thread, with nothing to do but 02474 answer this channel and get it going. 02475 */ 02476 /* NOTE: 02477 The launcher of this function _MUST_ increment 'countcalls' 02478 before invoking the function; it will be decremented when the 02479 PBX has finished running on the channel 02480 */ 02481 struct ast_channel *c = data; 02482 02483 __ast_pbx_run(c); 02484 decrease_call_count(); 02485 02486 pthread_exit(NULL); 02487 02488 return NULL; 02489 } 02490 02491 enum ast_pbx_result ast_pbx_start(struct ast_channel *c) 02492 { 02493 pthread_t t; 02494 pthread_attr_t attr; 02495 02496 if (!c) { 02497 ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n"); 02498 return AST_PBX_FAILED; 02499 } 02500 02501 if (increase_call_count(c)) 02502 return AST_PBX_CALL_LIMIT; 02503 02504 /* Start a new thread, and get something handling this channel. */ 02505 pthread_attr_init(&attr); 02506 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 02507 if (ast_pthread_create(&t, &attr, pbx_thread, c)) { 02508 ast_log(LOG_WARNING, "Failed to create new channel thread\n"); 02509 pthread_attr_destroy(&attr); 02510 return AST_PBX_FAILED; 02511 } 02512 pthread_attr_destroy(&attr); 02513 02514 return AST_PBX_SUCCESS; 02515 } 02516 02517 enum ast_pbx_result ast_pbx_run(struct ast_channel *c) 02518 { 02519 enum ast_pbx_result res = AST_PBX_SUCCESS; 02520 02521 if (increase_call_count(c)) 02522 return AST_PBX_CALL_LIMIT; 02523 02524 res = __ast_pbx_run(c); 02525 decrease_call_count(); 02526 02527 return res; 02528 } 02529 02530 int ast_active_calls(void) 02531 { 02532 return countcalls; 02533 } 02534 02535 int pbx_set_autofallthrough(int newval) 02536 { 02537 int oldval = autofallthrough; 02538 autofallthrough = newval; 02539 return oldval; 02540 } 02541 02542 /* lookup for a context with a given name, 02543 * return with conlock held if found, NULL if not found 02544 */ 02545 static struct ast_context *find_context_locked(const char *context) 02546 { 02547 struct ast_context *c = NULL; 02548 02549 ast_lock_contexts(); 02550 while ( (c = ast_walk_contexts(c)) ) { 02551 if (!strcmp(ast_get_context_name(c), context)) 02552 return c; 02553 } 02554 ast_unlock_contexts(); 02555 02556 return NULL; 02557 } 02558 02559 /* 02560 * This function locks contexts list by &conlist, search for the right context 02561 * structure, leave context list locked and call ast_context_remove_include2 02562 * which removes include, unlock contexts list and return ... 02563 */ 02564 int ast_context_remove_include(const char *context, const char *include, const char *registrar) 02565 { 02566 int ret = -1; 02567 struct ast_context *c = find_context_locked(context); 02568 02569 if (c) { 02570 /* found, remove include from this context ... */ 02571 ret = ast_context_remove_include2(c, include, registrar); 02572 ast_unlock_contexts(); 02573 } 02574 return ret; 02575 } 02576 02577 /* 02578 * When we call this function, &conlock lock must be locked, because when 02579 * we giving *con argument, some process can remove/change this context 02580 * and after that there can be segfault. 02581 * 02582 * This function locks given context, removes include, unlock context and 02583 * return. 02584 */ 02585 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar) 02586 { 02587 struct ast_include *i, *pi = NULL; 02588 int ret = -1; 02589 02590 ast_mutex_lock(&con->lock); 02591 02592 /* find our include */ 02593 for (i = con->includes; i; pi = i, i = i->next) { 02594 if (!strcmp(i->name, include) && 02595 (!registrar || !strcmp(i->registrar, registrar))) { 02596 /* remove from list */ 02597 if (pi) 02598 pi->next = i->next; 02599 else 02600 con->includes = i->next; 02601 /* free include and return */ 02602 free(i); 02603 ret = 0; 02604 break; 02605 } 02606 } 02607 02608 ast_mutex_unlock(&con->lock); 02609 return ret; 02610 } 02611 02612 /*! 02613 * \note This function locks contexts list by &conlist, search for the rigt context 02614 * structure, leave context list locked and call ast_context_remove_switch2 02615 * which removes switch, unlock contexts list and return ... 02616 */ 02617 int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar) 02618 { 02619 int ret = -1; /* default error return */ 02620 struct ast_context *c = find_context_locked(context); 02621 02622 if (c) { 02623 /* remove switch from this context ... */ 02624 ret = ast_context_remove_switch2(c, sw, data, registrar); 02625 ast_unlock_contexts(); 02626 } 02627 return ret; 02628 } 02629 02630 /*! 02631 * \brief This function locks given context, removes switch, unlock context and 02632 * return. 02633 * \note When we call this function, &conlock lock must be locked, because when 02634 * we giving *con argument, some process can remove/change this context 02635 * and after that there can be segfault. 02636 * 02637 */ 02638 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar) 02639 { 02640 struct ast_sw *i; 02641 int ret = -1; 02642 02643 ast_mutex_lock(&con->lock); 02644 02645 /* walk switches */ 02646 AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) { 02647 if (!strcmp(i->name, sw) && !strcmp(i->data, data) && 02648 (!registrar || !strcmp(i->registrar, registrar))) { 02649 /* found, remove from list */ 02650 AST_LIST_REMOVE_CURRENT(&con->alts, list); 02651 free(i); /* free switch and return */ 02652 ret = 0; 02653 break; 02654 } 02655 } 02656 AST_LIST_TRAVERSE_SAFE_END 02657 02658 ast_mutex_unlock(&con->lock); 02659 02660 return ret; 02661 } 02662 02663 /* 02664 * \note This functions lock contexts list, search for the right context, 02665 * call ast_context_remove_extension2, unlock contexts list and return. 02666 * In this function we are using 02667 */ 02668 int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar) 02669 { 02670 int ret = -1; /* default error return */ 02671 struct ast_context *c = find_context_locked(context); 02672 02673 if (c) { /* ... remove extension ... */ 02674 ret = ast_context_remove_extension2(c, extension, priority, registrar); 02675 ast_unlock_contexts(); 02676 } 02677 return ret; 02678 } 02679 02680 /*! 02681 * \brief This functionc locks given context, search for the right extension and 02682 * fires out all peer in this extensions with given priority. If priority 02683 * is set to 0, all peers are removed. After that, unlock context and 02684 * return. 02685 * \note When do you want to call this function, make sure that &conlock is locked, 02686 * because some process can handle with your *con context before you lock 02687 * it. 02688 * 02689 */ 02690 int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar) 02691 { 02692 struct ast_exten *exten, *prev_exten = NULL; 02693 struct ast_exten *peer; 02694 02695 ast_mutex_lock(&con->lock); 02696 02697 /* scan the extension list to find matching extension-registrar */ 02698 for (exten = con->root; exten; prev_exten = exten, exten = exten->next) { 02699 if (!strcmp(exten->exten, extension) && 02700 (!registrar || !strcmp(exten->registrar, registrar))) 02701 break; 02702 } 02703 if (!exten) { 02704 /* we can't find right extension */ 02705 ast_mutex_unlock(&con->lock); 02706 return -1; 02707 } 02708 02709 /* should we free all peers in this extension? (priority == 0)? */ 02710 if (priority == 0) { 02711 /* remove this extension from context list */ 02712 if (prev_exten) 02713 prev_exten->next = exten->next; 02714 else 02715 con->root = exten->next; 02716 02717 /* fire out all peers */ 02718 while ( (peer = exten) ) { 02719 exten = peer->peer; /* prepare for next entry */ 02720 destroy_exten(peer); 02721 } 02722 } else { 02723 /* scan the priority list to remove extension with exten->priority == priority */ 02724 struct ast_exten *previous_peer = NULL; 02725 02726 for (peer = exten; peer; previous_peer = peer, peer = peer->peer) { 02727 if (peer->priority == priority && 02728 (!registrar || !strcmp(peer->registrar, registrar) )) 02729 break; /* found our priority */ 02730 } 02731 if (!peer) { /* not found */ 02732 ast_mutex_unlock(&con->lock); 02733 return -1; 02734 } 02735 /* we are first priority extension? */ 02736 if (!previous_peer) { 02737 /* 02738 * We are first in the priority chain, so must update the extension chain. 02739 * The next node is either the next priority or the next extension 02740 */ 02741 struct ast_exten *next_node = peer->peer ? peer->peer : peer->next; 02742 02743 if (!prev_exten) /* change the root... */ 02744 con->root = next_node; 02745 else 02746 prev_exten->next = next_node; /* unlink */ 02747 if (peer->peer) /* XXX update the new head of the pri list */ 02748 peer->peer->next = peer->next; 02749 } else { /* easy, we are not first priority in extension */ 02750 previous_peer->peer = peer->peer; 02751 } 02752 02753 /* now, free whole priority extension */ 02754 destroy_exten(peer); 02755 /* XXX should we return -1 ? */ 02756 } 02757 ast_mutex_unlock(&con->lock); 02758 return 0; 02759 } 02760 02761 02762 /*! 02763 * \note This function locks contexts list by &conlist, searches for the right context 02764 * structure, and locks the macrolock mutex in that context. 02765 * macrolock is used to limit a macro to be executed by one call at a time. 02766 */ 02767 int ast_context_lockmacro(const char *context) 02768 { 02769 struct ast_context *c = NULL; 02770 int ret = -1; 02771 02772 ast_lock_contexts(); 02773 02774 while ((c = ast_walk_contexts(c))) { 02775 if (!strcmp(ast_get_context_name(c), context)) { 02776 ret = 0; 02777 break; 02778 } 02779 } 02780 02781 ast_unlock_contexts(); 02782 02783 /* if we found context, lock macrolock */ 02784 if (ret == 0) 02785 ret = ast_mutex_lock(&c->macrolock); 02786 02787 return ret; 02788 } 02789 02790 /*! 02791 * \note This function locks contexts list by &conlist, searches for the right context 02792 * structure, and unlocks the macrolock mutex in that context. 02793 * macrolock is used to limit a macro to be executed by one call at a time. 02794 */ 02795 int ast_context_unlockmacro(const char *context) 02796 { 02797 struct ast_context *c = NULL; 02798 int ret = -1; 02799 02800 ast_lock_contexts(); 02801 02802 while ((c = ast_walk_contexts(c))) { 02803 if (!strcmp(ast_get_context_name(c), context)) { 02804 ret = 0; 02805 break; 02806 } 02807 } 02808 02809 ast_unlock_contexts(); 02810 02811 /* if we found context, unlock macrolock */ 02812 if (ret == 0) 02813 ret = ast_mutex_unlock(&c->macrolock); 02814 02815 return ret; 02816 } 02817 02818 /*! \brief Dynamically register a new dial plan application */ 02819 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description) 02820 { 02821 struct ast_app *tmp, *cur = NULL; 02822 char tmps[80]; 02823 int length; 02824 02825 AST_RWLIST_WRLOCK(&apps); 02826 AST_RWLIST_TRAVERSE(&apps, tmp, list) { 02827 if (!strcasecmp(app, tmp->name)) { 02828 ast_log(LOG_WARNING, "Already have an application '%s'\n", app); 02829 AST_RWLIST_UNLOCK(&apps); 02830 return -1; 02831 } 02832 } 02833 02834 length = sizeof(*tmp) + strlen(app) + 1; 02835 02836 if (!(tmp = ast_calloc(1, length))) { 02837 AST_RWLIST_UNLOCK(&apps); 02838 return -1; 02839 } 02840 02841 strcpy(tmp->name, app); 02842 tmp->execute = execute; 02843 tmp->synopsis = synopsis; 02844 tmp->description = description; 02845 02846 /* Store in alphabetical order */ 02847 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) { 02848 if (strcasecmp(tmp->name, cur->name) < 0) { 02849 AST_RWLIST_INSERT_BEFORE_CURRENT(&apps, tmp, list); 02850 break; 02851 } 02852 } 02853 AST_RWLIST_TRAVERSE_SAFE_END 02854 if (!cur) 02855 AST_RWLIST_INSERT_TAIL(&apps, tmp, list); 02856 02857 if (option_verbose > 1) 02858 ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps))); 02859 02860 AST_RWLIST_UNLOCK(&apps); 02861 02862 return 0; 02863 } 02864 02865 /* 02866 * Append to the list. We don't have a tail pointer because we need 02867 * to scan the list anyways to check for duplicates during insertion. 02868 */ 02869 int ast_register_switch(struct ast_switch *sw) 02870 { 02871 struct ast_switch *tmp; 02872 02873 AST_RWLIST_WRLOCK(&switches); 02874 AST_RWLIST_TRAVERSE(&switches, tmp, list) { 02875 if (!strcasecmp(tmp->name, sw->name)) { 02876 AST_RWLIST_UNLOCK(&switches); 02877 ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name); 02878 return -1; 02879 } 02880 } 02881 AST_RWLIST_INSERT_TAIL(&switches, sw, list); 02882 AST_RWLIST_UNLOCK(&switches); 02883 02884 return 0; 02885 } 02886 02887 void ast_unregister_switch(struct ast_switch *sw) 02888 { 02889 AST_RWLIST_WRLOCK(&switches); 02890 AST_RWLIST_REMOVE(&switches, sw, list); 02891 AST_RWLIST_UNLOCK(&switches); 02892 } 02893 02894 /* 02895 * Help for CLI commands ... 02896 */ 02897 static char show_applications_help[] = 02898 "Usage: core show applications [{like|describing} <text>]\n" 02899 " List applications which are currently available.\n" 02900 " If 'like', <text> will be a substring of the app name\n" 02901 " If 'describing', <text> will be a substring of the description\n"; 02902 02903 static char show_functions_help[] = 02904 "Usage: core show functions [like <text>]\n" 02905 " List builtin functions, optionally only those matching a given string\n"; 02906 02907 static char show_switches_help[] = 02908 "Usage: core show switches\n" 02909 " List registered switches\n"; 02910 02911 static char show_hints_help[] = 02912 "Usage: core show hints\n" 02913 " List registered hints\n"; 02914 02915 static char show_globals_help[] = 02916 "Usage: core show globals\n" 02917 " List current global dialplan variables and their values\n"; 02918 02919 static char show_application_help[] = 02920 "Usage: core show application <application> [<application> [<application> [...]]]\n" 02921 " Describes a particular application.\n"; 02922 02923 static char show_function_help[] = 02924 "Usage: core show function <function>\n" 02925 " Describe a particular dialplan function.\n"; 02926 02927 static char show_dialplan_help[] = 02928 "Usage: core show dialplan [exten@][context]\n" 02929 " Show dialplan\n"; 02930 02931 static char set_global_help[] = 02932 "Usage: core set global <name> <value>\n" 02933 " Set global dialplan variable <name> to <value>\n"; 02934 02935 02936 /* 02937 * \brief 'show application' CLI command implementation functions ... 02938 */ 02939 02940 /* 02941 * There is a possibility to show informations about more than one 02942 * application at one time. You can type 'show application Dial Echo' and 02943 * you will see informations about these two applications ... 02944 */ 02945 static char *complete_show_application(const char *line, const char *word, int pos, int state) 02946 { 02947 struct ast_app *a; 02948 char *ret = NULL; 02949 int which = 0; 02950 int wordlen = strlen(word); 02951 02952 /* return the n-th [partial] matching entry */ 02953 AST_RWLIST_RDLOCK(&apps); 02954 AST_RWLIST_TRAVERSE(&apps, a, list) { 02955 if (!strncasecmp(word, a->name, wordlen) && ++which > state) { 02956 ret = strdup(a->name); 02957 break; 02958 } 02959 } 02960 AST_RWLIST_UNLOCK(&apps); 02961 02962 return ret; 02963 } 02964 02965 static int handle_show_application(int fd, int argc, char *argv[]) 02966 { 02967 struct ast_app *a; 02968 int app, no_registered_app = 1; 02969 02970 if (argc < 4) 02971 return RESULT_SHOWUSAGE; 02972 02973 /* ... go through all applications ... */ 02974 AST_RWLIST_RDLOCK(&apps); 02975 AST_RWLIST_TRAVERSE(&apps, a, list) { 02976 /* ... compare this application name with all arguments given 02977 * to 'show application' command ... */ 02978 for (app = 3; app < argc; app++) { 02979 if (!strcasecmp(a->name, argv[app])) { 02980 /* Maximum number of characters added by terminal coloring is 22 */ 02981 char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40]; 02982 char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL; 02983 int synopsis_size, description_size; 02984 02985 no_registered_app = 0; 02986 02987 if (a->synopsis) 02988 synopsis_size = strlen(a->synopsis) + 23; 02989 else 02990 synopsis_size = strlen("Not available") + 23; 02991 synopsis = alloca(synopsis_size); 02992 02993 if (a->description) 02994 description_size = strlen(a->description) + 23; 02995 else 02996 description_size = strlen("Not available") + 23; 02997 description = alloca(description_size); 02998 02999 if (synopsis && description) { 03000 snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", a->name); 03001 term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22); 03002 term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40); 03003 term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40); 03004 term_color(synopsis, 03005 a->synopsis ? a->synopsis : "Not available", 03006 COLOR_CYAN, 0, synopsis_size); 03007 term_color(description, 03008 a->description ? a->description : "Not available", 03009 COLOR_CYAN, 0, description_size); 03010 03011 ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description); 03012 } else { 03013 /* ... one of our applications, show info ...*/ 03014 ast_cli(fd,"\n -= Info about application '%s' =- \n\n" 03015 "[Synopsis]\n %s\n\n" 03016 "[Description]\n%s\n", 03017 a->name, 03018 a->synopsis ? a->synopsis : "Not available", 03019 a->description ? a->description : "Not available"); 03020 } 03021 } 03022 } 03023 } 03024 AST_RWLIST_UNLOCK(&apps); 03025 03026 /* we found at least one app? no? */ 03027 if (no_registered_app) { 03028 ast_cli(fd, "Your application(s) is (are) not registered\n"); 03029 return RESULT_FAILURE; 03030 } 03031 03032 return RESULT_SUCCESS; 03033 } 03034 03035 /*! \brief handle_show_hints: CLI support for listing registered dial plan hints */ 03036 static int handle_show_hints(int fd, int argc, char *argv[]) 03037 { 03038 struct ast_hint *hint; 03039 int num = 0; 03040 int watchers; 03041 struct ast_state_cb *watcher; 03042 03043 AST_RWLIST_RDLOCK(&hints); 03044 if (AST_RWLIST_EMPTY(&hints)) { 03045 ast_cli(fd, "There are no registered dialplan hints\n"); 03046 AST_RWLIST_UNLOCK(&hints); 03047 return RESULT_SUCCESS; 03048 } 03049 /* ... we have hints ... */ 03050 ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n"); 03051 AST_RWLIST_TRAVERSE(&hints, hint, list) { 03052 watchers = 0; 03053 for (watcher = hint->callbacks; watcher; watcher = watcher->next) 03054 watchers++; 03055 ast_cli(fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n", 03056 ast_get_extension_name(hint->exten), 03057 ast_get_context_name(ast_get_extension_context(hint->exten)), 03058 ast_get_extension_app(hint->exten), 03059 ast_extension_state2str(hint->laststate), watchers); 03060 num++; 03061 } 03062 ast_cli(fd, "----------------\n"); 03063 ast_cli(fd, "- %d hints registered\n", num); 03064 AST_RWLIST_UNLOCK(&hints); 03065 return RESULT_SUCCESS; 03066 } 03067 03068 /*! \brief handle_show_switches: CLI support for listing registered dial plan switches */ 03069 static int handle_show_switches(int fd, int argc, char *argv[]) 03070 { 03071 struct ast_switch *sw; 03072 03073 AST_RWLIST_RDLOCK(&switches); 03074 03075 if (AST_RWLIST_EMPTY(&switches)) { 03076 AST_RWLIST_UNLOCK(&switches); 03077 ast_cli(fd, "There are no registered alternative switches\n"); 03078 return RESULT_SUCCESS; 03079 } 03080 03081 ast_cli(fd, "\n -= Registered Asterisk Alternative Switches =-\n"); 03082 AST_RWLIST_TRAVERSE(&switches, sw, list) 03083 ast_cli(fd, "%s: %s\n", sw->name, sw->description); 03084 03085 AST_RWLIST_UNLOCK(&switches); 03086 03087 return RESULT_SUCCESS; 03088 } 03089 03090 static int handle_show_applications(int fd, int argc, char *argv[]) 03091 { 03092 struct ast_app *a; 03093 int like = 0, describing = 0; 03094 int total_match = 0; /* Number of matches in like clause */ 03095 int total_apps = 0; /* Number of apps registered */ 03096 03097 AST_RWLIST_RDLOCK(&apps); 03098 03099 if (AST_RWLIST_EMPTY(&apps)) { 03100 ast_cli(fd, "There are no registered applications\n"); 03101 AST_RWLIST_UNLOCK(&apps); 03102 return -1; 03103 } 03104 03105 /* core list applications like <keyword> */ 03106 if ((argc == 5) && (!strcmp(argv[3], "like"))) { 03107 like = 1; 03108 } else if ((argc > 4) && (!strcmp(argv[3], "describing"))) { 03109 describing = 1; 03110 } 03111 03112 /* core list applications describing <keyword1> [<keyword2>] [...] */ 03113 if ((!like) && (!describing)) { 03114 ast_cli(fd, " -= Registered Asterisk Applications =-\n"); 03115 } else { 03116 ast_cli(fd, " -= Matching Asterisk Applications =-\n"); 03117 } 03118 03119 AST_RWLIST_TRAVERSE(&apps, a, list) { 03120 int printapp = 0; 03121 total_apps++; 03122 if (like) { 03123 if (strcasestr(a->name, argv[4])) { 03124 printapp = 1; 03125 total_match++; 03126 } 03127 } else if (describing) { 03128 if (a->description) { 03129 /* Match all words on command line */ 03130 int i; 03131 printapp = 1; 03132 for (i = 4; i < argc; i++) { 03133 if (!strcasestr(a->description, argv[i])) { 03134 printapp = 0; 03135 } else { 03136 total_match++; 03137 } 03138 } 03139 } 03140 } else { 03141 printapp = 1; 03142 } 03143 03144 if (printapp) { 03145 ast_cli(fd," %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>"); 03146 } 03147 } 03148 if ((!like) && (!describing)) { 03149 ast_cli(fd, " -= %d Applications Registered =-\n",total_apps); 03150 } else { 03151 ast_cli(fd, " -= %d Applications Matching =-\n",total_match); 03152 } 03153 03154 AST_RWLIST_UNLOCK(&apps); 03155 03156 return RESULT_SUCCESS; 03157 } 03158 03159 static char *complete_show_applications(const char *line, const char *word, int pos, int state) 03160 { 03161 static char* choices[] = { "like", "describing", NULL }; 03162 03163 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state); 03164 } 03165 03166 /* 03167 * 'show dialplan' CLI command implementation functions ... 03168 */ 03169 static char *complete_show_dialplan_context(const char *line, const char *word, int pos, 03170 int state) 03171 { 03172 struct ast_context *c = NULL; 03173 char *ret = NULL; 03174 int which = 0; 03175 int wordlen; 03176 03177 /* we are do completion of [exten@]context on second position only */ 03178 if (pos != 2) 03179 return NULL; 03180 03181 ast_lock_contexts(); 03182 03183 wordlen = strlen(word); 03184 03185 /* walk through all contexts and return the n-th match */ 03186 while ( (c = ast_walk_contexts(c)) ) { 03187 if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) { 03188 ret = ast_strdup(ast_get_context_name(c)); 03189 break; 03190 } 03191 } 03192 03193 ast_unlock_contexts(); 03194 03195 return ret; 03196 } 03197 03198 /*! \brief Counters for the show dialplan manager command */ 03199 struct dialplan_counters { 03200 int total_items; 03201 int total_context; 03202 int total_exten; 03203 int total_prio; 03204 int context_existence; 03205 int extension_existence; 03206 }; 03207 03208 /*! \brief helper function to print an extension */ 03209 static void print_ext(struct ast_exten *e, char * buf, int buflen) 03210 { 03211 int prio = ast_get_extension_priority(e); 03212 if (prio == PRIORITY_HINT) { 03213 snprintf(buf, buflen, "hint: %s", 03214 ast_get_extension_app(e)); 03215 } else { 03216 snprintf(buf, buflen, "%d. %s(%s)", 03217 prio, ast_get_extension_app(e), 03218 (char *)ast_get_extension_app_data(e)); 03219 } 03220 } 03221 03222 /* XXX not verified */ 03223 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[]) 03224 { 03225 struct ast_context *c = NULL; 03226 int res = 0, old_total_exten = dpc->total_exten; 03227 03228 ast_lock_contexts(); 03229 03230 /* walk all contexts ... */ 03231 while ( (c = ast_walk_contexts(c)) ) { 03232 struct ast_exten *e; 03233 struct ast_include *i; 03234 struct ast_ignorepat *ip; 03235 char buf[256], buf2[256]; 03236 int context_info_printed = 0; 03237 03238 if (context && strcmp(ast_get_context_name(c), context)) 03239 continue; /* skip this one, name doesn't match */ 03240 03241 dpc->context_existence = 1; 03242 03243 ast_lock_context(c); 03244 03245 /* are we looking for exten too? if yes, we print context 03246 * only if we find our extension. 03247 * Otherwise print context even if empty ? 03248 * XXX i am not sure how the rinclude is handled. 03249 * I think it ought to go inside. 03250 */ 03251 if (!exten) { 03252 dpc->total_context++; 03253 ast_cli(fd, "[ Context '%s' created by '%s' ]\n", 03254 ast_get_context_name(c), ast_get_context_registrar(c)); 03255 context_info_printed = 1; 03256 } 03257 03258 /* walk extensions ... */ 03259 e = NULL; 03260 while ( (e = ast_walk_context_extensions(c, e)) ) { 03261 struct ast_exten *p; 03262 03263 if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) 03264 continue; /* skip, extension match failed */ 03265 03266 dpc->extension_existence = 1; 03267 03268 /* may we print context info? */ 03269 if (!context_info_printed) { 03270 dpc->total_context++; 03271 if (rinclude) { /* TODO Print more info about rinclude */ 03272 ast_cli(fd, "[ Included context '%s' created by '%s' ]\n", 03273 ast_get_context_name(c), ast_get_context_registrar(c)); 03274 } else { 03275 ast_cli(fd, "[ Context '%s' created by '%s' ]\n", 03276 ast_get_context_name(c), ast_get_context_registrar(c)); 03277 } 03278 context_info_printed = 1; 03279 } 03280 dpc->total_prio++; 03281 03282 /* write extension name and first peer */ 03283 if (e->matchcid) 03284 snprintf(buf, sizeof(buf), "'%s' (CID match '%s') => ", ast_get_extension_name(e), e->cidmatch); 03285 else 03286 snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e)); 03287 03288 print_ext(e, buf2, sizeof(buf2)); 03289 03290 ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2, 03291 ast_get_extension_registrar(e)); 03292 03293 dpc->total_exten++; 03294 /* walk next extension peers */ 03295 p = e; /* skip the first one, we already got it */ 03296 while ( (p = ast_walk_extension_priorities(e, p)) ) { 03297 const char *el = ast_get_extension_label(p); 03298 dpc->total_prio++; 03299 if (el) 03300 snprintf(buf, sizeof(buf), " [%s]", el); 03301 else 03302 buf[0] = '\0'; 03303 print_ext(p, buf2, sizeof(buf2)); 03304 03305 ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2, 03306 ast_get_extension_registrar(p)); 03307 } 03308 } 03309 03310 /* walk included and write info ... */ 03311 i = NULL; 03312 while ( (i = ast_walk_context_includes(c, i)) ) { 03313 snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i)); 03314 if (exten) { 03315 /* Check all includes for the requested extension */ 03316 if (includecount >= AST_PBX_MAX_STACK) { 03317 ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n"); 03318 } else { 03319 int dupe=0; 03320 int x; 03321 for (x=0;x<includecount;x++) { 03322 if (!strcasecmp(includes[x], ast_get_include_name(i))) { 03323 dupe++; 03324 break; 03325 } 03326 } 03327 if (!dupe) { 03328 includes[includecount] = ast_get_include_name(i); 03329 show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes); 03330 } else { 03331 ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context); 03332 } 03333 } 03334 } else { 03335 ast_cli(fd, " Include => %-45s [%s]\n", 03336 buf, ast_get_include_registrar(i)); 03337 } 03338 } 03339 03340 /* walk ignore patterns and write info ... */ 03341 ip = NULL; 03342 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) { 03343 const char *ipname = ast_get_ignorepat_name(ip); 03344 char ignorepat[AST_MAX_EXTENSION]; 03345 snprintf(buf, sizeof(buf), "'%s'", ipname); 03346 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname); 03347 if (!exten || ast_extension_match(ignorepat, exten)) { 03348 ast_cli(fd, " Ignore pattern => %-45s [%s]\n", 03349 buf, ast_get_ignorepat_registrar(ip)); 03350 } 03351 } 03352 if (!rinclude) { 03353 struct ast_sw *sw = NULL; 03354 while ( (sw = ast_walk_context_switches(c, sw)) ) { 03355 snprintf(buf, sizeof(buf), "'%s/%s'", 03356 ast_get_switch_name(sw), 03357 ast_get_switch_data(sw)); 03358 ast_cli(fd, " Alt. Switch => %-45s [%s]\n", 03359 buf, ast_get_switch_registrar(sw)); 03360 } 03361 } 03362 03363 ast_unlock_context(c); 03364 03365 /* if we print something in context, make an empty line */ 03366 if (context_info_printed) 03367 ast_cli(fd, "\r\n"); 03368 } 03369 ast_unlock_contexts(); 03370 03371 return (dpc->total_exten == old_total_exten) ? -1 : res; 03372 } 03373 03374 static int handle_show_dialplan(int fd, int argc, char *argv[]) 03375 { 03376 char *exten = NULL, *context = NULL; 03377 /* Variables used for different counters */ 03378 struct dialplan_counters counters; 03379 03380 const char *incstack[AST_PBX_MAX_STACK]; 03381 memset(&counters, 0, sizeof(counters)); 03382 03383 if (argc != 2 && argc != 3) 03384 return RESULT_SHOWUSAGE; 03385 03386 /* we obtain [exten@]context? if yes, split them ... */ 03387 if (argc == 3) { 03388 if (strchr(argv[2], '@')) { /* split into exten & context */ 03389 context = ast_strdupa(argv[2]); 03390 exten = strsep(&context, "@"); 03391 /* change empty strings to NULL */ 03392 if (ast_strlen_zero(exten)) 03393 exten = NULL; 03394 } else { /* no '@' char, only context given */ 03395 context = argv[2]; 03396 } 03397 if (ast_strlen_zero(context)) 03398 context = NULL; 03399 } 03400 /* else Show complete dial plan, context and exten are NULL */ 03401 show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack); 03402 03403 /* check for input failure and throw some error messages */ 03404 if (context && !counters.context_existence) { 03405 ast_cli(fd, "There is no existence of '%s' context\n", context); 03406 return RESULT_FAILURE; 03407 } 03408 03409 if (exten && !counters.extension_existence) { 03410 if (context) 03411 ast_cli(fd, "There is no existence of %s@%s extension\n", 03412 exten, context); 03413 else 03414 ast_cli(fd, 03415 "There is no existence of '%s' extension in all contexts\n", 03416 exten); 03417 return RESULT_FAILURE; 03418 } 03419 03420 ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n", 03421 counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions", 03422 counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities", 03423 counters.total_context, counters.total_context == 1 ? "context" : "contexts"); 03424 03425 /* everything ok */ 03426 return RESULT_SUCCESS; 03427 } 03428 03429 /*! \brief Send ack once */ 03430 static void manager_dpsendack(struct mansession *s, const struct message *m) 03431 { 03432 astman_send_listack(s, m, "DialPlan list will follow", "start"); 03433 } 03434 03435 /*! \brief Show dialplan extensions 03436 * XXX this function is similar but not exactly the same as the CLI's 03437 * show dialplan. Must check whether the difference is intentional or not. 03438 */ 03439 static int manager_show_dialplan_helper(struct mansession *s, const struct message *m, 03440 const char *actionidtext, const char *context, 03441 const char *exten, struct dialplan_counters *dpc, 03442 struct ast_include *rinclude) 03443 { 03444 struct ast_context *c; 03445 int res=0, old_total_exten = dpc->total_exten; 03446 03447 if (ast_strlen_zero(exten)) 03448 exten = NULL; 03449 if (ast_strlen_zero(context)) 03450 context = NULL; 03451 03452 if (option_debug > 2) 03453 ast_log(LOG_DEBUG, "manager_show_dialplan: Context: -%s- Extension: -%s-\n", context, exten); 03454 03455 /* try to lock contexts */ 03456 if (ast_lock_contexts()) { 03457 astman_send_error(s, m, "Failed to lock contexts\r\n"); 03458 ast_log(LOG_WARNING, "Failed to lock contexts list for manager: listdialplan\n"); 03459 return -1; 03460 } 03461 03462 c = NULL; /* walk all contexts ... */ 03463 while ( (c = ast_walk_contexts(c)) ) { 03464 struct ast_exten *e; 03465 struct ast_include *i; 03466 struct ast_ignorepat *ip; 03467 03468 if (context && strcmp(ast_get_context_name(c), context) != 0) 03469 continue; /* not the name we want */ 03470 03471 dpc->context_existence = 1; 03472 03473 if (option_debug > 2) 03474 ast_log(LOG_DEBUG, "manager_show_dialplan: Found Context: %s \n", ast_get_context_name(c)); 03475 03476 if (ast_lock_context(c)) { /* failed to lock */ 03477 if (option_debug > 2) 03478 ast_log(LOG_DEBUG, "manager_show_dialplan: Failed to lock context\n"); 03479 continue; 03480 } 03481 03482 /* XXX note- an empty context is not printed */ 03483 e = NULL; /* walk extensions in context */ 03484 while ( (e = ast_walk_context_extensions(c, e)) ) { 03485 struct ast_exten *p; 03486 03487 /* looking for extension? is this our extension? */ 03488 if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) { 03489 /* not the one we are looking for, continue */ 03490 if (option_debug > 2) 03491 ast_log(LOG_DEBUG, "manager_show_dialplan: Skipping extension %s\n", ast_get_extension_name(e)); 03492 continue; 03493 } 03494 if (option_debug > 2) 03495 ast_log(LOG_DEBUG, "manager_show_dialplan: Found Extension: %s \n", ast_get_extension_name(e)); 03496 03497 dpc->extension_existence = 1; 03498 03499 /* may we print context info? */ 03500 dpc->total_context++; 03501 dpc->total_exten++; 03502 03503 p = NULL; /* walk next extension peers */ 03504 while ( (p = ast_walk_extension_priorities(e, p)) ) { 03505 int prio = ast_get_extension_priority(p); 03506 03507 dpc->total_prio++; 03508 if (!dpc->total_items++) 03509 manager_dpsendack(s, m); 03510 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext); 03511 astman_append(s, "Context: %s\r\nExtension: %s\r\n", ast_get_context_name(c), ast_get_extension_name(e) ); 03512 03513 /* XXX maybe make this conditional, if p != e ? */ 03514 if (ast_get_extension_label(p)) 03515 astman_append(s, "ExtensionLabel: %s\r\n", ast_get_extension_label(p)); 03516 03517 if (prio == PRIORITY_HINT) { 03518 astman_append(s, "Priority: hint\r\nApplication: %s\r\n", ast_get_extension_app(p)); 03519 } else { 03520 astman_append(s, "Priority: %d\r\nApplication: %s\r\nAppData: %s\r\n", prio, ast_get_extension_app(p), (char *) ast_get_extension_app_data(p)); 03521 } 03522 astman_append(s, "Registrar: %s\r\n\r\n", ast_get_extension_registrar(e)); 03523 } 03524 } 03525 03526 i = NULL; /* walk included and write info ... */ 03527 while ( (i = ast_walk_context_includes(c, i)) ) { 03528 if (exten) { 03529 /* Check all includes for the requested extension */ 03530 manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i); 03531 } else { 03532 if (!dpc->total_items++) 03533 manager_dpsendack(s, m); 03534 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext); 03535 astman_append(s, "Context: %s\r\nIncludeContext: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_include_name(i), ast_get_include_registrar(i)); 03536 astman_append(s, "\r\n"); 03537 if (option_debug > 2) 03538 ast_log(LOG_DEBUG, "manager_show_dialplan: Found Included context: %s \n", ast_get_include_name(i)); 03539 } 03540 } 03541 03542 ip = NULL; /* walk ignore patterns and write info ... */ 03543 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) { 03544 const char *ipname = ast_get_ignorepat_name(ip); 03545 char ignorepat[AST_MAX_EXTENSION]; 03546 03547 snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname); 03548 if (!exten || ast_extension_match(ignorepat, exten)) { 03549 if (!dpc->total_items++) 03550 manager_dpsendack(s, m); 03551 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext); 03552 astman_append(s, "Context: %s\r\nIgnorePattern: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ipname, ast_get_ignorepat_registrar(ip)); 03553 astman_append(s, "\r\n"); 03554 } 03555 } 03556 if (!rinclude) { 03557 struct ast_sw *sw = NULL; 03558 while ( (sw = ast_walk_context_switches(c, sw)) ) { 03559 if (!dpc->total_items++) 03560 manager_dpsendack(s, m); 03561 astman_append(s, "Event: ListDialplan\r\n%s", actionidtext); 03562 astman_append(s, "Context: %s\r\nSwitch: %s/%s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_registrar(sw)); 03563 astman_append(s, "\r\n"); 03564 if (option_debug > 2) 03565 ast_log(LOG_DEBUG, "manager_show_dialplan: Found Switch : %s \n", ast_get_switch_name(sw)); 03566 } 03567 } 03568 03569 ast_unlock_context(c); 03570 } 03571 ast_unlock_contexts(); 03572 03573 if (dpc->total_exten == old_total_exten) { 03574 if (option_debug > 2) 03575 ast_log(LOG_DEBUG, "manager_show_dialplan: Found nothing new\n"); 03576 /* Nothing new under the sun */ 03577 return -1; 03578 } else { 03579 return res; 03580 } 03581 } 03582 03583 /*! \brief Manager listing of dial plan */ 03584 static int manager_show_dialplan(struct mansession *s, const struct message *m) 03585 { 03586 const char *exten, *context; 03587 const char *id = astman_get_header(m, "ActionID"); 03588 char idtext[256]; 03589 int res; 03590 03591 /* Variables used for different counters */ 03592 struct dialplan_counters counters; 03593 03594 if (id && !ast_strlen_zero(id)) 03595 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id); 03596 else 03597 idtext[0] = '\0'; 03598 03599 memset(&counters, 0, sizeof(counters)); 03600 03601 exten = astman_get_header(m, "Extension"); 03602 context = astman_get_header(m, "Context"); 03603 03604 res = manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL); 03605 03606 if (context && !counters.context_existence) { 03607 char errorbuf[BUFSIZ]; 03608 03609 snprintf(errorbuf, sizeof(errorbuf), "Did not find context %s\r\n", context); 03610 astman_send_error(s, m, errorbuf); 03611 return 0; 03612 } 03613 if (exten && !counters.extension_existence) { 03614 char errorbuf[BUFSIZ]; 03615 03616 if (context) 03617 snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s@%s\r\n", exten, context); 03618 else 03619 snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s in any context\r\n", exten); 03620 astman_send_error(s, m, errorbuf); 03621 return 0; 03622 } 03623 03624 manager_event(EVENT_FLAG_CONFIG, "ShowDialPlanComplete", 03625 "EventList: Complete\r\n" 03626 "ListItems: %d\r\n" 03627 "ListExtensions: %d\r\n" 03628 "ListPriorities: %d\r\n" 03629 "ListContexts: %d\r\n" 03630 "%s" 03631 "\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext); 03632 03633 /* everything ok */ 03634 return 0; 03635 } 03636 03637 static char mandescr_show_dialplan[] = 03638 "Description: Show dialplan contexts and extensions.\n" 03639 "Be aware that showing the full dialplan may take a lot of capacity\n" 03640 "Variables: \n" 03641 " ActionID: <id> Action ID for this AMI transaction (optional)\n" 03642 " Extension: <extension> Extension (Optional)\n" 03643 " Context: <context> Context (Optional)\n" 03644 "\n"; 03645 03646 03647 /*! \brief CLI support for listing global variables in a parseable way */ 03648 static int handle_show_globals(int fd, int argc, char *argv[]) 03649 { 03650 int i = 0; 03651 struct ast_var_t *newvariable; 03652 03653 ast_rwlock_rdlock(&globalslock); 03654 AST_LIST_TRAVERSE (&globals, newvariable, entries) { 03655 i++; 03656 ast_cli(fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable)); 03657 } 03658 ast_rwlock_unlock(&globalslock); 03659 ast_cli(fd, "\n -- %d variables\n", i); 03660 03661 return RESULT_SUCCESS; 03662 } 03663 03664 static int handle_set_global(int fd, int argc, char *argv[]) 03665 { 03666 if (argc != 5) 03667 return RESULT_SHOWUSAGE; 03668 03669 pbx_builtin_setvar_helper(NULL, argv[3], argv[4]); 03670 ast_cli(fd, "\n -- Global variable %s set to %s\n", argv[3], argv[4]); 03671 03672 return RESULT_SUCCESS; 03673 } 03674 03675 03676 03677 /* 03678 * CLI entries for upper commands ... 03679 */ 03680 static struct ast_cli_entry pbx_cli[] = { 03681 { { "core", "show", "applications", NULL }, 03682 handle_show_applications, "Shows registered dialplan applications", 03683 show_applications_help, complete_show_applications }, 03684 03685 { { "core", "show", "functions", NULL }, 03686 handle_show_functions, "Shows registered dialplan functions", 03687 show_functions_help }, 03688 03689 { { "core", "show", "switches", NULL }, 03690 handle_show_switches, "Show alternative switches", 03691 show_switches_help }, 03692 03693 { { "core", "show", "hints", NULL }, 03694 handle_show_hints, "Show dialplan hints", 03695 show_hints_help }, 03696 03697 { { "core", "show", "globals", NULL }, 03698 handle_show_globals, "Show global dialplan variables", 03699 show_globals_help }, 03700 03701 { { "core", "show" , "function", NULL }, 03702 handle_show_function, "Describe a specific dialplan function", 03703 show_function_help, complete_show_function }, 03704 03705 { { "core", "show", "application", NULL }, 03706 handle_show_application, "Describe a specific dialplan application", 03707 show_application_help, complete_show_application }, 03708 03709 { { "core", "set", "global", NULL }, 03710 handle_set_global, "Set global dialplan variable", 03711 set_global_help }, 03712 03713 { { "dialplan", "show", NULL }, 03714 handle_show_dialplan, "Show dialplan", 03715 show_dialplan_help, complete_show_dialplan_context }, 03716 }; 03717 03718 static void unreference_cached_app(struct ast_app *app) 03719 { 03720 struct ast_context *context = NULL; 03721 struct ast_exten *eroot = NULL, *e = NULL; 03722 03723 ast_lock_contexts(); 03724 while ((context = ast_walk_contexts(context))) { 03725 while ((eroot = ast_walk_context_extensions(context, eroot))) { 03726 while ((e = ast_walk_extension_priorities(eroot, e))) { 03727 if (e->cached_app == app) 03728 e->cached_app = NULL; 03729 } 03730 } 03731 } 03732 ast_unlock_contexts(); 03733 03734 return; 03735 } 03736 03737 int ast_unregister_application(const char *app) 03738 { 03739 struct ast_app *tmp; 03740 03741 AST_RWLIST_WRLOCK(&apps); 03742 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) { 03743 if (!strcasecmp(app, tmp->name)) { 03744 unreference_cached_app(tmp); 03745 AST_RWLIST_REMOVE_CURRENT(&apps, list); 03746 if (option_verbose > 1) 03747 ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name); 03748 free(tmp); 03749 break; 03750 } 03751 } 03752 AST_RWLIST_TRAVERSE_SAFE_END 03753 AST_RWLIST_UNLOCK(&apps); 03754 03755 return tmp ? 0 : -1; 03756 } 03757 03758 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay) 03759 { 03760 struct ast_context *tmp, **local_contexts; 03761 int length = sizeof(struct ast_context) + strlen(name) + 1; 03762 03763 if (!extcontexts) { 03764 ast_mutex_lock(&conlock); 03765 local_contexts = &contexts; 03766 } else 03767 local_contexts = extcontexts; 03768 03769 for (tmp = *local_contexts; tmp; tmp = tmp->next) { 03770 if (!strcasecmp(tmp->name, name)) { 03771 if (!existsokay) { 03772 ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); 03773 tmp = NULL; 03774 } 03775 if (!extcontexts) 03776 ast_mutex_unlock(&conlock); 03777 return tmp; 03778 } 03779 } 03780 if ((tmp = ast_calloc(1, length))) { 03781 ast_mutex_init(&tmp->lock); 03782 ast_mutex_init(&tmp->macrolock); 03783 strcpy(tmp->name, name); 03784 tmp->root = NULL; 03785 tmp->registrar = registrar; 03786 tmp->next = *local_contexts; 03787 tmp->includes = NULL; 03788 tmp->ignorepats = NULL; 03789 *local_contexts = tmp; 03790 if (option_debug) 03791 ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name); 03792 if (option_verbose > 2) 03793 ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name); 03794 } 03795 03796 if (!extcontexts) 03797 ast_mutex_unlock(&conlock); 03798 return tmp; 03799 } 03800 03801 struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) 03802 { 03803 return __ast_context_create(extcontexts, name, registrar, 0); 03804 } 03805 03806 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) 03807 { 03808 return __ast_context_create(extcontexts, name, registrar, 1); 03809 } 03810 void __ast_context_destroy(struct ast_context *con, const char *registrar); 03811 03812 struct store_hint { 03813 char *context; 03814 char *exten; 03815 struct ast_state_cb *callbacks; 03816 int laststate; 03817 AST_LIST_ENTRY(store_hint) list; 03818 char data[1]; 03819 }; 03820 03821 AST_LIST_HEAD(store_hints, store_hint); 03822 03823 /* XXX this does not check that multiple contexts are merged */ 03824 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) 03825 { 03826 struct ast_context *tmp, *lasttmp = NULL; 03827 struct store_hints store = AST_LIST_HEAD_INIT_VALUE; 03828 struct store_hint *this; 03829 struct ast_hint *hint; 03830 struct ast_exten *exten; 03831 int length; 03832 struct ast_state_cb *thiscb, *prevcb; 03833 03834 /* it is very important that this function hold the hint list lock _and_ the conlock 03835 during its operation; not only do we need to ensure that the list of contexts 03836 and extensions does not change, but also that no hint callbacks (watchers) are 03837 added or removed during the merge/delete process 03838 03839 in addition, the locks _must_ be taken in this order, because there are already 03840 other code paths that use this order 03841 */ 03842 ast_mutex_lock(&conlock); 03843 AST_RWLIST_WRLOCK(&hints); 03844 03845 /* preserve all watchers for hints associated with this registrar */ 03846 AST_RWLIST_TRAVERSE(&hints, hint, list) { 03847 if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) { 03848 length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this); 03849 if (!(this = ast_calloc(1, length))) 03850 continue; 03851 this->callbacks = hint->callbacks; 03852 hint->callbacks = NULL; 03853 this->laststate = hint->laststate; 03854 this->context = this->data; 03855 strcpy(this->data, hint->exten->parent->name); 03856 this->exten = this->data + strlen(this->context) + 1; 03857 strcpy(this->exten, hint->exten->exten); 03858 AST_LIST_INSERT_HEAD(&store, this, list); 03859 } 03860 } 03861 03862 tmp = *extcontexts; 03863 if (registrar) { 03864 /* XXX remove previous contexts from same registrar */ 03865 if (option_debug) 03866 ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar); 03867 __ast_context_destroy(NULL,registrar); 03868 while (tmp) { 03869 lasttmp = tmp; 03870 tmp = tmp->next; 03871 } 03872 } else { 03873 /* XXX remove contexts with the same name */ 03874 while (tmp) { 03875 ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar); 03876 __ast_context_destroy(tmp,tmp->registrar); 03877 lasttmp = tmp; 03878 tmp = tmp->next; 03879 } 03880 } 03881 if (lasttmp) { 03882 lasttmp->next = contexts; 03883 contexts = *extcontexts; 03884 *extcontexts = NULL; 03885 } else 03886 ast_log(LOG_WARNING, "Requested contexts didn't get merged\n"); 03887 03888 /* restore the watchers for hints that can be found; notify those that 03889 cannot be restored 03890 */ 03891 while ((this = AST_LIST_REMOVE_HEAD(&store, list))) { 03892 exten = ast_hint_extension(NULL, this->context, this->exten); 03893 /* Find the hint in the list of hints */ 03894 AST_RWLIST_TRAVERSE(&hints, hint, list) { 03895 if (hint->exten == exten) 03896 break; 03897 } 03898 if (!exten || !hint) { 03899 /* this hint has been removed, notify the watchers */ 03900 prevcb = NULL; 03901 thiscb = this->callbacks; 03902 while (thiscb) { 03903 prevcb = thiscb; 03904 thiscb = thiscb->next; 03905 prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data); 03906 free(prevcb); 03907 } 03908 } else { 03909 thiscb = this->callbacks; 03910 while (thiscb->next) 03911 thiscb = thiscb->next; 03912 thiscb->next = hint->callbacks; 03913 hint->callbacks = this->callbacks; 03914 hint->laststate = this->laststate; 03915 } 03916 free(this); 03917 } 03918 03919 AST_RWLIST_UNLOCK(&hints); 03920 ast_mutex_unlock(&conlock); 03921 03922 return; 03923 } 03924 03925 /* 03926 * errno values 03927 * EBUSY - can't lock 03928 * ENOENT - no existence of context 03929 */ 03930 int ast_context_add_include(const char *context, const char *include, const char *registrar) 03931 { 03932 int ret = -1; 03933 struct ast_context *c = find_context_locked(context); 03934 03935 if (c) { 03936 ret = ast_context_add_include2(c, include, registrar); 03937 ast_unlock_contexts(); 03938 } 03939 return ret; 03940 } 03941 03942 /*! \brief Helper for get_range. 03943 * return the index of the matching entry, starting from 1. 03944 * If names is not supplied, try numeric values. 03945 */ 03946 static int lookup_name(const char *s, char *const names[], int max) 03947 { 03948 int i; 03949 03950 if (names) { 03951 for (i = 0; names[i]; i++) { 03952 if (!strcasecmp(s, names[i])) 03953 return i+1; 03954 } 03955 } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) { 03956 return i; 03957 } 03958 return 0; /* error return */ 03959 } 03960 03961 /*! \brief helper function to return a range up to max (7, 12, 31 respectively). 03962 * names, if supplied, is an array of names that should be mapped to numbers. 03963 */ 03964 static unsigned get_range(char *src, int max, char *const names[], const char *msg) 03965 { 03966 int s, e; /* start and ending position */ 03967 unsigned int mask = 0; 03968 03969 /* Check for whole range */ 03970 if (ast_strlen_zero(src) || !strcmp(src, "*")) { 03971 s = 0; 03972 e = max - 1; 03973 } else { 03974 /* Get start and ending position */ 03975 char *c = strchr(src, '-'); 03976 if (c) 03977 *c++ = '\0'; 03978 /* Find the start */ 03979 s = lookup_name(src, names, max); 03980 if (!s) { 03981 ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src); 03982 return 0; 03983 } 03984 s--; 03985 if (c) { /* find end of range */ 03986 e = lookup_name(c, names, max); 03987 if (!e) { 03988 ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c); 03989 return 0; 03990 } 03991 e--; 03992 } else 03993 e = s; 03994 } 03995 /* Fill the mask. Remember that ranges are cyclic */ 03996 mask = 1 << e; /* initialize with last element */ 03997 while (s != e) { 03998 if (s >= max) { 03999 s = 0; 04000 mask |= (1 << s); 04001 } else { 04002 mask |= (1 << s); 04003 s++; 04004 } 04005 } 04006 return mask; 04007 } 04008 04009 /*! \brief store a bitmask of valid times, one bit each 2 minute */ 04010 static void get_timerange(struct ast_timing *i, char *times) 04011 { 04012 char *e; 04013 int x; 04014 int s1, s2; 04015 int e1, e2; 04016 /* int cth, ctm; */ 04017 04018 /* start disabling all times, fill the fields with 0's, as they may contain garbage */ 04019 memset(i->minmask, 0, sizeof(i->minmask)); 04020 04021 /* 2-minutes per bit, since the mask has only 32 bits :( */ 04022 /* Star is all times */ 04023 if (ast_strlen_zero(times) || !strcmp(times, "*")) { 04024 for (x=0; x<24; x++) 04025 i->minmask[x] = 0x3fffffff; /* 30 bits */ 04026 return; 04027 } 04028 /* Otherwise expect a range */ 04029 e = strchr(times, '-'); 04030 if (!e) { 04031 ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n"); 04032 return; 04033 } 04034 *e++ = '\0'; 04035 /* XXX why skip non digits ? */ 04036 while (*e && !isdigit(*e)) 04037 e++; 04038 if (!*e) { 04039 ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n"); 04040 return; 04041 } 04042 if (sscanf(times, "%d:%d", &s1, &s2) != 2) { 04043 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times); 04044 return; 04045 } 04046 if (sscanf(e, "%d:%d", &e1, &e2) != 2) { 04047 ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e); 04048 return; 04049 } 04050 /* XXX this needs to be optimized */ 04051 #if 1 04052 s1 = s1 * 30 + s2/2; 04053 if ((s1 < 0) || (s1 >= 24*30)) { 04054 ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times); 04055 return; 04056 } 04057 e1 = e1 * 30 + e2/2; 04058 if ((e1 < 0) || (e1 >= 24*30)) { 04059 ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e); 04060 return; 04061 } 04062 /* Go through the time and enable each appropriate bit */ 04063 for (x=s1;x != e1;x = (x + 1) % (24 * 30)) { 04064 i->minmask[x/30] |= (1 << (x % 30)); 04065 } 04066 /* Do the last one */ 04067 i->minmask[x/30] |= (1 << (x % 30)); 04068 #else 04069 for (cth=0; cth<24; cth++) { 04070 /* Initialize masks to blank */ 04071 i->minmask[cth] = 0; 04072 for (ctm=0; ctm<30; ctm++) { 04073 if ( 04074 /* First hour with more than one hour */ 04075 (((cth == s1) && (ctm >= s2)) && 04076 ((cth < e1))) 04077 /* Only one hour */ 04078 || (((cth == s1) && (ctm >= s2)) && 04079 ((cth == e1) && (ctm <= e2))) 04080 /* In between first and last hours (more than 2