From 6837c5ac80e3624c6dc3a82d03e50d68b2f5a989 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 26 Oct 2026 17:13:36 +0100 Subject: Second run of making variable and function names more readable, urlencoder now matches overall naming scheme --- ot_urlencoded.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 ot_urlencoded.c (limited to 'ot_urlencoded.c') diff --git a/ot_urlencoded.c b/ot_urlencoded.c new file mode 100644 index 0000000..1ca6e81 --- /dev/null +++ b/ot_urlencoded.c @@ -0,0 +1,183 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* Opentracker */ +#include "ot_urlencoded.h" + +/* System */ +#include + +/* Idea is to do a in place replacement or guarantee at least + strlen( string ) bytes in deststring + watch http://www.ietf.org/rfc/rfc2396.txt + unreserved = alphanum | mark + mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + we add '%' to the matrix to not stop at encoded chars. + After losing too many requests to being too strict, add the following characters to reserved matrix + relax = "+" | "," | "/" | ";" | "<" | ">" | ":" +*/ + +/* This matrix holds for each ascii character the information, + whether it is a non-terminating character for one of the three + scan states we are in, that is 'path', 'param' and 'value' from + /path?param=value¶m=value, it is encoded in bit 0, 1 and 2 + respectively + + The top bit of lower nibble indicates, whether this character is + a hard terminator, ie. \0, \n or \s, where the whole scanning + process should terminate + */ +static const unsigned char is_unreserved[256] = { + 8,0,0,0,0,0,0,0,0,0,8,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 8,7,8,8,8,7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,7,6, + 4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,7, + 8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,7,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +/* Do a fast nibble to hex representation conversion */ +static unsigned char fromhex(unsigned char x) { + x -= '0'; + if (x <= 9) + return x; + x &= ~0x20; + x -= 'A' - '0'; + if (x < 6) + return x + 10; + return 0xff; +} + +/* Skip the value of a param=value pair */ +void urlencoded_skip_value(char **string) { + const unsigned char *s = *(const unsigned char **)string; + unsigned char f; + + /* Since we are asked to skip the 'value', we assume to stop at + terminators for a 'value' string position */ + while ((f = is_unreserved[*s++]) & SCAN_SEARCHPATH_VALUE) + ; + + /* If we stopped at a hard terminator like \0 or \n, make the + next urlencoded_parse encounter it again */ + if (f & SCAN_SEARCHPATH_TERMINATOR) + --s; + + *string = (char *)s; +} + +int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { + char *deststring = *string; + ssize_t match_length = urlencoded_parse(string, deststring, flags); + + if (match_length < 0) + return match_length; + if (match_length == 0) + return -3; + + while (keywords->key) { + if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length]) + return keywords->value; + keywords++; + } + + return -3; +} + +ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { + const unsigned char *s = *(const unsigned char **)string; + unsigned char *d = (unsigned char *)deststring; + unsigned char b, c; + + /* This is the main decoding loop. + 'flag' determines, which characters are non-terminating in current context + (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path ) + */ + while (is_unreserved[c = *s++] & flags) { + + /* When encountering an url escaped character, try to decode */ + if (c == '%') { + if ((b = fromhex(*s++)) == 0xff) + return -1; + if ((c = fromhex(*s++)) == 0xff) + return -1; + c |= (b << 4); + } + + /* Write (possibly decoded) character to output */ + *d++ = c; + } + + switch (c) { + case 0: + case '\r': + case '\n': + case ' ': + /* If we started scanning on a hard terminator, indicate we've finished */ + if (d == (unsigned char *)deststring) + return -2; + + /* Else make the next call to urlencoded_parse encounter it again */ + --s; + break; + case '?': + if (flags != SCAN_PATH) + return -1; + break; + case '=': + if (flags != SCAN_SEARCHPATH_PARAM) + return -1; + break; + case '&': + if (flags == SCAN_PATH) + return -1; + if (flags == SCAN_SEARCHPATH_PARAM) + --s; + break; + default: + return -1; + } + + *string = (char *)s; + return d - (unsigned char *)deststring; +} + +ssize_t scan_fixed_int(char *data, size_t len, int *tmp) { + int minus = 0; + *tmp = 0; + if (*data == '-') + --len, ++data, ++minus; + while ((len > 0) && (*data >= '0') && (*data <= '9')) { + --len; + *tmp = 10 * *tmp + *data++ - '0'; + } + if (minus) + *tmp = -*tmp; + return len; +} + +size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) { + static const char safe[] = "$/.=~-_"; + static const char hexdigit[] = "0123456789abcdef"; + size_t written = 0, i; + + for (i = 0; i < len; ++i) { + uint8_t c = src[i]; + + /* strchr would find the terminator, so a zero byte never counts as safe */ + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) { + dest[written++] = (char)c; + } else { + dest[written] = '%'; + dest[written + 1] = hexdigit[c >> 4]; + dest[written + 2] = hexdigit[c & 15]; + written += 3; + } + } + + return written; +} -- cgit v1.3