Subversion Repositories Tewi

Rev

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