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