summaryrefslogtreecommitdiff
path: root/src/ot_urlencoded.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_urlencoded.c')
-rw-r--r--src/ot_urlencoded.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/ot_urlencoded.c b/src/ot_urlencoded.c
new file mode 100644
index 0000000..4109bb5
--- /dev/null
+++ b/src/ot_urlencoded.c
@@ -0,0 +1,168 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* Opentracker */
5#include "ot_urlencoded.h"
6
7/* System */
8#include <string.h>
9
10/* Idea is to do a in place replacement or guarantee at least
11 strlen( string ) bytes in deststring
12 watch http://www.ietf.org/rfc/rfc2396.txt
13 unreserved = alphanum | mark
14 mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
15 we add '%' to the matrix to not stop at encoded chars.
16 After losing too many requests to being too strict, add the following characters to reserved matrix
17 relax = "+" | "," | "/" | ";" | "<" | ">" | ":"
18*/
19
20/* This matrix holds for each ascii character the information,
21 whether it is a non-terminating character for one of the three
22 scan states we are in, that is 'path', 'param' and 'value' from
23 /path?param=value&param=value, it is encoded in bit 0, 1 and 2
24 respectively
25
26 The top bit of lower nibble indicates, whether this character is
27 a hard terminator, ie. \0, \n or \s, where the whole scanning
28 process should terminate
29 */
30static const unsigned char is_unreserved[256] = {
31 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,
32 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,
33 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,
34 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,
35 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,
36 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,
37 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,
38 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
39};
40
41/* Do a fast nibble to hex representation conversion */
42static unsigned char fromhex(unsigned char x) {
43 x -= '0';
44 if (x <= 9)
45 return x;
46 x &= ~0x20;
47 x -= 'A' - '0';
48 if (x < 6)
49 return x + 10;
50 return 0xff;
51}
52
53/* Skip the value of a param=value pair */
54void urlencoded_skip_value(char **string) {
55 const unsigned char *s = *(const unsigned char **)string;
56 unsigned char f;
57
58 /* Since we are asked to skip the 'value', we assume to stop at
59 terminators for a 'value' string position */
60 while ((f = is_unreserved[*s++]) & SCAN_SEARCHPATH_VALUE)
61 ;
62
63 /* If we stopped at a hard terminator like \0 or \n, make the
64 next urlencoded_parse encounter it again */
65 if (f & SCAN_SEARCHPATH_TERMINATOR)
66 --s;
67
68 *string = (char *)s;
69}
70
71int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
72 char *deststring = *string;
73 ssize_t match_length = urlencoded_parse(string, deststring, flags);
74
75 if (match_length < 0)
76 return match_length;
77 if (match_length == 0)
78 return -3;
79
80 while (keywords->key) {
81 if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length])
82 return keywords->value;
83 keywords++;
84 }
85
86 return -3;
87}
88
89ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
90 const unsigned char *s = *(const unsigned char **)string;
91 unsigned char *d = (unsigned char *)deststring;
92 unsigned char b, c;
93
94 /* This is the main decoding loop.
95 'flag' determines, which characters are non-terminating in current context
96 (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path )
97 */
98 while (is_unreserved[c = *s++] & flags) {
99
100 /* When encountering an url escaped character, try to decode */
101 if (c == '%') {
102 if ((b = fromhex(*s++)) == 0xff)
103 return -1;
104 if ((c = fromhex(*s++)) == 0xff)
105 return -1;
106 c |= (b << 4);
107 }
108
109 /* Write (possibly decoded) character to output */
110 *d++ = c;
111 }
112
113 switch (c) {
114 case 0:
115 case '\r':
116 case '\n':
117 case ' ':
118 /* If we started scanning on a hard terminator, indicate we've finished */
119 if (d == (unsigned char *)deststring)
120 return -2;
121
122 /* Else make the next call to urlencoded_parse encounter it again */
123 --s;
124 break;
125 case '?':
126 if (flags != SCAN_PATH)
127 return -1;
128 break;
129 case '=':
130 if (flags != SCAN_SEARCHPATH_PARAM)
131 return -1;
132 break;
133 case '&':
134 if (flags == SCAN_PATH)
135 return -1;
136 if (flags == SCAN_SEARCHPATH_PARAM)
137 --s;
138 break;
139 default:
140 return -1;
141 }
142
143 *string = (char *)s;
144 return d - (unsigned char *)deststring;
145}
146
147
148size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) {
149 static const char safe[] = "$/.=~-_";
150 static const char hexdigit[] = "0123456789abcdef";
151 size_t written = 0, i;
152
153 for (i = 0; i < len; ++i) {
154 uint8_t c = src[i];
155
156 /* strchr would find the terminator, so a zero byte never counts as safe */
157 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) {
158 dest[written++] = (char)c;
159 } else {
160 dest[written] = '%';
161 dest[written + 1] = hexdigit[c >> 4];
162 dest[written + 2] = hexdigit[c & 15];
163 written += 3;
164 }
165 }
166
167 return written;
168}