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 /*** MODULEINFO
00028    <support_level>core</support_level>
00029  ***/
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403960 $")
00034 
00035 #include "asterisk/file.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/config.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/app.h"
00043 
00044 /*** DOCUMENTATION
00045    <function name="REALTIME" language="en_US">
00046       <synopsis>
00047          RealTime Read/Write Functions.
00048       </synopsis>
00049       <syntax>
00050          <parameter name="family" required="true" />
00051          <parameter name="fieldmatch" required="true" />
00052          <parameter name="matchvalue" />
00053          <parameter name="delim1|field">
00054             <para>Use <replaceable>delim1</replaceable> with <replaceable>delim2</replaceable> on
00055             read and <replaceable>field</replaceable> without <replaceable>delim2</replaceable> on
00056             write</para>
00057             <para>If we are reading and <replaceable>delim1</replaceable> is not specified, defaults
00058             to <literal>,</literal></para>
00059          </parameter>
00060          <parameter name="delim2">
00061             <para>Parameter only used when reading, if not specified defaults to <literal>=</literal></para>
00062          </parameter>
00063       </syntax>
00064       <description>
00065          <para>This function will read or write values from/to a RealTime repository.
00066          REALTIME(....) will read names/values from the repository, and
00067          REALTIME(....)= will write a new value/field to the repository. On a
00068          read, this function returns a delimited text string. The name/value
00069          pairs are delimited by <replaceable>delim1</replaceable>, and the name and value are delimited
00070          between each other with delim2.
00071          If there is no match, NULL will be returned by the function.
00072          On a write, this function will always return NULL.</para>
00073       </description>
00074       <see-also>
00075          <ref type="function">REALTIME_STORE</ref>
00076          <ref type="function">REALTIME_DESTROY</ref>
00077          <ref type="function">REALTIME_FIELD</ref>
00078          <ref type="function">REALTIME_HASH</ref>
00079       </see-also>
00080    </function>
00081    <function name="REALTIME_STORE" language="en_US">
00082       <synopsis>
00083          RealTime Store Function.
00084       </synopsis>
00085       <syntax>
00086          <parameter name="family" required="true" />
00087          <parameter name="field1" required="true" />
00088          <parameter name="fieldN" required="true" multiple="true" />
00089          <parameter name="field30" required="true" />
00090       </syntax>
00091       <description>
00092          <para>This function will insert a new set of values into the RealTime repository.
00093          If RT engine provides an unique ID of the stored record, REALTIME_STORE(...)=..
00094          creates channel variable named RTSTOREID, which contains value of unique ID.
00095          Currently, a maximum of 30 field/value pairs is supported.</para>
00096       </description>
00097       <see-also>
00098          <ref type="function">REALTIME</ref>
00099          <ref type="function">REALTIME_DESTROY</ref>
00100          <ref type="function">REALTIME_FIELD</ref>
00101          <ref type="function">REALTIME_HASH</ref>
00102       </see-also>
00103    </function>
00104    <function name="REALTIME_DESTROY" language="en_US">
00105       <synopsis>
00106          RealTime Destroy Function.
00107       </synopsis>
00108       <syntax>
00109          <parameter name="family" required="true" />
00110          <parameter name="fieldmatch" required="true" />
00111          <parameter name="matchvalue" />
00112          <parameter name="delim1" />
00113          <parameter name="delim2" />
00114       </syntax>
00115       <description>
00116          <para>This function acts in the same way as REALTIME(....) does, except that
00117          it destroys the matched record in the RT engine.</para>
00118          <note>
00119             <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
00120             is set to <literal>no</literal>, this function can only be read from the
00121             dialplan, and not directly from external protocols. It can, however, be
00122             executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
00123          </note>
00124       </description>
00125       <see-also>
00126          <ref type="function">REALTIME</ref>
00127          <ref type="function">REALTIME_STORE</ref>
00128          <ref type="function">REALTIME_FIELD</ref>
00129          <ref type="function">REALTIME_HASH</ref>
00130       </see-also>
00131    </function>
00132    <function name="REALTIME_FIELD" language="en_US">
00133       <synopsis>
00134          RealTime query function.
00135       </synopsis>
00136       <syntax>
00137          <parameter name="family" required="true" />
00138          <parameter name="fieldmatch" required="true" />
00139          <parameter name="matchvalue" required="true" />
00140          <parameter name="fieldname" required="true" />
00141       </syntax>
00142       <description>
00143          <para>This function retrieves a single item, <replaceable>fieldname</replaceable>
00144          from the RT engine, where <replaceable>fieldmatch</replaceable> contains the value
00145          <replaceable>matchvalue</replaceable>.  When written to, the REALTIME_FIELD() function
00146          performs identically to the REALTIME() function.</para>
00147       </description>
00148       <see-also>
00149          <ref type="function">REALTIME</ref>
00150          <ref type="function">REALTIME_STORE</ref>
00151          <ref type="function">REALTIME_DESTROY</ref>
00152          <ref type="function">REALTIME_HASH</ref>
00153       </see-also>
00154    </function>
00155    <function name="REALTIME_HASH" language="en_US">
00156       <synopsis>
00157          RealTime query function.
00158       </synopsis>
00159       <syntax>
00160          <parameter name="family" required="true" />
00161          <parameter name="fieldmatch" required="true" />
00162          <parameter name="matchvalue" required="true" />
00163       </syntax>
00164       <description>
00165          <para>This function retrieves a single record from the RT engine, where
00166          <replaceable>fieldmatch</replaceable> contains the value
00167          <replaceable>matchvalue</replaceable> and formats the output suitably, such that
00168          it can be assigned to the HASH() function.  The HASH() function then provides
00169          a suitable method for retrieving each field value of the record.</para>
00170       </description>
00171       <see-also>
00172          <ref type="function">REALTIME</ref>
00173          <ref type="function">REALTIME_STORE</ref>
00174          <ref type="function">REALTIME_DESTROY</ref>
00175          <ref type="function">REALTIME_FIELD</ref>
00176       </see-also>
00177    </function>
00178  ***/
00179 
00180 AST_THREADSTORAGE(buf1);
00181 AST_THREADSTORAGE(buf2);
00182 AST_THREADSTORAGE(buf3);
00183 
00184 static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00185 {
00186    struct ast_variable *var, *head;
00187    struct ast_str *out;
00188    size_t resultslen;
00189    int n;
00190    AST_DECLARE_APP_ARGS(args,
00191       AST_APP_ARG(family);
00192       AST_APP_ARG(fieldmatch);
00193       AST_APP_ARG(value);
00194       AST_APP_ARG(delim1);
00195       AST_APP_ARG(delim2);
00196    );
00197 
00198    if (ast_strlen_zero(data)) {
00199       ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
00200       return -1;
00201    }
00202 
00203    AST_STANDARD_APP_ARGS(args, data);
00204 
00205    if (!args.delim1)
00206       args.delim1 = ",";
00207    if (!args.delim2)
00208       args.delim2 = "=";
00209 
00210    if (chan)
00211       ast_autoservice_start(chan);
00212 
00213    head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
00214 
00215    if (!head) {
00216       if (chan)
00217          ast_autoservice_stop(chan);
00218       return -1;
00219    }
00220 
00221    resultslen = 0;
00222    n = 0;
00223    for (var = head; var; n++, var = var->next)
00224       resultslen += strlen(var->name) + strlen(var->value);
00225    /* add space for delimiters and final '\0' */
00226    resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
00227 
00228    if (resultslen > len) {
00229       ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
00230       return -1;
00231    }
00232 
00233    /* len is going to be sensible, so we don't need to check for stack
00234     * overflows here. */
00235    out = ast_str_alloca(resultslen);
00236    for (var = head; var; var = var->next)
00237       ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
00238    ast_copy_string(buf, ast_str_buffer(out), len);
00239 
00240    ast_variables_destroy(head);
00241 
00242    if (chan)
00243       ast_autoservice_stop(chan);
00244 
00245    return 0;
00246 }
00247 
00248 static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00249 {
00250    int res = 0;
00251    AST_DECLARE_APP_ARGS(args,
00252       AST_APP_ARG(family);
00253       AST_APP_ARG(fieldmatch);
00254       AST_APP_ARG(value);
00255       AST_APP_ARG(field);
00256    );
00257 
00258    if (ast_strlen_zero(data)) {
00259       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
00260       return -1;
00261    }
00262 
00263    AST_STANDARD_APP_ARGS(args, data);
00264 
00265    if (ast_strlen_zero(args.fieldmatch) || ast_strlen_zero(args.field)) {
00266       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
00267       return -1;
00268    }
00269 
00270    if (chan) {
00271       ast_autoservice_start(chan);
00272    }
00273 
00274    res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, SENTINEL);
00275 
00276    if (res < 0) {
00277       ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
00278    }
00279 
00280    if (chan) {
00281       ast_autoservice_stop(chan);
00282    }
00283 
00284    return res;
00285 }
00286 
00287 static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00288 {
00289    struct ast_variable *var, *head;
00290    struct ast_str *escapebuf = ast_str_thread_get(&buf1, 16);
00291    struct ast_str *fields = ast_str_thread_get(&buf2, 16);
00292    struct ast_str *values = ast_str_thread_get(&buf3, 16);
00293    int first = 0;
00294    enum { rtfield, rthash } which;
00295    AST_DECLARE_APP_ARGS(args,
00296       AST_APP_ARG(family);
00297       AST_APP_ARG(fieldmatch);
00298       AST_APP_ARG(value);
00299       AST_APP_ARG(fieldname);
00300    );
00301 
00302    if (!strcmp(cmd, "REALTIME_FIELD")) {
00303       which = rtfield;
00304    } else {
00305       which = rthash;
00306    }
00307 
00308    if (ast_strlen_zero(data)) {
00309       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
00310       return -1;
00311    }
00312 
00313    AST_STANDARD_APP_ARGS(args, data);
00314 
00315    if ((which == rtfield && args.argc != 4) || (which == rthash && args.argc != 3)) {
00316       ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
00317       return -1;
00318    }
00319 
00320    if (chan) {
00321       ast_autoservice_start(chan);
00322    }
00323 
00324    if (!(head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL))) {
00325       if (chan) {
00326          ast_autoservice_stop(chan);
00327       }
00328       return -1;
00329    }
00330 
00331    ast_str_reset(fields);
00332    ast_str_reset(values);
00333 
00334    for (var = head; var; var = var->next) {
00335       if (which == rtfield) {
00336          ast_debug(1, "Comparing %s to %s\n", var->name, args.fieldname);
00337          if (!strcasecmp(var->name, args.fieldname)) {
00338             ast_debug(1, "Match! Value is %s\n", var->value);
00339             ast_copy_string(buf, var->value, len);
00340             break;
00341          }
00342       } else if (which == rthash) {
00343          ast_debug(1, "Setting hash key %s to value %s\n", var->name, var->value);
00344          ast_str_append(&fields, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->name, INT_MAX));
00345          ast_str_append(&values, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->value, INT_MAX));
00346          first = 0;
00347       }
00348    }
00349    ast_variables_destroy(head);
00350 
00351    if (which == rthash) {
00352       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00353       ast_copy_string(buf, ast_str_buffer(values), len);
00354    }
00355 
00356    if (chan) {
00357       ast_autoservice_stop(chan);
00358    }
00359 
00360    return 0;
00361 }
00362 
00363 static int function_realtime_store(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00364 {
00365    int res = 0;
00366    char storeid[32];
00367    char *valcopy;
00368    AST_DECLARE_APP_ARGS(a,
00369       AST_APP_ARG(family);
00370       AST_APP_ARG(f)[30]; /* fields */
00371    );
00372 
00373    AST_DECLARE_APP_ARGS(v,
00374       AST_APP_ARG(v)[30]; /* values */
00375    );
00376 
00377    if (ast_strlen_zero(data)) {
00378       ast_log(LOG_WARNING, "Syntax: REALTIME_STORE(family,field1,field2,...,field30) - missing argument!\n");
00379       return -1;
00380    }
00381 
00382    if (chan)
00383       ast_autoservice_start(chan);
00384 
00385    valcopy = ast_strdupa(value);
00386    AST_STANDARD_APP_ARGS(a, data);
00387    AST_STANDARD_APP_ARGS(v, valcopy);
00388 
00389    res = ast_store_realtime(a.family,
00390       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],
00391       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],
00392       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],
00393       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],
00394       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],
00395       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
00396    );
00397 
00398    if (res < 0) {
00399       ast_log(LOG_WARNING, "Failed to store. Check the debug log for possible data repository related entries.\n");
00400    } else {
00401       snprintf(storeid, sizeof(storeid), "%d", res);
00402       pbx_builtin_setvar_helper(chan, "RTSTOREID", storeid);
00403    }
00404 
00405    if (chan)
00406       ast_autoservice_stop(chan);
00407 
00408    return 0;
00409 }
00410 
00411 static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00412 {
00413    struct ast_variable *var, *head;
00414    struct ast_str *out;
00415    size_t resultslen;
00416    int n;
00417    AST_DECLARE_APP_ARGS(args,
00418       AST_APP_ARG(family);
00419       AST_APP_ARG(fieldmatch);
00420       AST_APP_ARG(value);
00421       AST_APP_ARG(delim1);
00422       AST_APP_ARG(delim2);
00423    );
00424 
00425    if (ast_strlen_zero(data)) {
00426       ast_log(LOG_WARNING, "Syntax: REALTIME_DESTROY(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
00427       return -1;
00428    }
00429 
00430    AST_STANDARD_APP_ARGS(args, data);
00431 
00432    if (!args.delim1)
00433       args.delim1 = ",";
00434    if (!args.delim2)
00435       args.delim2 = "=";
00436 
00437    if (chan)
00438       ast_autoservice_start(chan);
00439 
00440    head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
00441 
00442    if (!head) {
00443       if (chan)
00444          ast_autoservice_stop(chan);
00445       return -1;
00446    }
00447 
00448    if (len > 0) {
00449       resultslen = 0;
00450       n = 0;
00451       for (var = head; var; n++, var = var->next) {
00452          resultslen += strlen(var->name) + strlen(var->value);
00453       }
00454       /* add space for delimiters and final '\0' */
00455       resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
00456 
00457       if (resultslen > len) {
00458          /* Unfortunately this does mean that we cannot destroy
00459           * the row anymore. But OTOH, we're not destroying
00460           * someones data without giving him the chance to look
00461           * at it. */
00462          ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
00463          return -1;
00464       }
00465 
00466       /* len is going to be sensible, so we don't need to check for
00467        * stack overflows here. */
00468       out = ast_str_alloca(resultslen);
00469       for (var = head; var; var = var->next) {
00470          ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
00471       }
00472       ast_copy_string(buf, ast_str_buffer(out), len);
00473    }
00474 
00475    ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
00476    ast_variables_destroy(head);
00477 
00478    if (chan)
00479       ast_autoservice_stop(chan);
00480 
00481    return 0;
00482 }
00483 
00484 /*!
00485  * \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
00486  * execution even if live_dangerously is disabled.
00487  */
00488 static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00489 {
00490    return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
00491 }
00492 
00493 static struct ast_custom_function realtime_function = {
00494    .name = "REALTIME",
00495    .read = function_realtime_read,
00496    .write = function_realtime_write,
00497 };
00498 
00499 static struct ast_custom_function realtimefield_function = {
00500    .name = "REALTIME_FIELD",
00501    .read = realtimefield_read,
00502    .write = function_realtime_write,
00503 };
00504 
00505 static struct ast_custom_function realtimehash_function = {
00506    .name = "REALTIME_HASH",
00507    .read = realtimefield_read,
00508 };
00509 
00510 static struct ast_custom_function realtime_store_function = {
00511    .name = "REALTIME_STORE",
00512    .write = function_realtime_store,
00513 };
00514 
00515 static struct ast_custom_function realtime_destroy_function = {
00516    .name = "REALTIME_DESTROY",
00517    .read = function_realtime_readdestroy,
00518    .write = function_realtime_writedestroy,
00519 };
00520 
00521 static int unload_module(void)
00522 {
00523    int res = 0;
00524    res |= ast_custom_function_unregister(&realtime_function);
00525    res |= ast_custom_function_unregister(&realtime_store_function);
00526    res |= ast_custom_function_unregister(&realtime_destroy_function);
00527    res |= ast_custom_function_unregister(&realtimefield_function);
00528    res |= ast_custom_function_unregister(&realtimehash_function);
00529    return res;
00530 }
00531 
00532 static int load_module(void)
00533 {
00534    int res = 0;
00535    res |= ast_custom_function_register(&realtime_function);
00536    res |= ast_custom_function_register(&realtime_store_function);
00537    res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
00538    res |= ast_custom_function_register(&realtimefield_function);
00539    res |= ast_custom_function_register(&realtimehash_function);
00540    return res;
00541 }
00542 
00543 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read/Write/Store/Destroy values from a RealTime repository");

Generated on Thu Apr 16 06:27:36 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6