Subversion Repositories Tewi

Rev

Rev 312 | Rev 318 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4 nishi 1
/* $Id: config.c 315 2024-10-14 10:01:02Z nishi $ */
2
 
16 nishi 3
#define SOURCE
4
 
312 nishi 5
#include "../config.h"
4 nishi 6
 
7
#include <stdio.h>
7 nishi 8
#include <stdint.h>
4 nishi 9
#include <stdlib.h>
10
#include <string.h>
257 nishi 11
#include <sys/stat.h>
315 nishi 12
#include <errno.h>
212 nishi 13
 
215 nishi 14
#if !defined(_MSC_VER) && !defined(__BORLANDC__)
12 nishi 15
#include <unistd.h>
212 nishi 16
#endif
4 nishi 17
 
312 nishi 18
#if defined(__MINGW32__) || defined(_MSC_VER) || defined(__BORLANDC__) || (defined(__WATCOMC__) && !defined(__OS2__) && !defined(__WATCOMC__))
240 nishi 19
#ifdef USE_WINSOCK1
20
#include <winsock.h>
21
#else
20 nishi 22
#include <winsock2.h>
23
#endif
240 nishi 24
#endif
20 nishi 25
 
4 nishi 26
#include <cm_string.h>
27
#include <cm_log.h>
28
 
312 nishi 29
#ifdef __OS2__
30
#include <types.h>
31
#include <netinet/in.h>
32
#include <tcpustd.h>
33
#endif
34
 
35
#include "tw_config.h"
36
#include "tw_module.h"
37
 
6 nishi 38
struct tw_config config;
39
 
12 nishi 40
struct tw_config_entry* tw_vhost_match(const char* name, int port) {
41
	int i;
42
	for(i = 0; i < config.vhost_count; i++) {
13 nishi 43
		if(strcmp(config.vhosts[i].name, name) == 0 && (config.vhosts[i].port == -1 ? 1 : config.vhosts[i].port == port)) {
12 nishi 44
			return &config.vhosts[i];
45
		}
46
	}
47
	return &config.root;
48
}
49
 
22 nishi 50
bool tw_permission_allowed(const char* path, SOCKADDR addr, struct tw_http_request req, struct tw_config_entry* vhost) {
21 nishi 51
	int i;
52
	bool found = false;
53
	bool pathstart = false;
54
	bool perm = false;
55
again:
22 nishi 56
	for(i = 0; i < vhost->dir_count; i++) {
212 nishi 57
		char* noslash;
21 nishi 58
		struct tw_dir_entry* e = &vhost->dirs[i];
59
		pathstart = false;
22 nishi 60
		if(strlen(path) >= strlen(e->dir)) {
212 nishi 61
			int j;
21 nishi 62
			pathstart = true;
22 nishi 63
			for(j = 0; path[j] != 0 && e->dir[j] != 0; j++) {
64
				if(path[j] != e->dir[j]) {
21 nishi 65
					pathstart = false;
66
					break;
67
				}
68
			}
69
		}
212 nishi 70
		noslash = cm_strdup(e->dir);
21 nishi 71
		noslash[strlen(noslash) - 1] = 0;
22 nishi 72
		if(strcmp(e->dir, path) == 0 || strcmp(noslash, path) == 0 || pathstart) {
21 nishi 73
			found = true;
22 nishi 74
			if(strcmp(e->name, "all") == 0) {
21 nishi 75
				perm = e->type == TW_DIR_ALLOW;
76
			}
77
		}
78
		free(noslash);
79
	}
22 nishi 80
	if(!found && vhost != &config.root) {
21 nishi 81
		vhost = &config.root;
82
		goto again;
83
	}
84
	return perm;
85
}
86
 
