diff --git a/Makefile b/Makefile index b2e5765..6d30aee 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CC = gcc -CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L +CFLAGS = -std=c99 -Wall -D_POSIX_C_SOURCE=201112L -D_XOPEN_SOURCE=500 -D_GNU_SOURCE LD = gcc LDFLAGS = -lpthread -lrt BIN_NAME = cfloor -OBJS = obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o obj/status.o +OBJS = obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o obj/status.o obj/files.o obj/mime.o DEPS = $(OBJS:%.o=%.d) all: $(BIN_NAME) diff --git a/src/files.c b/src/files.c index e69de29..7e2b20a 100644 --- a/src/files.c +++ b/src/files.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "files.h" +#include "misc.h" +#include "logging.h" +#include "status.h" +#include "mime.h" + +const char* documentRoot = NULL; +bool indexes = false; + +void files_init(const char* _documentRoot, bool _index) { + documentRoot = _documentRoot; + indexes = _index; +} + +void showIndex(int fd, const char* path) { + FILE* stream = fdopen(fd, "w"); + if (stream == NULL) { + error("status: Couldn't get stream from fd: %s", strerror(errno)); + return; + } + + // TODO index + + fclose(stream); +} + + +void fuckyouHandler(struct request request, struct response response) { + struct headers headers = headers_create(); + headers_mod(&headers, "Content-Type", "text/plain"); + int fd = response.sendHeader(400, &headers, &request); + headers_free(&headers); + + FILE* stream = fdopen(fd, "w"); + if (stream == NULL) { + error("files: Couldn't get stream from fd: %s", strerror(errno)); + return; + } + + fprintf(stream, "Status 400\nYou know... I'm not an idiot...\n"); + + fclose(stream); +} + +void fileHandler(struct request request, struct response response) { + if (documentRoot == NULL) { + error("files: No document root given."); + status(request, response, 500); + return; + } + + char* path = request.metaData.path; + + char* tmp = malloc(strlen(path) + 1 + strlen(documentRoot) + 1); + if (tmp == NULL) { + error("files: Couldn't malloc for path construction: %s", strerror(errno)); + status(request, response, 500); + return; + } + strcpy(tmp, documentRoot); + strcat(tmp, "/"); + strcat(tmp, path); + path = realpath(tmp, NULL); + if (path == NULL) { + free(tmp); + error("files: Couldn't get constructed realpath: %s", strerror(errno)); + status(request, response, 500); + return; + } + free(tmp); + + info("files: file path is: %s", path); + + if (strncmp(documentRoot, path, strlen(documentRoot)) != 0) { + free(path); + warn("files: Requested path not in document root."); + fuckyouHandler(request, response); + + return; + } + + if (access(path, F_OK | R_OK) < 0) { + free(path); + + switch(errno) { + case EACCES: + status(request, response, 403); + return; + case ENOENT: + case ENOTDIR: + status(request, response, 404); + return; + default: + warn("files: Couldn't access file: %s", strerror(errno)); + status(request, response, 500); + return; + } + } + + struct stat statObj; + if (stat(path, &statObj) < 0) { + free(path); + + error("files: Couldn't stat file: %s", strerror(errno)); + status(request, response, 500); + return; + } + + if (S_ISDIR(statObj.st_mode)) { + struct headers headers = headers_create(); + headers_mod(&headers, "Content-Type", "text/html; charset=utf-8"); + int fd = response.sendHeader(200, &headers, &request); + headers_free(&headers); + + // if indexes + + showIndex(fd, path); + } else if (S_ISREG(statObj.st_mode)) { + int filefd = open(path, O_RDONLY); + if (filefd < 0) { + free(path); + status(request, response, 500); + return; + } + + off_t size = statObj.st_size; + int length = strlenOfNumber(size); + tmp = malloc(length + 1); + if (tmp == NULL) { + free(path); + close(filefd); + error("files: Couldn't allocate for content length: %s", strerror(errno)); + status(request, response, 500); + return; + } + snprintf(tmp, length + 1, "%ld", size); + + struct headers headers = headers_create(); + headers_mod(&headers, "Content-Type", getMineFromFileName(path)); + headers_mod(&headers, "Content-Length", tmp); + free(tmp); + + int sockfd = response.sendHeader(200, &headers, &request); + headers_free(&headers); + + char c; + + while(read(filefd, &c, 1)) + write(sockfd, &c, 1); + + close(filefd); + close(sockfd); + } else { + status(request, response, 500); + } + + free(path); +} diff --git a/src/files.h b/src/files.h index e69de29..1e7de71 100644 --- a/src/files.h +++ b/src/files.h @@ -0,0 +1,13 @@ +#ifndef FILES_H +#define FILES_H + +#include + +#include "files.h" +#include "misc.h" + +void files_init(const char* documentRoot, bool index); + +void fileHandler(struct request request, struct response response); + +#endif diff --git a/src/headers.c b/src/headers.c index 87e15e3..453a164 100644 --- a/src/headers.c +++ b/src/headers.c @@ -3,6 +3,7 @@ #include "headers.h" #include "misc.h" +#include "logging.h" struct headers headers_create() { return (struct headers) { @@ -35,6 +36,8 @@ int headers_mod(struct headers* headers, const char* _key, const char* _value) { if (tmp == NULL) { return HEADERS_ALLOC_ERROR; } + + char* value = tmp; int index = headers_find(headers, key); @@ -202,11 +205,13 @@ int headers_metadata(struct metaData* metaData, char* header) { if (path == NULL) { return HEADERS_ALLOC_ERROR; } + strcpy(path, _path); char* queryString = malloc(strlen(_queryString) + 1); if (queryString == NULL) { free(path); return HEADERS_ALLOC_ERROR; } + strcpy(queryString, _queryString); metaData->method = method; metaData->httpVersion = httpVersion; diff --git a/src/main.c b/src/main.c index 15b95c9..022133c 100644 --- a/src/main.c +++ b/src/main.c @@ -5,15 +5,19 @@ #include "networking.h" #include "logging.h" #include "headers.h" +#include "files.h" handler_t handlerGetter(struct metaData metaData, const char* host, struct bind* bind) { - return NULL; + return &fileHandler; } int main(int argc, char** argv) { setLogging(stderr, DEBUG, true); setCriticalHandler(NULL); + char* documentRoot = realpath(".", NULL); + files_init(documentRoot, true); + struct headers headers = headers_create(); headers_mod(&headers, "Server", "CFloor 0.1"); @@ -33,11 +37,12 @@ int main(int argc, char** argv) { .getHandler = &handlerGetter }; - initNetworking(config); + networking_init(config); while(true) { sleep(0xffff); } headers_free(&headers); + free(documentRoot); } diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..2e7af61 --- /dev/null +++ b/src/mime.c @@ -0,0 +1,135 @@ +#include +#include + +#include "mime.h" + +struct mime { + const char* mime; + int number; + const char** extensions; +} mimelist[] = { + { + .mime = "application/pdf", + .number = 1, + .extensions = (const char* []) { + "pdf" + } + },{ + .mime = "text/css", + .number = 1, + .extensions = (const char* []) { + "css" + } + },{ + .mime = "application/html", + .number = 2, + .extensions = (const char* []) { + "html", + "htm" + } + },{ + .mime = "application/javascript", + .number = 1, + .extensions = (const char* []) { + "js" + } + },{ + .mime = "application/json", + .number = 1, + .extensions = (const char* []) { + "json" + } + },{ + .mime = "application/xml", + .number = 1, + .extensions = (const char* []) { + "xml" + } + },{ + .mime = "image/jpeg", + .number = 2, + .extensions = (const char* []) { + "jpg", + "jpeg" + } + },{ + .mime = "image/gif", + .number = 1, + .extensions = (const char* []) { + "gif" + } + },{ + .mime = "image/x-icon", + .number = 1, + .extensions = (const char* []) { + "ico" + } + },{ + .mime = "image/png", + .number = 1, + .extensions = (const char* []) { + "png" + } + },{ + .mime = "image/svg+xml", + .number = 1, + .extensions = (const char* []) { + "svg" + } + },{ + .mime = "audio/mpeg", + .number = 1, + .extensions = (const char* []) { + "mpga" + } + },{ + .mime = "video/mpeg", + .number = 1, + .extensions = (const char* []) { + "mpeg" + } + },{ + .mime = "audio/mp4", + .number = 4, + .extensions = (const char* []) { + "mp4a", + "m4a" + } + },{ + .mime = "video/mp4", + .number = 1, + .extensions = (const char* []) { + "mp4" + } + },{ + .mime = "video/webm", + .number = 1, + .extensions = (const char* []) { + "webm" + } + }, +}; + +const char* unknownMime = "application/octet-stream"; + + +const char* getMineFromFileName(const char* filename) { + const char* extension = NULL; + const char* tmp = filename; + + while ((tmp = strcasestr(tmp, ".")) != NULL) { + tmp += 1; + extension = tmp; + } + if (extension == NULL) + return unknownMime; + + for (int i = 0; i < (sizeof(mimelist) / sizeof(mimelist[0])); i++) { + for(int j = 0; j < mimelist[i].number; j++) { + if (strcmp(extension, mimelist[i].extensions[j]) == 0) + return mimelist[i].mime; + } + } + + return unknownMime; +} diff --git a/src/mime.h b/src/mime.h new file mode 100644 index 0000000..aac59fc --- /dev/null +++ b/src/mime.h @@ -0,0 +1,6 @@ +#ifndef MINE_H +#define MINE_H + +const char* getMineFromFileName(const char* filename); + +#endif diff --git a/src/misc.c b/src/misc.c index fa81682..8d6f1c4 100644 --- a/src/misc.c +++ b/src/misc.c @@ -54,3 +54,14 @@ char* strclone(const char* string) { strcpy(result, string); return result; } + +int strlenOfNumber(long long number) { + int result = 1; + + while(number > 9) { + number /= 10; + result++; + } + + return result; +} diff --git a/src/misc.h b/src/misc.h index ad55aa1..e0d7a81 100644 --- a/src/misc.h +++ b/src/misc.h @@ -58,4 +58,6 @@ void* fileCopyThread(void* data); char* strclone(const char* string); +int strlenOfNumber(long long number); + #endif diff --git a/src/networking.c b/src/networking.c index cdf9e82..3fe12c5 100644 --- a/src/networking.c +++ b/src/networking.c @@ -625,7 +625,7 @@ void* listenThread(void* _bind) { } } -void initNetworking(struct networkingConfig _networkingConfig) { +void networking_init(struct networkingConfig _networkingConfig) { networkingConfig = _networkingConfig; connectionList = linked_create(); diff --git a/src/networking.h b/src/networking.h index e641c64..0795e9f 100644 --- a/src/networking.h +++ b/src/networking.h @@ -84,6 +84,6 @@ struct networkingConfig { #define TIMING_CLOCK CLOCK_REALTIME -void initNetworking(struct networkingConfig networkingConfig); +void networking_init(struct networkingConfig networkingConfig); #endif diff --git a/src/status.c b/src/status.c index df4f155..9721a7b 100644 --- a/src/status.c +++ b/src/status.c @@ -334,6 +334,7 @@ struct statusStrings getStatusStrings(int status) { void status(struct request request, struct response response, int status) { struct headers headers = headers_create(); + headers_mod(&headers, "Content-Type", "text/html; charset=utf-8"); int fd = response.sendHeader(status, &headers, &request); headers_free(&headers); @@ -355,7 +356,7 @@ void status(struct request request, struct response response, int status) { fprintf(stream, string.statusFormat, request.metaData.path); fprintf(stream, "
\n"); fprintf(stream, " \n"); - fprintf(stream, "\n"); + fprintf(stream, "\n"); fclose(stream); } diff --git a/src/status.h b/src/status.h index a1f0361..b2d47ef 100644 --- a/src/status.h +++ b/src/status.h @@ -11,5 +11,6 @@ struct statusStrings { struct statusStrings getStatusStrings(int status); void status500(struct request request, struct response response); +void status(struct request request, struct response response, int status); #endif