![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
pbx_config.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 Populate and remember extensions from static config file 00022 * 00023 * 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 52701 $") 00029 00030 #include <sys/types.h> 00031 #include <stdlib.h> 00032 #include <stdio.h> 00033 #include <string.h> 00034 #include <ctype.h> 00035 #include <errno.h> 00036 00037 #include "asterisk/pbx.h" 00038 #include "asterisk/config.h" 00039 #include "asterisk/options.h" 00040 #include "asterisk/module.h" 00041 #include "asterisk/logger.h" 00042 #include "asterisk/cli.h" 00043 #include "asterisk/callerid.h" 00044 00045 static char *config = "extensions.conf"; 00046 static char *registrar = "pbx_config"; 00047 static char userscontext[AST_MAX_EXTENSION] = "default"; 00048 00049 static int static_config = 0; 00050 static int write_protect_config = 1; 00051 static int autofallthrough_config = 0; 00052 static int clearglobalvars_config = 0; 00053 00054 AST_MUTEX_DEFINE_STATIC(save_dialplan_lock); 00055 00056 static struct ast_context *local_contexts = NULL; 00057 00058 /* 00059 * Help for commands provided by this module ... 00060 */ 00061 static char context_add_extension_help[] = 00062 "Usage: dialplan add extension <exten>,<priority>,<app>,<app-data>\n" 00063 " into <context> [replace]\n\n" 00064 " This command will add new extension into <context>. If there is an\n" 00065 " existence of extension with the same priority and last 'replace'\n" 00066 " arguments is given here we simply replace this extension.\n" 00067 "\n" 00068 "Example: dialplan add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n" 00069 " Now, you can dial 6123 and talk to Markster :)\n"; 00070 00071 static char context_remove_extension_help[] = 00072 "Usage: dialplan remove extension exten@context [priority]\n" 00073 " Remove an extension from a given context. If a priority\n" 00074 " is given, only that specific priority from the given extension\n" 00075 " will be removed.\n"; 00076 00077 static char context_add_ignorepat_help[] = 00078 "Usage: dialplan add ignorepat <pattern> into <context>\n" 00079 " This command adds a new ignore pattern into context <context>\n" 00080 "\n" 00081 "Example: dialplan add ignorepat _3XX into local\n"; 00082 00083 static char context_remove_ignorepat_help[] = 00084 "Usage: dialplan remove ignorepat <pattern> from <context>\n" 00085 " This command removes an ignore pattern from context <context>\n" 00086 "\n" 00087 "Example: dialplan remove ignorepat _3XX from local\n"; 00088 00089 static char context_add_include_help[] = 00090 "Usage: dialplan add include <context> into <context>\n" 00091 " Include a context in another context.\n"; 00092 00093 static char context_remove_include_help[] = 00094 "Usage: dialplan remove include <context> from <context>\n" 00095 " Remove an included context from another context.\n"; 00096 00097 static char save_dialplan_help[] = 00098 "Usage: dialplan save [/path/to/extension/file]\n" 00099 " Save dialplan created by pbx_config module.\n" 00100 "\n" 00101 "Example: dialplan save (/etc/asterisk/extensions.conf)\n" 00102 " dialplan save /home/markster (/home/markster/extensions.conf)\n"; 00103 00104 static char reload_extensions_help[] = 00105 "Usage: dialplan reload\n" 00106 " reload extensions.conf without reloading any other modules\n" 00107 " This command does not delete global variables unless\n" 00108 " clearglobalvars is set to yes in extensions.conf\n"; 00109 00110 /* 00111 * Implementation of functions provided by this module 00112 */ 00113 00114 /*! 00115 * REMOVE INCLUDE command stuff 00116 */ 00117 static int handle_context_remove_include(int fd, int argc, char *argv[]) 00118 { 00119 if (argc != 6) 00120 return RESULT_SHOWUSAGE; 00121 00122 if (strcmp(argv[4], "into")) 00123 return RESULT_SHOWUSAGE; 00124 00125 if (!ast_context_remove_include(argv[5], argv[3], registrar)) { 00126 ast_cli(fd, "We are not including '%s' into '%s' now\n", 00127 argv[3], argv[5]); 00128 return RESULT_SUCCESS; 00129 } 00130 00131 ast_cli(fd, "Failed to remove '%s' include from '%s' context\n", 00132 argv[3], argv[5]); 00133 return RESULT_FAILURE; 00134 } 00135 00136 /*! \brief return true if 'name' is included by context c */ 00137 static int lookup_ci(struct ast_context *c, const char *name) 00138 { 00139 struct ast_include *i = NULL; 00140 00141 if (ast_lock_context(c)) /* error, skip */ 00142 return 0; 00143 while ( (i = ast_walk_context_includes(c, i)) ) 00144 if (!strcmp(name, ast_get_include_name(i))) 00145 break; 00146 ast_unlock_context(c); 00147 return i ? -1 /* success */ : 0; 00148 } 00149 00150 /*! \brief return true if 'name' is in the ignorepats for context c */ 00151 static int lookup_c_ip(struct ast_context *c, const char *name) 00152 { 00153 struct ast_ignorepat *ip = NULL; 00154 00155 if (ast_lock_context(c)) /* error, skip */ 00156 return 0; 00157 while ( (ip = ast_walk_context_ignorepats(c, ip)) ) 00158 if (!strcmp(name, ast_get_ignorepat_name(ip))) 00159 break; 00160 ast_unlock_context(c); 00161 return ip ? -1 /* success */ : 0; 00162 } 00163 00164 /*! \brief moves to the n-th word in the string, or empty string if none */ 00165 static const char *skip_words(const char *p, int n) 00166 { 00167 int in_blank = 0; 00168 for (;n && *p; p++) { 00169 if (isblank(*p) /* XXX order is important */ && !in_blank) { 00170 n--; /* one word is gone */ 00171 in_blank = 1; 00172 } else if (/* !is_blank(*p), we know already, && */ in_blank) { 00173 in_blank = 0; 00174 } 00175 } 00176 return p; 00177 } 00178 00179 /*! \brief match the first 'len' chars of word. len==0 always succeeds */ 00180 static int partial_match(const char *s, const char *word, int len) 00181 { 00182 return (len == 0 || !strncmp(s, word, len)); 00183 } 00184 00185 /*! \brief split extension\@context in two parts, return -1 on error. 00186 * The return string is malloc'ed and pointed by *ext 00187 */ 00188 static int split_ec(const char *src, char **ext, char ** const ctx) 00189 { 00190 char *c, *e = ast_strdup(src); /* now src is not used anymore */ 00191 00192 if (e == NULL) 00193 return -1; /* malloc error */ 00194 /* now, parse values from 'exten@context' */ 00195 *ext = e; 00196 c = strchr(e, '@'); 00197 if (c == NULL) /* no context part */ 00198 *ctx = ""; /* it is not overwritten, anyways */ 00199 else { /* found context, check for duplicity ... */ 00200 *c++ = '\0'; 00201 *ctx = c; 00202 if (strchr(c, '@')) { /* two @, not allowed */ 00203 free(e); 00204 return -1; 00205 } 00206 } 00207 return 0; 00208 } 00209 00210 /* _X_ is the string we need to complete */ 00211 static char *complete_context_remove_include(const char *line, const char *word, 00212 int pos, int state) 00213 { 00214 int which = 0; 00215 char *res = NULL; 00216 int len = strlen(word); /* how many bytes to match */ 00217 struct ast_context *c = NULL; 00218 00219 if (pos == 3) { /* "dialplan remove include _X_" */ 00220 if (ast_lock_contexts()) { 00221 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00222 return NULL; 00223 } 00224 /* walk contexts and their includes, return the n-th match */ 00225 while (!res && (c = ast_walk_contexts(c))) { 00226 struct ast_include *i = NULL; 00227 00228 if (ast_lock_context(c)) /* error ? skip this one */ 00229 continue; 00230 00231 while ( !res && (i = ast_walk_context_includes(c, i)) ) { 00232 const char *i_name = ast_get_include_name(i); 00233 struct ast_context *nc = NULL; 00234 int already_served = 0; 00235 00236 if (!partial_match(i_name, word, len)) 00237 continue; /* not matched */ 00238 00239 /* check if this include is already served or not */ 00240 00241 /* go through all contexts again till we reach actual 00242 * context or already_served = 1 00243 */ 00244 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served) 00245 already_served = lookup_ci(nc, i_name); 00246 00247 if (!already_served && ++which > state) 00248 res = strdup(i_name); 00249 } 00250 ast_unlock_context(c); 00251 } 00252 00253 ast_unlock_contexts(); 00254 return res; 00255 } else if (pos == 4) { /* "dialplan remove include CTX _X_" */ 00256 /* 00257 * complete as 'from', but only if previous context is really 00258 * included somewhere 00259 */ 00260 char *context, *dupline; 00261 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */ 00262 00263 if (state > 0) 00264 return NULL; 00265 context = dupline = strdup(s); 00266 if (!dupline) { 00267 ast_log(LOG_ERROR, "Out of free memory\n"); 00268 return NULL; 00269 } 00270 strsep(&dupline, " "); 00271 00272 if (ast_lock_contexts()) { 00273 ast_log(LOG_ERROR, "Failed to lock contexts list\n"); 00274 free(context); 00275 return NULL; 00276 } 00277 00278 /* go through all contexts and check if is included ... */ 00279 while (!res && (c = ast_walk_contexts(c))) 00280 if (lookup_ci(c, context)) /* context is really included, complete "from" command */ 00281 res = strdup("from"); 00282 ast_unlock_contexts(); 00283 if (!res) 00284 ast_log(LOG_WARNING, "%s not included anywhere\n", context); 00285 free(context); 00286 return res; 00287 } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */ 00288 /* 00289 * Context from which we removing include ... 00290 */ 00291 char *context, *dupline, *from; 00292 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */ 00293 context = dupline = strdup(s); 00294 if (!dupline) { 00295 ast_log(LOG_ERROR, "Out of free memory\n"); 00296 return NULL; 00297 } 00298 00299 strsep(&dupline, " "); /* skip context */ 00300 00301 /* fourth word must be 'from' */ 00302 from = strsep(&dupline, " "); 00303 if (!from || strcmp(from, "from")) { 00304 free(context); 00305 return NULL; 00306 } 00307 00308 if (ast_lock_contexts()) { 00309 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00310 free(context); 00311 return NULL; 00312 } 00313 00314 /* walk through all contexts ... */ 00315 c = NULL; 00316 while ( !res && (c = ast_walk_contexts(c))) { 00317 const char *c_name = ast_get_context_name(c); 00318 if (!partial_match(c_name, word, len)) /* not a good target */ 00319 continue; 00320 /* walk through all includes and check if it is our context */ 00321 if (lookup_ci(c, context) && ++which > state) 00322 res = strdup(c_name); 00323 } 00324 ast_unlock_contexts(); 00325 free(context); 00326 return res; 00327 } 00328 00329 return NULL; 00330 } 00331 00332 /*! 00333 * REMOVE EXTENSION command stuff 00334 */ 00335 static int handle_context_remove_extension(int fd, int argc, char *argv[]) 00336 { 00337 int removing_priority = 0; 00338 char *exten, *context; 00339 int ret = RESULT_FAILURE; 00340 00341 if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE; 00342 00343 /* 00344 * Priority input checking ... 00345 */ 00346 if (argc == 5) { 00347 char *c = argv[4]; 00348 00349 /* check for digits in whole parameter for right priority ... 00350 * why? because atoi (strtol) returns 0 if any characters in 00351 * string and whole extension will be removed, it's not good 00352 */ 00353 if (!strcmp("hint", c)) 00354 removing_priority = PRIORITY_HINT; 00355 else { 00356 while (*c && isdigit(*c)) 00357 c++; 00358 if (*c) { /* non-digit in string */ 00359 ast_cli(fd, "Invalid priority '%s'\n", argv[4]); 00360 return RESULT_FAILURE; 00361 } 00362 removing_priority = atoi(argv[4]); 00363 } 00364 00365 if (removing_priority == 0) { 00366 ast_cli(fd, "If you want to remove whole extension, please " \ 00367 "omit priority argument\n"); 00368 return RESULT_FAILURE; 00369 } 00370 } 00371 00372 /* XXX original overwrote argv[3] */ 00373 /* 00374 * Format exten@context checking ... 00375 */ 00376 if (split_ec(argv[3], &exten, &context)) 00377 return RESULT_FAILURE; /* XXX malloc failure */ 00378 if ((!strlen(exten)) || (!(strlen(context)))) { 00379 ast_cli(fd, "Missing extension or context name in third argument '%s'\n", 00380 argv[3]); 00381 free(exten); 00382 return RESULT_FAILURE; 00383 } 00384 00385 if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) { 00386 if (!removing_priority) 00387 ast_cli(fd, "Whole extension %s@%s removed\n", 00388 exten, context); 00389 else 00390 ast_cli(fd, "Extension %s@%s with priority %d removed\n", 00391 exten, context, removing_priority); 00392 00393 ret = RESULT_SUCCESS; 00394 } else { 00395 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context); 00396 ret = RESULT_FAILURE; 00397 } 00398 free(exten); 00399 return ret; 00400 } 00401 00402 #define BROKEN_READLINE 1 00403 00404 #ifdef BROKEN_READLINE 00405 /* 00406 * There is one funny thing, when you have word like 300@ and you hit 00407 * <tab>, you arguments will like as your word is '300 ', so it '@' 00408 * characters acts sometimes as word delimiter and sometimes as a part 00409 * of word 00410 * 00411 * This fix function, allocates new word variable and store here every 00412 * time xxx@yyy always as one word and correct pos is set too 00413 * 00414 * It's ugly, I know, but I'm waiting for Mark suggestion if upper is 00415 * bug or feature ... 00416 */ 00417 static int fix_complete_args(const char *line, char **word, int *pos) 00418 { 00419 char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL; 00420 int words = 0; 00421 00422 _line = strdup(line); 00423 00424 _strsep_line = _line; 00425 while (_strsep_line) { 00426 _previous_word = _word; 00427 _word = strsep(&_strsep_line, " "); 00428 00429 if (_word && strlen(_word)) words++; 00430 } 00431 00432 00433 if (_word || _previous_word) { 00434 if (_word) { 00435 if (!strlen(_word)) words++; 00436 *word = strdup(_word); 00437 } else 00438 *word = strdup(_previous_word); 00439 *pos = words - 1; 00440 free(_line); 00441 return 0; 00442 } 00443 00444 free(_line); 00445 return -1; 00446 } 00447 #endif /* BROKEN_READLINE */ 00448 00449 static char *complete_context_remove_extension(const char *line, const char *word, int pos, 00450 int state) 00451 { 00452 char *ret = NULL; 00453 int which = 0; 00454 00455 #ifdef BROKEN_READLINE 00456 char *word2; 00457 /* 00458 * Fix arguments, *word is a new allocated structure, REMEMBER to 00459 * free *word when you want to return from this function ... 00460 */ 00461 if (fix_complete_args(line, &word2, &pos)) { 00462 ast_log(LOG_ERROR, "Out of free memory\n"); 00463 return NULL; 00464 } 00465 word = word2; 00466 #endif 00467 00468 if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */ 00469 struct ast_context *c = NULL; 00470 char *context = NULL, *exten = NULL; 00471 int le = 0; /* length of extension */ 00472 int lc = 0; /* length of context */ 00473 00474 lc = split_ec(word, &exten, &context); 00475 #ifdef BROKEN_READLINE 00476 free(word2); 00477 #endif 00478 if (lc) /* error */ 00479 return NULL; 00480 le = strlen(exten); 00481 lc = strlen(context); 00482 00483 if (ast_lock_contexts()) { 00484 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00485 goto error2; 00486 } 00487 00488 /* find our context ... */ 00489 while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */ 00490 struct ast_exten *e = NULL; 00491 /* XXX locking ? */ 00492 if (!partial_match(ast_get_context_name(c), context, lc)) 00493 continue; /* context not matched */ 00494 while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */ 00495 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */ 00496 /* If there is an extension then return exten@context. XXX otherwise ? */ 00497 if (exten) 00498 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)); 00499 break; 00500 } 00501 } 00502 if (e) /* got a match */ 00503 break; 00504 } 00505 00506 ast_unlock_contexts(); 00507 error2: 00508 if (exten) 00509 free(exten); 00510 } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */ 00511 char *exten = NULL, *context, *p; 00512 struct ast_context *c; 00513 int le, lc, len; 00514 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */ 00515 int i = split_ec(s, &exten, &context); /* parse ext@context */ 00516 00517 if (i) /* error */ 00518 goto error3; 00519 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */ 00520 *p = '\0'; 00521 if ( (p = strchr(context, ' ')) ) /* remove space after context */ 00522 *p = '\0'; 00523 le = strlen(exten); 00524 lc = strlen(context); 00525 len = strlen(word); 00526 if (le == 0 || lc == 0) 00527 goto error3; 00528 00529 if (ast_lock_contexts()) { 00530 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00531 goto error3; 00532 } 00533 00534 /* walk contexts */ 00535 c = NULL; 00536 while ( (c = ast_walk_contexts(c)) ) { 00537 /* XXX locking on c ? */ 00538 struct ast_exten *e; 00539 if (strcmp(ast_get_context_name(c), context) != 0) 00540 continue; 00541 /* got it, we must match here */ 00542 e = NULL; 00543 while ( (e = ast_walk_context_extensions(c, e)) ) { 00544 struct ast_exten *priority; 00545 char buffer[10]; 00546 00547 if (strcmp(ast_get_extension_name(e), exten) != 0) 00548 continue; 00549 /* XXX lock e ? */ 00550 priority = NULL; 00551 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) { 00552 snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority)); 00553 if (partial_match(buffer, word, len) && ++which > state) /* n-th match */ 00554 ret = strdup(buffer); 00555 } 00556 break; 00557 } 00558 break; 00559 } 00560 ast_unlock_contexts(); 00561 error3: 00562 if (exten) 00563 free(exten); 00564 #ifdef BROKEN_READLINE 00565 free(word2); 00566 #endif 00567 } 00568 return ret; 00569 } 00570 00571 /*! 00572 * Include context ... 00573 */ 00574 static int handle_context_add_include(int fd, int argc, char *argv[]) 00575 { 00576 if (argc != 6) /* dialplan add include CTX in CTX */ 00577 return RESULT_SHOWUSAGE; 00578 00579 /* fifth arg must be 'into' ... */ 00580 if (strcmp(argv[4], "into")) 00581 return RESULT_SHOWUSAGE; 00582 00583 if (ast_context_add_include(argv[5], argv[3], registrar)) { 00584 switch (errno) { 00585 case ENOMEM: 00586 ast_cli(fd, "Out of memory for context addition\n"); 00587 break; 00588 00589 case EBUSY: 00590 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); 00591 break; 00592 00593 case EEXIST: 00594 ast_cli(fd, "Context '%s' already included in '%s' context\n", 00595 argv[3], argv[5]); 00596 break; 00597 00598 case ENOENT: 00599 case EINVAL: 00600 ast_cli(fd, "There is no existence of context '%s'\n", 00601 errno == ENOENT ? argv[5] : argv[3]); 00602 break; 00603 00604 default: 00605 ast_cli(fd, "Failed to include '%s' in '%s' context\n", 00606 argv[3], argv[5]); 00607 break; 00608 } 00609 return RESULT_FAILURE; 00610 } 00611 00612 /* show some info ... */ 00613 ast_cli(fd, "Context '%s' included in '%s' context\n", 00614 argv[3], argv[5]); 00615 00616 return RESULT_SUCCESS; 00617 } 00618 00619 static char *complete_context_add_include(const char *line, const char *word, int pos, 00620 int state) 00621 { 00622 struct ast_context *c; 00623 int which = 0; 00624 char *ret = NULL; 00625 int len = strlen(word); 00626 00627 if (pos == 3) { /* 'dialplan add include _X_' (context) ... */ 00628 if (ast_lock_contexts()) { 00629 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00630 return NULL; 00631 } 00632 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) 00633 if (partial_match(ast_get_context_name(c), word, len) && ++which > state) 00634 ret = strdup(ast_get_context_name(c)); 00635 ast_unlock_contexts(); 00636 return ret; 00637 } else if (pos == 4) { /* dialplan add include CTX _X_ */ 00638 /* complete as 'into' if context exists or we are unable to check */ 00639 char *context, *dupline; 00640 struct ast_context *c; 00641 const char *s = skip_words(line, 3); /* should not fail */ 00642 00643 if (state != 0) /* only once */ 00644 return NULL; 00645 00646 /* parse context from line ... */ 00647 context = dupline = strdup(s); 00648 if (!context) { 00649 ast_log(LOG_ERROR, "Out of free memory\n"); 00650 return strdup("into"); 00651 } 00652 strsep(&dupline, " "); 00653 00654 /* check for context existence ... */ 00655 if (ast_lock_contexts()) { 00656 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00657 /* our fault, we can't check, so complete 'into' ... */ 00658 ret = strdup("into"); 00659 } else { 00660 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) 00661 if (!strcmp(context, ast_get_context_name(c))) 00662 ret = strdup("into"); /* found */ 00663 ast_unlock_contexts(); 00664 } 00665 free(context); 00666 return ret; 00667 } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */ 00668 char *context, *dupline, *into; 00669 const char *s = skip_words(line, 3); /* should not fail */ 00670 context = dupline = strdup(s); 00671 if (!dupline) { 00672 ast_log(LOG_ERROR, "Out of free memory\n"); 00673 return NULL; 00674 } 00675 strsep(&dupline, " "); /* skip context */ 00676 into = strsep(&dupline, " "); 00677 /* error if missing context or fifth word is not 'into' */ 00678 if (!strlen(context) || strcmp(into, "into")) { 00679 ast_log(LOG_ERROR, "bad context %s or missing into %s\n", 00680 context, into); 00681 goto error3; 00682 } 00683 00684 if (ast_lock_contexts()) { 00685 ast_log(LOG_ERROR, "Failed to lock context list\n"); 00686 goto error3; 00687 } 00688 00689 for (c = NULL; (c = ast_walk_contexts(c)); ) 00690 if (!strcmp(context, ast_get_context_name(c))) 00691 break; 00692 if (c) { /* first context exists, go on... */ 00693 /* go through all contexts ... */ 00694 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { 00695 if (!strcmp(context, ast_get_context_name(c))) 00696 continue; /* skip ourselves */ 00697 if (partial_match(ast_get_context_name(c), word, len) && 00698 !lookup_ci(c, context) /* not included yet */ && 00699 ++which > state) 00700 ret = strdup(ast_get_context_name(c)); 00701 } 00702 } else { 00703 ast_log(LOG_ERROR, "context %s not found\n", context); 00704 } 00705 ast_unlock_contexts(); 00706 error3: 00707 free(context); 00708 return ret; 00709 } 00710 00711 return NULL; 00712 } 00713 00714 /*! 00715 * \brief 'save dialplan' CLI command implementation functions ... 00716 */ 00717 static int handle_save_dialplan(int fd, int argc, char *argv[]) 00718 { 00719 char filename[256]; 00720 struct ast_context *c; 00721 struct ast_config *cfg; 00722 struct ast_variable *v; 00723 int incomplete = 0; /* incomplete config write? */ 00724 FILE *output; 00725 00726 const char *base, *slash, *file; 00727 00728 if (! (static_config && !write_protect_config)) { 00729 ast_cli(fd, 00730 "I can't save dialplan now, see '%s' example file.\n", 00731 config); 00732 return RESULT_FAILURE; 00733 } 00734 00735 if (argc != 2 && argc != 3) 00736 return RESULT_SHOWUSAGE; 00737 00738 if (ast_mutex_lock(&save_dialplan_lock)) { 00739 ast_cli(fd, 00740 "Failed to lock dialplan saving (another proccess saving?)\n"); 00741 return RESULT_FAILURE; 00742 } 00743 /* XXX the code here is quite loose, a pathname with .conf in it 00744 * is assumed to be a complete pathname 00745 */ 00746 if (argc == 3) { /* have config path. Look for *.conf */ 00747 base = argv[2]; 00748 if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */ 00749 /* if filename ends with '/', do not add one */ 00750 slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : ""; 00751 file = config; /* default: 'extensions.conf' */ 00752 } else { /* yes, complete file name */ 00753 slash = ""; 00754 file = ""; 00755 } 00756 } else { 00757 /* no config file, default one */ 00758 base = ast_config_AST_CONFIG_DIR; 00759 slash = "/"; 00760 file = config; 00761 } 00762 snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config); 00763 00764 cfg = ast_config_load("extensions.conf"); 00765 00766 /* try to lock contexts list */ 00767 if (ast_lock_contexts()) { 00768 ast_cli(fd, "Failed to lock contexts list\n"); 00769 ast_mutex_unlock(&save_dialplan_lock); 00770 ast_config_destroy(cfg); 00771 return RESULT_FAILURE; 00772 } 00773 00774 /* create new file ... */ 00775 if (!(output = fopen(filename, "wt"))) { 00776 ast_cli(fd, "Failed to create file '%s'\n", 00777 filename); 00778 ast_unlock_contexts(); 00779 ast_mutex_unlock(&save_dialplan_lock); 00780 ast_config_destroy(cfg); 00781 return RESULT_FAILURE; 00782 } 00783 00784 /* fireout general info */ 00785 fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n", 00786 static_config ? "yes" : "no", 00787 write_protect_config ? "yes" : "no", 00788 autofallthrough_config ? "yes" : "no", 00789 clearglobalvars_config ? "yes" : "no", 00790 ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")) ? "yes" : "no"); 00791 00792 if ((v = ast_variable_browse(cfg, "globals"))) { 00793 fprintf(output, "[globals]\n"); 00794 while(v) { 00795 fprintf(output, "%s => %s\n", v->name, v->value); 00796 v = v->next; 00797 } 00798 fprintf(output, "\n"); 00799 } 00800 00801 ast_config_destroy(cfg); 00802 00803 #define PUT_CTX_HDR do { \ 00804 if (!context_header_written) { \ 00805 fprintf(output, "[%s]\n", ast_get_context_name(c)); \ 00806 context_header_written = 1; \ 00807 } \ 00808 } while (0) 00809 00810 /* walk all contexts */ 00811 for (c = NULL; (c = ast_walk_contexts(c)); ) { 00812 int context_header_written = 0; 00813 struct ast_exten *e, *last_written_e = NULL; 00814 struct ast_include *i; 00815 struct ast_ignorepat *ip; 00816 struct ast_sw *sw; 00817 00818 /* try to lock context and fireout all info */ 00819 if (ast_lock_context(c)) { /* lock failure */ 00820 incomplete = 1; 00821 continue; 00822 } 00823 /* registered by this module? */ 00824 /* XXX do we need this ? */ 00825 if (!strcmp(ast_get_context_registrar(c), registrar)) { 00826 fprintf(output, "[%s]\n", ast_get_context_name(c)); 00827 context_header_written = 1; 00828 } 00829 00830 /* walk extensions ... */ 00831 for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) { 00832 struct ast_exten *p = NULL; 00833 00834 /* fireout priorities */ 00835 while ( (p = ast_walk_extension_priorities(e, p)) ) { 00836 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */ 00837 continue; 00838 00839 /* make empty line between different extensions */ 00840 if (last_written_e != NULL && 00841 strcmp(ast_get_extension_name(last_written_e), 00842 ast_get_extension_name(p))) 00843 fprintf(output, "\n"); 00844 last_written_e = p; 00845 00846 PUT_CTX_HDR; 00847 00848 if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */ 00849 fprintf(output, "exten => %s,hint,%s\n", 00850 ast_get_extension_name(p), 00851 ast_get_extension_app(p)); 00852 } else { /* copy and replace '|' with ',' */ 00853 const char *sep, *cid; 00854 char *tempdata; 00855 char *s; 00856 const char *el = ast_get_extension_label(p); 00857 char label[128]; 00858 00859 tempdata = ast_strdupa(ast_get_extension_app_data(p)); 00860 00861 for (s = tempdata; *s; s++) { 00862 if (*s == '|') 00863 *s = ','; 00864 } 00865 00866 if (ast_get_extension_matchcid(p)) { 00867 sep = "/"; 00868 cid = ast_get_extension_cidmatch(p); 00869 } else 00870 sep = cid = ""; 00871 00872 if (el && (snprintf(label, 127, "(%s)", el) != (strlen(el) + 2))) 00873 incomplete = 1; /* error encountered or label > 125 chars */ 00874 00875 fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n", 00876 ast_get_extension_name(p), sep, cid, 00877 ast_get_extension_priority(p), label, 00878 ast_get_extension_app(p), tempdata); 00879 } 00880 } 00881 } 00882 00883 /* written any extensions? ok, write space between exten & inc */ 00884 if (last_written_e) 00885 fprintf(output, "\n"); 00886 00887 /* walk through includes */ 00888 for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) { 00889 if (strcmp(ast_get_include_registrar(i), registrar) != 0) 00890 continue; /* not mine */ 00891 PUT_CTX_HDR; 00892 fprintf(output, "include => %s\n", ast_get_include_name(i)); 00893 } 00894 if (ast_walk_context_includes(c, NULL)) 00895 fprintf(output, "\n"); 00896 00897 /* walk through switches */ 00898 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) { 00899 if (strcmp(ast_get_switch_registrar(sw), registrar) != 0) 00900 continue; /* not mine */ 00901 PUT_CTX_HDR; 00902 fprintf(output, "switch => %s/%s\n", 00903 ast_get_switch_name(sw), ast_get_switch_data(sw)); 00904 } 00905 00906 if (ast_walk_context_switches(c, NULL)) 00907 fprintf(output, "\n"); 00908 00909 /* fireout ignorepats ... */ 00910 for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) { 00911 if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0) 00912 continue; /* not mine */ 00913 PUT_CTX_HDR; 00914 fprintf(output, "ignorepat => %s\n", 00915 ast_get_ignorepat_name(ip)); 00916 } 00917 00918 ast_unlock_context(c); 00919 } 00920 00921 ast_unlock_contexts(); 00922 ast_mutex_unlock(&save_dialplan_lock); 00923 fclose(output); 00924 00925 if (incomplete) { 00926 ast_cli(fd, "Saved dialplan is incomplete\n"); 00927 return RESULT_FAILURE; 00928 } 00929 00930 ast_cli(fd, "Dialplan successfully saved into '%s'\n", 00931 filename); 00932 return RESULT_SUCCESS; 00933 } 00934 00935 /*! 00936 * \brief ADD EXTENSION command stuff 00937 */ 00938 static int handle_context_add_extension(int fd, int argc, char *argv[]) 00939 { 00940 char *whole_exten; 00941 char *exten, *prior; 00942 int iprior = -2; 00943 char *cidmatch, *app, *app_data; 00944 char *start, *end; 00945 00946 /* check for arguments at first */ 00947 if (argc != 6 && argc != 7) 00948 return RESULT_SHOWUSAGE; 00949 if (strcmp(argv[4], "into")) 00950 return RESULT_SHOWUSAGE; 00951 if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE; 00952 00953 /* XXX overwrite argv[3] */ 00954 whole_exten = argv[3]; 00955 exten = strsep(&whole_exten,","); 00956 if (strchr(exten, '/')) { 00957 cidmatch = exten; 00958 strsep(&cidmatch,"/"); 00959 } else { 00960 cidmatch = NULL; 00961 } 00962 prior = strsep(&whole_exten,","); 00963 if (prior) { 00964 if (!strcmp(prior, "hint")) { 00965 iprior = PRIORITY_HINT; 00966 } else { 00967 if (sscanf(prior, "%d", &iprior) != 1) { 00968 ast_cli(fd, "'%s' is not a valid priority\n", prior); 00969 prior = NULL; 00970 } 00971 } 00972 } 00973 app = whole_exten; 00974 if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) { 00975 *start = *end = '\0'; 00976 app_data = start + 1; 00977 ast_process_quotes_and_slashes(app_data, ',', '|'); 00978 } else { 00979 if (app) { 00980 app_data = strchr(app, ','); 00981 if (app_data) { 00982 *app_data = '\0'; 00983 app_data++; 00984 } 00985 } else 00986 app_data = NULL; 00987 } 00988 00989 if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT)) 00990 return RESULT_SHOWUSAGE; 00991 00992 if (!app_data) 00993 app_data=""; 00994 if (ast_add_extension(argv[5], argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app, 00995 (void *)strdup(app_data), free, registrar)) { 00996 switch (errno) { 00997 case ENOMEM: 00998 ast_cli(fd, "Out of free memory\n"); 00999 break; 01000 01001 case EBUSY: 01002 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); 01003 break; 01004 01005 case ENOENT: 01006 ast_cli(fd, "No existence of '%s' context\n", argv[5]); 01007 break; 01008 01009 case EEXIST: 01010 ast_cli(fd, "Extension %s@%s with priority %s already exists\n", 01011 exten, argv[5], prior); 01012 break; 01013 01014 default: 01015 ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n", 01016 exten, prior, app, app_data, argv[5]); 01017 break; 01018 } 01019 return RESULT_FAILURE; 01020 } 01021 01022 if (argc == 7) 01023 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n", 01024 exten, argv[5], prior, exten, prior, app, app_data); 01025 else 01026 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n", 01027 exten, prior, app, app_data, argv[5]); 01028 01029 return RESULT_SUCCESS; 01030 } 01031 01032 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */ 01033 static char *complete_context_add_extension(const char *line, const char *word, int pos, int state) 01034 { 01035 int which = 0; 01036 01037 if (pos == 4) { /* complete 'into' word ... */ 01038 return (state == 0) ? strdup("into") : NULL; 01039 } else if (pos == 5) { /* complete context */ 01040 struct ast_context *c = NULL; 01041 int len = strlen(word); 01042 char *res = NULL; 01043 01044 /* try to lock contexts list ... */ 01045 if (ast_lock_contexts()) { 01046 ast_log(LOG_WARNING, "Failed to lock contexts list\n"); 01047 return NULL; 01048 } 01049 01050 /* walk through all contexts */ 01051 while ( !res && (c = ast_walk_contexts(c)) ) 01052 if (partial_match(ast_get_context_name(c), word, len) && ++which > state) 01053 res = strdup(ast_get_context_name(c)); 01054 ast_unlock_contexts(); 01055 return res; 01056 } else if (pos == 6) { 01057 return state == 0 ? strdup("replace") : NULL; 01058 } 01059 return NULL; 01060 } 01061 01062 /*! 01063 * IGNOREPAT CLI stuff 01064 */ 01065 static int handle_context_add_ignorepat(int fd, int argc, char *argv[]) 01066 { 01067 if (argc != 6) 01068 return RESULT_SHOWUSAGE; 01069 if (strcmp(argv[4], "into")) 01070 return RESULT_SHOWUSAGE; 01071 01072 if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) { 01073 switch (errno) { 01074 case ENOMEM: 01075 ast_cli(fd, "Out of free memory\n"); 01076 break; 01077 01078 case ENOENT: 01079 ast_cli(fd, "There is no existence of '%s' context\n", argv[5]); 01080 break; 01081 01082 case EEXIST: 01083 ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n", 01084 argv[3], argv[5]); 01085 break; 01086 01087 case EBUSY: 01088 ast_cli(fd, "Failed to lock context(s) list, please, try again later\n"); 01089 break; 01090 01091 default: 01092 ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n", 01093 argv[3], argv[5]); 01094 break; 01095 } 01096 return RESULT_FAILURE; 01097 } 01098 01099 ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n", 01100 argv[3], argv[5]); 01101 return RESULT_SUCCESS; 01102 } 01103 01104 static char *complete_context_add_ignorepat(const char *line, const char *word, 01105 int pos, int state) 01106 { 01107 if (pos == 4) 01108 return state == 0 ? strdup("into") : NULL; 01109 else if (pos == 5) { 01110 struct ast_context *c; 01111 int which = 0; 01112 char *dupline, *ignorepat = NULL; 01113 const char *s; 01114 char *ret = NULL; 01115 int len = strlen(word); 01116 01117 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */ 01118 s = skip_words(line, 3); 01119 if (s == NULL) 01120 return NULL; 01121 dupline = strdup(s); 01122 if (!dupline) { 01123 ast_log(LOG_ERROR, "Malloc failure\n"); 01124 return NULL; 01125 } 01126 ignorepat = strsep(&dupline, " "); 01127 01128 if (ast_lock_contexts()) { 01129 ast_log(LOG_ERROR, "Failed to lock contexts list\n"); 01130 return NULL; 01131 } 01132 01133 for (c = NULL; !ret && (c = ast_walk_contexts(c));) { 01134 int found = 0; 01135 01136 if (!partial_match(ast_get_context_name(c), word, len)) 01137 continue; /* not mine */ 01138 if (ignorepat) /* there must be one, right ? */ 01139 found = lookup_c_ip(c, ignorepat); 01140 if (!found && ++which > state) 01141 ret = strdup(ast_get_context_name(c)); 01142 } 01143 01144 if (ignorepat) 01145 free(ignorepat); 01146 ast_unlock_contexts(); 01147 return ret; 01148 } 01149 01150 return NULL; 01151 } 01152 01153 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[]) 01154 { 01155 if (argc != 6) 01156 return RESULT_SHOWUSAGE; 01157 if (strcmp(argv[4], "from")) 01158 return RESULT_SHOWUSAGE; 01159 01160 if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) { 01161 switch (errno) { 01162 case EBUSY: 01163 ast_cli(fd, "Failed to lock context(s) list, please try again later\n"); 01164 break; 01165 01166 case ENOENT: 01167 ast_cli(fd, "There is no existence of '%s' context\n", argv[5]); 01168 break; 01169 01170 case EINVAL: 01171 ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n", 01172 argv[3], argv[5]); 01173 break; 01174 01175 default: 01176 ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]); 01177 break; 01178 } 01179 return RESULT_FAILURE; 01180 } 01181 01182 ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n", 01183 argv[3], argv[5]); 01184 return RESULT_SUCCESS; 01185 } 01186 01187 static char *complete_context_remove_ignorepat(const char *line, const char *word, 01188 int pos, int state) 01189 { 01190 struct ast_context *c; 01191 int which = 0; 01192 char *ret = NULL; 01193 01194 if (pos == 3) { 01195 int len = strlen(word); 01196 if (ast_lock_contexts()) { 01197 ast_log(LOG_WARNING, "Failed to lock contexts list\n"); 01198 return NULL; 01199 } 01200 01201 for (c = NULL; !ret && (c = ast_walk_contexts(c));) { 01202 struct ast_ignorepat *ip; 01203 01204 if (ast_lock_context(c)) /* error, skip it */ 01205 continue; 01206 01207 for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) { 01208 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) { 01209 /* n-th match */ 01210 struct ast_context *cw = NULL; 01211 int found = 0; 01212 while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) { 01213 /* XXX do i stop on c, or skip it ? */ 01214 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip)); 01215 } 01216 if (!found) 01217 ret = strdup(ast_get_ignorepat_name(ip)); 01218 } 01219 } 01220 ast_unlock_context(c); 01221 } 01222 ast_unlock_contexts(); 01223 return ret; 01224 } else if (pos == 4) { 01225 return state == 0 ? strdup("from") : NULL; 01226 } else if (pos == 5) { /* XXX check this */ 01227 char *dupline, *duplinet, *ignorepat; 01228 int len = strlen(word); 01229 01230 dupline = strdup(line); 01231 if (!dupline) { 01232 ast_log(LOG_WARNING, "Out of free memory\n"); 01233 return NULL; 01234 } 01235 01236 duplinet = dupline; 01237 strsep(&duplinet, " "); 01238 strsep(&duplinet, " "); 01239 ignorepat = strsep(&duplinet, " "); 01240 01241 if (!ignorepat) { 01242 free(dupline); 01243 return NULL; 01244 } 01245 01246 if (ast_lock_contexts()) { 01247 ast_log(LOG_WARNING, "Failed to lock contexts list\n"); 01248 free(dupline); 01249 return NULL; 01250 } 01251 01252 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) { 01253 if (ast_lock_context(c)) /* fail, skip it */ 01254 continue; 01255 if (!partial_match(ast_get_context_name(c), word, len)) 01256 continue; 01257 if (lookup_c_ip(c, ignorepat) && ++which > state) 01258 ret = strdup(ast_get_context_name(c)); 01259 ast_unlock_context(c); 01260 } 01261 ast_unlock_contexts(); 01262 free(dupline); 01263 return NULL; 01264 } 01265 01266 return NULL; 01267 } 01268 01269 static int pbx_load_module(void); 01270 01271 static int handle_reload_extensions(int fd, int argc, char *argv[]) 01272 { 01273 if (argc != 2) 01274 return RESULT_SHOWUSAGE; 01275 pbx_load_module(); 01276 return RESULT_SUCCESS; 01277 } 01278 01279 /*! 01280 * CLI entries for commands provided by this module 01281 */ 01282 static struct ast_cli_entry cli_pbx_config[] = { 01283 { { "dialplan", "add", "extension", NULL }, 01284 handle_context_add_extension, "Add new extension into context", 01285 context_add_extension_help, complete_context_add_extension }, 01286 01287 { { "dialplan", "remove", "extension", NULL }, 01288 handle_context_remove_extension, "Remove a specified extension", 01289 context_remove_extension_help, complete_context_remove_extension }, 01290 01291 { { "dialplan", "add", "ignorepat", NULL }, 01292 handle_context_add_ignorepat, "Add new ignore pattern", 01293 context_add_ignorepat_help, complete_context_add_ignorepat }, 01294 01295 { { "dialplan", "remove", "ignorepat", NULL }, 01296 handle_context_remove_ignorepat, "Remove ignore pattern from context", 01297 context_remove_ignorepat_help, complete_context_remove_ignorepat }, 01298 01299 { { "dialplan", "add", "include", NULL }, 01300 handle_context_add_include, "Include context in other context", 01301 context_add_include_help, complete_context_add_include }, 01302 01303 { { "dialplan", "remove", "include", NULL }, 01304 handle_context_remove_include, "Remove a specified include from context", 01305 context_remove_include_help, complete_context_remove_include }, 01306 01307 { { "dialplan", "reload", NULL }, 01308 handle_reload_extensions, "Reload extensions and *only* extensions", 01309 reload_extensions_help }, 01310 }; 01311 01312 01313 static struct ast_cli_entry cli_dialplan_save = { 01314 { "dialplan", "save", NULL }, 01315 handle_save_dialplan, "Save dialplan", 01316 save_dialplan_help }; 01317 01318 /*! 01319 * Standard module functions ... 01320 */ 01321 static int unload_module(void) 01322 { 01323 if (static_config && !write_protect_config) 01324 ast_cli_unregister(&cli_dialplan_save); 01325 ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); 01326 ast_context_destroy(NULL, registrar); 01327 return 0; 01328 } 01329 01330 static int pbx_load_config(const char *config_file) 01331 { 01332 struct ast_config *cfg; 01333 char *end; 01334 char *label; 01335 char realvalue[256]; 01336 int lastpri = -2; 01337 struct ast_context *con; 01338 struct ast_variable *v; 01339 const char *cxt; 01340 01341 cfg = ast_config_load(config_file); 01342 if (!cfg) 01343 return 0; 01344 01345 /* Use existing config to populate the PBX table */ 01346 static_config = ast_true(ast_variable_retrieve(cfg, "general", "static")); 01347 write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect")); 01348 autofallthrough_config = ast_true(ast_variable_retrieve(cfg, "general", "autofallthrough")); 01349 clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars")); 01350 ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING); 01351 01352 if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) 01353 ast_copy_string(userscontext, cxt, sizeof(userscontext)); 01354 else 01355 ast_copy_string(userscontext, "default", sizeof(userscontext)); 01356 01357 for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) { 01358 memset(realvalue, 0, sizeof(realvalue)); 01359 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); 01360 pbx_builtin_setvar_helper(NULL, v->name, realvalue); 01361 } 01362 for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) { 01363 /* All categories but "general" or "globals" are considered contexts */ 01364 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) 01365 continue; 01366 con=ast_context_find_or_create(&local_contexts,cxt, registrar); 01367 if (con == NULL) 01368 continue; 01369 01370 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) { 01371 if (!strcasecmp(v->name, "exten")) { 01372 char *tc = ast_strdup(v->value); 01373 if (tc) { 01374 int ipri = -2; 01375 char realext[256]=""; 01376 char *plus, *firstp, *firstc; 01377 char *pri, *appl, *data, *cidmatch; 01378 char *stringp = tc; 01379 char *ext = strsep(&stringp, ","); 01380 if (!ext) 01381 ext=""; 01382 pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1); 01383 cidmatch = strchr(realext, '/'); 01384 if (cidmatch) { 01385 *cidmatch++ = '\0'; 01386 ast_shrink_phone_number(cidmatch); 01387 } 01388 pri = strsep(&stringp, ","); 01389 if (!pri) 01390 pri=""; 01391 label = strchr(pri, '('); 01392 if (label) { 01393 *label++ = '\0'; 01394 end = strchr(label, ')'); 01395 if (end) 01396 *end = '\0'; 01397 else 01398 ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno); 01399 } 01400 plus = strchr(pri, '+'); 01401 if (plus) 01402 *plus++ = '\0'; 01403 if (!strcmp(pri,"hint")) 01404 ipri=PRIORITY_HINT; 01405 else if (!strcmp(pri, "next") || !strcmp(pri, "n")) { 01406 if (lastpri > -2) 01407 ipri = lastpri + 1; 01408 else 01409 ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n"); 01410 } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) { 01411 if (lastpri > -2) 01412 ipri = lastpri; 01413 else 01414 ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n"); 01415 } else if (sscanf(pri, "%d", &ipri) != 1 && 01416 (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) { 01417 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno); 01418 ipri = 0; 01419 } 01420 appl = S_OR(stringp, ""); 01421 /* Find the first occurrence of either '(' or ',' */ 01422 firstc = strchr(appl, ','); 01423 firstp = strchr(appl, '('); 01424 if (firstc && (!firstp || firstc < firstp)) { 01425 /* comma found, no parenthesis */ 01426 /* or both found, but comma found first */ 01427 appl = strsep(&stringp, ","); 01428 data = stringp; 01429 } else if (!firstc && !firstp) { 01430 /* Neither found */ 01431 data = ""; 01432 } else { 01433 /* Final remaining case is parenthesis found first */ 01434 appl = strsep(&stringp, "("); 01435 data = stringp; 01436 end = strrchr(data, ')'); 01437 if ((end = strrchr(data, ')'))) { 01438 *end = '\0'; 01439 } else { 01440 ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data); 01441 } 01442 ast_process_quotes_and_slashes(data, ',', '|'); 01443 } 01444 01445 if (!data) 01446 data=""; 01447 appl = ast_skip_blanks(appl); 01448 if (ipri) { 01449 if (plus) 01450 ipri += atoi(plus); 01451 lastpri = ipri; 01452 if (!ast_opt_dont_warn && !strcmp(realext, "_.")) 01453 ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno); 01454 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) { 01455 ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); 01456 } 01457 } 01458 free(tc); 01459 } 01460 } else if (!strcasecmp(v->name, "include")) { 01461 memset(realvalue, 0, sizeof(realvalue)); 01462 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); 01463 if (ast_context_add_include2(con, realvalue, registrar)) 01464 ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt); 01465 } else if (!strcasecmp(v->name, "ignorepat")) { 01466 memset(realvalue, 0, sizeof(realvalue)); 01467 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); 01468 if (ast_context_add_ignorepat2(con, realvalue, registrar)) 01469 ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt); 01470 } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) { 01471 char *stringp= realvalue; 01472 char *appl, *data; 01473 01474 memset(realvalue, 0, sizeof(realvalue)); 01475 if (!strcasecmp(v->name, "switch")) 01476 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); 01477 else 01478 ast_copy_string(realvalue, v->value, sizeof(realvalue)); 01479 appl = strsep(&stringp, "/"); 01480 data = strsep(&stringp, ""); /* XXX what for ? */ 01481 if (!data) 01482 data = ""; 01483 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) 01484 ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); 01485 } else { 01486 ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno); 01487 } 01488 } 01489 } 01490 ast_config_destroy(cfg); 01491 return 1; 01492 } 01493 01494 static void append_interface(char *iface, int maxlen, char *add) 01495 { 01496 int len = strlen(iface); 01497 if (strlen(add) + len < maxlen - 2) { 01498 if (strlen(iface)) { 01499 iface[len] = '&'; 01500 strcpy(iface + len + 1, add); 01501 } else 01502 strcpy(iface, add); 01503 } 01504 } 01505 01506 static void pbx_load_users(void) 01507 { 01508 struct ast_config *cfg; 01509 char *cat, *chan; 01510 const char *zapchan; 01511 const char *hasexten; 01512 char tmp[256]; 01513 char iface[256]; 01514 char zapcopy[256]; 01515 char *c; 01516 int len; 01517 int hasvoicemail; 01518 int start, finish, x; 01519 struct ast_context *con; 01520 01521 cfg = ast_config_load("users.conf"); 01522 if (!cfg) 01523 return; 01524 con = ast_context_find_or_create(&local_contexts, userscontext, registrar); 01525 if (!con) 01526 return; 01527 01528 for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) { 01529 if (!strcasecmp(cat, "general")) 01530 continue; 01531 iface[0] = '\0'; 01532 len = sizeof(iface); 01533 if (ast_true(ast_config_option(cfg, cat, "hassip"))) { 01534 snprintf(tmp, sizeof(tmp), "SIP/%s", cat); 01535 append_interface(iface, sizeof(iface), tmp); 01536 } 01537 if (ast_true(ast_config_option(cfg, cat, "hasiax"))) { 01538 snprintf(tmp, sizeof(tmp), "IAX2/%s", cat); 01539 append_interface(iface, sizeof(iface), tmp); 01540 } 01541 if (ast_true(ast_config_option(cfg, cat, "hash323"))) { 01542 snprintf(tmp, sizeof(tmp), "H323/%s", cat); 01543 append_interface(iface, sizeof(iface), tmp); 01544 } 01545 hasexten = ast_config_option(cfg, cat, "hasexten"); 01546 if (hasexten && !ast_true(hasexten)) 01547 continue; 01548 hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail")); 01549 zapchan = ast_variable_retrieve(cfg, cat, "zapchan"); 01550 if (!zapchan) 01551 zapchan = ast_variable_retrieve(cfg, "general", "zapchan"); 01552 if (!ast_strlen_zero(zapchan)) { 01553 ast_copy_string(zapcopy, zapchan, sizeof(zapcopy)); 01554 c = zapcopy; 01555 chan = strsep(&c, ","); 01556 while (chan) { 01557 if (sscanf(chan, "%d-%d", &start, &finish) == 2) { 01558 /* Range */ 01559 } else if (sscanf(chan, "%d", &start)) { 01560 /* Just one */ 01561 finish = start; 01562 } else { 01563 start = 0; finish = 0; 01564 } 01565 if (finish < start) { 01566 x = finish; 01567 finish = start; 01568 start = x; 01569 } 01570 for (x = start; x <= finish; x++) { 01571 snprintf(tmp, sizeof(tmp), "Zap/%d", x); 01572 append_interface(iface, sizeof(iface), tmp); 01573 } 01574 chan = strsep(&c, ","); 01575 } 01576 } 01577 if (!ast_strlen_zero(iface)) { 01578 /* Add hint */ 01579 ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar); 01580 /* If voicemail, use "stdexten" else use plain old dial */ 01581 if (hasvoicemail) { 01582 snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat); 01583 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar); 01584 } else { 01585 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar); 01586 } 01587 } 01588 } 01589 ast_config_destroy(cfg); 01590 } 01591 01592 static int pbx_load_module(void) 01593 { 01594 struct ast_context *con; 01595 01596 if(!pbx_load_config(config)) 01597 return AST_MODULE_LOAD_DECLINE; 01598 01599 pbx_load_users(); 01600 01601 ast_merge_contexts_and_delete(&local_contexts, registrar); 01602 01603 for (con = NULL; (con = ast_walk_contexts(con));) 01604 ast_context_verify_includes(con); 01605 01606 pbx_set_autofallthrough(autofallthrough_config); 01607 01608 return 0; 01609 } 01610 01611 static int load_module(void) 01612 { 01613 if (pbx_load_module()) 01614 return AST_MODULE_LOAD_DECLINE; 01615 01616 if (static_config && !write_protect_config) 01617 ast_cli_register(&cli_dialplan_save); 01618 ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry)); 01619 01620 return 0; 01621 } 01622 01623 static int reload(void) 01624 { 01625 if (clearglobalvars_config) 01626 pbx_builtin_clear_globals(); 01627 pbx_load_module(); 01628 return 0; 01629 } 01630 01631 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration", 01632 .load = load_module, 01633 .unload = unload_module, 01634 .reload = reload, 01635 );