Index: trunk/Common/Makefile
===================================================================
--- trunk/Common/Makefile	(revision 20)
+++ trunk/Common/Makefile	(revision 21)
@@ -6,5 +6,5 @@
 .SUFFIXES: .c .o
 
-OBJS = string.o log.o
+OBJS = string.o log.o dir.o
 
 all: common.a
Index: trunk/Common/cm_dir.h
===================================================================
--- trunk/Common/cm_dir.h	(revision 21)
+++ trunk/Common/cm_dir.h	(revision 21)
@@ -0,0 +1,8 @@
+/* $Id$ */
+
+#ifndef __CM_DIR_H__
+#define __CM_DIR_H__
+
+char** cm_scandir(const char* path);
+
+#endif
Index: trunk/Common/cm_string.h
===================================================================
--- trunk/Common/cm_string.h	(revision 20)
+++ trunk/Common/cm_string.h	(revision 21)
@@ -8,4 +8,5 @@
 int cm_hex(const char* str, int len);
 char* cm_html_escape(const char* str);
+char* cm_url_escape(const char* str);
 char* cm_strcat(const char* a, const char* b);
 char* cm_strcat3(const char* a, const char* b, const char* c);
Index: trunk/Common/dir.c
===================================================================
--- trunk/Common/dir.c	(revision 21)
+++ trunk/Common/dir.c	(revision 21)
@@ -0,0 +1,48 @@
+/* $Id$ */
+
+#include "cm_dir.h"
+
+#include "cm_string.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+int cm_sort(const void* _a, const void* _b){
+	char* a = *(char**)_a;
+	char* b = *(char**)_b;
+	return strcmp(a, b);
+}
+
+char** cm_scandir(const char* path){
+	DIR* dir = opendir(path);
+	if(dir != NULL){
+		char** r = malloc(sizeof(*r));
+		r[0] = NULL;
+		struct dirent* d;
+		while((d = readdir(dir)) != NULL){
+			if(strcmp(d->d_name, ".") != 0){
+				struct stat s;
+				char* p = cm_strcat3(path, "/", d->d_name);
+				stat(p, &s);
+				free(p);
+
+				char** old = r;
+				int i;
+				for(i = 0; old[i] != NULL; i++);
+				r = malloc(sizeof(*r) * (i + 2));
+				for(i = 0; old[i] != NULL; i++) r[i] = old[i];
+				r[i] = cm_strcat(d->d_name, S_ISDIR(s.st_mode) ? "/" : "");
+				r[i + 1] = NULL;
+				free(old);
+			}
+		}
+		int len;
+		for(len = 0; r[len] != NULL; len++);
+		qsort(r, len, sizeof(char*), cm_sort);
+		return r;
+	}else{
+		return NULL;
+	}
+}
Index: trunk/Common/string.c
===================================================================
--- trunk/Common/string.c	(revision 20)
+++ trunk/Common/string.c	(revision 21)
@@ -160,2 +160,25 @@
 	return result;
 }
+
+char* cm_url_escape(const char* str) {
+	int i;
+	char* result = malloc(1);
+	result[0] = 0;
+	char cbuf[2];
+	cbuf[1] = 0;
+	for(i = 0; str[i] != 0; i++) {
+		cbuf[0] = str[i];
+		if('!' <= str[i] && str[i] <= '@' && str[i] != '.' && str[i] != '-' && str[i] != '/' && !('0' <= str[i] && str[i] <= '9')) {
+			char code[4];
+			sprintf(code, "%%%02X", str[i]);
+			char* tmp = result;
+			result = cm_strcat(tmp, code);
+			free(tmp);
+		} else {
+			char* tmp = result;
+			result = cm_strcat(tmp, cbuf);
+			free(tmp);
+		}
+	}
+	return result;
+}
Index: trunk/Server/config.c
===================================================================
--- trunk/Server/config.c	(revision 20)
+++ trunk/Server/config.c	(revision 21)
@@ -31,4 +31,40 @@
 }
 