7 nishi 87
void tw_config_init(void) {
88
	int i;
89
	for(i = 0; i < MAX_PORTS + 1; i++) {
90
		config.ports[i] = -1;
91
	}
12 nishi 92
	for(i = 0; i < MAX_VHOSTS; i++) {
156 nishi 93
#ifndef NO_SSL
12 nishi 94
		config.vhosts[i].sslkey = NULL;
95
		config.vhosts[i].sslcert = NULL;
156 nishi 96
#endif
19 nishi 97
		config.vhosts[i].root = NULL;
156 nishi 98
#ifdef HAS_CHROOT
99
		config.vhosts[i].chroot_path = NULL;
100
#endif
12 nishi 101
	}
156 nishi 102
#ifndef NO_SSL
12 nishi 103
	config.root.sslkey = NULL;
104
	config.root.sslcert = NULL;
156 nishi 105
#endif
19 nishi 106
	config.root.root = NULL;
21 nishi 107
	config.root.mime_count = 0;
108
	config.root.dir_count = 0;
22 nishi 109
	config.root.icon_count = 0;
24 nishi 110
	config.root.index_count = 0;
33 nishi 111
	config.root.readme_count = 0;
123 nishi 112
	config.root.hideport = 0;
156 nishi 113
#ifdef HAS_CHROOT
114
	config.root.chroot_path = NULL;
115
#endif
12 nishi 116
	config.vhost_count = 0;
18 nishi 117
	config.module_count = 0;
118
	config.extension = NULL;
17 nishi 119
	config.server_root = cm_strdup(PREFIX);
128 nishi 120
	config.server_admin = cm_strdup(SERVER_ADMIN);
156 nishi 121
	config.defined[0] = NULL;
187 nishi 122
#if defined(_PSP)
182 nishi 123
	strcpy(config.hostname, "psp");
187 nishi 124
#elif defined(__PPU__)
125
	strcpy(config.hostname, "ps3");
189 nishi 126
#elif defined(__ps2sdk__)
127
	strcpy(config.hostname, "ps2");
315 nishi 128
#elif defined(__NETWARE__)
129
	strcpy(config.hostname, "netware");
182 nishi 130
#else
12 nishi 131
	gethostname(config.hostname, 1024);
182 nishi 132
#endif
161 nishi 133
#ifdef HAS_CHROOT
134
	tw_add_define("HAS_CHROOT");
135
#endif
174 nishi 136
#ifndef NO_SSL
137
	tw_add_define("HAS_SSL");
138
#endif
7 nishi 139
}
6 nishi 140
 
