Thu Apr 28 2011 17:13:33

Asterisk developer's documentation


func_realtime.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2005-2006, BJ Weschke. All rights reserved.
00005  * 
00006  * BJ Weschke <bweschke@btwtech.com>
00007  * 
00008  * This code is released by the author with no restrictions on usage. 
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  */
00017 
00018 /*! \file
00019  *
00020  * \brief REALTIME dialplan function
00021  * 
00022  * \author BJ Weschke <bweschke@btwtech.com>
00023  * 
00024  * \ingroup functions
00025  */
00026 
00027 #include "asterisk.h"
00028 
00029 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 206811 $")
00030 
00031 #include "asterisk/file.h"
00032 #include "asterisk/channel.h"
00033 #include "asterisk/pbx.h"
00034 #include "asterisk/config.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/lock.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/app.h"
00039 
00040 /*** DOCUMENTATION
00041    <function name="REALTIME" language="en_US">
00042       <synopsis>
00043          RealTime Read/Write Functions.
00044       </synopsis>
00045       <syntax>
00046          <parameter name="family" required="true" />
00047          <parameter name="fieldmatch" required="true" />
00048          <parameter name="value" />
00049          <parameter name="delim1|field">
00050             <para>Use <replaceable>delim1</replaceable> with <replaceable>delim2</replaceable> on
00051             read and <replaceable>field</replaceable> without <replaceable>delim2</replaceable> on
00052             write</para>
00053             <para>If we are reading and <replaceable>delim1</replaceable> is not specified, defaults
00054             to <literal>,</literal></para>
00055          </parameter>
00056          <parameter name="delim2">
00057             <para>Parameter only used when reading, if not specified defaults to <literal>=</literal></para>
00058          </parameter>
00059       </syntax>
00060       <description>
00061          <para>This function will read or write values from/to a RealTime repository.
00062          REALTIME(....) will read names/values from the repository, and 
00063          REALTIME(....)= will write a new value/field to the repository. On a
00064          read, this function returns a delimited text string. The name/value
00065          pairs are delimited by <replaceable>delim1</replaceable>, and the name and value are delimited
00066          between each other with delim2. 
00067          If there is no match, NULL will be returned by the function.
00068          On a write, this function will always return NULL.</para>
00069       </description>
00070    </function>
00071    <function name="REALTIME_STORE" language="en_US">
00072       <synopsis>
00073          RealTime Store Function.
00074       </synopsis>
00075       <syntax>
00076          <parameter name="family" required="true" />
00077          <parameter name="field1" required="true" />
00078          <parameter name="fieldN" required="true" multiple="true" />
00079          <parameter name="field30" required="true" />
00080       </syntax>
00081       <description>
00082          <para>This function will insert a new set of values into the RealTime repository.
00083          If RT engine provides an unique ID of the stored record, REALTIME_STORE(...)=..
00084          creates channel variable named RTSTOREID, which contains value of unique ID.
00085          Currently, a maximum of 30 field/value pairs is supported.</para>
00086       </description>
00087    </function>
00088    <function name="REALTIME_DESTROY" language="en_US">
00089       <synopsis>
00090          RealTime Destroy Function.
00091       </synopsis>
00092       <syntax>
00093          <parameter name="family" required="true" />
00094          <parameter name="fieldmatch" required="true" />
00095          <parameter name="value" />
00096          <parameter name="delim1" />
00097          <parameter name="delim2" />
00098       </syntax>
00099       <description>
00100          <para>This function acts in the same way as REALTIME(....) does, except that
00101          it destroys the matched record in the RT engine.</para>
00102       </description>
00103    </function>
00104    <function name="REALTIME_FIELD" language="en_US">
00105       <synopsis>
00106          RealTime query function.
00107       </synopsis>
00108       <syntax>
00109          <parameter name="family" required="true" />
00110          <parameter name="fieldmatch" required="true" />
00111          <parameter name="value" required="true" />
00112          <parameter name="fieldname" required="true" />
00113       </syntax>
00114       <description>
00115          <para>This function retrieves a single item, <replaceable>fieldname</replaceable>
00116          from the RT engine, where <replaceable>fieldmatch</replaceable> contains the value
00117          <replaceable>value</replaceable>.  When written to, the REALTIME_FIELD() function
00118          performs identically to the REALTIME() function.</para>
00119       </description>
00120    </function>
00121    <function name="REALTIME_HASH" language="en_US">
00122       <synopsis>
00123          RealTime query function.
00124       </synopsis>
00125       <syntax>
00126          <parameter name="family" required="true" />
00127          <parameter name="fieldmatch" required="true" />
00128          <parameter name="value" required="true" />
00129       </syntax>
00130       <description>
00131          <para>This function retrieves a single record from the RT engine, where
00132          <replaceable>fieldmatch</replaceable> contains the value
00133          <replaceable>value</replaceable> and formats the output suitably, such that
00134          it can be assigned to the HASH() function.  The HASH() function then provides
00135          a suitable method for retrieving each field value of the record.</para>
00136       </description>
00137    </function>
00138  ***/
00139 
00140 AST_THREADSTORAGE(buf1);
00141 AST_THREADSTORAGE(buf2);
00142 AST_THREADSTORAGE(buf3);
00143 
00144 static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
00145 {
00146    struct ast_variable *var, *head;
00147    struct ast_str *out;
00148    size_t resultslen;
00149    int n;
00150    AST_DECLARE_APP_ARGS(args,
00151       AST_APP_ARG(family);
00152       AST_APP_ARG(fieldmatch);
00153       AST_APP_ARG(value);
00154       AST_APP_ARG(delim1);
00155       AST_APP_ARG(delim2);
00156    );
00157 
00158    if (ast_strlen_zero(data)) {
00159       ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
00160       return -1;
00161    }
00162 
00163    AST_STANDARD_APP_ARGS(args, data);
00164 
00165    if (!args.delim1)
00166       args.delim1 = ",";
00167    if (!args.delim2)
00168       args.delim2 = "=";
00169 
00170    if (chan)
00171       ast_autoservice_start(chan);
00172 
00173    head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
00174 
00175    if (!head) {
00176       if (chan)
00177          ast_autoservice_stop(chan);
00178       return -1;
00179    }
00180 
00181    resultslen = 0;
00182    n = 0;
00183    for (var = head; var; n++, var = var->next)
00184       resultslen += strlen(var->name) + strlen(var->value);
00185    /* add space for delimiters and final '\0' */
00186    resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
00187 
00188    out = ast_str_alloca(resultslen);
00189    for (var = head; var; var = var->next)
00190       ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
00191    ast_copy_string(buf, ast_str_buffer(out), len);
00192 
00193    ast_variables_destroy(head);
00194 
00195    if (chan)
00196       ast_autoservice_stop(chan);
00197 
00198    return 0;
00199 }
00200 
00201 static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00202 {
00203    int res = 0;
00204    AST_DECLARE_APP_ARGS(args,
00205       AST_APP_ARG(family);
00206       AST_APP_ARG(fieldmatch);
00207       AST_APP_ARG(value);
00208       AST_APP_ARG(field);
00209    );
00210 
00211    if (ast_strlen_zero(data)) {
00212       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value,newcol) - missing argument!\n", cmd);
00213       return -1;
00214    }
00215 
00216    if (chan)
00217       ast_autoservice_start(chan);
00218 
00219    AST_STANDARD_APP_ARGS(args, data);
00220 
00221    res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, SENTINEL);
00222 
00223    if (res < 0) {
00224       ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
00225    }
00226 
00227    if (chan)
00228       ast_autoservice_stop(chan);
00229 
00230    return 0;
00231 }
00232 
00233 static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
00234 {
00235    struct ast_variable *var, *head;
00236    struct ast_str *escapebuf = ast_str_thread_get(&buf1, 16);
00237    struct ast_str *fields = ast_str_thread_get(&buf2, 16);
00238    struct ast_str *values = ast_str_thread_get(&buf3, 16);
00239    int first = 0;
00240    enum { rtfield, rthash } which;
00241    AST_DECLARE_APP_ARGS(args,
00242       AST_APP_ARG(family);
00243       AST_APP_ARG(fieldmatch);
00244       AST_APP_ARG(value);
00245       AST_APP_ARG(fieldname);
00246    );
00247 
00248    if (!strcmp(cmd, "REALTIME_FIELD")) {
00249       which = rtfield;
00250    } else {
00251       which = rthash;
00252    }
00253 
00254    if (ast_strlen_zero(data)) {
00255       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
00256       return -1;
00257    }
00258 
00259    AST_STANDARD_APP_ARGS(args, data);
00260 
00261    if ((which == rtfield && args.argc != 4) || (which == rthash && args.argc != 3)) {
00262       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
00263       return -1;
00264    }
00265 
00266    if (chan) {
00267       ast_autoservice_start(chan);
00268    }
00269 
00270    if (!(head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL))) {
00271       if (chan) {
00272          ast_autoservice_stop(chan);
00273       }
00274       return -1;
00275    }
00276 
00277    ast_str_reset(fields);
00278    ast_str_reset(values);
00279 
00280    for (var = head; var; var = var->next) {
00281       if (which == rtfield) {
00282          ast_debug(1, "Comparing %s to %s\n", var->name, args.fieldname);
00283          if (!strcasecmp(var->name, args.fieldname)) {
00284             ast_debug(1, "Match! Value is %s\n", var->value);
00285             ast_copy_string(buf, var->value, len);
00286             break;
00287          }
00288       } else if (which == rthash) {
00289          ast_debug(1, "Setting hash key %s to value %s\n", var->name, var->value);
00290          ast_str_append(&fields, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->name, INT_MAX));
00291          ast_str_append(&values, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->value, INT_MAX));
00292          first = 0;
00293       }
00294    }
00295    ast_variables_destroy(head);
00296 
00297    if (which == rthash) {
00298       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00299       ast_copy_string(buf, ast_str_buffer(values), len);
00300    }
00301 
00302    if (chan) {
00303       ast_autoservice_stop(chan);
00304    }
00305 
00306    return 0;
00307 }
00308 
00309 static int function_realtime_store(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00310 {
00311    int res = 0;
00312    char storeid[32];
00313    char *valcopy;
00314    AST_DECLARE_APP_ARGS(a,
00315       AST_APP_ARG(family);
00316       AST_APP_ARG(f)[30]; /* fields */
00317    );
00318 
00319    AST_DECLARE_APP_ARGS(v,
00320       AST_APP_ARG(v)[30]; /* values */
00321    );
00322 
00323    if (ast_strlen_zero(data)) {
00324       ast_log(LOG_WARNING, "Syntax: REALTIME_STORE(family,field1,field2,...,field30) - missing argument!\n");
00325       return -1;
00326    }
00327 
00328    if (chan)
00329       ast_autoservice_start(chan);
00330 
00331    valcopy = ast_strdupa(value);
00332    AST_STANDARD_APP_ARGS(a, data);
00333    AST_STANDARD_APP_ARGS(v, valcopy);
00334 
00335    res = ast_store_realtime(a.family, 
00336       a.f[0], v.v[0], a.f[1], v.v[1], a.f[2], v.v[2], a.f[3], v.v[3], a.f[4], v.v[4],
00337       a.f[5], v.v[5], a.f[6], v.v[6], a.f[7], v.v[7], a.f[8], v.v[8], a.f[9], v.v[9],
00338       a.f[10], v.v[10], a.f[11], v.v[11], a.f[12], v.v[12], a.f[13], v.v[13], a.f[14], v.v[14],
00339       a.f[15], v.v[15], a.f[16], v.v[16], a.f[17], v.v[17], a.f[18], v.v[18], a.f[19], v.v[19],
00340       a.f[20], v.v[20], a.f[21], v.v[21], a.f[22], v.v[22], a.f[23], v.v[23], a.f[24], v.v[24],
00341       a.f[25], v.v[25], a.f[26], v.v[26], a.f[27], v.v[27], a.f[28], v.v[28], a.f[29], v.v[29], SENTINEL
00342    );
00343 
00344    if (res < 0) {
00345       ast_log(LOG_WARNING, "Failed to store. Check the debug log for possible data repository related entries.\n");
00346    } else {
00347       snprintf(storeid, sizeof(storeid), "%d", res);
00348       pbx_builtin_setvar_helper(chan, "RTSTOREID", storeid);
00349    }
00350 
00351    if (chan)
00352       ast_autoservice_stop(chan);
00353 
00354    return 0;
00355 }
00356 
00357 static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
00358 {
00359    struct ast_variable *var, *head;
00360    struct ast_str *out;
00361    size_t resultslen;
00362    int n;
00363    AST_DECLARE_APP_ARGS(args,
00364       AST_APP_ARG(family);
00365       AST_APP_ARG(fieldmatch);
00366       AST_APP_ARG(value);
00367       AST_APP_ARG(delim1);
00368       AST_APP_ARG(delim2);
00369    );
00370 
00371    if (ast_strlen_zero(data)) {
00372       ast_log(LOG_WARNING, "Syntax: REALTIME_DESTROY(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
00373       return -1;
00374    }
00375 
00376    AST_STANDARD_APP_ARGS(args, data);
00377 
00378    if (!args.delim1)
00379       args.delim1 = ",";
00380    if (!args.delim2)
00381       args.delim2 = "=";
00382 
00383    if (chan)
00384       ast_autoservice_start(chan);
00385 
00386    head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
00387 
00388    if (!head) {
00389       if (chan)
00390          ast_autoservice_stop(chan);
00391       return -1;
00392    }
00393 
00394    resultslen = 0;
00395    n = 0;
00396    for (var = head; var; n++, var = var->next)
00397       resultslen += strlen(var->name) + strlen(var->value);
00398    /* add space for delimiters and final '\0' */
00399    resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
00400 
00401    out = ast_str_alloca(resultslen);
00402    for (var = head; var; var = var->next) {
00403       ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
00404    }
00405    ast_copy_string(buf, ast_str_buffer(out), len);
00406 
00407    ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
00408    ast_variables_destroy(head);
00409 
00410    if (chan)
00411       ast_autoservice_stop(chan);
00412 
00413    return 0;
00414 }
00415 
00416 struct ast_custom_function realtime_function = {
00417    .name = "REALTIME",
00418    .read = function_realtime_read,
00419    .write = function_realtime_write,
00420 };
00421 
00422 struct ast_custom_function realtimefield_function = {
00423    .name = "REALTIME_FIELD",
00424    .read = realtimefield_read,
00425    .write = function_realtime_write,
00426 };
00427 
00428 struct ast_custom_function realtimehash_function = {
00429    .name = "REALTIME_HASH",
00430    .read = realtimefield_read,
00431 };
00432 
00433 struct ast_custom_function realtime_store_function = {
00434    .name = "REALTIME_STORE",
00435    .write = function_realtime_store,
00436 };
00437 
00438 struct ast_custom_function realtime_destroy_function = {
00439    .name = "REALTIME_DESTROY",
00440    .read = function_realtime_readdestroy,
00441 };
00442 
00443 static int unload_module(void)
00444 {
00445    int res = 0;
00446    res |= ast_custom_function_unregister(&realtime_function);
00447    res |= ast_custom_function_unregister(&realtime_store_function);
00448    res |= ast_custom_function_unregister(&realtime_destroy_function);
00449    res |= ast_custom_function_unregister(&realtimefield_function);
00450    res |= ast_custom_function_unregister(&realtimehash_function);
00451    return res;
00452 }
00453 
00454 static int load_module(void)
00455 {
00456    int res = 0;
00457    res |= ast_custom_function_register(&realtime_function);
00458    res |= ast_custom_function_register(&realtime_store_function);
00459    res |= ast_custom_function_register(&realtime_destroy_function);
00460    res |= ast_custom_function_register(&realtimefield_function);
00461    res |= ast_custom_function_register(&realtimehash_function);
00462    return res;
00463 }
00464 
00465 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read/Write/Store/Destroy values from a RealTime repository");