--- funcs/func_uri.c.orig 2010-09-23 17:05:20.000000000 +0200 +++ funcs/func_uri.c 2010-09-23 17:05:09.000000000 +0200 @@ -65,6 +65,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi Returns the decoded URI-encoded data string. + + + Gets a single field from a application/x-www-form-urlencoded query string. + + + + The variable holding the query string to process. + + + The field name to get. + + + Optionally skip the first N fields. A negative index is allowed to count from the end. + + + + Returns the URI-decoded content from the index-th field + with name field from query string found in variable + varname. + + ***/ /*! \brief uriencode: Encode URL according to RFC 2396 */ @@ -96,6 +117,157 @@ static int uridecode(struct ast_channel return 0; } +/*! \brief querycomponent_uri_decode: Decode URI-encoded query component URI + * + * Within a query component, the characters ";", "/", "?", ":", "@", + * "&", "=", "+", ",", and "$" are reserved. + * (For other components, the rules are different and you can use + * ast_uri_decode.) + */ +static void querycomponent_uri_decode(char *s) +{ + char *o; + unsigned int tmp; + + for (o = s; *s; s++, o++) { + if (*s == '+') { + *o = ' '; + } else if (*s == '%' && s[1] != '\0' && s[2] != '\0' && sscanf(s + 1, "%2x", &tmp) == 1) { + /* have '%', two chars and correct parsing */ + *o = tmp; + s += 2; /* Will be incremented once more when we break out */ + } else { + /* Yes, we accept ";", "/", "?", ":", "@", "&", "=", + * "," and "$" here. (Postel's Law) + */ + *o = *s; + } + } + *o = '\0'; +} + +/*!\brief qsfield: Get value from a single field from a query string */ +static int qsfield(struct ast_channel *chan, const char *cmd, char *parse, + char *buf, size_t len) +{ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(varname); + AST_APP_ARG(field); + AST_APP_ARG(index); + ); + char const *querystring, *p; + char *field; + int index = 0; + size_t field_len; + + AST_STANDARD_APP_ARGS(args, parse); + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Usage: %s(,[,])\n", cmd); + return -1; + } + + /* Decode escapes */ + field_len = strlen(args.field) + 2; /* field "=" NUL */ + field = alloca(field_len); + ast_get_encoded_str(args.field, field, field_len); + field_len = strlen(field); /* re-get decoded field length */ + field[field_len++] = '='; + field[field_len] = '\0'; + + /* Fetch and check values */ + if (ast_strlen_zero(args.varname) || ast_strlen_zero(field)) { + ast_log(LOG_ERROR, "The field to search for and the variable name must not be empty.\n"); + return -1; + } + querystring = pbx_builtin_getvar_helper(chan, args.varname); + if (querystring == NULL) { + ast_log(LOG_ERROR, "Variable %s not found.\n", args.varname); + return -1; + } + if (args.argc >= 3) { /* be forward compatible and allow 4+ args */ + index = (int)atoi(args.index); + } + + /* TODO: in theory, the field names could be encoded as well, but in + * practice this doesn't happen. For now, we do not decode field names + * because this makes both coding and running this function a lot + * faster ;) */ + if (index >= 0) { + /* Search forwards */ + p = querystring; + while (1) { + /* Compare */ + if (!memcmp(p, field, field_len)) { + if (index == 0) { + break; /* found */ + } else { + --index; + } + } + /* Search to after next amp */ + p = strchr(p, '&'); + if (p == NULL) { + break; /* not found */ + } + ++p; + } + } else { + /* Search backwards */ + p = querystring + strlen(querystring); + while (1) { + /* Search to after last amp */ + while (p != querystring && *p != '&') + --p; + if (*p == '&') + ++p; + /* Compare */ + if (!memcmp(p, field, field_len)) { + if (index == -1) { + break; /* found */ + } else { + ++index; + } + } + if (p == querystring || p - 1 == querystring) { + p = NULL; + break; /* not found */ + } + p -= 2; /* or we'd infloop on "&&field=val" */ + } + } + + /* Okay, we're out of the loop. Do we have a match? */ + if (p == NULL) { + /* No luck */ + ast_log(LOG_NOTICE, "Field %s not found in query string from %s on requested index\n", field, args.varname); + buf[0] = '\0'; /* len is guaranteed to be >= 1 */ + return 0; + } + + /* Copy data and decode (p is start, e is end) */ + { + char const *e; + size_t value_len; + p += field_len; + e = strchr(p, '&'); + if (e == NULL) { + value_len = strlen(p); + } else { + value_len = e - p; + } + if (value_len >= len) { + value_len = len - 1; + } + memcpy(buf, p, value_len); + buf[value_len] = '\0'; + + /* Decode */ + querycomponent_uri_decode(buf); + } + return 0; +} + static struct ast_custom_function urldecode_function = { .name = "URIDECODE", .read = uridecode, @@ -106,16 +278,23 @@ static struct ast_custom_function urlenc .read = uriencode, }; +static struct ast_custom_function qsfield_function = { + .name = "QSFIELD", + .read = qsfield, +}; + static int unload_module(void) { return ast_custom_function_unregister(&urldecode_function) - || ast_custom_function_unregister(&urlencode_function); + || ast_custom_function_unregister(&urlencode_function) + || ast_custom_function_unregister(&qsfield_function); } static int load_module(void) { return ast_custom_function_register(&urldecode_function) - || ast_custom_function_register(&urlencode_function); + || ast_custom_function_register(&urlencode_function) + || ast_custom_function_register(&qsfield_function); } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "URI encode/decode dialplan functions");