Subversion Repositories Tewi

Rev

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