Merge "res_rtp_asterisk: Add ice_blacklist option"
[asterisk/asterisk.git] / funcs / func_realtime.c
index 9add212..0ce191b 100644 (file)
@@ -2,10 +2,10 @@
  * Asterisk -- An open source telephony toolkit.
  *
  * Copyright (C) 2005-2006, BJ Weschke. All rights reserved.
- * 
+ *
  * BJ Weschke <bweschke@btwtech.com>
- * 
- * This code is released by the author with no restrictions on usage. 
+ *
+ * This code is released by the author with no restrictions on usage.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
 /*! \file
  *
  * \brief REALTIME dialplan function
- * 
+ *
  * \author BJ Weschke <bweschke@btwtech.com>
- * 
+ *
  * \ingroup functions
  */
 
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -45,7 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <syntax>
                        <parameter name="family" required="true" />
                        <parameter name="fieldmatch" required="true" />
-                       <parameter name="value" />
+                       <parameter name="matchvalue" />
                        <parameter name="delim1|field">
                                <para>Use <replaceable>delim1</replaceable> with <replaceable>delim2</replaceable> on
                                read and <replaceable>field</replaceable> without <replaceable>delim2</replaceable> on
@@ -59,14 +63,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </syntax>
                <description>
                        <para>This function will read or write values from/to a RealTime repository.
-                       REALTIME(....) will read names/values from the repository, and 
+                       REALTIME(....) will read names/values from the repository, and
                        REALTIME(....)= will write a new value/field to the repository. On a
                        read, this function returns a delimited text string. The name/value
                        pairs are delimited by <replaceable>delim1</replaceable>, and the name and value are delimited
-                       between each other with delim2. 
+                       between each other with delim2.
                        If there is no match, NULL will be returned by the function.
                        On a write, this function will always return NULL.</para>
                </description>
+               <see-also>
+                       <ref type="function">REALTIME_STORE</ref>
+                       <ref type="function">REALTIME_DESTROY</ref>
+                       <ref type="function">REALTIME_FIELD</ref>
+                       <ref type="function">REALTIME_HASH</ref>
+               </see-also>
        </function>
        <function name="REALTIME_STORE" language="en_US">
                <synopsis>
@@ -84,6 +94,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        creates channel variable named RTSTOREID, which contains value of unique ID.
                        Currently, a maximum of 30 field/value pairs is supported.</para>
                </description>
+               <see-also>
+                       <ref type="function">REALTIME</ref>
+                       <ref type="function">REALTIME_DESTROY</ref>
+                       <ref type="function">REALTIME_FIELD</ref>
+                       <ref type="function">REALTIME_HASH</ref>
+               </see-also>
        </function>
        <function name="REALTIME_DESTROY" language="en_US">
                <synopsis>
@@ -92,14 +108,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <syntax>
                        <parameter name="family" required="true" />
                        <parameter name="fieldmatch" required="true" />
-                       <parameter name="value" />
+                       <parameter name="matchvalue" />
                        <parameter name="delim1" />
                        <parameter name="delim2" />
                </syntax>
                <description>
                        <para>This function acts in the same way as REALTIME(....) does, except that
                        it destroys the matched record in the RT engine.</para>
+                       <note>
+                               <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+                               is set to <literal>no</literal>, this function can only be read from the
+                               dialplan, and not directly from external protocols. It can, however, be
+                               executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
+                       </note>
                </description>
+               <see-also>
+                       <ref type="function">REALTIME</ref>
+                       <ref type="function">REALTIME_STORE</ref>
+                       <ref type="function">REALTIME_FIELD</ref>
+                       <ref type="function">REALTIME_HASH</ref>
+               </see-also>
        </function>
        <function name="REALTIME_FIELD" language="en_US">
                <synopsis>
@@ -108,15 +136,21 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <syntax>
                        <parameter name="family" required="true" />
                        <parameter name="fieldmatch" required="true" />
-                       <parameter name="value" required="true" />
+                       <parameter name="matchvalue" required="true" />
                        <parameter name="fieldname" required="true" />
                </syntax>
                <description>
                        <para>This function retrieves a single item, <replaceable>fieldname</replaceable>
                        from the RT engine, where <replaceable>fieldmatch</replaceable> contains the value
-                       <replaceable>value</replaceable>.  When written to, the REALTIME_FIELD() function
+                       <replaceable>matchvalue</replaceable>.  When written to, the REALTIME_FIELD() function
                        performs identically to the REALTIME() function.</para>
                </description>
+               <see-also>
+                       <ref type="function">REALTIME</ref>
+                       <ref type="function">REALTIME_STORE</ref>
+                       <ref type="function">REALTIME_DESTROY</ref>
+                       <ref type="function">REALTIME_HASH</ref>
+               </see-also>
        </function>
        <function name="REALTIME_HASH" language="en_US">
                <synopsis>
@@ -125,15 +159,21 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <syntax>
                        <parameter name="family" required="true" />
                        <parameter name="fieldmatch" required="true" />
-                       <parameter name="value" required="true" />
+                       <parameter name="matchvalue" required="true" />
                </syntax>
                <description>
                        <para>This function retrieves a single record from the RT engine, where
                        <replaceable>fieldmatch</replaceable> contains the value
-                       <replaceable>value</replaceable> and formats the output suitably, such that
+                       <replaceable>matchvalue</replaceable> and formats the output suitably, such that
                        it can be assigned to the HASH() function.  The HASH() function then provides
                        a suitable method for retrieving each field value of the record.</para>
                </description>
