main: fix two one-byte overreads in header_value()
[project/cgi-io.git] / util.c
1 #include <ctype.h>
2 #include <stddef.h>
3 #include <stdbool.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #include <stdio.h>
9
10 #include "util.h"
11
12 char **
13 parse_command(const char *cmdline)
14 {
15 const char *p = cmdline, *s;
16 char **argv = NULL, *out;
17 size_t arglen = 0;
18 int argnum = 0;
19 bool esc;
20
21 while (isspace(*cmdline))
22 cmdline++;
23
24 for (p = cmdline, s = p, esc = false; p; p++) {
25 if (esc) {
26 esc = false;
27 }
28 else if (*p == '\\' && p[1] != 0) {
29 esc = true;
30 }
31 else if (isspace(*p) || *p == 0) {
32 if (p > s) {
33 argnum += 1;
34 arglen += sizeof(char *) + (p - s) + 1;
35 }
36
37 s = p + 1;
38 }
39
40 if (*p == 0)
41 break;
42 }
43
44 if (arglen == 0)
45 return NULL;
46
47 argv = calloc(1, arglen + sizeof(char *));
48
49 if (!argv)
50 return NULL;
51
52 out = (char *)argv + sizeof(char *) * (argnum + 1);
53 argv[0] = out;
54
55 for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) {
56 if (esc) {
57 esc = false;
58 *out++ = *p;
59 }
60 else if (*p == '\\' && p[1] != 0) {
61 esc = true;
62 }
63 else if (isspace(*p) || *p == 0) {
64 if (p > s) {
65 *out++ = ' ';
66 argv[++argnum] = out;
67 }
68
69 s = p + 1;
70 }
71 else {
72 *out++ = *p;
73 }
74
75 if (*p == 0)
76 break;
77 }
78
79 argv[argnum] = NULL;
80 out[-1] = 0;
81
82 return argv;
83 }
84
85 char *
86 postdecode_fields(char *postbuf, ssize_t len, char **fields, int n_fields)
87 {
88 char *p;
89 int i, field, found = 0;
90
91 for (p = postbuf, i = 0; i < len; i++)
92 {
93 if (postbuf[i] == '=')
94 {
95 postbuf[i] = 0;
96
97 for (field = 0; field < (n_fields * 2); field += 2)
98 {
99 if (!strcmp(p, fields[field]))
100 {
101 fields[field + 1] = postbuf + i + 1;
102 found++;
103 }
104 }
105 }
106 else if (postbuf[i] == '&' || postbuf[i] == '\0')
107 {
108 postbuf[i] = 0;
109
110 if (found >= n_fields)
111 break;
112
113 p = postbuf + i + 1;
114 }
115 }
116
117 for (field = 0; field < (n_fields * 2); field += 2)
118 {
119 if (!urldecode(fields[field + 1]))
120 {
121 free(postbuf);
122 return NULL;
123 }
124 }
125
126 return postbuf;
127 }
128
129 char *
130 postdecode(char **fields, int n_fields)
131 {
132 const char *var;
133 char *p, *postbuf;
134 ssize_t len = 0, rlen = 0, content_length = 0;
135
136 var = getenv("CONTENT_TYPE");
137
138 if (!var || strncmp(var, "application/x-www-form-urlencoded", 33))
139 return NULL;
140
141 var = getenv("CONTENT_LENGTH");
142
143 if (!var)
144 return NULL;
145
146 content_length = strtol(var, &p, 10);
147
148 if (p == var || content_length <= 0 || content_length >= POST_LIMIT)
149 return NULL;
150
151 postbuf = calloc(1, content_length + 1);
152
153 if (postbuf == NULL)
154 return NULL;
155
156 for (len = 0; len < content_length; )
157 {
158 rlen = read(0, postbuf + len, content_length - len);
159
160 if (rlen <= 0)
161 break;
162
163 len += rlen;
164 }
165
166 if (len < content_length)
167 {
168 free(postbuf);
169 return NULL;
170 }
171
172 return postdecode_fields(postbuf, len, fields, n_fields);
173 }
174
175 char *
176 datadup(const void *in, size_t len)
177 {
178 char *out = malloc(len + 1);
179
180 if (!out)
181 return NULL;
182
183 memcpy(out, in, len);
184
185 *(out + len) = 0;
186
187 return out;
188 }
189
190 char *
191 canonicalize_path(const char *path, size_t len)
192 {
193 char *canonpath, *cp;
194 const char *p, *e;
195
196 if (path == NULL || *path == '\0')
197 return NULL;
198
199 canonpath = datadup(path, len);
200
201 if (canonpath == NULL)
202 return NULL;
203
204 /* normalize */
205 for (cp = canonpath, p = path, e = path + len; p < e; ) {
206 if (*p != '/')
207 goto next;
208
209 /* skip repeating / */
210 if ((p + 1 < e) && (p[1] == '/')) {
211 p++;
212 continue;
213 }
214
215 /* /./ or /../ */
216 if ((p + 1 < e) && (p[1] == '.')) {
217 /* skip /./ */
218 if ((p + 2 >= e) || (p[2] == '/')) {
219 p += 2;
220 continue;
221 }
222
223 /* collapse /x/../ */
224 if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) {
225 while ((cp > canonpath) && (*--cp != '/'))
226 ;
227
228 p += 3;
229 continue;
230 }
231 }
232
233 next:
234 *cp++ = *p++;
235 }
236
237 /* remove trailing slash if not root / */
238 if ((cp > canonpath + 1) && (cp[-1] == '/'))
239 cp--;
240 else if (cp == canonpath)
241 *cp++ = '/';
242
243 *cp = '\0';
244
245 return canonpath;
246 }
247
248 bool
249 urldecode(char *buf)
250 {
251 char *c, *p;
252
253 if (!buf || !*buf)
254 return true;
255
256 #define hex(x) \
257 (((x) <= '9') ? ((x) - '0') : \
258 (((x) <= 'F') ? ((x) - 'A' + 10) : \
259 ((x) - 'a' + 10)))
260
261 for (c = p = buf; *p; c++)
262 {
263 if (*p == '%')
264 {
265 if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2)))
266 return false;
267
268 *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2)));
269
270 p += 3;
271 }
272 else if (*p == '+')
273 {
274 *c = ' ';
275 p++;
276 }
277 else
278 {
279 *c = *p++;
280 }
281 }
282
283 *c = 0;
284
285 return true;
286 }