Subversion Repositories Tewi

Rev

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