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