Subversion Repositories Tewi

Rev

Rev 104 | Rev 147 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
16 nishi 1
/* $Id: http.c 118 2024-09-21 09:03:27Z nishi $ */
2
 
3
#define SOURCE
4
 
43 nishi 5
#include "../config.h"
6
 
16 nishi 7
#include "tw_http.h"
8
 
9
#include "tw_server.h"
10
 
11
#include <cm_log.h>
12
#include <cm_string.h>
13
 
14
#include <stdbool.h>
15
#include <stdlib.h>
17 nishi 16
#include <string.h>
16 nishi 17
 
17 nishi 18
#ifdef __MINGW32__
19
#include <winsock2.h>
20
#else
118 nishi 21
#ifdef USE_POLL
22
#include <poll.h>
23
#else
17 nishi 24
#include <sys/select.h>
25
#endif
118 nishi 26
#endif
17 nishi 27
 
16 nishi 28
void tw_free_request(struct tw_http_request* req) {
29
	if(req->method != NULL) free(req->method);
30
	if(req->path != NULL) free(req->path);
20 nishi 31
	if(req->query != NULL) free(req->query);
16 nishi 32
	if(req->headers != NULL) {
33
		int i;
34
		for(i = 0; req->headers[i] != NULL; i++) free(req->headers[i]);
35
		free(req->headers);
36
	}
37
	if(req->body != NULL) free(req->body);
38
	if(req->version != NULL) free(req->version);
64 nishi 39
 
40
	req->method = NULL;
41
	req->path = NULL;
42
	req->query = NULL;
43
	req->headers = NULL;
44
	req->body = NULL;
45
	req->version = NULL;
16 nishi 46
}
47
 
