Subversion Repositories Tewi

Rev

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