Subversion Repositories Tewi

Rev

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