Thu Apr 28 2011 17:13:35

Asterisk developer's documentation


pbx_realtime.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, 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 Realtime PBX Module
00022  *
00023  * \arg See also: \ref AstARA
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 164485 $")
00029 
00030 #include "asterisk/file.h"
00031 #include "asterisk/logger.h"
00032 #include "asterisk/channel.h"
00033 #include "asterisk/config.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/frame.h"
00037 #include "asterisk/term.h"
00038 #include "asterisk/manager.h"
00039 #include "asterisk/cli.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/md5.h"
00042 #include "asterisk/linkedlists.h"
00043 #include "asterisk/chanvars.h"
00044 #include "asterisk/sched.h"
00045 #include "asterisk/io.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/crypto.h"
00048 #include "asterisk/astdb.h"
00049 #include "asterisk/app.h"
00050 
00051 #define MODE_MATCH      0
00052 #define MODE_MATCHMORE  1
00053 #define MODE_CANMATCH   2
00054 
00055 #define EXT_DATA_SIZE 256
00056 
00057 enum {
00058    OPTION_PATTERNS_DISABLED = (1 << 0),
00059 } option_flags;
00060 
00061 AST_APP_OPTIONS(switch_opts, {
00062    AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
00063 });
00064 
00065 /* Realtime switch looks up extensions in the supplied realtime table.
00066 
00067    [context@][realtimetable][/options]
00068 
00069    If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
00070    specified the context is assumed to be whatever is the container.
00071 
00072    The realtime table should have entries for context,exten,priority,app,args
00073    
00074    The realtime table currently does not support callerid fields.
00075 
00076 */
00077 
00078 
00079 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
00080 {
00081    struct ast_variable *var;
00082    struct ast_config *cfg;
00083    char pri[20];
00084    char *ematch;
00085    char rexten[AST_MAX_EXTENSION + 20]="";
00086    int match;
00087    /* Optimization: since we don't support hints in realtime, it's silly to
00088     * query for a hint here, since we won't actually do anything with it.
00089     * This just wastes CPU time and resources. */
00090    if (priority < 0) {
00091       return NULL;
00092    }
00093    snprintf(pri, sizeof(pri), "%d", priority);
00094    switch(mode) {
00095    case MODE_MATCHMORE:
00096       ematch = "exten LIKE";
00097       snprintf(rexten, sizeof(rexten), "%s_%%", exten);
00098       break;
00099    case MODE_CANMATCH:
00100       ematch = "exten LIKE";
00101       snprintf(rexten, sizeof(rexten), "%s%%", exten);
00102       break;
00103    case MODE_MATCH:
00104    default:
00105       ematch = "exten";
00106       ast_copy_string(rexten, exten, sizeof(rexten));
00107    }
00108    var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
00109    if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
00110       cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL);   
00111       if (cfg) {
00112          char *cat = ast_category_browse(cfg, NULL);
00113 
00114          while(cat) {
00115             switch(mode) {
00116             case MODE_MATCHMORE:
00117                match = ast_extension_close(cat, exten, 1);
00118                break;
00119             case MODE_CANMATCH:
00120                match = ast_extension_close(cat, exten, 0);
00121                break;
00122             case MODE_MATCH:
00123             default:
00124                match = ast_extension_match(cat, exten);
00125             }
00126             if (match) {
00127                var = ast_category_detach_variables(ast_category_get(cfg, cat));
00128                break;
00129             }
00130             cat = ast_category_browse(cfg, cat);
00131          }
00132          ast_config_destroy(cfg);
00133       }
00134    }
00135    return var;
00136 }
00137 
00138 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
00139 {
00140    const char *ctx = NULL;
00141    char *table;
00142    struct ast_variable *var=NULL;
00143    struct ast_flags flags = { 0, };
00144    char *buf = ast_strdupa(data);
00145    if (buf) {
00146       /* "Realtime" prefix is stripped off in the parent engine.  The
00147        * remaining string is: [[context@]table][/opts] */
00148       char *opts = strchr(buf, '/');
00149       if (opts)
00150          *opts++ = '\0';
00151       table = strchr(buf, '@');
00152       if (table) {
00153          *table++ = '\0';
00154          ctx = buf;
00155       }
00156       ctx = S_OR(ctx, context);
00157       table = S_OR(table, "extensions");
00158       if (!ast_strlen_zero(opts)) {
00159          ast_app_parse_options(switch_opts, &flags, NULL, opts);
00160       }
00161       var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
00162    }
00163    return var;
00164 }
00165 
00166 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00167 {
00168    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
00169    if (var) {
00170       ast_variables_destroy(var);
00171       return 1;
00172    }
00173    return 0;
00174 }
00175 
00176 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00177 {
00178    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
00179    if (var) {
00180       ast_variables_destroy(var);
00181       return 1;
00182    }
00183    return 0;
00184 }
00185 
00186 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00187 {
00188    int res = -1;
00189    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
00190 
00191    if (var) {
00192       char *tmp="";
00193       char *app = NULL;
00194       struct ast_variable *v;
00195 
00196       for (v = var; v ; v = v->next) {
00197          if (!strcasecmp(v->name, "app"))
00198             app = ast_strdupa(v->value);
00199          else if (!strcasecmp(v->name, "appdata")) {
00200             if (ast_compat_pbx_realtime) {
00201                char *ptr;
00202                int in = 0;
00203                tmp = alloca(strlen(v->value) * 2 + 1);
00204                for (ptr = tmp; *v->value; v->value++) {
00205                   if (*v->value == ',') {
00206                      *ptr++ = '\\';
00207                      *ptr++ = ',';
00208                   } else if (*v->value == '|' && !in) {
00209                      *ptr++ = ',';
00210                   } else {
00211                      *ptr++ = *v->value;
00212                   }
00213 
00214                   /* Don't escape '|', meaning 'or', inside expressions ($[ ]) */
00215                   if (v->value[0] == '[' && v->value[-1] == '$') {
00216                      in++;
00217                   } else if (v->value[0] == ']' && in) {
00218                      in--;
00219                   }
00220                }
00221                *ptr = '\0';
00222             } else {
00223                tmp = ast_strdupa(v->value);
00224             }
00225          }
00226       }
00227       ast_variables_destroy(var);
00228       if (!ast_strlen_zero(app)) {
00229          struct ast_app *a = pbx_findapp(app);
00230          if (a) {
00231             char appdata[512];
00232             char tmp1[80];
00233             char tmp2[80];
00234             char tmp3[EXT_DATA_SIZE];
00235 
00236             appdata[0] = 0; /* just in case the substitute var func isn't called */
00237             if(!ast_strlen_zero(tmp))
00238                pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
00239             ast_verb(3, "Executing %s(\"%s\", \"%s\")\n",
00240                    term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
00241                    term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
00242                    term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
00243             manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
00244                        "Channel: %s\r\n"
00245                        "Context: %s\r\n"
00246                        "Extension: %s\r\n"
00247                        "Priority: %d\r\n"
00248                        "Application: %s\r\n"
00249                        "AppData: %s\r\n"
00250                        "Uniqueid: %s\r\n",
00251                        chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid);
00252             
00253             res = pbx_exec(chan, a, appdata);
00254          } else
00255             ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
00256       } else {
00257          ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
00258       }
00259    }
00260    return res;
00261 }
00262 
00263 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00264 {
00265    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
00266    if (var) {
00267       ast_variables_destroy(var);
00268       return 1;
00269    }
00270    return 0;
00271 }
00272 
00273 static struct ast_switch realtime_switch =
00274 {
00275         name:                   "Realtime",
00276         description:       "Realtime Dialplan Switch",
00277         exists:                 realtime_exists,
00278         canmatch:               realtime_canmatch,
00279         exec:                   realtime_exec,
00280         matchmore:              realtime_matchmore,
00281 };
00282 
00283 static int unload_module(void)
00284 {
00285    ast_unregister_switch(&realtime_switch);
00286    return 0;
00287 }
00288 
00289 static int load_module(void)
00290 {
00291    if (ast_register_switch(&realtime_switch))
00292       return AST_MODULE_LOAD_FAILURE;
00293    return AST_MODULE_LOAD_SUCCESS;
00294 }
00295 
00296 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");