48
int tw_http_parse(SSL* ssl, int sock, struct tw_http_request* req) {
49
	char buffer[512];
50
	char cbuf[2];
51
	int phase = 0;
118 nishi 52
 
53
#ifdef USE_POLL
54
	struct pollfd pollfds[1];
55
	pollfds[0].fd = sock;
56
	pollfds[0].events = POLLIN | POLLPRI;
57
#else
16 nishi 58
	fd_set fds;
118 nishi 59
#endif
16 nishi 60
 
61
	bool bad = false;
62
 
63
	cbuf[1] = 0;
64
 
65
	req->method = NULL;
66
	req->path = NULL;
20 nishi 67
	req->query = NULL;
16 nishi 68
	req->headers = NULL;
69
	req->body = NULL;
70
	req->version = NULL;
71
 
72
	char* header = malloc(1);
73
	header[0] = 0;
74
	int nl = 0;
75
 
76
	while(1) {
118 nishi 77
#ifndef USE_POLL
16 nishi 78
		FD_ZERO(&fds);
79
		FD_SET(sock, &fds);
80
		struct timeval tv;
81
		tv.tv_sec = 5;
82
		tv.tv_usec = 0;
118 nishi 83
#endif
43 nishi 84
#ifndef NO_SSL
23 nishi 85
		if(ssl == NULL || !SSL_has_pending(ssl)) {
43 nishi 86
#endif
118 nishi 87
#ifdef USE_POLL
88
			int n = poll(pollfds, 1, 5000);
89
#else
88 nishi 90
#ifdef __HAIKU__
118 nishi 91
		int n = select(32, &fds, NULL, NULL, &tv);
88 nishi 92
#else
104 nishi 93
		int n = select(FD_SETSIZE, &fds, NULL, NULL, &tv);
88 nishi 94
#endif
118 nishi 95
#endif
22 nishi 96
			if(n <= 0) {
46 nishi 97
				cm_log("HTTP", "Timeout, disconncting");
22 nishi 98
				free(header);
99
				tw_free_request(req);
100
				return -1;
101
			}
43 nishi 102
#ifndef NO_SSL
22 nishi 103
		}
43 nishi 104
#endif
16 nishi 105
		int len = tw_read(ssl, sock, buffer, 512);
70 nishi 106
		if(len <= 0) {
64 nishi 107
			bad = true;
108
			break;
109
		}
16 nishi 110
		int i;
111
		for(i = 0; i < len; i++) {
112
			char c = buffer[i];
113
			if(phase == 0) {
114
				if(c == ' ') {
115
					if(req->method == NULL) {
116
						tw_free_request(req);
117
						bad = true;
118
						goto getout;
119
					} else {
120
						phase++;
121
					}
122
				} else {
123
					if(req->method == NULL) {
124
						req->method = malloc(1);
125
						req->method[0] = 0;
126
					}
127
					cbuf[0] = c;
128
					char* tmp = req->method;
129
					req->method = cm_strcat(tmp, cbuf);
130
					free(tmp);
131
				}
132
			} else if(phase == 1) {
133
				if(c == ' ') {
134
					if(req->path == NULL) {
135
						tw_free_request(req);
136
						bad = true;
137
						goto getout;
138
					} else {
139
						phase++;
140
					}
141
				} else {
142
					if(req->path == NULL) {
143
						req->path = malloc(1);
144
						req->path[0] = 0;
145
					}
146
					cbuf[0] = c;
147
					char* tmp = req->path;
148
					req->path = cm_strcat(tmp, cbuf);
149
					free(tmp);
150
				}
151
			} else if(phase == 2) {
152
				if(c == '\n') {
153
					if(req->version == NULL) {
154
						tw_free_request(req);
155
						bad = true;
156
						goto getout;
157
					} else {
158
						/* We have Method, Path, Version now */
159
 
160
						if(strcmp(req->version, "HTTP/1.1") != 0 && strcmp(req->version, "HTTP/1.0") != 0) {
161
							cm_log("HTTP", "Bad HTTP Version");
162
							bad = true;
163
							goto getout;
164
						}
165
 
166
						int j;
167
						char* p = malloc(1);
168
						p[0] = 0;
169
						for(j = 0; req->path[j] != 0; j++) {
170
							if(req->path[j] == '/') {
171
								cbuf[0] = '/';
172
								for(; req->path[j] != 0 && req->path[j] == '/'; j++)
173
									;
174
								j--;
175
							} else {
176
								cbuf[0] = req->path[j];
177
							}
178
							char* tmp = p;
179
							p = cm_strcat(tmp, cbuf);
180
							free(tmp);
181
						}
182
						free(req->path);
183
						req->path = p;
184
 
185
						int incr = 0;
186
						p = malloc(1);
187
						p[0] = 0;
188
						for(j = 0;; j++) {
189
							if(req->path[j] == '/' || req->path[j] == 0) {
190
								char oldc = req->path[j];
191
								cbuf[0] = oldc;
192
								req->path[j] = 0;
193
 
194
								char* pth = req->path + incr;
195
 
196
								if(strcmp(pth, "..") == 0) {
197
									int k;
198
									if(p[strlen(p) - 1] == '/') p[strlen(p) - 1] = 0;
199
									for(k = strlen(p) - 1; k >= 0; k--) {
200
										if(p[k] == '/') {
201
											p[k + 1] = 0;
202
											break;
203
										}
204
									}
205
									if(strlen(p) == 0) {
206
										free(p);
207
										p = cm_strdup("/");
208
									}
209
								} else if(strcmp(pth, ".") == 0) {
210
								} else {
211
									char* tmp = p;
212
									p = cm_strcat3(tmp, pth, cbuf);
213
									free(tmp);
214
								}
215
 
216
								incr = j + 1;
217
								if(oldc == 0) break;
218
							}
219
						}
220
						free(req->path);
221
						req->path = p;
222
 
223
						cm_log("HTTP", "Request: %s %s %s", req->method, req->path, req->version);
224
 
225
						phase++;
226
					}
227
				} else if(c != '\r') {
228
					if(req->version == NULL) {
229
						req->version = malloc(1);
230
						req->version[0] = 0;
231
					}
232
					cbuf[0] = c;
233
					char* tmp = req->version;
234
					req->version = cm_strcat(tmp, cbuf);
235
					free(tmp);
236
				}
237
			} else if(phase == 3) {
238
				if(c == '\n') {
239
					nl++;
240
					if(nl == 2) {
241
						phase++;
242
						goto getout;
243
					} else {
244
						if(req->headers == NULL) {
245
							req->headers = malloc(sizeof(*req->headers));
246
							req->headers[0] = NULL;
247
						}
248
						int j;
249
						for(j = 0; header[j] != 0; j++) {
250
							if(header[j] == ':') {
251
								header[j] = 0;
252
								j++;
253
								for(; header[j] != 0 && (header[j] == ' ' || header[j] == '\t'); j++)
254
									;
255
								char* kv = header;
256
								char* vv = header + j;
257
 
258
								char** old = req->headers;
259
								int k;
260
								for(k = 0; old[k] != NULL; k++)
261
									;
262
								req->headers = malloc(sizeof(*req->headers) * (k + 3));
263
								for(k = 0; old[k] != NULL; k++) req->headers[k] = old[k];
264
								req->headers[k] = cm_strdup(kv);
265
								req->headers[k + 1] = cm_strdup(vv);
266
								req->headers[k + 2] = NULL;
267
								free(old);
268
 
269
								cm_log("HTTP", "Header: %s: %s", kv, vv);
270
 
271
								break;
272
							}
273
						}
274
						free(header);
275
						header = malloc(1);
276
						header[0] = 0;
277
					}
278
				} else if(c != '\r') {
279
					nl = 0;
280
					cbuf[0] = c;
281
					char* tmp = header;
282
					header = cm_strcat(tmp, cbuf);
283
					free(tmp);
284
				}
285
			}
286
		}
287
	}
288
getout:
289
	free(header);
290
	if(bad) {
291
		tw_free_request(req);
292
		return 1;
293
	}
20 nishi 294
	char* result = malloc(1);
295
	result[0] = 0;
296
	int i;
297
	for(i = 0; req->path[i] != 0; i++) {
298
		if(req->path[i] == '?') {
299
			req->path[i] = 0;
300
			req->query = cm_strdup(req->path + i + 1);
301
			break;
302
		}
303
	}
304
	for(i = 0; req->path[i] != 0; i++) {
305
		if(req->path[i] == '%') {
306
			if(req->path[i + 1] == 0) continue;
307
			cbuf[0] = cm_hex(req->path + i + 1, 2);
70 nishi 308
			if(cbuf[0] != '\\') {
309
				char* tmp = result;
310
				result = cm_strcat(tmp, cbuf);
311
				free(tmp);
312
			}
20 nishi 313
			i += 2;
70 nishi 314
		} else if(req->path[i] != '\\') {
20 nishi 315
			cbuf[0] = req->path[i];
316
			char* tmp = result;
317
			result = cm_strcat(tmp, cbuf);
318
			free(tmp);
319
		}
320
	}
321
	free(req->path);
322
	req->path = result;
69 nishi 323
 
324
	int incr = 0;
325
	char* p = malloc(1);
326
	p[0] = 0;
327
	int j;
328
	for(j = 0;; j++) {
329
		if(req->path[j] == '/' || req->path[j] == 0) {
330
			char oldc = req->path[j];
331
			cbuf[0] = oldc;
332
			req->path[j] = 0;
333
 
334
			char* pth = req->path + incr;
335
 
336
			if(strcmp(pth, "..") == 0) {
337
				int k;
338
				if(p[strlen(p) - 1] == '/') p[strlen(p) - 1] = 0;
339
				for(k = strlen(p) - 1; k >= 0; k--) {
340
					if(p[k] == '/') {
341
						p[k + 1] = 0;
342
						break;
343
					}
344
				}
345
				if(strlen(p) == 0) {
346
					free(p);
347
					p = cm_strdup("/");
348
				}
349
			} else if(strcmp(pth, ".") == 0) {
70 nishi 350
			} else {
69 nishi 351
				char* tmp = p;
352
				p = cm_strcat3(tmp, pth, cbuf);
353
				free(tmp);
354
			}
355
 
356
			incr = j + 1;
357
			if(oldc == 0) break;
358
		}
359
	}
360
	free(req->path);
361
	req->path = p;
16 nishi 362
	return 0;
363
}