+bool tw_permission_allowed(const char* path, SOCKADDR addr, struct tw_http_request req, struct tw_config_entry* vhost){
+	int i;
+	bool found = false;
+	bool pathstart = false;
+	bool perm = false;
+again:
+	for(i = 0; i < vhost->dir_count; i++){
+		struct tw_dir_entry* e = &vhost->dirs[i];
+		pathstart = false;
+		if(strlen(path) >= strlen(e->dir)){
+			pathstart = true;
+			int j;
+			for(j = 0; path[j] != 0 && e->dir[j] != 0; j++){
+				if(path[j] != e->dir[j]){
+					pathstart = false;
+					break;
+				}
+			}
+		}
+		char* noslash = cm_strdup(e->dir);
+		noslash[strlen(noslash) - 1] = 0;
+		if(strcmp(e->dir, path) == 0 || strcmp(noslash, path) == 0 || pathstart){
+			found = true;
+			if(strcmp(e->name, "all") == 0){
+				perm = e->type == TW_DIR_ALLOW;
+			}
+		}
+		free(noslash);
+	}
+	if(!found && vhost != &config.root){
+		vhost = &config.root;
+		goto again;
+	}
+	return perm;
+}
+
 void tw_config_init(void) {
 	int i;
@@ -44,4 +80,6 @@
 	config.root.sslcert = NULL;
 	config.root.root = NULL;
+	config.root.mime_count = 0;
+	config.root.dir_count = 0;
 	config.vhost_count = 0;
 	config.module_count = 0;
@@ -63,4 +101,5 @@
 		struct tw_config_entry* current = &config.root;
 		char* vhost = NULL;
+		char* dir = NULL;
 		while(stop == 0) {
 			int c = fread(cbuf, 1, 1, f);
@@ -78,4 +117,54 @@
 							}
 						}
