Wed Feb 11 11:59:57 2009

Asterisk developer's documentation


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: 153823 $")
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 = 1;
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[/cid]@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_dont_include_deprecated(int fd, int argc, char *argv[])
00118 {
00119    if (argc != 5)
00120       return RESULT_SHOWUSAGE;
00121 
00122    if (strcmp(argv[3], "into"))
00123       return RESULT_SHOWUSAGE;
00124 
00125    if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
00126       ast_cli(fd, "We are not including '%s' into '%s' now\n",
00127          argv[2], argv[4]);
00128       return RESULT_SUCCESS;
00129    }
00130 
00131    ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
00132       argv[2], argv[4]);
00133    return RESULT_FAILURE;
00134 }
00135 
00136 static int handle_context_remove_include(int fd, int argc, char *argv[])
00137 {
00138    if (argc != 6) {
00139       return RESULT_SHOWUSAGE;
00140    }
00141 
00142    if (strcmp(argv[4], "from")) {
00143       return RESULT_SHOWUSAGE;
00144    }
00145 
00146    if (!ast_context_remove_include(argv[5], argv[3], registrar)) {
00147       ast_cli(fd, "The dialplan no longer includes '%s' into '%s'\n",
00148          argv[3], argv[5]);
00149       return RESULT_SUCCESS;
00150    }
00151 
00152    ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
00153       argv[3], argv[5]);
00154 
00155    return RESULT_FAILURE;
00156 }
00157 
00158 /*! \brief return true if 'name' is included by context c */
00159 static int lookup_ci(struct ast_context *c, const char *name)
00160 {
00161    struct ast_include *i = NULL;
00162 
00163    if (ast_lock_context(c))   /* error, skip */
00164       return 0;
00165    while ( (i = ast_walk_context_includes(c, i)) )
00166       if (!strcmp(name, ast_get_include_name(i)))
00167          break;
00168    ast_unlock_context(c);
00169    return i ? -1 /* success */ : 0;
00170 }
00171 
00172 /*! \brief return true if 'name' is in the ignorepats for context c */
00173 static int lookup_c_ip(struct ast_context *c, const char *name)
00174 {
00175    struct ast_ignorepat *ip = NULL;
00176 
00177    if (ast_lock_context(c))   /* error, skip */
00178       return 0;
00179    while ( (ip = ast_walk_context_ignorepats(c, ip)) )
00180       if (!strcmp(name, ast_get_ignorepat_name(ip)))
00181          break;
00182    ast_unlock_context(c);
00183    return ip ? -1 /* success */ : 0;
00184 }
00185 
00186 /*! \brief moves to the n-th word in the string, or empty string if none */
00187 static const char *skip_words(const char *p, int n)
00188 {
00189    int in_blank = 0;
00190    for (;n && *p; p++) {
00191       if (isblank(*p) /* XXX order is important */ && !in_blank) {
00192          n--;  /* one word is gone */
00193          in_blank = 1;
00194       } else if (/* !is_blank(*p), we know already, && */ in_blank) {
00195          in_blank = 0;
00196       }
00197    }
00198    return p;
00199 }
00200 
00201 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
00202 static int partial_match(const char *s, const char *word, int len)
00203 {
00204    return (len == 0 || !strncmp(s, word, len));
00205 }
00206 
00207 /*! \brief split extension\@context in two parts, return -1 on error.
00208  * The return string is malloc'ed and pointed by *ext
00209  */
00210 static int split_ec(const char *src, char **ext, char ** const ctx, char ** const cid)
00211 {
00212    char *i, *c, *e = ast_strdup(src); /* now src is not used anymore */
00213 
00214    if (e == NULL)
00215       return -1;  /* malloc error */
00216    /* now, parse values from 'exten@context' */
00217    *ext = e;
00218    c = strchr(e, '@');
00219    if (c == NULL) /* no context part */
00220       *ctx = "";  /* it is not overwritten, anyways */
00221    else {   /* found context, check for duplicity ... */
00222       *c++ = '\0';
00223       *ctx = c;
00224       if (strchr(c, '@')) { /* two @, not allowed */
00225          free(e);
00226          return -1;
00227       }
00228    }
00229    if (cid && (i = strchr(e, '/'))) {
00230       *i++ = '\0';
00231       *cid = i;
00232    } else if (cid) {
00233       /* Signal none detected */
00234       *cid = NULL;
00235    }
00236    return 0;
00237 }
00238 
00239 /* _X_ is the string we need to complete */
00240 static char *complete_context_dont_include_deprecated(const char *line, const char *word,
00241    int pos, int state)
00242 {
00243    int which = 0;
00244    char *res = NULL;
00245    int len = strlen(word); /* how many bytes to match */
00246    struct ast_context *c = NULL;
00247 
00248    if (pos == 2) {      /* "dont include _X_" */
00249       if (ast_wrlock_contexts()) {
00250          ast_log(LOG_ERROR, "Failed to lock context list\n");
00251          return NULL;
00252       }
00253       /* walk contexts and their includes, return the n-th match */
00254       while (!res && (c = ast_walk_contexts(c))) {
00255          struct ast_include *i = NULL;
00256 
00257          if (ast_lock_context(c))   /* error ? skip this one */
00258             continue;
00259 
00260          while ( !res && (i = ast_walk_context_includes(c, i)) ) {
00261             const char *i_name = ast_get_include_name(i);
00262             struct ast_context *nc = NULL;
00263             int already_served = 0;
00264 
00265             if (!partial_match(i_name, word, len))
00266                continue;   /* not matched */
00267 
00268             /* check if this include is already served or not */
00269 
00270             /* go through all contexts again till we reach actual
00271              * context or already_served = 1
00272              */
00273             while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
00274                already_served = lookup_ci(nc, i_name);
00275 
00276             if (!already_served && ++which > state)
00277                res = strdup(i_name);
00278          }
00279          ast_unlock_context(c);
00280       }
00281 
00282       ast_unlock_contexts();
00283       return res;
00284    } else if (pos == 3) { /* "dont include CTX _X_" */
00285       /*
00286        * complete as 'in', but only if previous context is really
00287        * included somewhere
00288        */
00289       char *context, *dupline;
00290       const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
00291 
00292       if (state > 0)
00293          return NULL;
00294       context = dupline = strdup(s);
00295       if (!dupline) {
00296          ast_log(LOG_ERROR, "Out of free memory\n");
00297          return NULL;
00298       }
00299       strsep(&dupline, " ");
00300 
00301       if (ast_rdlock_contexts()) {
00302          ast_log(LOG_ERROR, "Failed to lock contexts list\n");
00303          free(context);
00304          return NULL;
00305       }
00306 
00307       /* go through all contexts and check if is included ... */
00308       while (!res && (c = ast_walk_contexts(c)))
00309          if (lookup_ci(c, context)) /* context is really included, complete "in" command */
00310             res = strdup("in");
00311       ast_unlock_contexts();
00312       if (!res)
00313          ast_log(LOG_WARNING, "%s not included anywhere\n", context);
00314       free(context);
00315       return res;
00316    } else if (pos == 4) { /* "dont include CTX in _X_" */
00317       /*
00318        * Context from which we removing include ... 
00319        */
00320       char *context, *dupline, *in;
00321       const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
00322       context = dupline = strdup(s);
00323       if (!dupline) {
00324          ast_log(LOG_ERROR, "Out of free memory\n");
00325          return NULL;
00326       }
00327 
00328       strsep(&dupline, " "); /* skip context */
00329 
00330       /* third word must be 'in' */
00331       in = strsep(&dupline, " ");
00332       if (!in || strcmp(in, "in")) {
00333          free(context);
00334          return NULL;
00335       }
00336 
00337       if (ast_rdlock_contexts()) {
00338          ast_log(LOG_ERROR, "Failed to lock context list\n");
00339          free(context);
00340          return NULL;
00341       }
00342 
00343       /* walk through all contexts ... */
00344       c = NULL;
00345       while ( !res && (c = ast_walk_contexts(c))) {
00346          const char *c_name = ast_get_context_name(c);
00347          if (!partial_match(c_name, word, len)) /* not a good target */
00348             continue;
00349          /* walk through all includes and check if it is our context */ 
00350          if (lookup_ci(c, context) && ++which > state)
00351             res = strdup(c_name);
00352       }
00353       ast_unlock_contexts();
00354       free(context);
00355       return res;
00356    }
00357 
00358    return NULL;
00359 }
00360 
00361 static char *complete_context_remove_include(const char *line, const char *word,
00362    int pos, int state)
00363 {
00364    int which = 0;
00365    char *res = NULL;
00366    int len = strlen(word); /* how many bytes to match */
00367    struct ast_context *c = NULL;
00368 
00369    if (pos == 3) {      /* "dialplan remove include _X_" */
00370       if (ast_rdlock_contexts()) {
00371          ast_log(LOG_ERROR, "Failed to lock context list\n");
00372          return NULL;
00373       }
00374       /* walk contexts and their includes, return the n-th match */
00375       while (!res && (c = ast_walk_contexts(c))) {
00376          struct ast_include *i = NULL;
00377 
00378          if (ast_lock_context(c))   /* error ? skip this one */
00379             continue;
00380 
00381          while ( !res && (i = ast_walk_context_includes(c, i)) ) {
00382             const char *i_name = ast_get_include_name(i);
00383             struct ast_context *nc = NULL;
00384             int already_served = 0;
00385 
00386             if (!partial_match(i_name, word, len))
00387                continue;   /* not matched */
00388 
00389             /* check if this include is already served or not */
00390 
00391             /* go through all contexts again till we reach actual
00392              * context or already_served = 1
00393              */
00394             while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
00395                already_served = lookup_ci(nc, i_name);
00396 
00397             if (!already_served && ++which > state)
00398                res = strdup(i_name);
00399          }
00400          ast_unlock_context(c);
00401       }
00402 
00403       ast_unlock_contexts();
00404       return res;
00405    } else if (pos == 4) { /* "dialplan remove include CTX _X_" */
00406       /*
00407        * complete as 'from', but only if previous context is really
00408        * included somewhere
00409        */
00410       char *context, *dupline;
00411       const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
00412 
00413       if (state > 0)
00414          return NULL;
00415       context = dupline = strdup(s);
00416       if (!dupline) {
00417          ast_log(LOG_ERROR, "Out of free memory\n");
00418          return NULL;
00419       }
00420       strsep(&dupline, " ");
00421 
00422       if (ast_rdlock_contexts()) {
00423          ast_log(LOG_ERROR, "Failed to lock contexts list\n");
00424          free(context);
00425          return NULL;
00426       }
00427 
00428       /* go through all contexts and check if is included ... */
00429       while (!res && (c = ast_walk_contexts(c)))
00430          if (lookup_ci(c, context)) /* context is really included, complete "from" command */
00431             res = strdup("from");
00432       ast_unlock_contexts();
00433       if (!res)
00434          ast_log(LOG_WARNING, "%s not included anywhere\n", context);
00435       free(context);
00436       return res;
00437    } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */
00438       /*
00439        * Context from which we removing include ... 
00440        */
00441       char *context, *dupline, *from;
00442       const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
00443       context = dupline = strdup(s);
00444       if (!dupline) {
00445          ast_log(LOG_ERROR, "Out of free memory\n");
00446          return NULL;
00447       }
00448 
00449       strsep(&dupline, " "); /* skip context */
00450 
00451       /* fourth word must be 'from' */
00452       from = strsep(&dupline, " ");
00453       if (!from || strcmp(from, "from")) {
00454          free(context);
00455          return NULL;
00456       }
00457 
00458       if (ast_rdlock_contexts()) {
00459          ast_log(LOG_ERROR, "Failed to lock context list\n");
00460          free(context);
00461          return NULL;
00462       }
00463 
00464       /* walk through all contexts ... */
00465       c = NULL;
00466       while ( !res && (c = ast_walk_contexts(c))) {
00467          const char *c_name = ast_get_context_name(c);
00468          if (!partial_match(c_name, word, len)) /* not a good target */
00469             continue;
00470          /* walk through all includes and check if it is our context */ 
00471          if (lookup_ci(c, context) && ++which > state)
00472             res = strdup(c_name);
00473       }
00474       ast_unlock_contexts();
00475       free(context);
00476       return res;
00477    }
00478 
00479    return NULL;
00480 }
00481 
00482 /*!
00483  * REMOVE EXTENSION command stuff
00484  */
00485 static int handle_context_remove_extension_deprecated(int fd, int argc, char *argv[])
00486 {
00487    int removing_priority = 0;
00488    char *exten, *context, *cid;
00489    int ret = RESULT_FAILURE;
00490 
00491    if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
00492 
00493    /*
00494     * Priority input checking ...
00495     */
00496    if (argc == 4) {
00497       char *c = argv[3];
00498 
00499       /* check for digits in whole parameter for right priority ...
00500        * why? because atoi (strtol) returns 0 if any characters in
00501        * string and whole extension will be removed, it's not good
00502        */
00503       if (!strcmp("hint", c))
00504          removing_priority = PRIORITY_HINT;
00505       else {
00506          while (*c && isdigit(*c))
00507             c++;
00508          if (*c) { /* non-digit in string */
00509             ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
00510             return RESULT_FAILURE;
00511          }
00512          removing_priority = atoi(argv[3]);
00513       }
00514 
00515       if (removing_priority == 0) {
00516          ast_cli(fd, "If you want to remove whole extension, please " \
00517             "omit priority argument\n");
00518          return RESULT_FAILURE;
00519       }
00520    }
00521 
00522    /* XXX original overwrote argv[2] */
00523    /*
00524     * Format exten@context checking ...
00525     */
00526    if (split_ec(argv[2], &exten, &context, &cid))
00527       return RESULT_FAILURE; /* XXX malloc failure */
00528    if ((!strlen(exten)) || (!(strlen(context)))) {
00529       ast_cli(fd, "Missing extension or context name in second argument '%s'\n",
00530          argv[2]);
00531       free(exten);
00532       return RESULT_FAILURE;
00533    }
00534 
00535    if (!ast_context_remove_extension_callerid(context, exten, removing_priority,
00536          /* Do NOT substitute S_OR; it is NOT the same thing */
00537          cid ? cid : (removing_priority ? "" : NULL), cid ? 1 : 0, registrar)) {
00538       if (!removing_priority)
00539          ast_cli(fd, "Whole extension %s@%s removed\n",
00540             exten, context);
00541       else
00542          ast_cli(fd, "Extension %s@%s with priority %d removed\n",
00543             exten, context, removing_priority);
00544          
00545       ret = RESULT_SUCCESS;
00546    } else {
00547       ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
00548       ret = RESULT_FAILURE;
00549    }
00550    free(exten);
00551    return ret;
00552 }
00553 
00554 static int handle_context_remove_extension(int fd, int argc, char *argv[])
00555 {
00556    int removing_priority = 0;
00557    char *exten, *context, *cid;
00558    int ret = RESULT_FAILURE;
00559 
00560    if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE;
00561 
00562    /*
00563     * Priority input checking ...
00564     */
00565    if (argc == 5) {
00566       char *c = argv[4];
00567 
00568       /* check for digits in whole parameter for right priority ...
00569        * why? because atoi (strtol) returns 0 if any characters in
00570        * string and whole extension will be removed, it's not good
00571        */
00572       if (!strcmp("hint", c))
00573          removing_priority = PRIORITY_HINT;
00574       else {
00575          while (*c && isdigit(*c))
00576             c++;
00577          if (*c) { /* non-digit in string */
00578             ast_cli(fd, "Invalid priority '%s'\n", argv[4]);
00579             return RESULT_FAILURE;
00580          }
00581          removing_priority = atoi(argv[4]);
00582       }
00583 
00584       if (removing_priority == 0) {
00585          ast_cli(fd, "If you want to remove whole extension, please " \
00586             "omit priority argument\n");
00587          return RESULT_FAILURE;
00588       }
00589    }
00590 
00591    /* XXX original overwrote argv[3] */
00592    /*
00593     * Format exten@context checking ...
00594     */
00595    if (split_ec(argv[3], &exten, &context, &cid))
00596       return RESULT_FAILURE; /* XXX malloc failure */
00597    if ((!strlen(exten)) || (!(strlen(context)))) {
00598       ast_cli(fd, "Missing extension or context name in third argument '%s'\n",
00599          argv[3]);
00600       free(exten);
00601       return RESULT_FAILURE;
00602    }
00603 
00604    if (!ast_context_remove_extension_callerid(context, exten, removing_priority,
00605          /* Do NOT substitute S_OR; it is NOT the same thing */
00606          cid ? cid : (removing_priority ? "" : NULL), cid ? 1 : 0, registrar)) {
00607       if (!removing_priority)
00608          ast_cli(fd, "Whole extension %s@%s removed\n",
00609             exten, context);
00610       else
00611          ast_cli(fd, "Extension %s@%s with priority %d removed\n",
00612             exten, context, removing_priority);
00613          
00614       ret = RESULT_SUCCESS;
00615    } else {
00616       ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
00617       ret = RESULT_FAILURE;
00618    }
00619    free(exten);
00620    return ret;
00621 }
00622 
00623 #define BROKEN_READLINE 1
00624 
00625 #ifdef BROKEN_READLINE
00626 /*
00627  * There is one funny thing, when you have word like 300@ and you hit
00628  * <tab>, you arguments will like as your word is '300 ', so it '@'
00629  * characters acts sometimes as word delimiter and sometimes as a part
00630  * of word
00631  *
00632  * This fix function, allocates new word variable and store here every
00633  * time xxx@yyy always as one word and correct pos is set too
00634  *
00635  * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
00636  * bug or feature ...
00637  */
00638 static int fix_complete_args(const char *line, char **word, int *pos)
00639 {
00640    char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
00641    int words = 0;
00642 
00643    _line = strdup(line);
00644 
00645    _strsep_line = _line;
00646    while (_strsep_line) {
00647       _previous_word = _word;
00648       _word = strsep(&_strsep_line, " ");
00649 
00650       if (_word && strlen(_word)) words++;
00651    }
00652 
00653 
00654    if (_word || _previous_word) {
00655       if (_word) {
00656          if (!strlen(_word)) words++;
00657          *word = strdup(_word);
00658       } else
00659          *word = strdup(_previous_word);
00660       *pos = words - 1;
00661       free(_line);
00662       return 0;
00663    }
00664 
00665    free(_line);
00666    return -1;
00667 }
00668 #endif /* BROKEN_READLINE */
00669 
00670 static char *complete_context_remove_extension_deprecated(const char *line, const char *word, int pos,
00671    int state)
00672 {
00673    char *ret = NULL;
00674    int which = 0;
00675 
00676 #ifdef BROKEN_READLINE
00677    char *word2;
00678    /*
00679     * Fix arguments, *word is a new allocated structure, REMEMBER to
00680     * free *word when you want to return from this function ...
00681     */
00682    if (fix_complete_args(line, &word2, &pos)) {
00683       ast_log(LOG_ERROR, "Out of free memory\n");
00684       return NULL;
00685    }
00686    word = word2;
00687 #endif
00688 
00689    if (pos == 2) { /* 'remove extension _X_' (exten/cid@context ... */
00690       struct ast_context *c = NULL;
00691       char *context = NULL, *exten = NULL, *cid = NULL;
00692       int le = 0; /* length of extension */
00693       int lc = 0; /* length of context */
00694       int lcid = 0; /* length of cid */
00695 
00696       lc = split_ec(word, &exten, &context, &cid);
00697 #ifdef BROKEN_READLINE
00698       free(word2);
00699 #endif
00700       if (lc)  /* error */
00701          return NULL;
00702       le = strlen(exten);
00703       lc = strlen(context);
00704       lcid = cid ? strlen(cid) : -1;
00705 
00706       if (ast_rdlock_contexts()) {
00707          ast_log(LOG_ERROR, "Failed to lock context list\n");
00708          goto error2;
00709       }
00710 
00711       /* find our context ... */
00712       while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
00713          struct ast_exten *e = NULL;
00714          /* XXX locking ? */
00715          if (!partial_match(ast_get_context_name(c), context, lc))
00716             continue;   /* context not matched */
00717          while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
00718             if ( !strchr(word, '/') ||
00719                   (!strchr(word, '@') && partial_match(ast_get_extension_cidmatch(e), cid, lcid)) ||
00720                   (strchr(word, '@') && !strcmp(ast_get_extension_cidmatch(e), cid))) {
00721                if ( ((strchr(word, '/') || strchr(word, '@')) && !strcmp(ast_get_extension_name(e), exten)) ||
00722                    (!strchr(word, '/') && !strchr(word, '@') && partial_match(ast_get_extension_name(e), exten, le))) { /* n-th match */
00723                   if (++which > state) {
00724                      /* If there is an extension then return exten@context. */
00725                      if (ast_get_extension_matchcid(e) && (!strchr(word, '@') || strchr(word, '/'))) {
00726                         if (asprintf(&ret, "%s/%s@%s", ast_get_extension_name(e), ast_get_extension_cidmatch(e), ast_get_context_name(c)) < 0) {
00727                            ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00728                            ret = NULL;
00729                         }
00730                         break;
00731                      } else if (!ast_get_extension_matchcid(e) && !strchr(word, '/')) {
00732                         if (asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)) < 0) {
00733                            ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00734                            ret = NULL;
00735                         }
00736                         break;
00737                      }
00738                   }
00739                }
00740             }
00741          }
00742          if (e)   /* got a match */
00743             break;
00744       }
00745 
00746       ast_unlock_contexts();
00747    error2:
00748       if (exten)
00749          free(exten);
00750    } else if (pos == 3) { /* 'remove extension EXT _X_' (priority) */
00751       char *exten = NULL, *context, *cid, *p;
00752       struct ast_context *c;
00753       int le, lc, lcid, len;
00754       const char *s = skip_words(line, 2); /* skip 'remove' 'extension' */
00755       int i = split_ec(s, &exten, &context, &cid); /* parse ext@context */
00756 
00757       if (i)   /* error */
00758          goto error3;
00759       if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
00760          *p = '\0';
00761       if ( (p = strchr(context, ' ')) ) /* remove space after context */
00762          *p = '\0';
00763       le = strlen(exten);
00764       lc = strlen(context);
00765       lcid = strlen(cid);
00766       len = strlen(word);
00767       if (le == 0 || lc == 0)
00768          goto error3;
00769 
00770       if (ast_rdlock_contexts()) {
00771          ast_log(LOG_ERROR, "Failed to lock context list\n");
00772          goto error3;
00773       }
00774 
00775       /* walk contexts */
00776       c = NULL;
00777       while ( (c = ast_walk_contexts(c)) ) {
00778          /* XXX locking on c ? */
00779          struct ast_exten *e;
00780          if (strcmp(ast_get_context_name(c), context) != 0)
00781             continue;
00782          /* got it, we must match here */
00783          e = NULL;
00784          while ( (e = ast_walk_context_extensions(c, e)) ) {
00785             struct ast_exten *priority;
00786             char buffer[10];
00787 
00788             if (cid && strcmp(ast_get_extension_cidmatch(e), cid) != 0) {
00789                continue;
00790             }
00791             if (strcmp(ast_get_extension_name(e), exten) != 0)
00792                continue;
00793             /* XXX lock e ? */
00794             priority = NULL;
00795             while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
00796                snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
00797                if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
00798                   ret = strdup(buffer);
00799             }
00800             break;
00801          }
00802          break;
00803       }
00804       ast_unlock_contexts();
00805    error3:
00806       if (exten)
00807          free(exten);
00808    }
00809 #ifdef BROKEN_READLINE
00810    free(word2);
00811 #endif
00812    return ret; 
00813 }
00814 
00815 static char *complete_context_remove_extension(const char *line, const char *word, int pos,
00816    int state)
00817 {
00818    char *ret = NULL;
00819    int which = 0;
00820 
00821 #ifdef BROKEN_READLINE
00822    char *word2;
00823    /*
00824     * Fix arguments, *word is a new allocated structure, REMEMBER to
00825     * free *word when you want to return from this function ...
00826     */
00827    if (fix_complete_args(line, &word2, &pos)) {
00828       ast_log(LOG_ERROR, "Out of free memory\n");
00829       return NULL;
00830    }
00831    word = word2;
00832 #endif
00833 
00834    if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
00835       struct ast_context *c = NULL;
00836       char *context = NULL, *exten = NULL, *cid = NULL;
00837       int le = 0; /* length of extension */
00838       int lc = 0; /* length of context */
00839       int lcid = 0; /* length of cid */
00840 
00841       lc = split_ec(word, &exten, &context, &cid);
00842       if (lc)  { /* error */
00843 #ifdef BROKEN_READLINE
00844          free(word2);
00845 #endif
00846          return NULL;
00847       }
00848       le = strlen(exten);
00849       lc = strlen(context);
00850       lcid = cid ? strlen(cid) : -1;
00851 
00852       if (ast_rdlock_contexts()) {
00853          ast_log(LOG_ERROR, "Failed to lock context list\n");
00854          goto error2;
00855       }
00856 
00857       /* find our context ... */
00858       while ( (c = ast_walk_contexts(c)) ) { /* match our context if any */
00859          struct ast_exten *e = NULL;
00860          /* XXX locking ? */
00861          if (!partial_match(ast_get_context_name(c), context, lc))
00862             continue;   /* context not matched */
00863          while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
00864             if ( !strchr(word, '/') ||
00865                   (!strchr(word, '@') && partial_match(ast_get_extension_cidmatch(e), cid, lcid)) ||
00866                   (strchr(word, '@') && !strcmp(ast_get_extension_cidmatch(e), cid))) {
00867                if ( ((strchr(word, '/') || strchr(word, '@')) && !strcmp(ast_get_extension_name(e), exten)) ||
00868                    (!strchr(word, '/') && !strchr(word, '@') && partial_match(ast_get_extension_name(e), exten, le))) { /* n-th match */
00869                   if (++which > state) {
00870                      /* If there is an extension then return exten@context. */
00871                      if (ast_get_extension_matchcid(e) && (!strchr(word, '@') || strchr(word, '/'))) {
00872                         if (asprintf(&ret, "%s/%s@%s", ast_get_extension_name(e), ast_get_extension_cidmatch(e), ast_get_context_name(c)) < 0) {
00873                            ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00874                            ret = NULL;
00875                         }
00876                         break;
00877                      } else if (!ast_get_extension_matchcid(e) && !strchr(word, '/')) {
00878                         if (asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)) < 0) {
00879                            ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00880                            ret = NULL;
00881                         }
00882                         break;
00883                      }
00884                   }
00885                }
00886             }
00887          }
00888          if (e)   /* got a match */
00889             break;
00890       }
00891 #ifdef BROKEN_READLINE
00892       free(word2);
00893 #endif
00894 
00895       ast_unlock_contexts();
00896    error2:
00897       if (exten)
00898          free(exten);
00899    } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
00900       char *exten = NULL, *context, *cid, *p;
00901       struct ast_context *c;
00902       int le, lc, lcid, len;
00903       const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */
00904       int i = split_ec(s, &exten, &context, &cid); /* parse ext@context */
00905 
00906       if (i)   /* error */
00907          goto error3;
00908       if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
00909          *p = '\0';
00910       if ( (p = strchr(context, ' ')) ) /* remove space after context */
00911          *p = '\0';
00912       le = strlen(exten);
00913       lc = strlen(context);
00914       lcid = cid ? strlen(cid) : -1;
00915       len = strlen(word);
00916       if (le == 0 || lc == 0)
00917          goto error3;
00918 
00919       if (ast_rdlock_contexts()) {
00920          ast_log(LOG_ERROR, "Failed to lock context list\n");
00921          goto error3;
00922       }
00923 
00924       /* walk contexts */
00925       c = NULL;
00926       while ( (c = ast_walk_contexts(c)) ) {
00927          /* XXX locking on c ? */
00928          struct ast_exten *e;
00929          if (strcmp(ast_get_context_name(c), context) != 0)
00930             continue;
00931          /* got it, we must match here */
00932          e = NULL;
00933          while ( (e = ast_walk_context_extensions(c, e)) ) {
00934             struct ast_exten *priority;
00935             char buffer[10];
00936 
00937             if (cid && strcmp(ast_get_extension_cidmatch(e), cid) != 0) {
00938                continue;
00939             }
00940             if (strcmp(ast_get_extension_name(e), exten) != 0)
00941                continue;
00942             /* XXX lock e ? */
00943             priority = NULL;
00944             while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
00945                snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
00946                if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
00947                   ret = strdup(buffer);
00948             }
00949             break;
00950          }
00951          break;
00952       }
00953       ast_unlock_contexts();
00954    error3:
00955       if (exten)
00956          free(exten);
00957 #ifdef BROKEN_READLINE
00958       free(word2);
00959 #endif
00960    }
00961    return ret; 
00962 }
00963 
00964 /*!
00965  * Include context ...
00966  */
00967 static int handle_context_add_include_deprecated(int fd, int argc, char *argv[])
00968 {
00969    if (argc != 5) /* include context CTX in CTX */
00970       return RESULT_SHOWUSAGE;
00971 
00972    /* third arg must be 'in' ... */
00973    if (strcmp(argv[3], "in") && strcmp(argv[3], "into")) /* XXX why both ? */
00974       return RESULT_SHOWUSAGE;
00975 
00976    if (ast_context_add_include(argv[4], argv[2], registrar)) {
00977       switch (errno) {
00978       case ENOMEM:
00979          ast_cli(fd, "Out of memory for context addition\n");
00980          break;
00981 
00982       case EBUSY:
00983          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
00984          break;
00985 
00986       case EEXIST:
00987          ast_cli(fd, "Context '%s' already included in '%s' context\n",
00988             argv[2], argv[4]);
00989          break;
00990 
00991       case ENOENT:
00992       case EINVAL:
00993          ast_cli(fd, "There is no existence of context '%s'\n",
00994             errno == ENOENT ? argv[4] : argv[2]);
00995          break;
00996 
00997       default:
00998          ast_cli(fd, "Failed to include '%s' in '%s' context\n",
00999             argv[2], argv[4]);
01000          break;
01001       }
01002       return RESULT_FAILURE;
01003    }
01004 
01005    /* show some info ... */
01006    ast_cli(fd, "Context '%s' included in '%s' context\n",
01007       argv[2], argv[4]);
01008 
01009    return RESULT_SUCCESS;
01010 }
01011 
01012 static int handle_context_add_include(int fd, int argc, char *argv[])
01013 {
01014    if (argc != 6) /* dialplan add include CTX in CTX */
01015       return RESULT_SHOWUSAGE;
01016 
01017    /* fifth arg must be 'into' ... */
01018    if (strcmp(argv[4], "into"))
01019       return RESULT_SHOWUSAGE;
01020 
01021    if (ast_context_add_include(argv[5], argv[3], registrar)) {
01022       switch (errno) {
01023       case ENOMEM:
01024          ast_cli(fd, "Out of memory for context addition\n");
01025          break;
01026 
01027       case EBUSY:
01028          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
01029          break;
01030 
01031       case EEXIST:
01032          ast_cli(fd, "Context '%s' already included in '%s' context\n",
01033             argv[3], argv[5]);
01034          break;
01035 
01036       case ENOENT:
01037       case EINVAL:
01038          ast_cli(fd, "There is no existence of context '%s'\n",
01039             errno == ENOENT ? argv[5] : argv[3]);
01040          break;
01041 
01042       default:
01043          ast_cli(fd, "Failed to include '%s' in '%s' context\n",
01044             argv[3], argv[5]);
01045          break;
01046       }
01047       return RESULT_FAILURE;
01048    }
01049 
01050    /* show some info ... */
01051    ast_cli(fd, "Context '%s' included in '%s' context\n",
01052       argv[3], argv[5]);
01053 
01054    return RESULT_SUCCESS;
01055 }
01056 
01057 static char *complete_context_add_include_deprecated(const char *line, const char *word, int pos,
01058     int state)
01059 {
01060    struct ast_context *c;
01061    int which = 0;
01062    char *ret = NULL;
01063    int len = strlen(word);
01064 
01065    if (pos == 2) {      /* 'include context _X_' (context) ... */
01066       if (ast_rdlock_contexts()) {
01067          ast_log(LOG_ERROR, "Failed to lock context list\n");
01068          return NULL;
01069       }
01070       for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
01071          if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
01072             ret = strdup(ast_get_context_name(c));
01073       ast_unlock_contexts();
01074       return ret;
01075    } else if (pos == 3) { /* include context CTX _X_ */
01076       /* complete  as 'in' if context exists or we are unable to check */
01077       char *context, *dupline;
01078       struct ast_context *c;
01079       const char *s = skip_words(line, 2);   /* should not fail */
01080 
01081       if (state != 0)   /* only once */
01082          return NULL;
01083 
01084       /* parse context from line ... */
01085       context = dupline = strdup(s);
01086       if (!context) {
01087          ast_log(LOG_ERROR, "Out of free memory\n");
01088          return strdup("in");
01089       }
01090       strsep(&dupline, " ");
01091 
01092       /* check for context existence ... */
01093       if (ast_rdlock_contexts()) {
01094          ast_log(LOG_ERROR, "Failed to lock context list\n");
01095          /* our fault, we can't check, so complete 'in' ... */
01096          ret = strdup("in");
01097       } else {
01098          for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
01099             if (!strcmp(context, ast_get_context_name(c)))
01100                ret = strdup("in"); /* found */
01101          ast_unlock_contexts();
01102       }
01103       free(context);
01104       return ret;
01105    } else if (pos == 4) { /* 'include context CTX in _X_' (dst context) */
01106       char *context, *dupline, *in;
01107       const char *s = skip_words(line, 2); /* should not fail */
01108       context = dupline = strdup(s);
01109       if (!dupline) {
01110          ast_log(LOG_ERROR, "Out of free memory\n");
01111          return NULL;
01112       }
01113       strsep(&dupline, " "); /* skip context */
01114       in = strsep(&dupline, " ");
01115       /* error if missing context or third word is not 'in' */
01116       if (!strlen(context) || strcmp(in, "in")) {
01117          ast_log(LOG_ERROR, "bad context %s or missing in %s\n",
01118             context, in);
01119          goto error3;
01120       }
01121 
01122       if (ast_rdlock_contexts()) {
01123          ast_log(LOG_ERROR, "Failed to lock context list\n");
01124          goto error3;
01125       }
01126 
01127       for (c = NULL; (c = ast_walk_contexts(c)); )
01128          if (!strcmp(context, ast_get_context_name(c)))
01129             break;
01130       if (c) { /* first context exists, go on... */
01131          /* go through all contexts ... */
01132          for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
01133             if (!strcmp(context, ast_get_context_name(c)))
01134                continue; /* skip ourselves */
01135             if (partial_match(ast_get_context_name(c), word, len) &&
01136                   !lookup_ci(c, context) /* not included yet */ &&
01137                   ++which > state)
01138                ret = strdup(ast_get_context_name(c));
01139          }
01140       } else {
01141          ast_log(LOG_ERROR, "context %s not found\n", context);
01142       }
01143       ast_unlock_contexts();
01144    error3:
01145       free(context);
01146       return ret;
01147    }
01148 
01149    return NULL;
01150 }
01151 
01152 static char *complete_context_add_include(const char *line, const char *word, int pos,
01153     int state)
01154 {
01155    struct ast_context *c;
01156    int which = 0;
01157    char *ret = NULL;
01158    int len = strlen(word);
01159 
01160    if (pos == 3) {      /* 'dialplan add include _X_' (context) ... */
01161       if (ast_rdlock_contexts()) {
01162          ast_log(LOG_ERROR, "Failed to lock context list\n");
01163          return NULL;
01164       }
01165       for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
01166          if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
01167             ret = strdup(ast_get_context_name(c));
01168       ast_unlock_contexts();
01169       return ret;
01170    } else if (pos == 4) { /* dialplan add include CTX _X_ */
01171       /* complete  as 'into' if context exists or we are unable to check */
01172       char *context, *dupline;
01173       struct ast_context *c;
01174       const char *s = skip_words(line, 3); /* should not fail */
01175 
01176       if (state != 0)   /* only once */
01177          return NULL;
01178 
01179       /* parse context from line ... */
01180       context = dupline = strdup(s);
01181       if (!context) {
01182          ast_log(LOG_ERROR, "Out of free memory\n");
01183          return strdup("into");
01184       }
01185       strsep(&dupline, " ");
01186 
01187       /* check for context existence ... */
01188       if (ast_rdlock_contexts()) {
01189          ast_log(LOG_ERROR, "Failed to lock context list\n");
01190          /* our fault, we can't check, so complete 'into' ... */
01191          ret = strdup("into");
01192       } else {
01193          for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
01194             if (!strcmp(context, ast_get_context_name(c)))
01195                ret = strdup("into"); /* found */
01196          ast_unlock_contexts();
01197       }
01198       free(context);
01199       return ret;
01200    } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
01201       char *context, *dupline, *into;
01202       const char *s = skip_words(line, 3); /* should not fail */
01203       context = dupline = strdup(s);
01204       if (!dupline) {
01205          ast_log(LOG_ERROR, "Out of free memory\n");
01206          return NULL;
01207       }
01208       strsep(&dupline, " "); /* skip context */
01209       into = strsep(&dupline, " ");
01210       /* error if missing context or fifth word is not 'into' */
01211       if (!strlen(context) || strcmp(into, "into")) {
01212          ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
01213             context, into);
01214          goto error3;
01215       }
01216 
01217       if (ast_rdlock_contexts()) {
01218          ast_log(LOG_ERROR, "Failed to lock context list\n");
01219          goto error3;
01220       }
01221 
01222       for (c = NULL; (c = ast_walk_contexts(c)); )
01223          if (!strcmp(context, ast_get_context_name(c)))
01224             break;
01225       if (c) { /* first context exists, go on... */
01226          /* go through all contexts ... */
01227          for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
01228             if (!strcmp(context, ast_get_context_name(c)))
01229                continue; /* skip ourselves */
01230             if (partial_match(ast_get_context_name(c), word, len) &&
01231                   !lookup_ci(c, context) /* not included yet */ &&
01232                   ++which > state)
01233                ret = strdup(ast_get_context_name(c));
01234          }
01235       } else {
01236          ast_log(LOG_ERROR, "context %s not found\n", context);
01237       }
01238       ast_unlock_contexts();
01239    error3:
01240       free(context);
01241       return ret;
01242    }
01243 
01244    return NULL;
01245 }
01246 
01247 /*!
01248  * \brief 'save dialplan' CLI command implementation functions ...
01249  */
01250 static int handle_save_dialplan(int fd, int argc, char *argv[])
01251 {
01252    char filename[256];
01253    struct ast_context *c;
01254    struct ast_config *cfg;
01255    struct ast_variable *v;
01256    int incomplete = 0; /* incomplete config write? */
01257    FILE *output;
01258 
01259    const char *base, *slash, *file;
01260 
01261    if (! (static_config && !write_protect_config)) {
01262       ast_cli(fd,
01263          "I can't save dialplan now, see '%s' example file.\n",
01264          config);
01265       return RESULT_FAILURE;
01266    }
01267 
01268    if (argc != 2 && argc != 3)
01269       return RESULT_SHOWUSAGE;
01270 
01271    if (ast_mutex_lock(&save_dialplan_lock)) {
01272       ast_cli(fd,
01273          "Failed to lock dialplan saving (another proccess saving?)\n");
01274       return RESULT_FAILURE;
01275    }
01276    /* XXX the code here is quite loose, a pathname with .conf in it
01277     * is assumed to be a complete pathname
01278     */
01279    if (argc == 3) {  /* have config path. Look for *.conf */
01280       base = argv[2];
01281       if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
01282          /* if filename ends with '/', do not add one */
01283          slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
01284          file = config; /* default: 'extensions.conf' */
01285       } else { /* yes, complete file name */
01286          slash = "";
01287          file = "";
01288       }
01289    } else {
01290       /* no config file, default one */
01291       base = ast_config_AST_CONFIG_DIR;
01292       slash = "/";
01293       file = config;
01294    }
01295    snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
01296 
01297    cfg = ast_config_load("extensions.conf");
01298 
01299    /* try to lock contexts list */
01300    if (ast_rdlock_contexts()) {
01301       ast_cli(fd, "Failed to lock contexts list\n");
01302       ast_mutex_unlock(&save_dialplan_lock);
01303       ast_config_destroy(cfg);
01304       return RESULT_FAILURE;
01305    }
01306 
01307    /* create new file ... */
01308    if (!(output = fopen(filename, "wt"))) {
01309       ast_cli(fd, "Failed to create file '%s'\n",
01310          filename);
01311       ast_unlock_contexts();
01312       ast_mutex_unlock(&save_dialplan_lock);
01313       ast_config_destroy(cfg);
01314       return RESULT_FAILURE;
01315    }
01316 
01317    /* fireout general info */
01318    fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\npriorityjumping=%s\n\n",
01319       static_config ? "yes" : "no",
01320       write_protect_config ? "yes" : "no",
01321                 autofallthrough_config ? "yes" : "no",
01322                 clearglobalvars_config ? "yes" : "no",
01323       ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")) ? "yes" : "no");
01324 
01325    if ((v = ast_variable_browse(cfg, "globals"))) {
01326       fprintf(output, "[globals]\n");
01327       while(v) {
01328          fprintf(output, "%s => %s\n", v->name, v->value);
01329          v = v->next;
01330       }
01331       fprintf(output, "\n");
01332    }
01333 
01334    ast_config_destroy(cfg);
01335    
01336 #define PUT_CTX_HDR  do { \
01337    if (!context_header_written) {   \
01338       fprintf(output, "[%s]\n", ast_get_context_name(c));   \
01339       context_header_written = 1;   \
01340    }  \
01341    } while (0)
01342 
01343    /* walk all contexts */
01344    for (c = NULL; (c = ast_walk_contexts(c)); ) {
01345       int context_header_written = 0;
01346       struct ast_exten *e, *last_written_e = NULL;
01347       struct ast_include *i;
01348       struct ast_ignorepat *ip;
01349       struct ast_sw *sw;
01350 
01351       /* try to lock context and fireout all info */  
01352       if (ast_lock_context(c)) { /* lock failure */
01353          incomplete = 1;
01354          continue;
01355       }
01356       /* registered by this module? */
01357       /* XXX do we need this ? */
01358       if (!strcmp(ast_get_context_registrar(c), registrar)) {
01359          fprintf(output, "[%s]\n", ast_get_context_name(c));
01360          context_header_written = 1;
01361       }
01362 
01363       /* walk extensions ... */
01364       for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
01365          struct ast_exten *p = NULL;
01366 
01367          /* fireout priorities */
01368          while ( (p = ast_walk_extension_priorities(e, p)) ) {
01369             if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
01370                continue;
01371       
01372             /* make empty line between different extensions */ 
01373             if (last_written_e != NULL &&
01374                    strcmp(ast_get_extension_name(last_written_e),
01375                       ast_get_extension_name(p)))
01376                fprintf(output, "\n");
01377             last_written_e = p;
01378          
01379             PUT_CTX_HDR;
01380 
01381             if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
01382                fprintf(output, "exten => %s,hint,%s\n",
01383                       ast_get_extension_name(p),
01384                       ast_get_extension_app(p));
01385             } else { /* copy and replace '|' with ',' */
01386                const char *sep, *cid;
01387                char *tempdata = "";
01388                char *s;
01389                const char *el = ast_get_extension_label(p);
01390                char label[128] = "";
01391  
01392                s = ast_get_extension_app_data(p);
01393                if (s) {
01394                   char *t;
01395                   tempdata = alloca(strlen(tempdata) * 2 + 1);
01396 
01397                   for (t = tempdata; *s; s++, t++) {
01398                      if (*s == '|')
01399                         *t = ',';
01400                      else {
01401                         if (*s == ',' || *s == ';')
01402                            *t++ = '\\';
01403                         *t = *s;
01404                      }
01405                   }
01406                   /* Terminating NULL */
01407                   *t = *s;
01408                }
01409 
01410                if (ast_get_extension_matchcid(p)) {
01411                   sep = "/";
01412                   cid = ast_get_extension_cidmatch(p);
01413                } else
01414                   sep = cid = "";
01415             
01416                if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2)))
01417                   incomplete = 1;   /* error encountered or label > 125 chars */
01418                
01419                fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
01420                    ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
01421                    ast_get_extension_priority(p), label,
01422                    ast_get_extension_app(p), (ast_strlen_zero(tempdata) ? "" : tempdata));
01423             }
01424          }
01425       }
01426 
01427       /* written any extensions? ok, write space between exten & inc */
01428       if (last_written_e)
01429          fprintf(output, "\n");
01430 
01431       /* walk through includes */
01432       for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
01433          if (strcmp(ast_get_include_registrar(i), registrar) != 0)
01434             continue; /* not mine */
01435          PUT_CTX_HDR;
01436          fprintf(output, "include => %s\n", ast_get_include_name(i));
01437       }
01438       if (ast_walk_context_includes(c, NULL))
01439          fprintf(output, "\n");
01440 
01441       /* walk through switches */
01442       for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
01443          if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
01444             continue; /* not mine */
01445          PUT_CTX_HDR;
01446          fprintf(output, "switch => %s/%s\n",
01447                 ast_get_switch_name(sw), ast_get_switch_data(sw));
01448       }
01449 
01450       if (ast_walk_context_switches(c, NULL))
01451          fprintf(output, "\n");
01452 
01453       /* fireout ignorepats ... */
01454       for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
01455          if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
01456             continue; /* not mine */
01457          PUT_CTX_HDR;
01458          fprintf(output, "ignorepat => %s\n",
01459                   ast_get_ignorepat_name(ip));
01460       }
01461 
01462       ast_unlock_context(c);
01463    }  
01464 
01465    ast_unlock_contexts();
01466    ast_mutex_unlock(&save_dialplan_lock);
01467    fclose(output);
01468 
01469    if (incomplete) {
01470       ast_cli(fd, "Saved dialplan is incomplete\n");
01471       return RESULT_FAILURE;
01472    }
01473 
01474    ast_cli(fd, "Dialplan successfully saved into '%s'\n",
01475       filename);
01476    return RESULT_SUCCESS;
01477 }
01478 
01479 /*!
01480  * \brief ADD EXTENSION command stuff
01481  */
01482 static int handle_context_add_extension_deprecated(int fd, int argc, char *argv[])
01483 {
01484    char *whole_exten;
01485    char *exten, *prior;
01486    int iprior = -2;
01487    char *cidmatch, *app, *app_data;
01488    char *start, *end;
01489 
01490    /* check for arguments at first */
01491    if (argc != 5 && argc != 6)
01492       return RESULT_SHOWUSAGE;
01493    if (strcmp(argv[3], "into"))
01494       return RESULT_SHOWUSAGE;
01495    if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
01496 
01497    /* XXX overwrite argv[2] */
01498    whole_exten = argv[2];
01499    exten    = strsep(&whole_exten,",");
01500    if (strchr(exten, '/')) {
01501       cidmatch = exten;
01502       strsep(&cidmatch,"/");
01503    } else {
01504       cidmatch = NULL;
01505    }
01506    prior       = strsep(&whole_exten,",");
01507    if (prior) {
01508       if (!strcmp(prior, "hint")) {
01509          iprior = PRIORITY_HINT;
01510       } else {
01511          if (sscanf(prior, "%d", &iprior) != 1) {
01512             ast_cli(fd, "'%s' is not a valid priority\n", prior);
01513             prior = NULL;
01514          }
01515       }
01516    }
01517    app = whole_exten;
01518    if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
01519       *start = *end = '\0';
01520       app_data = start + 1;
01521       ast_process_quotes_and_slashes(app_data, ',', '|');
01522    } else {
01523       if (app) {
01524          app_data = strchr(app, ',');
01525          if (app_data) {
01526             *app_data = '\0';
01527             app_data++;
01528          }
01529       } else   
01530          app_data = NULL;
01531    }
01532 
01533    if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
01534       return RESULT_SHOWUSAGE;
01535 
01536    if (!app_data)
01537       app_data="";
01538    if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
01539       (void *)strdup(app_data), ast_free, registrar)) {
01540       switch (errno) {
01541       case ENOMEM:
01542          ast_cli(fd, "Out of free memory\n");
01543          break;
01544 
01545       case EBUSY:
01546          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
01547          break;
01548 
01549       case ENOENT:
01550          ast_cli(fd, "No existence of '%s' context\n", argv[4]);
01551          break;
01552 
01553       case EEXIST:
01554          ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
01555             exten, argv[4], prior);
01556          break;
01557 
01558       default:
01559          ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
01560                exten, prior, app, app_data, argv[4]);
01561          break;
01562       }
01563       return RESULT_FAILURE;
01564    }
01565 
01566    if (argc == 6) 
01567       ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
01568          exten, argv[4], prior, exten, prior, app, app_data);
01569    else
01570       ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
01571          exten, prior, app, app_data, argv[4]);
01572 
01573    return RESULT_SUCCESS;
01574 }
01575 static int handle_context_add_extension(int fd, int argc, char *argv[])
01576 {
01577    char *whole_exten;
01578    char *exten, *prior;
01579    int iprior = -2;
01580    char *cidmatch, *app, *app_data;
01581    char *start, *end;
01582 
01583    /* check for arguments at first */
01584    if (argc != 6 && argc != 7)
01585       return RESULT_SHOWUSAGE;
01586    if (strcmp(argv[4], "into"))
01587       return RESULT_SHOWUSAGE;
01588    if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE;
01589 
01590    /* XXX overwrite argv[3] */
01591    whole_exten = argv[3];
01592    exten    = strsep(&whole_exten,",");
01593    if (strchr(exten, '/')) {
01594       cidmatch = exten;
01595       strsep(&cidmatch,"/");
01596    } else {
01597       cidmatch = NULL;
01598    }
01599    prior = strsep(&whole_exten,",");
01600    if (prior) {
01601       if (!strcmp(prior, "hint")) {
01602          iprior = PRIORITY_HINT;
01603       } else {
01604          if (sscanf(prior, "%d", &iprior) != 1) {
01605             ast_cli(fd, "'%s' is not a valid priority\n", prior);
01606             prior = NULL;
01607          }
01608       }
01609    }
01610    app = whole_exten;
01611    if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
01612       *start = *end = '\0';
01613       app_data = start + 1;
01614       ast_process_quotes_and_slashes(app_data, ',', '|');
01615    } else {
01616       if (app) {
01617          app_data = strchr(app, ',');
01618          if (app_data) {
01619             *app_data = '\0';
01620             app_data++;
01621          }
01622       } else   
01623          app_data = NULL;
01624    }
01625 
01626    if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
01627       return RESULT_SHOWUSAGE;
01628 
01629    if (!app_data)
01630       app_data="";
01631    if (ast_add_extension(argv[5], argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
01632       (void *)strdup(app_data), ast_free, registrar)) {
01633       switch (errno) {
01634       case ENOMEM:
01635          ast_cli(fd, "Out of free memory\n");
01636          break;
01637 
01638       case EBUSY:
01639          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
01640          break;
01641 
01642       case ENOENT:
01643          ast_cli(fd, "No existence of '%s' context\n", argv[5]);
01644          break;
01645 
01646       case EEXIST:
01647          ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
01648             exten, argv[5], prior);
01649          break;
01650 
01651       default:
01652          ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
01653                exten, prior, app, app_data, argv[5]);
01654          break;
01655       }
01656       return RESULT_FAILURE;
01657    }
01658 
01659    if (argc == 7)
01660       ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
01661          exten, argv[5], prior, exten, prior, app, app_data);
01662    else
01663       ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
01664          exten, prior, app, app_data, argv[5]);
01665 
01666    return RESULT_SUCCESS;
01667 }
01668 
01669 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
01670 static char *complete_context_add_extension_deprecated(const char *line, const char *word, int pos, int state)
01671 {
01672    int which = 0;
01673 
01674    if (pos == 3) {      /* complete 'into' word ... */
01675       return (state == 0) ? strdup("into") : NULL;
01676    } else if (pos == 4) { /* complete context */
01677       struct ast_context *c = NULL;
01678       int len = strlen(word);
01679       char *res = NULL;
01680 
01681       /* try to lock contexts list ... */
01682       if (ast_rdlock_contexts()) {
01683          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
01684          return NULL;
01685       }
01686 
01687       /* walk through all contexts */
01688       while ( !res && (c = ast_walk_contexts(c)) )
01689          if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
01690             res = strdup(ast_get_context_name(c));
01691       ast_unlock_contexts();
01692       return res;
01693    } else if (pos == 5) {
01694       return state == 0 ? strdup("replace") : NULL;
01695    }
01696    return NULL;
01697 }
01698 
01699 static char *complete_context_add_extension(const char *line, const char *word, int pos, int state)
01700 {
01701    int which = 0;
01702 
01703    if (pos == 4) {      /* complete 'into' word ... */
01704       return (state == 0) ? strdup("into") : NULL;
01705    } else if (pos == 5) { /* complete context */
01706       struct ast_context *c = NULL;
01707       int len = strlen(word);
01708       char *res = NULL;
01709 
01710       /* try to lock contexts list ... */
01711       if (ast_rdlock_contexts()) {
01712          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
01713          return NULL;
01714       }
01715 
01716       /* walk through all contexts */
01717       while ( !res && (c = ast_walk_contexts(c)) )
01718          if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
01719             res = strdup(ast_get_context_name(c));
01720       ast_unlock_contexts();
01721       return res;
01722    } else if (pos == 6) {
01723       return state == 0 ? strdup("replace") : NULL;
01724    }
01725    return NULL;
01726 }
01727 
01728 /*!
01729  * IGNOREPAT CLI stuff
01730  */
01731 static int handle_context_add_ignorepat_deprecated(int fd, int argc, char *argv[])
01732 {
01733    if (argc != 5)
01734       return RESULT_SHOWUSAGE;
01735    if (strcmp(argv[3], "into"))
01736       return RESULT_SHOWUSAGE;
01737 
01738    if (ast_context_add_ignorepat(argv[4], argv[2], registrar)) {
01739       switch (errno) {
01740       case ENOMEM:
01741          ast_cli(fd, "Out of free memory\n");
01742          break;
01743 
01744       case ENOENT:
01745          ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
01746          break;
01747 
01748       case EEXIST:
01749          ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
01750             argv[2], argv[4]);
01751          break;
01752 
01753       case EBUSY:
01754          ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
01755          break;
01756 
01757       default:
01758          ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
01759             argv[2], argv[4]);
01760          break;
01761       }
01762       return RESULT_FAILURE;
01763    }
01764 
01765    ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
01766       argv[2], argv[4]);
01767    return RESULT_SUCCESS;
01768 }
01769 
01770 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
01771 {
01772    if (argc != 6)
01773       return RESULT_SHOWUSAGE;
01774    if (strcmp(argv[4], "into"))
01775       return RESULT_SHOWUSAGE;
01776 
01777    if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) {
01778       switch (errno) {
01779       case ENOMEM:
01780          ast_cli(fd, "Out of free memory\n");
01781          break;
01782 
01783       case ENOENT:
01784          ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
01785          break;
01786 
01787       case EEXIST:
01788          ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
01789             argv[3], argv[5]);
01790          break;
01791 
01792       case EBUSY:
01793          ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
01794          break;
01795 
01796       default:
01797          ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
01798             argv[3], argv[5]);
01799          break;
01800       }
01801       return RESULT_FAILURE;
01802    }
01803 
01804    ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
01805       argv[3], argv[5]);
01806    return RESULT_SUCCESS;
01807 }
01808 
01809 static char *complete_context_add_ignorepat_deprecated(const char *line, const char *word,
01810    int pos, int state)
01811 {
01812    if (pos == 3)
01813       return state == 0 ? strdup("into") : NULL;
01814    else if (pos == 4) {
01815       struct ast_context *c;
01816       int which = 0;
01817       char *dupline, *ignorepat = NULL;
01818       const char *s;
01819       char *ret = NULL;
01820       int len = strlen(word);
01821 
01822       /* XXX skip first two words 'add' 'ignorepat' */
01823       s = skip_words(line, 2);
01824       if (s == NULL)
01825          return NULL;
01826       dupline = strdup(s);
01827       if (!dupline) {
01828          ast_log(LOG_ERROR, "Malloc failure\n");
01829          return NULL;
01830       }
01831       ignorepat = strsep(&dupline, " ");
01832 
01833       if (ast_rdlock_contexts()) {
01834          ast_log(LOG_ERROR, "Failed to lock contexts list\n");
01835          return NULL;
01836       }
01837 
01838       for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
01839          int found = 0;
01840 
01841          if (!partial_match(ast_get_context_name(c), word, len))
01842             continue; /* not mine */
01843          if (ignorepat) /* there must be one, right ? */
01844             found = lookup_c_ip(c, ignorepat);
01845          if (!found && ++which > state)
01846             ret = strdup(ast_get_context_name(c));
01847       }
01848 
01849       if (ignorepat)
01850          free(ignorepat);
01851       ast_unlock_contexts();
01852       return ret;
01853    }
01854 
01855    return NULL;
01856 }
01857 
01858 static char *complete_context_add_ignorepat(const char *line, const char *word,
01859    int pos, int state)
01860 {
01861    if (pos == 4)
01862       return state == 0 ? strdup("into") : NULL;
01863    else if (pos == 5) {
01864       struct ast_context *c;
01865       int which = 0;
01866       char *dupline, *ignorepat = NULL;
01867       const char *s;
01868       char *ret = NULL;
01869       int len = strlen(word);
01870 
01871       /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
01872       s = skip_words(line, 3);
01873       if (s == NULL)
01874          return NULL;
01875       dupline = strdup(s);
01876       if (!dupline) {
01877          ast_log(LOG_ERROR, "Malloc failure\n");
01878          return NULL;
01879       }
01880       ignorepat = strsep(&dupline, " ");
01881 
01882       if (ast_rdlock_contexts()) {
01883          ast_log(LOG_ERROR, "Failed to lock contexts list\n");
01884          return NULL;
01885       }
01886 
01887       for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
01888          int found = 0;
01889 
01890          if (!partial_match(ast_get_context_name(c), word, len))
01891             continue; /* not mine */
01892          if (ignorepat) /* there must be one, right ? */
01893             found = lookup_c_ip(c, ignorepat);
01894          if (!found && ++which > state)
01895             ret = strdup(ast_get_context_name(c));
01896       }
01897 
01898       if (ignorepat)
01899          free(ignorepat);
01900       ast_unlock_contexts();
01901       return ret;
01902    }
01903 
01904    return NULL;
01905 }
01906 
01907 static int handle_context_remove_ignorepat_deprecated(int fd, int argc, char *argv[])
01908 {
01909    if (argc != 5)
01910       return RESULT_SHOWUSAGE;
01911    if (strcmp(argv[3], "from"))
01912       return RESULT_SHOWUSAGE;
01913 
01914    if (ast_context_remove_ignorepat(argv[4], argv[2], registrar)) {
01915       switch (errno) {
01916       case EBUSY:
01917          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
01918          break;
01919 
01920       case ENOENT:
01921          ast_cli(fd, "There is no existence of '%s' context\n", argv[4]);
01922          break;
01923 
01924       case EINVAL:
01925          ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
01926                argv[2], argv[4]);
01927          break;
01928 
01929       default:
01930          ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[2], argv[4]);
01931          break;
01932       }
01933       return RESULT_FAILURE;
01934    }
01935 
01936    ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
01937       argv[2], argv[4]);
01938    return RESULT_SUCCESS;
01939 }
01940 
01941 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
01942 {
01943    if (argc != 6)
01944       return RESULT_SHOWUSAGE;
01945    if (strcmp(argv[4], "from"))
01946       return RESULT_SHOWUSAGE;
01947 
01948    if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) {
01949       switch (errno) {
01950       case EBUSY:
01951          ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
01952          break;
01953 
01954       case ENOENT:
01955          ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
01956          break;
01957 
01958       case EINVAL:
01959          ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
01960                argv[3], argv[5]);
01961          break;
01962 
01963       default:
01964          ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]);
01965          break;
01966       }
01967       return RESULT_FAILURE;
01968    }
01969 
01970    ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
01971       argv[3], argv[5]);
01972    return RESULT_SUCCESS;
01973 }
01974 
01975 static char *complete_context_remove_ignorepat_deprecated(const char *line, const char *word,
01976    int pos, int state)
01977 {
01978    struct ast_context *c;
01979    int which = 0;
01980    char *ret = NULL;
01981 
01982    if (pos == 2) {
01983       int len = strlen(word);
01984       if (ast_rdlock_contexts()) {
01985          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
01986          return NULL;
01987       }
01988 
01989       for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
01990          struct ast_ignorepat *ip;
01991 
01992          if (ast_lock_context(c))   /* error, skip it */
01993             continue;
01994          
01995          for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
01996             if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
01997                /* n-th match */
01998                struct ast_context *cw = NULL;
01999                int found = 0;
02000                while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
02001                   /* XXX do i stop on c, or skip it ? */
02002                   found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
02003                }
02004                if (!found)
02005                   ret = strdup(ast_get_ignorepat_name(ip));
02006             }
02007          }
02008          ast_unlock_context(c);
02009       }
02010       ast_unlock_contexts();
02011       return ret;
02012    } else if (pos == 3) {
02013        return state == 0 ? strdup("from") : NULL;
02014    } else if (pos == 4) { /* XXX check this */
02015       char *dupline, *duplinet, *ignorepat;
02016       int len = strlen(word);
02017 
02018       dupline = strdup(line);
02019       if (!dupline) {
02020          ast_log(LOG_WARNING, "Out of free memory\n");
02021          return NULL;
02022       }
02023 
02024       duplinet = dupline;
02025       strsep(&duplinet, " ");
02026       strsep(&duplinet, " ");
02027       ignorepat = strsep(&duplinet, " ");
02028 
02029       if (!ignorepat) {
02030          free(dupline);
02031          return NULL;
02032       }
02033 
02034       if (ast_rdlock_contexts()) {
02035          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
02036          free(dupline);
02037          return NULL;
02038       }
02039 
02040       for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
02041          if (ast_lock_context(c))   /* fail, skip it */
02042             continue;
02043          if (!partial_match(ast_get_context_name(c), word, len))
02044             continue;
02045          if (lookup_c_ip(c, ignorepat) && ++which > state)
02046             ret = strdup(ast_get_context_name(c));
02047          ast_unlock_context(c);
02048       }
02049       ast_unlock_contexts();
02050       free(dupline);
02051       return NULL;
02052    }
02053 
02054    return NULL;
02055 }
02056 
02057 static char *complete_context_remove_ignorepat(const char *line, const char *word,
02058    int pos, int state)
02059 {
02060    struct ast_context *c;
02061    int which = 0;
02062    char *ret = NULL;
02063 
02064    if (pos == 3) {
02065       int len = strlen(word);
02066       if (ast_rdlock_contexts()) {
02067          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
02068          return NULL;
02069       }
02070 
02071       for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
02072          struct ast_ignorepat *ip;
02073 
02074          if (ast_lock_context(c))   /* error, skip it */
02075             continue;
02076          
02077          for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
02078             if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
02079                /* n-th match */
02080                struct ast_context *cw = NULL;
02081                int found = 0;
02082                while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
02083                   /* XXX do i stop on c, or skip it ? */
02084                   found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
02085                }
02086                if (!found)
02087                   ret = strdup(ast_get_ignorepat_name(ip));
02088             }
02089          }
02090          ast_unlock_context(c);
02091       }
02092       ast_unlock_contexts();
02093       return ret;
02094    } else if (pos == 4) {
02095        return state == 0 ? strdup("from") : NULL;
02096    } else if (pos == 5) { /* XXX check this */
02097       char *dupline, *duplinet, *ignorepat;
02098       int len = strlen(word);
02099 
02100       dupline = strdup(line);
02101       if (!dupline) {
02102          ast_log(LOG_WARNING, "Out of free memory\n");
02103          return NULL;
02104       }
02105 
02106       duplinet = dupline;
02107       strsep(&duplinet, " ");
02108       strsep(&duplinet, " ");
02109       ignorepat = strsep(&duplinet, " ");
02110 
02111       if (!ignorepat) {
02112          free(dupline);
02113          return NULL;
02114       }
02115 
02116       if (ast_rdlock_contexts()) {
02117          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
02118          free(dupline);
02119          return NULL;
02120       }
02121 
02122       for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
02123          if (ast_lock_context(c))   /* fail, skip it */
02124             continue;
02125          if (!partial_match(ast_get_context_name(c), word, len))
02126             continue;
02127          if (lookup_c_ip(c, ignorepat) && ++which > state)
02128             ret = strdup(ast_get_context_name(c));
02129          ast_unlock_context(c);
02130       }
02131       ast_unlock_contexts();
02132       free(dupline);
02133       return NULL;
02134    }
02135 
02136    return NULL;
02137 }
02138 
02139 static int pbx_load_module(void);
02140 
02141 static int handle_reload_extensions(int fd, int argc, char *argv[])
02142 {
02143    if (argc != 2)
02144       return RESULT_SHOWUSAGE;
02145    if (clearglobalvars_config)
02146       pbx_builtin_clear_globals();
02147    pbx_load_module();
02148    ast_cli(fd, "Dialplan reloaded.\n");
02149    return RESULT_SUCCESS;
02150 }
02151 
02152 /*!
02153  * CLI entries for commands provided by this module
02154  */
02155 static struct ast_cli_entry cli_dont_include_deprecated = {
02156    { "dont", "include", NULL },
02157    handle_context_dont_include_deprecated, NULL,
02158    NULL, complete_context_dont_include_deprecated };
02159 
02160 static struct ast_cli_entry cli_remove_extension_deprecated = {
02161    { "remove", "extension", NULL },
02162    handle_context_remove_extension_deprecated, NULL,
02163    NULL, complete_context_remove_extension_deprecated };
02164 
02165 static struct ast_cli_entry cli_include_context_deprecated = {
02166    { "include", "context", NULL },
02167    handle_context_add_include_deprecated, NULL,
02168    NULL, complete_context_add_include_deprecated };
02169 
02170 static struct ast_cli_entry cli_add_extension_deprecated = {
02171    { "add", "extension", NULL },
02172    handle_context_add_extension_deprecated, NULL,
02173    NULL, complete_context_add_extension_deprecated };
02174 
02175 static struct ast_cli_entry cli_add_ignorepat_deprecated = {
02176    { "add", "ignorepat", NULL },
02177    handle_context_add_ignorepat_deprecated, NULL,
02178    NULL, complete_context_add_ignorepat_deprecated };
02179 
02180 static struct ast_cli_entry cli_remove_ignorepat_deprecated = {
02181    { "remove", "ignorepat", NULL },
02182    handle_context_remove_ignorepat_deprecated, NULL,
02183    NULL, complete_context_remove_ignorepat_deprecated };
02184 
02185 static struct ast_cli_entry cli_extensions_reload_deprecated = {
02186    { "extensions", "reload", NULL },
02187    handle_reload_extensions, NULL,
02188    NULL };
02189 
02190 static struct ast_cli_entry cli_save_dialplan_deprecated = {
02191    { "save", "dialplan", NULL },
02192    handle_save_dialplan, NULL,
02193    NULL };
02194 
02195 static struct ast_cli_entry cli_pbx_config[] = {
02196    { { "dialplan", "add", "extension", NULL },
02197    handle_context_add_extension, "Add new extension into context",
02198    context_add_extension_help, complete_context_add_extension, &cli_add_extension_deprecated },
02199 
02200    { { "dialplan", "remove", "extension", NULL },
02201    handle_context_remove_extension, "Remove a specified extension",
02202    context_remove_extension_help, complete_context_remove_extension, &cli_remove_extension_deprecated },
02203 
02204    { { "dialplan", "add", "ignorepat", NULL },
02205    handle_context_add_ignorepat, "Add new ignore pattern",
02206    context_add_ignorepat_help, complete_context_add_ignorepat, &cli_add_ignorepat_deprecated },
02207 
02208    { { "dialplan", "remove", "ignorepat", NULL },
02209    handle_context_remove_ignorepat, "Remove ignore pattern from context",
02210    context_remove_ignorepat_help, complete_context_remove_ignorepat, &cli_remove_ignorepat_deprecated },
02211 
02212    { { "dialplan", "add", "include", NULL },
02213    handle_context_add_include, "Include context in other context",
02214    context_add_include_help, complete_context_add_include, &cli_include_context_deprecated },
02215 
02216    { { "dialplan", "remove", "include", NULL },
02217    handle_context_remove_include, "Remove a specified include from context",
02218    context_remove_include_help, complete_context_remove_include, &cli_dont_include_deprecated },
02219 
02220    { { "dialplan", "reload", NULL },
02221    handle_reload_extensions, "Reload extensions and *only* extensions",
02222    reload_extensions_help, NULL, &cli_extensions_reload_deprecated },
02223 };
02224 
02225 
02226 static struct ast_cli_entry cli_dialplan_save = {
02227    { "dialplan", "save", NULL },
02228    handle_save_dialplan, "Save dialplan",
02229    save_dialplan_help, NULL, &cli_save_dialplan_deprecated };
02230 
02231 /*!
02232  * Standard module functions ...
02233  */
02234 static int unload_module(void)
02235 {
02236    if (static_config && !write_protect_config)
02237       ast_cli_unregister(&cli_dialplan_save);
02238    ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
02239    ast_context_destroy(NULL, registrar);
02240    return 0;
02241 }
02242 
02243 static int pbx_load_config(const char *config_file)
02244 {
02245    struct ast_config *cfg;
02246    char *end;
02247    char *label;
02248    char realvalue[256];
02249    int lastpri = -2;
02250    struct ast_context *con;
02251    struct ast_variable *v;
02252    const char *cxt;
02253    const char *aft;
02254 
02255    cfg = ast_config_load(config_file);
02256    if (!cfg)
02257       return 0;
02258 
02259    /* Use existing config to populate the PBX table */
02260    static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
02261    write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
02262    if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
02263       autofallthrough_config = ast_true(aft);
02264    clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
02265    ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING);
02266 
02267    if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) 
02268       ast_copy_string(userscontext, cxt, sizeof(userscontext));
02269    else
02270       ast_copy_string(userscontext, "default", sizeof(userscontext));
02271                             
02272    for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
02273       memset(realvalue, 0, sizeof(realvalue));
02274       pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
02275       pbx_builtin_setvar_helper(NULL, v->name, realvalue);
02276    }
02277    for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
02278       /* All categories but "general" or "globals" are considered contexts */
02279       if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
02280          continue;
02281       con=ast_context_find_or_create(&local_contexts,cxt, registrar);
02282       if (con == NULL)
02283          continue;
02284 
02285       for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
02286          if (!strcasecmp(v->name, "exten")) {
02287             char *tc = ast_strdup(v->value);
02288             if (tc) {
02289                int ipri = -2;
02290                char realext[256]="";
02291                char *plus, *firstp, *firstc;
02292                char *pri, *appl, *data, *cidmatch;
02293                char *stringp = tc;
02294                char *ext = strsep(&stringp, ",");
02295                if (!ext)
02296                   ext="";
02297                pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
02298                cidmatch = strchr(realext, '/');
02299                if (cidmatch) {
02300                   *cidmatch++ = '\0';
02301                   ast_shrink_phone_number(cidmatch);
02302                }
02303                pri = strsep(&stringp, ",");
02304                if (!pri)
02305                   pri="";
02306                pri = ast_skip_blanks(pri);
02307                pri = ast_trim_blanks(pri);
02308                label = strchr(pri, '(');
02309                if (label) {
02310                   *label++ = '\0';
02311                   end = strchr(label, ')');
02312                   if (end)
02313                      *end = '\0';
02314                   else
02315                      ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
02316                }
02317                plus = strchr(pri, '+');
02318                if (plus)
02319                   *plus++ = '\0';
02320                if (!strcmp(pri,"hint"))
02321                   ipri=PRIORITY_HINT;
02322                else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
02323                   if (lastpri > -2)
02324                      ipri = lastpri + 1;
02325                   else
02326                      ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
02327                } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
02328                   if (lastpri > -2)
02329                      ipri = lastpri;
02330                   else
02331                      ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
02332                } else if (sscanf(pri, "%d", &ipri) != 1 &&
02333                    (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
02334                   ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
02335                   ipri = 0;
02336                }
02337                appl = S_OR(stringp, "");
02338                /* Find the first occurrence of either '(' or ',' */
02339                firstc = strchr(appl, ',');
02340                firstp = strchr(appl, '(');
02341                if (firstc && (!firstp || firstc < firstp)) {
02342                   /* comma found, no parenthesis */
02343                   /* or both found, but comma found first */
02344                   appl = strsep(&stringp, ",");
02345                   data = stringp;
02346                } else if (!firstc && !firstp) {
02347                   /* Neither found */
02348                   data = "";
02349                } else {
02350                   /* Final remaining case is parenthesis found first */
02351                   appl = strsep(&stringp, "(");
02352                   data = stringp;
02353                   end = strrchr(data, ')');
02354                   if ((end = strrchr(data, ')'))) {
02355                      *end = '\0';
02356                   } else {
02357                      ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
02358                   }
02359                   ast_process_quotes_and_slashes(data, ',', '|');
02360                }
02361 
02362                if (!data)
02363                   data="";
02364                appl = ast_skip_blanks(appl);
02365                if (ipri) {
02366                   if (plus)
02367                      ipri += atoi(plus);
02368                   lastpri = ipri;
02369                   if (!ast_opt_dont_warn && !strcmp(realext, "_."))
02370                      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);
02371                   if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
02372                      ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
02373                   }
02374                }
02375                free(tc);
02376             }
02377          } else if (!strcasecmp(v->name, "include")) {
02378             memset(realvalue, 0, sizeof(realvalue));
02379             pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
02380             if (ast_context_add_include2(con, realvalue, registrar))
02381                ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
02382          } else if (!strcasecmp(v->name, "ignorepat")) {
02383             memset(realvalue, 0, sizeof(realvalue));
02384             pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
02385             if (ast_context_add_ignorepat2(con, realvalue, registrar))
02386                ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
02387          } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
02388             char *stringp= realvalue;
02389             char *appl, *data;
02390 
02391             memset(realvalue, 0, sizeof(realvalue));
02392             if (!strcasecmp(v->name, "switch"))
02393                pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
02394             else
02395                ast_copy_string(realvalue, v->value, sizeof(realvalue));
02396             appl = strsep(&stringp, "/");
02397             data = strsep(&stringp, ""); /* XXX what for ? */
02398             if (!data)
02399                data = "";
02400             if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
02401                ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
02402          }
02403       }
02404    }
02405    ast_config_destroy(cfg);
02406    return 1;
02407 }
02408 
02409 static void append_interface(char *iface, int maxlen, char *add)
02410 {
02411    int len = strlen(iface);
02412    if (strlen(add) + len < maxlen - 2) {
02413       if (strlen(iface)) {
02414          iface[len] = '&';
02415          strcpy(iface + len + 1, add);
02416       } else
02417          strcpy(iface, add);
02418    }
02419 }
02420 
02421 static void pbx_load_users(void)
02422 {
02423    struct ast_config *cfg;
02424    char *cat, *chan;
02425    const char *dahdichan;
02426    const char *hasexten;
02427    char tmp[256];
02428    char iface[256];
02429    char zapcopy[256];
02430    char *c;
02431    int len;
02432    int hasvoicemail;
02433    int start, finish, x;
02434    struct ast_context *con = NULL;
02435    
02436    cfg = ast_config_load("users.conf");
02437    if (!cfg)
02438       return;
02439 
02440    for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
02441       if (!strcasecmp(cat, "general"))
02442          continue;
02443       iface[0] = '\0';
02444       len = sizeof(iface);
02445       if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
02446          snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
02447          append_interface(iface, sizeof(iface), tmp);
02448       }
02449       if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
02450          snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
02451          append_interface(iface, sizeof(iface), tmp);
02452       }
02453       if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
02454          snprintf(tmp, sizeof(tmp), "H323/%s", cat);
02455          append_interface(iface, sizeof(iface), tmp);
02456       }
02457       hasexten = ast_config_option(cfg, cat, "hasexten");
02458       if (hasexten && !ast_true(hasexten))
02459          continue;
02460       hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
02461       dahdichan = ast_variable_retrieve(cfg, cat, "dahdichan");
02462       if (!dahdichan)
02463          dahdichan = ast_variable_retrieve(cfg, "general", "dahdichan");
02464       if (!dahdichan) {
02465       /* no dahdichan, but look for zapchan too */
02466          dahdichan = ast_variable_retrieve(cfg, cat, "zapchan");
02467          if (!dahdichan) {
02468             dahdichan = ast_variable_retrieve(cfg, "general", "zapchan");
02469          }
02470          if (!ast_strlen_zero(dahdichan)) {
02471             ast_log(LOG_WARNING, "Use of zapchan in users.conf is deprecated. Please update configuration to use dahdichan instead.\n");
02472          }
02473       }
02474       if (!ast_strlen_zero(dahdichan)) {
02475          ast_copy_string(zapcopy, dahdichan, sizeof(zapcopy));
02476          c = zapcopy;
02477          chan = strsep(&c, ",");
02478          while (chan) {
02479             if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
02480                /* Range */
02481             } else if (sscanf(chan, "%d", &start)) {
02482                /* Just one */
02483                finish = start;
02484             } else {
02485                start = 0; finish = 0;
02486             }
02487             if (finish < start) {
02488                x = finish;
02489                finish = start;
02490                start = x;
02491             }
02492             for (x = start; x <= finish; x++) {
02493                snprintf(tmp, sizeof(tmp), "%s/%d", dahdi_chan_name, x);
02494                append_interface(iface, sizeof(iface), tmp);
02495             }
02496             chan = strsep(&c, ",");
02497          }
02498       }
02499       if (!ast_strlen_zero(iface)) {
02500          /* Only create a context here when it is really needed. Otherwise default empty context
02501          created by pbx_config may conflict with the one explicitly created by pbx_ael */
02502          if (!con)
02503             con = ast_context_find_or_create(&local_contexts, userscontext, registrar);
02504 
02505          if (!con) {
02506             ast_log(LOG_ERROR, "Can't find/create user context '%s'\n", userscontext);
02507             return;
02508          }
02509 
02510          /* Add hint */
02511          ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar);
02512          /* If voicemail, use "stdexten" else use plain old dial */
02513          if (hasvoicemail) {
02514             snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat);
02515             ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar);
02516          } else {
02517             ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar);
02518          }
02519       }
02520    }
02521    ast_config_destroy(cfg);
02522 }
02523 
02524 static int pbx_load_module(void)
02525 {
02526    struct ast_context *con;
02527 
02528    if(!pbx_load_config(config))
02529       return AST_MODULE_LOAD_DECLINE;
02530    
02531    pbx_load_users();
02532 
02533    ast_merge_contexts_and_delete(&local_contexts, registrar);
02534 
02535    for (con = NULL; (con = ast_walk_contexts(con));)
02536       ast_context_verify_includes(con);
02537 
02538    pbx_set_autofallthrough(autofallthrough_config);
02539 
02540    return 0;
02541 }
02542 
02543 static int load_module(void)
02544 {
02545    if (pbx_load_module())
02546       return AST_MODULE_LOAD_DECLINE;
02547  
02548    if (static_config && !write_protect_config)
02549       ast_cli_register(&cli_dialplan_save);
02550    ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
02551 
02552    return 0;
02553 }
02554 
02555 static int reload(void)
02556 {
02557    if (clearglobalvars_config)
02558       pbx_builtin_clear_globals();
02559    pbx_load_module();
02560    return 0;
02561 }
02562 
02563 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
02564       .load = load_module,
02565       .unload = unload_module,
02566       .reload = reload,
02567           );

Generated on Wed Feb 11 11:59:58 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7