141
int tw_config_read(const char* path) {
4 nishi 142
	char cbuf[2];
6 nishi 143
	int ln = 0;
156 nishi 144
	int ifbr = 0;
145
	int ignore = -1;
253 nishi 146
	int portcount;
212 nishi 147
	FILE* f;
148
	cm_log("Config", "Reading %s", path);
149
	f = fopen(path, "r");
150
	cbuf[1] = 0;
6 nishi 151
	if(f != NULL) {
4 nishi 152
		char* line = malloc(1);
6 nishi 153
		int stop = 0;
12 nishi 154
		struct tw_config_entry* current = &config.root;
6 nishi 155
		char* vhost = NULL;
21 nishi 156
		char* dir = NULL;
212 nishi 157
		line[0] = 0;
6 nishi 158
		while(stop == 0) {
4 nishi 159
			int c = fread(cbuf, 1, 1, f);
6 nishi 160
			if(cbuf[0] == '\n' || c <= 0) {
212 nishi 161
				char* l = cm_trim(line);
6 nishi 162
				ln++;
163
				if(strlen(l) > 0 && l[0] != '#') {
5 nishi 164
					char** r = cm_split(l, " \t");
165
					int i;
156 nishi 166
					if(ignore != -1 && ifbr >= ignore) {
167
						if(cm_strcaseequ(r[0], "EndIf")) ifbr--;
168
						if(ifbr == 0) {
169
							ignore = -1;
170
						}
171
					} else if(cm_strcaseequ(r[0], "Include") || cm_strcaseequ(r[0], "IncludeOptional")) {
6 nishi 172
						for(i = 1; r[i] != NULL; i++) {
173
							if(tw_config_read(r[i]) != 0 && cm_strcaseequ(r[0], "Include")) {
174
								stop = 1;
175
								break;
5 nishi 176
							}
177
						}
156 nishi 178
					} else if(cm_strcaseequ(r[0], "Define")) {
179
						if(r[1] == NULL) {
180
							cm_log("Config", "Missing name at line %d", ln);
181
							stop = 1;
182
						} else {
183
							tw_add_define(r[1]);
184
						}
185
					} else if(cm_strcaseequ(r[0], "Undefine")) {
186
						if(r[1] == NULL) {
187
							cm_log("Config", "Missing name at line %d", ln);
188
							stop = 1;
189
						} else {
190
							tw_delete_define(r[1]);
191
						}
21 nishi 192
					} else if(cm_strcaseequ(r[0], "BeginDirectory")) {
193
						if(dir != NULL) {
194
							cm_log("Config", "Already in directory section at line %d", ln);
195
							stop = 1;
196
						} else {
197
							if(r[1] == NULL) {
198
								cm_log("Config", "Missing directory at line %d", ln);
199
								stop = 1;
200
							} else {
201
								dir = cm_strcat(r[1], r[1][strlen(r[1]) - 1] == '/' ? "" : "/");
202
							}
203
						}
204
					} else if(cm_strcaseequ(r[0], "EndDirectory")) {
205
						if(dir == NULL) {
206
							cm_log("Config", "Not in directory section at line %d", ln);
207
							stop = 1;
208
						} else {
209
							free(dir);
210
							dir = NULL;
211
						}
212
					} else if(cm_strcaseequ(r[0], "Allow")) {
213
						if(dir == NULL) {
214
							cm_log("Config", "Not in directory section at line %d", ln);
215
							stop = 1;
216
						} else {
217
							if(r[1] == NULL) {
218
								cm_log("Config", "Missing argument at line %d", ln);
219
								stop = 1;
220
							} else {
221
								struct tw_dir_entry* e = &current->dirs[current->dir_count++];
222
								e->name = cm_strdup(r[1]);
223
								e->dir = cm_strdup(dir);
224
								e->type = TW_DIR_ALLOW;
225
							}
226
						}
227
					} else if(cm_strcaseequ(r[0], "Deny")) {
228
						if(dir == NULL) {
229
							cm_log("Config", "Not in directory section at line %d", ln);
230
							stop = 1;
231
						} else {
232
							if(r[1] == NULL) {
233
								cm_log("Config", "Missing argument at line %d", ln);
234
								stop = 1;
235
							} else {
236
								struct tw_dir_entry* e = &current->dirs[current->dir_count++];
237
								e->name = cm_strdup(r[1]);
238
								e->dir = cm_strdup(dir);
239
								e->type = TW_DIR_DENY;
240
							}
241
						}
6 nishi 242
					} else if(cm_strcaseequ(r[0], "BeginVirtualHost")) {
243
						if(vhost != NULL) {
12 nishi 244
							cm_log("Config", "Already in virtual host section at line %d", ln);
6 nishi 245
							stop = 1;
246
						} else {
247
							if(r[1] == NULL) {
12 nishi 248
								cm_log("Config", "Missing virtual host at line %d", ln);
6 nishi 249
								stop = 1;
250
							} else {
212 nishi 251
								int i;
6 nishi 252
								vhost = cm_strdup(r[1]);
12 nishi 253
								current = &config.vhosts[config.vhost_count++];
21 nishi 254
								current->dir_count = 0;
255
								current->mime_count = 0;
22 nishi 256
								current->icon_count = 0;
24 nishi 257
								current->index_count = 0;
33 nishi 258
								current->readme_count = 0;
123 nishi 259
								current->hideport = -1;
12 nishi 260
								current->name = cm_strdup(vhost);
13 nishi 261
								current->port = -1;
12 nishi 262
								for(i = 0; vhost[i] != 0; i++) {
263
									if(vhost[i] == ':') {
264
										current->name[i] = 0;
265
										current->port = atoi(current->name + i + 1);
266
										break;
267
									}
268
								}
6 nishi 269
							}
270
						}
271
					} else if(cm_strcaseequ(r[0], "EndVirtualHost")) {
272
						if(vhost == NULL) {
12 nishi 273
							cm_log("Config", "Not in virtual host section at line %d", ln);
6 nishi 274
							stop = 1;
275
						} else {
276
							free(vhost);
277
							vhost = NULL;
12 nishi 278
							current = &config.root;
6 nishi 279
						}
174 nishi 280
					} else if(cm_strcaseequ(r[0], "Listen")
281
#ifndef NO_SSL
282
						  || cm_strcaseequ(r[0], "ListenSSL")
283
#endif
284
					) {
7 nishi 285
						for(i = 1; r[i] != NULL; i++) {
215 nishi 286
#if defined(_MSC_VER) || defined(__BORLANDC__)
212 nishi 287
							uint32_t port = atoi(r[i]);
288
#else
7 nishi 289
							uint64_t port = atoi(r[i]);
212 nishi 290
#endif
291
							int j;
7 nishi 292
							cm_log("Config", "Going to listen at port %d%s", (int)port, cm_strcaseequ(r[0], "ListenSSL") ? " with SSL" : "");
215 nishi 293
#if defined(_MSC_VER) || defined(__BORLANDC__)
212 nishi 294
							if(cm_strcaseequ(r[0], "ListenSSL")) port |= (1UL << 31);
295
#else
296
							if(cm_strcaseequ(r[0], "ListenSSL")) port |= (1ULL << 31);
297
#endif
7 nishi 298
							for(j = 0; config.ports[j] != -1; j++)
299
								;
300
							config.ports[j] = port;
301
						}
123 nishi 302
					} else if(cm_strcaseequ(r[0], "HidePort")) {
303
						current->hideport = 1;
304
					} else if(cm_strcaseequ(r[0], "ShowPort")) {
305
						current->hideport = 0;
156 nishi 306
#ifndef NO_SSL
12 nishi 307
					} else if(cm_strcaseequ(r[0], "SSLKey")) {
308
						if(r[1] == NULL) {
309
							cm_log("Config", "Missing path at line %d", ln);
310
							stop = 1;
311
						} else {
312
							if(current->sslkey != NULL) free(current->sslkey);
313
							current->sslkey = cm_strdup(r[1]);
314
						}
315
					} else if(cm_strcaseequ(r[0], "SSLCertificate")) {
316
						if(r[1] == NULL) {
317
							cm_log("Config", "Missing path at line %d", ln);
318
							stop = 1;
319
						} else {
320
							if(current->sslcert != NULL) free(current->sslcert);
321
							current->sslcert = cm_strdup(r[1]);
322
						}
156 nishi 323
#endif
161 nishi 324
#ifdef HAS_CHROOT
325
					} else if(cm_strcaseequ(r[0], "ChrootDirectory")) {
326
						if(r[1] == NULL) {
327
							cm_log("Config", "Missing path at line %d", ln);
328
							stop = 1;
329
						} else {
330
							if(current->chroot_path != NULL) free(current->chroot_path);
331
							current->chroot_path = cm_strdup(r[1]);
332
						}
333
#endif
156 nishi 334
					} else if(cm_strcaseequ(r[0], "ForceLog")) {
335
						if(r[1] == NULL) {
336
							cm_log("Config", "Missing log at line %d", ln);
337
							stop = 1;
338
						} else {
339
							cm_force_log(r[1]);
340
						}
341
					} else if(cm_strcaseequ(r[0], "EndIf")) {
342
						if(ifbr == 0) {
343
							cm_log("Config", "Missing BeginIf at line %d", ln);
344
							stop = 1;
345
						}
346
						ifbr--;
347
					} else if(cm_strcaseequ(r[0], "BeginIf") || cm_strcaseequ(r[0], "BeginIfNot")) {
348
						if(r[1] == NULL) {
349
							cm_log("Config", "Missing condition type at line %d", ln);
350
						} else {
212 nishi 351
							bool ign = false;
156 nishi 352
							ifbr++;
353
							if(cm_strcaseequ(r[1], "False")) {
354
								ign = true;
355
							} else if(cm_strcaseequ(r[1], "True")) {
356
							} else if(cm_strcaseequ(r[1], "Defined")) {
357
								if(r[2] == NULL) {
358
									cm_log("Config", "Missing name at line %d", ln);
359
									stop = 1;
360
								} else {
361
									int i;
362
									bool fndit = false;
363
									for(i = 0; config.defined[i] != NULL; i++) {
364
										if(strcmp(config.defined[i], r[2]) == 0) {
365
											fndit = true;
366
											break;
367
										}
368
									}
369
									if(!fndit) {
370
										ign = true;
371
									}
372
								}
373
							} else {
374
								cm_log("Config", "Unknown condition type at line %d", ln);
375
								stop = 1;
376
							}
377
							if(cm_strcaseequ(r[0], "BeginIfNot")) ign = !ign;
378
							if(ign) {
379
								ignore = ifbr - 1;
380
							}
381
						}
61 nishi 382
					} else if(cm_strcaseequ(r[0], "ServerRoot")) {
383
						if(r[1] == NULL) {
384
							cm_log("Config", "Missing path at line %d", ln);
385
							stop = 1;
386
						} else {
387
							chdir(r[1]);
127 nishi 388
							free(config.server_root);
389
							config.server_root = cm_strdup(r[1]);
61 nishi 390
						}
128 nishi 391
					} else if(cm_strcaseequ(r[0], "ServerAdmin")) {
392
						if(r[1] == NULL) {
393
							cm_log("Config", "Missing email at line %d", ln);
394
							stop = 1;
395
						} else {
396
							free(config.server_admin);
397
							config.server_admin = cm_strdup(r[1]);
398
						}
19 nishi 399
					} else if(cm_strcaseequ(r[0], "DocumentRoot")) {
400
						if(r[1] == NULL) {
401
							cm_log("Config", "Missing path at line %d", ln);
402
							stop = 1;
403
						} else {
404
							if(current->root != NULL) free(current->root);
21 nishi 405
							current->root = cm_strdup(strcmp(r[1], "/") == 0 ? "" : r[1]);
19 nishi 406
						}
21 nishi 407
					} else if(cm_strcaseequ(r[0], "MIMEType")) {
408
						if(r[1] == NULL) {
409
							cm_log("Config", "Missing extension at line %d", ln);
410
							stop = 1;
22 nishi 411
						} else if(r[2] == NULL) {
21 nishi 412
							cm_log("Config", "Missing MIME at line %d", ln);
413
							stop = 1;
414
						} else {
415
							struct tw_mime_entry* e = &current->mimes[current->mime_count++];
416
							e->ext = cm_strdup(r[1]);
417
							e->mime = cm_strdup(r[2]);
418
						}
257 nishi 419
					} else if(cm_strcaseequ(r[0], "MIMEFile")) {
420
						if(r[1] == NULL) {
421
							cm_log("Config", "Missing path at line %d", ln);
422
							stop = 1;
423
						} else {
424
							FILE* mimefile = fopen(r[1], "r");
425
							if(mimefile == NULL) {
426
								cm_log("Config", "Could not load the file at line %d", ln);
427
								stop = 1;
428
							} else {
429
								char* line = malloc(1);
430
								int i;
431
								struct stat st;
432
								char* buf;
433
								int incr = 0;
434
								stat(r[1], &st);
435
 
436
								buf = malloc(st.st_size + 1);
437
								fread(buf, st.st_size, 1, mimefile);
438
 
439
								for(i = 0;; i++) {
440
									if(buf[i] == '\n' || buf[i] == 0) {
441
										char oldc = buf[i];
442
										char* line;
443
										buf[i] = 0;
444
 
445
										line = buf + incr;
446
 
447
										if(strlen(line) > 0 && line[0] != '#') {
448
											int j;
449
											for(j = 0; line[j] != 0; j++) {
450
												if(line[j] == ' ' || line[j] == '\t') {
451
													line[j] = 0;
452
													j++;
453
													for(; line[j] != 0; j++) {
454
														if(line[j] != ' ' && line[j] != '\t') {
455
															char* name = line;
456
															char* mimes = line + j;
457
															int k = 0;
458
															int incr2 = 0;
459
															for(k = 0;; k++) {
460
																if(mimes[k] == ' ' || mimes[k] == 0) {
461
																	char oldc2 = mimes[k];
462
																	struct tw_mime_entry* e;
463
																	mimes[k] = 0;
464
 
465
																	e = &current->mimes[current->mime_count++];
466
																	e->ext = cm_strcat(".", mimes + incr2);
467
																	e->mime = cm_strdup(name);
468
																	if(current->mime_count == MAX_MIME) {
469
																		cm_log("Config", "Too many MIME types, cannot handle");
470
																		stop = 1;
471
																		break;
472
																	}
473
 
474
																	incr2 = k + 1;
475
																	if(oldc2 == 0) break;
476
																}
477
															}
478
															break;
479
														}
480
													}
481
													break;
482
												}
483
												if(stop) break;
484
											}
485
										}
486
 
487
										incr = i + 1;
488
										if(oldc == 0) break;
489
									}
490
								}
491
 
492
								free(buf);
493
 
494
								fclose(mimefile);
495
							}
496
						}
22 nishi 497
					} else if(cm_strcaseequ(r[0], "Icon")) {
498
						if(r[1] == NULL) {
499
							cm_log("Config", "Missing MIME at line %d", ln);
500
							stop = 1;
501
						} else if(r[2] == NULL) {
502
							cm_log("Config", "Missing path at line %d", ln);
503
							stop = 1;
504
						} else {
505
							struct tw_icon_entry* e = &current->icons[current->icon_count++];
506
							e->mime = cm_strdup(r[1]);
507
							e->icon = cm_strdup(r[2]);
508
						}
17 nishi 509
					} else if(cm_strcaseequ(r[0], "LoadModule")) {
510
						for(i = 1; r[i] != NULL; i++) {
511
							void* mod = tw_module_load(r[i]);
512
							if(mod != NULL) {
18 nishi 513
								config.modules[config.module_count++] = mod;
17 nishi 514
								if(tw_module_init(mod) != 0) {
515
									stop = 1;
516
									break;
517
								}
518
							} else {
127 nishi 519
								cm_log("Config", "Could not load the module at line %d", ln);
17 nishi 520
								stop = 1;
521
								break;
522
							}
523
						}
24 nishi 524
					} else if(cm_strcaseequ(r[0], "DirectoryIndex")) {
525
						for(i = 1; r[i] != NULL; i++) {
526
							current->indexes[current->index_count++] = cm_strdup(r[i]);
527
						}
178 nishi 528
					} else if(cm_strcaseequ(r[0], "ReadmeFile") || cm_strcaseequ(r[0], "Readme")) {
182 nishi 529
						if(cm_strcaseequ(r[0], "Readme")) {
178 nishi 530
							cm_force_log("NOTE: Readme directive is deprecated.");
531
						}
33 nishi 532
						for(i = 1; r[i] != NULL; i++) {
533
							current->readmes[current->readme_count++] = cm_strdup(r[i]);
534
						}
6 nishi 535
					} else {
39 nishi 536
						stop = 1;
6 nishi 537
						if(r[0] != NULL) {
39 nishi 538
							int argc;
212 nishi 539
							int i;
540
							bool called = false;
541
							struct tw_tool tools;
39 nishi 542
							for(argc = 0; r[argc] != NULL; argc++)
543
								;
544
							stop = 0;
545
							tw_init_tools(&tools);
546
							for(i = 0; i < config.module_count; i++) {
547
								tw_mod_config_t mod_config = (tw_mod_config_t)tw_module_symbol(config.modules[i], "mod_config");
548
								int resp;
549
								if(mod_config != NULL && (resp = mod_config(&tools, r, argc)) == TW_CONFIG_PARSED) {
550
									called = true;
551
									break;
552
								}
553
								if(resp == TW_CONFIG_ERROR) {
554
									stop = 1;
555
									called = true;
556
									break;
557
								}
558
							}
559
							if(!called) {
560
								cm_log("Config", "Unknown directive `%s' at line %d", r[0], ln);
561
								stop = 1;
562
							}
6 nishi 563
						}
5 nishi 564
					}
565
					for(i = 0; r[i] != NULL; i++) free(r[i]);
566
					free(r);
4 nishi 567
				}
568
				free(l);
569
				free(line);
570
				line = malloc(1);
571
				line[0] = 0;
572
				if(c <= 0) break;
6 nishi 573
			} else if(cbuf[0] != '\r') {
4 nishi 574
				char* tmp = line;
575
				line = cm_strcat(tmp, cbuf);
576
				free(tmp);
577
			}
578
		}
579
		free(line);
580
		fclose(f);
255 nishi 581
		for(portcount = 0; config.ports[portcount] != -1; portcount++)
582
			;
583
		if(portcount == 0) {
253 nishi 584
			return 1;
255 nishi 585
		} else {
253 nishi 586
			return stop;
587
		}
6 nishi 588
	} else {
315 nishi 589
		printf("%d\n", errno);
5 nishi 590
		cm_log("Config", "Could not open the file");
4 nishi 591
		return 1;
592
	}
593
}