]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
func_odbc: Introduce minargs config and expose ARGC in addition to ARGn.
authorJaco Kroon <jaco@uls.co.za>
Wed, 17 Feb 2021 20:51:17 +0000 (22:51 +0200)
committerGeorge Joseph <gjoseph@digium.com>
Tue, 23 Feb 2021 18:18:28 +0000 (12:18 -0600)
minargs enables enforcing of minimum count of arguments to pass to
func_odbc, so if you're unconditionally using ARG1 through ARG4 then
this should be set to 4.  func_odbc will generate an error in this case,
so for example

[FOO]
minargs = 4

and ODBC_FOO(a,b,c) in dialplan will now error out instead of using a
potentially leaked ARG4 from Gosub().

ARGC is needed if you're using optional argument, to verify whether or
not an argument has been passed, else it's possible to use a leaked ARGn
from Gosub (app_stack).  So now you can safely do
${IF($[${ARGC}>3]?${ARGV}:default value)} kind of thing.

Change-Id: I6ca0b137d90b03f6aa9c496991f6cbf1518f6c24
Signed-off-by: Jaco Kroon <jaco@uls.co.za>
configs/samples/func_odbc.conf.sample
doc/CHANGES-staging/func_odbc_ARGC_minargs.txt [new file with mode: 0644]
funcs/func_odbc.c

index c467f7ec065c36b87ed6e528723bf498c4c28e0a..b825974ea780c7c697fd1c8dd8a83d224f819d00 100644 (file)
 ; For substitution, you have ${ARG1}, ${ARG2} ... ${ARGn}
 ; for the arguments to each SQL statement.
 ;
+; Additionally you can use ${ARGC} to determine the number of arguments that
+; was actually passed (or risk using leaked ARGn variables from the channel).
+; Also reference the minargs configuration option.
+;
 ; In addition, for write statements, you have ${VAL1}, ${VAL2} ... ${VALn}
 ; parsed, just like arguments, for the values.  In addition, if you want the
 ; whole value, never mind the parsing, you can get that with ${VALUE}.
 ;              These additional rows can be returned by using the name of the
 ;              function which was called to retrieve the first row as an
 ;              argument to ODBC_FETCH().
+; minargs      The minimum number of ARGUMENTS that has to be passed to the
+;              function.  If fewer arguments than this is passed, then the call
+;              will fail.  It is important to note that unlike Gosub() and friends,
+;              func_odbc will not mask out ARGn variables that it's not actively
+;              using, as such, without this, it's entirely possible to use say
+;              ARG2 from the Gosub() inside func_odbc when the intent was to
+;              use an argument passed to func_odbc, but it simply was never passed.
 
 
 ; ODBC_SQL - Allow an SQL statement to be built entirely in the dialplan
diff --git a/doc/CHANGES-staging/func_odbc_ARGC_minargs.txt b/doc/CHANGES-staging/func_odbc_ARGC_minargs.txt
new file mode 100644 (file)
index 0000000..0984b50
--- /dev/null
@@ -0,0 +1,20 @@
+Subject: func_odbc
+
+Introduce an ARGC variable for func_odbc functions, along with a minargs
+per-function configuration option.
+
+minargs enables enforcing of minimum count of arguments to pass to
+func_odbc, so if you're unconditionally using ARG1 through ARG4 then
+this should be set to 4.  func_odbc will generate an error in this case,
+so for example
+
+[FOO]
+minargs = 4
+
+and ODBC_FOO(a,b,c) in dialplan will now error out instead of using a
+potentially leaked ARG4 from Gosub().
+
+ARGC is needed if you're using optional argument, to verify whether or
+not an argument has been passed, else it's possible to use a leaked ARGn
+from Gosub (app_stack).  So now you can safely do
+${IF($[${ARGC}>3]?${ARGV}:default value)} kind of thing.
index 5cc0faaa98e64913b3b44e768b156591f90cda80..9d6d0fc3044d06d16d0dc372e94e81a5e9d2b04e 100644 (file)
@@ -120,6 +120,7 @@ struct acf_odbc_query {
        char *sql_insert;
        unsigned int flags;
        int rowlimit;
+       int minargs;
        struct ast_custom_function *acf;
 };
 
@@ -545,6 +546,14 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
                return -1;
        }
 
+       AST_STANDARD_APP_ARGS(args, s);
+       if (args.argc < query->minargs) {
+               ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
+                               args.argc, cmd, query->minargs);
+               AST_RWLIST_UNLOCK(&queries);
+               return -1;
+       }
+
        if (!chan) {
                if (!(chan = ast_dummy_channel_alloc())) {
                        AST_RWLIST_UNLOCK(&queries);
@@ -578,7 +587,8 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
                return -1;
        }
 
-       AST_STANDARD_APP_ARGS(args, s);
+       snprintf(varname, sizeof(varname), "%u", args.argc);
+       pbx_builtin_pushvar_helper(chan, "ARGC", varname);
        for (i = 0; i < args.argc; i++) {
                snprintf(varname, sizeof(varname), "ARG%d", i + 1);
                pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
@@ -603,6 +613,8 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
                chan = ast_channel_unref(chan);
        } else {
                /* Restore prior values */
+               pbx_builtin_setvar_helper(chan, "ARGC", NULL);
+
                for (i = 0; i < args.argc; i++) {
                        snprintf(varname, sizeof(varname), "ARG%d", i + 1);
                        pbx_builtin_setvar_helper(chan, varname, NULL);
@@ -756,6 +768,14 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
                return -1;
        }
 
+       AST_STANDARD_APP_ARGS(args, s);
+       if (args.argc < query->minargs) {
+               ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
+                               args.argc, cmd, query->minargs);
+               AST_RWLIST_UNLOCK(&queries);
+               return -1;
+       }
+
        if (!chan) {
                if (!(chan = ast_dummy_channel_alloc())) {
                        AST_RWLIST_UNLOCK(&queries);
@@ -768,7 +788,8 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
                ast_autoservice_start(chan);
        }
 
-       AST_STANDARD_APP_ARGS(args, s);
+       snprintf(varname, sizeof(varname), "%u", args.argc);
+       pbx_builtin_pushvar_helper(chan, "ARGC", varname);
        for (x = 0; x < args.argc; x++) {
                snprintf(varname, sizeof(varname), "ARG%d", x + 1);
                pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
@@ -780,6 +801,8 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
                chan = ast_channel_unref(chan);
        } else {
                /* Restore prior values */
+               pbx_builtin_setvar_helper(chan, "ARGC", NULL);
+
                for (x = 0; x < args.argc; x++) {
                        snprintf(varname, sizeof(varname), "ARG%d", x + 1);
                        pbx_builtin_setvar_helper(chan, varname, NULL);
@@ -1290,6 +1313,10 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu
                        sscanf(tmp, "%30d", &((*query)->rowlimit));
        }
 
+       if ((tmp = ast_variable_retrieve(cfg, catg, "minargs"))) {
+               sscanf(tmp, "%30d", &((*query)->minargs));
+       }
+
        (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
        if (!(*query)->acf) {
                free_acf_query(*query);