+					} else if(cm_strcaseequ(r[0], "BeginDirectory")) {
+						if(dir != NULL) {
+							cm_log("Config", "Already in directory section at line %d", ln);
+							stop = 1;
+						} else {
+							if(r[1] == NULL) {
+								cm_log("Config", "Missing directory at line %d", ln);
+								stop = 1;
+							} else {
+								dir = cm_strcat(r[1], r[1][strlen(r[1]) - 1] == '/' ? "" : "/");
+							}
+						}
+					} else if(cm_strcaseequ(r[0], "EndDirectory")) {
+						if(dir == NULL) {
+							cm_log("Config", "Not in directory section at line %d", ln);
+							stop = 1;
+						} else {
+							free(dir);
+							dir = NULL;
+						}
+					} else if(cm_strcaseequ(r[0], "Allow")) {
+						if(dir == NULL) {
+							cm_log("Config", "Not in directory section at line %d", ln);
+							stop = 1;
+						} else {
+							if(r[1] == NULL) {
+								cm_log("Config", "Missing argument at line %d", ln);
+								stop = 1;
+							} else {
+								struct tw_dir_entry* e = &current->dirs[current->dir_count++];
+								e->name = cm_strdup(r[1]);
+								e->dir = cm_strdup(dir);
+								e->type = TW_DIR_ALLOW;
+							}
+						}
+					} else if(cm_strcaseequ(r[0], "Deny")) {
+						if(dir == NULL) {
+							cm_log("Config", "Not in directory section at line %d", ln);
+							stop = 1;
+						} else {
+							if(r[1] == NULL) {
+								cm_log("Config", "Missing argument at line %d", ln);
+								stop = 1;
+							} else {
+								struct tw_dir_entry* e = &current->dirs[current->dir_count++];
+								e->name = cm_strdup(r[1]);
+								e->dir = cm_strdup(dir);
+								e->type = TW_DIR_DENY;
+							}
+						}
 					} else if(cm_strcaseequ(r[0], "BeginVirtualHost")) {
 						if(vhost != NULL) {
@@ -89,4 +178,6 @@
 								vhost = cm_strdup(r[1]);
 								current = &config.vhosts[config.vhost_count++];
+								current->dir_count = 0;
+								current->mime_count = 0;
 								int i;
 								current->name = cm_strdup(vhost);
@@ -142,5 +233,5 @@
 						} else {
 							if(current->root != NULL) free(current->root);
-							current->root = cm_strdup(r[1]);
+							current->root = cm_strdup(strcmp(r[1], "/") == 0 ? "" : r[1]);
 						}
 					} else if(cm_strcaseequ(r[0], "ServerRoot")) {
@@ -151,4 +242,16 @@
 							if(config.server_root != NULL) free(config.server_root);
 							config.server_root = cm_strdup(r[1]);
+						}
+					} else if(cm_strcaseequ(r[0], "MIMEType")) {
+						if(r[1] == NULL) {
+							cm_log("Config", "Missing extension at line %d", ln);
+							stop = 1;
+						}else if(r[2] == NULL) {
+							cm_log("Config", "Missing MIME at line %d", ln);
+							stop = 1;
+						} else {
+							struct tw_mime_entry* e = &current->mimes[current->mime_count++];
+							e->ext = cm_strdup(r[1]);
+							e->mime = cm_strdup(r[2]);
 						}
 					} else if(cm_strcaseequ(r[0], "LoadModule")) {
Index: trunk/Server/server.c
===================================================================
--- trunk/Server/server.c	(revision 20)
+++ trunk/Server/server.c	(revision 21)
@@ -15,12 +15,13 @@
 #include <stdbool.h>
 #include <stdarg.h>
+#include <sys/stat.h>
 
 #include <cm_string.h>
 #include <cm_log.h>
+#include <cm_dir.h>
 
 #ifdef __MINGW32__
 #include <winsock2.h>
 #include <process.h>
-#define NO_IPV6
 #else
 #include <sys/select.h>
@@ -37,9 +38,4 @@
 int sockcount = 0;
 
-#ifdef NO_IPV6
-#define SOCKADDR struct sockaddr_in
-#else
-#define SOCKADDR struct sockaddr_in6
-#endif
 SOCKADDR addresses[MAX_PORTS];
 int sockets[MAX_PORTS];
@@ -152,5 +148,5 @@
 	    "</html>\n"
 
-void tw_process_page(SSL* ssl, int sock, const char* status, const char* type, const unsigned char* doc, size_t size) {
+void tw_process_page(SSL* ssl, int sock, const char* status, const char* type, FILE* f, const unsigned char* doc, size_t size) {
 	char construct[512];
 	sprintf(construct, "%llu", (unsigned long long)size);
@@ -170,5 +166,11 @@
 	size_t incr = 0;
 	while(1) {
-		tw_write(ssl, sock, (unsigned char*)doc + incr, size < 128 ? size : 128);
+		if(f != NULL){
+			char buffer[128];
+			fread(buffer, size < 128 ? size : 128, 1, f);
+			tw_write(ssl, sock, buffer, size < 128 ? size : 128);
+		}else{
+			tw_write(ssl, sock, (unsigned char*)doc + incr, size < 128 ? size : 128);
+		}
 		incr += 128;
 		if(size <= 128) break;
@@ -216,5 +218,5 @@
 void tw_http_error(SSL* ssl, int sock, int error, char* name, int port) {
 	char* str = tw_http_default_error(error, name, port);
-	tw_process_page(ssl, sock, tw_http_status(error), "text/html", str, strlen(str));
+	tw_process_page(ssl, sock, tw_http_status(error), "text/html", NULL, str, strlen(str));
 	free(str);
 }
@@ -240,4 +242,10 @@
 				free(tmp);
 				free(h);
+			} else if(add[i] == 'l') {
+				char* h = cm_url_escape(va_arg(va, const char*));
+				char* tmp = *str;
+				*str = cm_strcat(tmp, h);
+				free(tmp);
+				free(h);
 			} else if(add[i] == 'd') {
 				int n = va_arg(va, int);
@@ -266,4 +274,5 @@
 	int port;
 	bool ssl;
+	SOCKADDR addr;
 };
 
@@ -272,7 +281,8 @@
 	bool ssl = ((struct pass_entry*)ptr)->ssl;
 	int port = ((struct pass_entry*)ptr)->port;
+	SOCKADDR addr = ((struct pass_entry*)ptr)->addr;
 	free(ptr);
 #else
-void tw_server_pass(int sock, bool ssl, int port) {
+void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) {
 #endif
 	char* name = config.hostname;
@@ -295,5 +305,25 @@
 	int ret = tw_http_parse(s, sock, &req);
 	if(ret == 0) {
+		char* vhost = cm_strdup(config.hostname);
 		int i;
+		for(i = 0; req.headers[i] != NULL; i += 2){
+			if(cm_strcaseequ(req.headers[i], "Host")){
+				free(vhost);
+				vhost = req.headers[i + 1];
+				break;
+			}
+		}
+		cm_log("Server", "Host is %s", vhost);
+		int port = s == NULL ? 80 : 443;
+		char* host = cm_strdup(vhost);
+		for(i = 0; vhost[i] != 0; i++){
+			if(vhost[i] == ':'){
+				host[i] = 0;
+				port = atoi(host + i + 1);
+				break;
+			}
+		}
+		cm_log("Server", "Hostname is `%s', port is `%d'", host, port);
+		struct tw_config_entry* vhost_entry = tw_vhost_match(host, port);
 		for(i = 0; i < config.module_count; i++) {
 			tw_mod_request_t mod_req = (tw_mod_request_t)tw_module_symbol(config.modules[i], "mod_request");
@@ -313,28 +343,80 @@
 		}
 		if(!res._processed) {
-			char* str = malloc(1);
-			str[0] = 0;
-			addstring(&str, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n");
-			addstring(&str, "<html>\n");
-			addstring(&str, "	<head>\n");
-			addstring(&str, "		<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
-			addstring(&str, "		<title>Index of %h</title>\n", req.path);
-			addstring(&str, "	</head>\n");
-			addstring(&str, "	<body>\n");
-			addstring(&str, "		<h1>Index of %h</h1>\n", req.path);
-			addstring(&str, "		<hr>\n");
-			addstring(&str, "		<table border=\"0\">\n");
-			addstring(&str, "			<tr>\n");
-			addstring(&str, "				<th></th>\n");
-			addstring(&str, "				<th>Filename</th>\n");
-			addstring(&str, "			</tr>\n");
-			addstring(&str, "		</table>\n");
-			addstring(&str, "		<hr>\n");
-			addstring(&str, "		<address>%s Server at %s Port %d</address>\n", tw_server, name, port);
-			addstring(&str, "	</body>\n");
-			addstring(&str, "</html>\n");
-			tw_process_page(s, sock, tw_http_status(200), "text/html", str, strlen(str));
-			free(str);
-		}
+			cm_log("Server", "Document root is %s", vhost_entry->root == NULL ? "not set" : vhost_entry->root);
+			char* path = cm_strcat(vhost_entry->root == NULL ? "" : vhost_entry->root, req.path);
+			cm_log("Server", "Filesystem path is %s", path);
+			struct stat st;
+			if(stat(path, &st) == 0){
+				if(!tw_permission_allowed(path, addr, req, vhost_entry)){
+					tw_http_error(s, sock, 403, name, port);
+				}else if(S_ISDIR(st.st_mode)){
+					char* str = malloc(1);
+					str[0] = 0;
+					char** items = cm_scandir(path);
+					addstring(&str, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n");
+					addstring(&str, "<html>\n");
+					addstring(&str, "	<head>\n");
+					addstring(&str, "		<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+					addstring(&str, "		<title>Index of %h</title>\n", req.path);
+					addstring(&str, "	</head>\n");
+					addstring(&str, "	<body>\n");
+					addstring(&str, "		<h1>Index of %h</h1>\n", req.path);
+					addstring(&str, "		<hr>\n");
+					addstring(&str, "		<table border=\"0\">\n");
+					addstring(&str, "			<tr>\n");
+					addstring(&str, "				<th></th>\n");
+					addstring(&str, "				<th>Filename</th>\n");
+					addstring(&str, "			</tr>\n");
+					if(items != NULL){
+						for(i = 0; items[i] != NULL; i++){
+							addstring(&str, "<tr>\n");
+							addstring(&str, "	<td></td>\n");
+							addstring(&str, "	<td><a href=\"%l\">%h</a></td>\n", items[i], items[i]);
+							addstring(&str, "</tr>\n");
+						}
+					}
+					addstring(&str, "		</table>\n");
+					addstring(&str, "		<hr>\n");
+					addstring(&str, "		<address>%s Server at %s Port %d</address>\n", tw_server, name, port);
+					addstring(&str, "	</body>\n");
+					addstring(&str, "</html>\n");
+					tw_process_page(s, sock, tw_http_status(200), "text/html", NULL, str, strlen(str));
+					free(str);
+				}else{
+					char* mime = "application/octet-stream";
+					bool set = false;
+					char* ext = NULL;
+					for(i = strlen(req.path) - 1; i >= 0; i--){
+						if(req.path[i] == '.'){
+							ext = cm_strdup(req.path + i);
+							break;
+						}
+					}
+					for(i = 0; i < vhost_entry->mime_count; i++){
+						if(strcmp(vhost_entry->mimes[i].ext, "all") == 0 || (ext != NULL && strcmp(vhost_entry->mimes[i].ext, ext) == 0)){
+							mime = vhost_entry->mimes[i].mime;
+							set = true;
+						}
+					}
+					if(!set){
+						for(i = 0; i < config.root.mime_count; i++){
+							if(strcmp(config.root.mimes[i].ext, "all") == 0 || (ext != NULL && strcmp(config.root.mimes[i].ext, ext) == 0)){
+								mime = config.root.mimes[i].mime;
+								set = true;
+							}
+						}
+					}
+					if(ext != NULL) free(ext);
+					FILE* f = fopen(path, "rb");
+					tw_process_page(s, sock, tw_http_status(200), mime, f, NULL, st.st_size);
+					fclose(f);
+				}
+			}else{
+				tw_http_error(s, sock, 404, name, port);
+			}
+			free(path);
+		}
+		free(vhost);
+		free(host);
 	} else {
 		tw_http_error(s, sock, 400, name, port);
@@ -379,9 +461,10 @@
 					e->ssl = config.ports[i] & (1ULL << 32);
 					e->port = config.ports[i];
+					e->addr = claddr;
 					thread = (HANDLE)_beginthreadex(NULL, 0, tw_server_pass, e, 0, NULL);
 #else
 					pid_t pid = fork();
 					if(pid == 0) {
-						tw_server_pass(sock, config.ports[i] & (1ULL << 32), config.ports[i]);
+						tw_server_pass(sock, config.ports[i] & (1ULL << 32), config.ports[i], claddr);
 						_exit(0);
 					} else {
Index: trunk/Server/tw_config.h
===================================================================
--- trunk/Server/tw_config.h	(revision 20)
+++ trunk/Server/tw_config.h	(revision 21)
@@ -4,9 +4,43 @@
 #define __TW_CONFIG_H__
 
+#include "tw_http.h"
+
 #include <stdint.h>
+#include <stdbool.h>
 
-#define MAX_PORTS 1024
-#define MAX_VHOSTS 1024
-#define MAX_MODULES 1024
+#ifdef __MINGW32__
+#include <winsock2.h>
+#define NO_IPV6
+#else
+#include <netinet/in.h>
+#endif
+
+#ifdef NO_IPV6
+#define SOCKADDR struct sockaddr_in
+#else
+#define SOCKADDR struct sockaddr_in6
+#endif
+
+#define MAX_PORTS	1024
+#define MAX_VHOSTS	1024
+#define MAX_MODULES	1024
+#define MAX_DIRS 	1024
+#define MAX_MIME 	1024
+
+enum TW_DIR_TYPE {
+	TW_DIR_ALLOW = 0,
+	TW_DIR_DENY
+};
+
+struct tw_dir_entry {
+	char* name;
+	char* dir;
+	int type;
+};
+
+struct tw_mime_entry {
+	char* ext;
+	char* mime;
+};
 
 struct tw_config_entry {
@@ -16,4 +50,8 @@
 	char* sslcert;
 	char* root;
+	struct tw_dir_entry dirs[MAX_DIRS];
+	int dir_count;
+	struct tw_mime_entry mimes[MAX_DIRS];
+	int mime_count;
 };
 
@@ -33,4 +71,5 @@
 int tw_config_read(const char* path);
 struct tw_config_entry* tw_vhost_match(const char* name, int port);
+bool tw_permission_allowed(const char* path, SOCKADDR addr, struct tw_http_request req, struct tw_config_entry* vhost);
 
 #endif
Index: trunk/example.conf
===================================================================
--- trunk/example.conf	(revision 20)
+++ trunk/example.conf	(revision 21)
@@ -10,5 +10,16 @@
 SSLCertificate cert.pem
 
-DocumentRoot /var/www
+MIMEType all application/octet-stream
+MIMEType .html text/html
+
+DocumentRoot /
+
+BeginDirectory /
+	Allow all
+EndDirectory
+
+BeginDirectory /var/www
+	Deny all
+EndDirectory
 
 BeginVirtualHost nishinbsd-ssd
