Add mg_next_query_string_entry_n() and mg_url_decode_n()

Move to mg_util.h so encode and decode are next ot each other.

Pull out mg_next_list_entry_n() for advanced use cases.

Add unit tests.
This commit is contained in:
Deomid Ryabkov committed 2020-10-24 22:53:50 +01:00
1 parent 9fe1c93c9b
commit be64f81eee
13 files changed
+424 -156

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
BasedOnStyle: Google
AllowShortFunctionsOnASingleLine: false
SpaceAfterCStyleCast: true
PointerBindsToType: false
DerivePointerBinding: false
IncludeBlocks: Preserve
+46 -31
View File
@@ -413,6 +413,43 @@ int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
return len;
}
static struct mg_str mg_next_list_entry_n(struct mg_str list, char sep1,
struct mg_str *val1, char sep2,
struct mg_str *val2) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val1 = list;
if ((chr = mg_strchr(*val1, sep1)) != NULL) {
/* Comma found. Store length and shift the list ptr */
val1->len = chr - val1->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (val2 != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
val2->len = 0;
val2->p = (const char *) memchr(val1->p, sep2, val1->len);
if (val2->p != NULL) {
val2->p++; /* Skip over sep2 character */
val2->len = val1->p + val1->len - val2->p;
val1->len = (val2->p - val1->p) - 1;
}
}
}
return list;
}
const char *mg_next_comma_list_entry(const char *, struct mg_str *,
struct mg_str *) WEAK;
const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
@@ -425,38 +462,16 @@ struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val) {
if (list.len == 0) {
/* End of the list */
list = mg_mk_str(NULL);
} else {
const char *chr = NULL;
*val = list;
return mg_next_list_entry_n(list, ',', val, '=', eq_val);
}
if ((chr = mg_strchr(*val, ',')) != NULL) {
/* Comma found. Store length and shift the list ptr */
val->len = chr - val->p;
chr++;
list.len -= (chr - list.p);
list.p = chr;
} else {
/* This value is the last one */
list = mg_mk_str_n(list.p + list.len, 0);
}
if (eq_val != NULL) {
/* Value has form "x=y", adjust pointers and lengths */
/* so that val points to "x", and eq_val points to "y". */
eq_val->len = 0;
eq_val->p = (const char *) memchr(val->p, '=', val->len);
if (eq_val->p != NULL) {
eq_val->p++; /* Skip over '=' character */
eq_val->len = val->p + val->len - eq_val->p;
val->len = (eq_val->p - val->p) - 1;
}
}
}
return list;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) WEAK;
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val) {
return mg_next_list_entry_n(list, '&', val, '=', eq_val);
}
size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
+8
View File
@@ -148,6 +148,14 @@ const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
struct mg_str *eq_val);
/*
* Helper for parsing query strings.
* Parses '&' and '=' entries. Does not perform unescaping.
*/
struct mg_str mg_next_query_string_entry_n(struct mg_str list,
struct mg_str *val,
struct mg_str *eq_val);
/*
* Matches 0-terminated string (mg_match_prefix) or string with given length
* mg_match_prefix_n against a glob pattern. Glob syntax:
-28
View File
@@ -1624,34 +1624,6 @@ static void mg_http_serve_file2(struct mg_connection *nc, const char *path,
#endif
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
return -1;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; /* Null-terminate the destination */
return i >= src_len ? j : -1;
}
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
size_t dst_len) {
const char *p, *e, *s;
-14
View File
@@ -329,20 +329,6 @@ void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
*Otherwise,
* a length of the decoded string is returned, not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
const size_t *msg_lens, uint8_t *digest);
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
+50
View File
@@ -338,3 +338,53 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
struct mg_str mg_url_encode(const struct mg_str src) {
return mg_url_encode_opt(src, mg_mk_str("._-$,;~()/"), 0);
}
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded) {
struct mg_str srcs = MG_MK_STR_N(src, (size_t) src_len);
struct mg_str dsts = MG_MK_STR_N(dst, (size_t) dst_len);
int res = mg_url_decode_n(srcs, &dsts, is_form_url_encoded);
if (res >= 0) {
if (res < dst_len) {
dst[res] = '\0';
} else {
res = -1; /* Not enough space for NUL-temrination. */
}
}
return res;
}
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
int mg_url_decode_n(struct mg_str srcs, struct mg_str *dsts,
int is_form_url_encoded) {
int i, j, a, b, src_len, dst_len;
const char *src = srcs.p;
char *dst;
if (dsts == NULL) return -1;
dst = (char *) dsts->p;
src_len = (int) srcs.len;
dst_len = (int) dsts->len;
for (i = j = 0; i < src_len && j < dst_len; i++, j++) {
if (src[i] == '%') {
if (i < src_len - 2 && isxdigit(*(const unsigned char *) (src + i + 1)) &&
isxdigit(*(const unsigned char *) (src + i + 2))) {
a = tolower(*(const unsigned char *) (src + i + 1));
b = tolower(*(const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else {
break;
}
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dsts->len = (size_t) j;
return i == src_len ? j : -1;
}
#undef HEXTOI
+21
View File
@@ -205,6 +205,27 @@ struct mg_str mg_url_encode_opt(const struct mg_str src,
/* Same as `mg_url_encode_opt(src, "._-$,;~()/", 0)`. */
struct mg_str mg_url_encode(const struct mg_str src);
/*
* Decodes a URL-encoded string.
*
* Source string is specified by (`src`, `src_len`), and destination is
* (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
* `+` character is decoded as a blank space character. This function
* guarantees to NUL-terminate the destination. If destination is too small,
* then the source string is partially decoded and `-1` is returned.
* Otherwise, the length of the decoded string is returned,
* not counting final NUL.
*/
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
int is_form_url_encoded);
/*
* mg_str variant of mg_url_decode. Does not NUL-terminate dst.
* It is ok for src and dst to be the same.
*/
int mg_url_decode_n(struct mg_str src, struct mg_str *dst,
int is_form_url_encoded);
#ifdef __cplusplus
}
#endif /* __cplusplus */