Subversion Repositories Tewi

Rev

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