Subversion Repositories Tewi

Rev

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