Subversion Repositories Tewi

Rev

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