--- func_curl.c.orig 2010-09-24 09:06:07.000000000 +0200 +++ func_curl.c 2010-09-24 11:03:52.000000000 +0200 @@ -91,6 +91,12 @@ enum optiontype { OT_ENUM, }; +enum hashcompat_modes { + HC_OFF, + HC_RFC1738, /* regular URI encoding */ + HC_RFC2396_QC, /* query component, replace plus with space */ +}; + static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot) { if (!strcasecmp(name, "header")) { @@ -153,7 +159,7 @@ static int parse_curlopt_key(const char *ot = OT_BOOLEAN; } else if (!strcasecmp(name, "hashcompat")) { *key = CURLOPT_SPECIAL_HASHCOMPAT; - *ot = OT_BOOLEAN; + *ot = OT_ENUM; } else { return -1; } @@ -242,6 +248,16 @@ static int acf_curlopt_write(struct ast_ if ((new = ast_calloc(1, sizeof(*new)))) { new->value = (void *)ptype; } + } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) { + if ((new = ast_calloc(1, sizeof(*new)))) { + if (!strcasecmp(value, "querycomponent")) { + new->value = (void *)HC_RFC2396_QC; + } else if (ast_true(value)) { + new->value = (void *)HC_RFC1738; + } else { + new->value = (void *)HC_OFF; + } + } } else { /* Highly unlikely */ goto yuck; @@ -394,6 +410,35 @@ static void curl_instance_cleanup(void * AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup); +/*! \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'; +} + static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len) { struct ast_str *str = ast_str_create(16); @@ -405,7 +450,7 @@ static int acf_curl_exec(struct ast_chan CURL **curl; struct curl_settings *cur; struct ast_datastore *store = NULL; - int hashcompat = 0; + enum hashcompat_modes hashcompat = HC_OFF; AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL; *buf = '\0'; @@ -430,7 +475,7 @@ static int acf_curl_exec(struct ast_chan AST_LIST_LOCK(&global_curl_info); AST_LIST_TRAVERSE(&global_curl_info, cur, list) { if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) { - hashcompat = (cur->value != NULL) ? 1 : 0; + hashcompat = (enum hashcompat_modes)cur->value; } else { curl_easy_setopt(*curl, cur->key, cur->value); } @@ -441,7 +486,7 @@ static int acf_curl_exec(struct ast_chan AST_LIST_LOCK(list); AST_LIST_TRAVERSE(list, cur, list) { if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) { - hashcompat = (cur->value != NULL) ? 1 : 0; + hashcompat = (enum hashcompat_modes)cur->value; } else { curl_easy_setopt(*curl, cur->key, cur->value); } @@ -471,7 +516,7 @@ static int acf_curl_exec(struct ast_chan ast_str_trim_blanks(str); ast_debug(3, "str='%s'\n", ast_str_buffer(str)); - if (hashcompat) { + if (hashcompat != HC_OFF) { char *remainder = ast_str_buffer(str); char *piece; struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2); @@ -479,8 +524,13 @@ static int acf_curl_exec(struct ast_chan int rowcount = 0; while ((piece = strsep(&remainder, "&"))) { char *name = strsep(&piece, "="); - ast_uri_decode(piece); - ast_uri_decode(name); + if (hashcompat == HC_RFC1738) { + ast_uri_decode(piece); + ast_uri_decode(name); + } else /*if (hashcompat == HC_RFC2396_QC)*/ { + querycomponent_uri_decode(piece); + querycomponent_uri_decode(name); + } ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", name); ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", piece); rowcount++; @@ -533,7 +583,9 @@ struct ast_custom_function acf_curlopt = " useragent - UserAgent string to use\n" " userpwd - A : to use for authentication\n" " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n" -" hashcompat - Result data will be compatible for use with HASH()\n" +" hashcompat - Result data will be compatible for use with HASH(). Takes a\n" +" boolean or the value 'querycomponent' which means that\n" +" pluses in the input will be decoded as spaces" "", .read = acf_curlopt_read, .write = acf_curlopt_write,