Subversion Repositories Tewi

Rev

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