+               <see-also>
+                       <ref type="function">REALTIME</ref>
+                       <ref type="function">REALTIME_STORE</ref>
+                       <ref type="function">REALTIME_DESTROY</ref>
+                       <ref type="function">REALTIME_FIELD</ref>
+               </see-also>
        </function>
  ***/
 
@@ -141,7 +181,7 @@ AST_THREADSTORAGE(buf1);
 AST_THREADSTORAGE(buf2);
 AST_THREADSTORAGE(buf3);
 
-static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
+static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
        struct ast_variable *var, *head;
        struct ast_str *out;
@@ -156,7 +196,7 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
        );
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
+               ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
                return -1;
        }
 
@@ -185,6 +225,13 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
        /* add space for delimiters and final '\0' */
        resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
 
+       if (resultslen > len) {
+               ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+               return -1;
+       }
+
+       /* len is going to be sensible, so we don't need to check for stack
+        * overflows here. */
        out = ast_str_alloca(resultslen);
        for (var = head; var; var = var->next)
                ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
@@ -209,28 +256,35 @@ static int function_realtime_write(struct ast_channel *chan, const char *cmd, ch
        );
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value,newcol) - missing argument!\n", cmd);
+               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
                return -1;
        }
 
-       if (chan)
-               ast_autoservice_start(chan);
-
        AST_STANDARD_APP_ARGS(args, data);
 
+       if (ast_strlen_zero(args.fieldmatch) || ast_strlen_zero(args.field)) {
+               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
+               return -1;
+       }
+
+       if (chan) {
+               ast_autoservice_start(chan);
+       }
+
        res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, SENTINEL);
 
        if (res < 0) {
                ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
        }
 
-       if (chan)
+       if (chan) {
                ast_autoservice_stop(chan);
+       }
 
-       return 0;
+       return res;
 }
 
-static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
+static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
        struct ast_variable *var, *head;
        struct ast_str *escapebuf = ast_str_thread_get(&buf1, 16);
@@ -252,14 +306,14 @@ static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *d
        }
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
+               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
                return -1;
        }
 
        AST_STANDARD_APP_ARGS(args, data);
 
        if ((which == rtfield && args.argc != 4) || (which == rthash && args.argc != 3)) {
-               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,value%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
+               ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
                return -1;
        }
 
@@ -332,7 +386,7 @@ static int function_realtime_store(struct ast_channel *chan, const char *cmd, ch
        AST_STANDARD_APP_ARGS(a, data);
        AST_STANDARD_APP_ARGS(v, valcopy);
 
-       res = ast_store_realtime(a.family, 
+       res = ast_store_realtime(a.family,
                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],
                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],
                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],
@@ -354,7 +408,7 @@ static int function_realtime_store(struct ast_channel *chan, const char *cmd, ch
        return 0;
 }
 
-static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 
+static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
        struct ast_variable *var, *head;
        struct ast_str *out;
@@ -369,7 +423,7 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
        );
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "Syntax: REALTIME_DESTROY(family,fieldmatch[,value[,delim1[,delim2]]]) - missing argument!\n");
+               ast_log(LOG_WARNING, "Syntax: REALTIME_DESTROY(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
                return -1;
        }
 
@@ -391,18 +445,32 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
                return -1;
        }
 
-       resultslen = 0;
-       n = 0;
-       for (var = head; var; n++, var = var->next)
-               resultslen += strlen(var->name) + strlen(var->value);
-       /* add space for delimiters and final '\0' */
-       resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+       if (len > 0) {
+               resultslen = 0;
+               n = 0;
+               for (var = head; var; n++, var = var->next) {
+                       resultslen += strlen(var->name) + strlen(var->value);
+               }
+               /* add space for delimiters and final '\0' */
+               resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+
+               if (resultslen > len) {
+                       /* Unfortunately this does mean that we cannot destroy
+                        * the row anymore. But OTOH, we're not destroying
+                        * someones data without giving him the chance to look
+                        * at it. */
+                       ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+                       return -1;
+               }
 
-       out = ast_str_alloca(resultslen);
-       for (var = head; var; var = var->next) {
-               ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+               /* len is going to be sensible, so we don't need to check for
+                * stack overflows here. */
+               out = ast_str_alloca(resultslen);
+               for (var = head; var; var = var->next) {
+                       ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+               }
+               ast_copy_string(buf, ast_str_buffer(out), len);
        }
-       ast_copy_string(buf, ast_str_buffer(out), len);
 
        ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
        ast_variables_destroy(head);
@@ -413,6 +481,15 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
        return 0;
 }
 
+/*!
+ * \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
+ * execution even if live_dangerously is disabled.
+ */
+static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+       return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
+}
+
 static struct ast_custom_function realtime_function = {
        .name = "REALTIME",
        .read = function_realtime_read,
@@ -438,6 +515,7 @@ static struct ast_custom_function realtime_store_function = {
 static struct ast_custom_function realtime_destroy_function = {
        .name = "REALTIME_DESTROY",
        .read = function_realtime_readdestroy,
+       .write = function_realtime_writedestroy,
 };
 
 static int unload_module(void)
@@ -456,7 +534,7 @@ static int load_module(void)
        int res = 0;
        res |= ast_custom_function_register(&realtime_function);
        res |= ast_custom_function_register(&realtime_store_function);
-       res |= ast_custom_function_register(&realtime_destroy_function);
+       res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
        res |= ast_custom_function_register(&realtimefield_function);
        res |= ast_custom_function_register(&realtimehash_function);
        return res;