diff --git a/Common/dir.c b/Common/dir.c index eca0e65..f830d9b 100644 --- a/Common/dir.c +++ b/Common/dir.c @@ -9,20 +9,20 @@ #include #include -int cm_sort(const void* _a, const void* _b){ +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){ +char** cm_scandir(const char* path) { DIR* dir = opendir(path); - if(dir != NULL){ + 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){ + while((d = readdir(dir)) != NULL) { + if(strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) { struct stat s; char* p = cm_strcat3(path, "/", d->d_name); stat(p, &s); @@ -30,7 +30,8 @@ char** cm_scandir(const char* path){ char** old = r; int i; - for(i = 0; old[i] != NULL; 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) ? "/" : ""); @@ -39,10 +40,22 @@ char** cm_scandir(const char* path){ } } int len; - for(len = 0; r[len] != NULL; len++); + for(len = 0; r[len] != NULL; len++) + ; qsort(r, len, sizeof(char*), cm_sort); + + 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 + 1] = old[i]; + r[0] = cm_strdup("../"); + r[i + 1] = NULL; + free(old); + return r; - }else{ + } else { return NULL; } } diff --git a/Server/config.c b/Server/config.c index 26e577f..15bcee7 100644 --- a/Server/config.c +++ b/Server/config.c @@ -30,20 +30,20 @@ struct tw_config_entry* tw_vhost_match(const char* name, int port) { return &config.root; } -bool tw_permission_allowed(const char* path, SOCKADDR addr, struct tw_http_request req, struct tw_config_entry* vhost){ +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++){ + for(i = 0; i < vhost->dir_count; i++) { struct tw_dir_entry* e = &vhost->dirs[i]; pathstart = false; - if(strlen(path) >= strlen(e->dir)){ + 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]){ + for(j = 0; path[j] != 0 && e->dir[j] != 0; j++) { + if(path[j] != e->dir[j]) { pathstart = false; break; } @@ -51,15 +51,15 @@ again: } char* noslash = cm_strdup(e->dir); noslash[strlen(noslash) - 1] = 0; - if(strcmp(e->dir, path) == 0 || strcmp(noslash, path) == 0 || pathstart){ + if(strcmp(e->dir, path) == 0 || strcmp(noslash, path) == 0 || pathstart) { found = true; - if(strcmp(e->name, "all") == 0){ + if(strcmp(e->name, "all") == 0) { perm = e->type == TW_DIR_ALLOW; } } free(noslash); } - if(!found && vhost != &config.root){ + if(!found && vhost != &config.root) { vhost = &config.root; goto again; } @@ -81,6 +81,7 @@ void tw_config_init(void) { config.root.root = NULL; config.root.mime_count = 0; config.root.dir_count = 0; + config.root.icon_count = 0; config.vhost_count = 0; config.module_count = 0; config.extension = NULL; @@ -179,6 +180,7 @@ int tw_config_read(const char* path) { current = &config.vhosts[config.vhost_count++]; current->dir_count = 0; current->mime_count = 0; + current->icon_count = 0; int i; current->name = cm_strdup(vhost); current->port = -1; @@ -246,7 +248,7 @@ int tw_config_read(const char* path) { if(r[1] == NULL) { cm_log("Config", "Missing extension at line %d", ln); stop = 1; - }else if(r[2] == NULL) { + } else if(r[2] == NULL) { cm_log("Config", "Missing MIME at line %d", ln); stop = 1; } else { @@ -254,6 +256,18 @@ int tw_config_read(const char* path) { e->ext = cm_strdup(r[1]); e->mime = cm_strdup(r[2]); } + } else if(cm_strcaseequ(r[0], "Icon")) { + if(r[1] == NULL) { + cm_log("Config", "Missing MIME at line %d", ln); + stop = 1; + } else if(r[2] == NULL) { + cm_log("Config", "Missing path at line %d", ln); + stop = 1; + } else { + struct tw_icon_entry* e = ¤t->icons[current->icon_count++]; + e->mime = cm_strdup(r[1]); + e->icon = cm_strdup(r[2]); + } } else if(cm_strcaseequ(r[0], "LoadModule")) { for(i = 1; r[i] != NULL; i++) { void* mod = tw_module_load(r[i]); diff --git a/Server/http.c b/Server/http.c index 5c6b5f9..6528d79 100644 --- a/Server/http.c +++ b/Server/http.c @@ -59,8 +59,14 @@ int tw_http_parse(SSL* ssl, int sock, struct tw_http_request* req) { struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; - int n = select(FD_SETSIZE, &fds, NULL, NULL, &tv); - if(n == 0) break; + if(!SSL_has_pending(ssl)) { + int n = select(FD_SETSIZE, &fds, NULL, NULL, &tv); + if(n <= 0) { + free(header); + tw_free_request(req); + return -1; + } + } int len = tw_read(ssl, sock, buffer, 512); if(len <= 0) break; int i; diff --git a/Server/server.c b/Server/server.c index 322e015..7d3755c 100644 --- a/Server/server.c +++ b/Server/server.c @@ -165,11 +165,11 @@ void tw_process_page(SSL* ssl, int sock, const char* status, const char* type, F tw_write(ssl, sock, "\r\n", 2); size_t incr = 0; while(1) { - if(f != NULL){ + if(f != NULL) { char buffer[128]; fread(buffer, size < 128 ? size : 128, 1, f); tw_write(ssl, sock, buffer, size < 128 ? size : 128); - }else{ + } else { tw_write(ssl, sock, (unsigned char*)doc + incr, size < 128 ? size : 128); } incr += 128; @@ -268,6 +268,48 @@ void addstring(char** str, const char* add, ...) { } } +char* tw_get_mime(const char* ext, struct tw_config_entry* vhost_entry) { + char* mime = "application/octet-stream"; + if(ext == NULL) return mime; + bool set = false; + int i; + 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; + } + } + } + return mime; +} + +char* tw_get_icon(const char* mime, struct tw_config_entry* vhost_entry) { + char* icon = ""; + if(mime == NULL) return ""; + bool set = false; + int i; + for(i = 0; i < vhost_entry->icon_count; i++) { + if(strcmp(vhost_entry->icons[i].mime, "all") == 0 || (mime != NULL && strcmp(vhost_entry->icons[i].mime, mime) == 0)) { + icon = vhost_entry->icons[i].icon; + set = true; + } + } + if(!set) { + for(i = 0; i < config.root.icon_count; i++) { + if(strcmp(config.root.icons[i].mime, "all") == 0 || (mime != NULL && strcmp(config.root.icons[i].mime, mime) == 0)) { + icon = config.root.icons[i].icon; + } + } + } + return icon; +} + #ifdef __MINGW32__ struct pass_entry { int sock; @@ -306,8 +348,8 @@ void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) { 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")){ + for(i = 0; req.headers[i] != NULL; i += 2) { + if(cm_strcaseequ(req.headers[i], "Host")) { free(vhost); vhost = req.headers[i + 1]; break; @@ -316,8 +358,8 @@ void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) { 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] == ':'){ + for(i = 0; vhost[i] != 0; i++) { + if(vhost[i] == ':') { host[i] = 0; port = atoi(host + i + 1); break; @@ -346,10 +388,10 @@ void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) { 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)){ + 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)){ + } else if(S_ISDIR(st.st_mode)) { char* str = malloc(1); str[0] = 0; char** items = cm_scandir(path); @@ -367,12 +409,44 @@ void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) { addstring(&str, " \n"); addstring(&str, " Filename\n"); addstring(&str, " \n"); - if(items != NULL){ - for(i = 0; items[i] != NULL; i++){ + if(items != NULL) { + for(i = 0; items[i] != NULL; i++) { + char* ext = NULL; + int j; + for(j = strlen(items[i]) - 1; j >= 0; j--) { + if(items[i][j] == '.') { + ext = cm_strdup(items[i] + j); + break; + } + } + char* mime = tw_get_mime(ext, vhost_entry); + if(strcmp(items[i], "../") == 0) { + mime = "misc/parent"; + } else if(items[i][strlen(items[i]) - 1] == '/') { + mime = "misc/dir"; + } + char* icon = tw_get_icon(mime, vhost_entry); + if(ext != NULL) free(ext); + char* itm = cm_strdup(items[i]); + if(strlen(itm) >= 32) { + if(itm[strlen(itm) - 1] == '/') { + itm[31] = 0; + itm[30] = '/'; + itm[29] = '.'; + itm[28] = '.'; + itm[27] = '.'; + } else { + itm[31] = 0; + itm[30] = '.'; + itm[29] = '.'; + itm[28] = '.'; + } + } addstring(&str, "\n"); - addstring(&str, " \n"); - addstring(&str, " %h\n", items[i], items[i]); + addstring(&str, " \"icon\"\n", icon); + addstring(&str, " %h\n", items[i], itm); addstring(&str, "\n"); + free(itm); } } addstring(&str, " \n"); @@ -382,42 +456,28 @@ void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) { addstring(&str, "\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; + } else { char* ext = NULL; - for(i = strlen(req.path) - 1; i >= 0; i--){ - if(req.path[i] == '.'){ + 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; - } - } - } + char* mime = tw_get_mime(ext, vhost_entry); 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{ + } else { tw_http_error(s, sock, 404, name, port); } free(path); } free(vhost); free(host); + } else if(ret == -1) { } else { tw_http_error(s, sock, 400, name, port); } diff --git a/Server/tw_config.h b/Server/tw_config.h index 1ec6e4e..7f63dda 100644 --- a/Server/tw_config.h +++ b/Server/tw_config.h @@ -21,11 +21,12 @@ #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 +#define MAX_PORTS 1024 +#define MAX_VHOSTS 1024 +#define MAX_MODULES 1024 +#define MAX_DIRS 1024 +#define MAX_MIME 1024 +#define MAX_ICON 1024 enum TW_DIR_TYPE { TW_DIR_ALLOW = 0, @@ -43,6 +44,11 @@ struct tw_mime_entry { char* mime; }; +struct tw_icon_entry { + char* mime; + char* icon; +}; + struct tw_config_entry { char* name; int port; @@ -53,6 +59,8 @@ struct tw_config_entry { int dir_count; struct tw_mime_entry mimes[MAX_DIRS]; int mime_count; + struct tw_icon_entry icons[MAX_DIRS]; + int icon_count; }; struct tw_config { diff --git a/example.conf b/example.conf index 163bae7..0d11fd1 100644 --- a/example.conf +++ b/example.conf @@ -11,16 +11,19 @@ SSLCertificate cert.pem MIMEType all application/octet-stream MIMEType .html text/html +MIMEType .txt text/plain +MIMEType .png image/png -DocumentRoot / +Icon all /icons/unknown.png +Icon text/plain /icons/text.png +Icon misc/dir /icons/folder.png +Icon misc/parent /icons/parent.png + +DocumentRoot /var/www BeginDirectory / Allow all EndDirectory -BeginDirectory /var/www - Deny all -EndDirectory - BeginVirtualHost nishinbsd-ssd EndVirtualHost