--- 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");