diff --git a/.ccls b/.ccls new file mode 100644 index 0000000..a66e36d --- /dev/null +++ b/.ccls @@ -0,0 +1,8 @@ +clang++ +-Iincludes +%cpp +%hpp --include=./includes/webserv.hpp +-Wall +-Wextra +-Werror +-std=c++98 diff --git a/.gitignore b/.gitignore index 3b39428..770e119 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ webserv *.o -.ccls -.vscode \ No newline at end of file +.vscode diff --git a/Makefile b/Makefile index 0c7df57..f659bb5 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,10 @@ SANFLAGS = -fsanitize=address CXXFLAGS = -Wall -Wextra -Werror -g -std=c++98 $(SANFLAGS) SRC = $(wildcard ./src/*.cpp) +SRC += $(wildcard ./src/*/*.cpp) HEADERS = $(wildcard ./includes/*.hpp) +HEADERS += $(wildcard src/config/*.hpp) INCLUDES = ./includes/ @@ -34,6 +36,6 @@ re: $(MAKE) all run: $(NAME) - ./$(NAME) + ASAN_OPTIONS=detect_leaks=0 ./$(NAME) .PHONY: all clean fclean re diff --git a/config/example.toml b/config/example.toml new file mode 100644 index 0000000..b2418f2 --- /dev/null +++ b/config/example.toml @@ -0,0 +1,45 @@ +[[server]] + name = "serv1" + host = "127.0.0.1" + port = 8080 + error_page = "error.html" + body_size_limit = 10 + [[server.location]] + location = "/" + # redirection ??? + root = "/var/www/html/jopa.html" + methods = ["GET", "POST"] + directory_list = true + directory_fallback = "its_a_directory.html" + upload_accept = true + upload_dir = "/var/www/html/upload" + + [[server.location]] + location = "/secret/" + root = "/var/www/html/secret.html" + methods = ["GET"] + directory_list = false + directory_fallback = "oops.html" + +[[server]] + name = "2222" + host = "10.0.0.1" + port = 8081 + error_page = "error2.html" + body_size_limit = 10 + [[server.location]] + location = "/root2/" + # redirection ??? + root = "/var/www/html/jopa.html" + methods = ["GET", "POST"] + directory_list = true + directory_fallback = "its_a_directory.html" + upload_accept = false + upload_dir = "/var/www/html/upload" + + [[server.location]] + location = "/secret2/" + root = "/var/www/html/secret.html" + methods = ["GET"] + directory_list = false + directory_fallback = "oops.html" diff --git a/config/simple.toml b/config/simple.toml new file mode 100644 index 0000000..4abbf7d --- /dev/null +++ b/config/simple.toml @@ -0,0 +1,34 @@ +[[server]] + name = "serv1" + host = "127.0.0.1" + port = 8080 + error_page = "error.html" + body_size_limit = 10 + [[server.location]] + location = "/" + root = "/var/www/html/jopa.html" + methods = ["GET", "POST"] + directory_list = true + directory_fallback = "its_a_directory.html" + upload_accept = true + upload_dir = "/var/www/html/upload" + [[server.location]] + location = "/secret/" + root = "/var/www/html/secret.html" + methods = ["GET"] + directory_list = false + directory_fallback = "oops.html" +[[server]] + name = "2222" + host = "10.0.0.1" + port = 8081 + error_page = "error2.html" + body_size_limit = 10 + [[server.location]] + location = "/root2/" + root = "/var/www/html/jopa.html" + methods = ["GET", "POST"] + directory_list = true + directory_fallback = "its_a_directory.html" + upload_accept = false + upload_dir = "/var/www/html/upload" diff --git a/includes/webserv.hpp b/includes/webserv.hpp index ef01b64..4ba7016 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -39,4 +39,19 @@ #include "Server.hpp" #include "Header.hpp" +#include +#include + +void parse(void); + +class toml_node; + +/* typedef std::vector TOMLArray; */ +/* typedef std::vector TOMLArrayOfMap; */ +typedef std::map TOMLMap; // = JSONObject +typedef std::vector TOMLMapArray; +typedef std::vector TOMLArray; + +void display(TOMLMap *config); + #endif diff --git a/src/config/TOMLNode.hpp b/src/config/TOMLNode.hpp new file mode 100644 index 0000000..2cf2c64 --- /dev/null +++ b/src/config/TOMLNode.hpp @@ -0,0 +1,181 @@ +#ifndef TOMLNODE_HPP +#define TOMLNODE_HPP + +#include "webserv.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + + + + +class toml_node +{ + + union u_value + { + std::string *str; + int integer; + bool boolean; + /* std::vector *array; */ + TOMLArray *array; + /* std::map *map; */ + TOMLMap *map; + /* std::vector > *map_array; */ + TOMLMapArray *map_array; + } value; + + public: + + enum e_type + { + STRING, NUM, BOOL, ARRAY, MAP, MAPARRAY, NIL + } type; + + enum e_type get_type(void) + { + return (type); + } + + TOMLMap *getMap(void) + { + return (value.map); + } + + TOMLMapArray *getMapArray(void) + { + return (value.map_array); + } + + void setString(std::string *str) + { + value.str = str; + type = STRING; + } + + void setNumber(int num) + { + value.integer = num; + type = NUM; + } + + void setArr(TOMLArray *toml_array) + { + value.array = toml_array; + type = ARRAY; + } + void setBool(bool b) + { + value.boolean = b; + type = BOOL; + } + void setNil(void) + { + type = NIL; + } + void setObject(TOMLMap *obj) + { + value.map = obj; + type = MAP; + } + void setMapArray(TOMLMapArray *map_array) + { + value.map_array = map_array; + type = MAPARRAY; + } + + static std::string *TOMLMap_to_string(TOMLMap *map) + { + std::stringstream ss; + std::string *result = new std::string(); + TOMLMap::iterator it; + + ss << "{\n"; + for (it = map->begin(); it != map->end(); ++it) + { + ss << it->first + << ": " + << *(it->second->toString()) + << std::endl; + } + + ss << "}" << std::endl; + + /* ss >> *result; */ + *result = ss.str(); + return (result); + } + + std::string *toString(void) const + { + switch (type) + { + case STRING: + { + return (value.str); + } + case NUM: + { + std::stringstream ss; + ss << value.integer; + std::string *result = new std::string(); + ss >> *result; + return (result); + } + case ARRAY: + { + TOMLArray::iterator it; + std::string *result = new std::string("[ "); + for (it = value.array->begin(); it != value.array->end(); ++it) + { + *result += *((*it)->toString()); + *result += ", "; + } + *result += " ]"; + return (result); + } + case MAP: + { + return (TOMLMap_to_string(value.map)); + } + case MAPARRAY: + { + std::stringstream ss; + std::string *result = new std::string(); + TOMLMapArray::iterator it; + TOMLMapArray *map_array = value.map_array; + + ss << "[\n"; + for (it = map_array->begin(); it != map_array->end(); ++it) + { + ss << (*TOMLMap_to_string(*it)); + ss << ", " << std::endl; + } + ss << "]\n"; + + + /* ss >> *result; */ + *result = ss.str(); + return (result); + } + case BOOL: + { + std::string *result; + if (value.boolean) + result = new std::string("true"); + else + result = new std::string("false"); + return (result); + } + default: + return ( new std::string("Not implemented :)")); + } + } +}; + +#endif diff --git a/src/config/TOMLParser.hpp b/src/config/TOMLParser.hpp new file mode 100644 index 0000000..556e898 --- /dev/null +++ b/src/config/TOMLParser.hpp @@ -0,0 +1,394 @@ +#ifndef TOMLPARSER_HPP +#define TOMLPARSER_HPP + +#include "TOMLNode.hpp" +#include "Tokenizer.hpp" +#include + +namespace config +{ + class TOMLParser + { + private: + std::fstream file; + TOMLMap *root; //root of TOML tree + /* toml_node *current; //node currently being parsed */ + Tokenizer tokenizer; + + public: + TOMLParser(const std::string filename) : tokenizer(filename) {} + TOMLMap *parse(void); + + toml_node *parseMap(void) + { + std::cerr << "Parsing object" << std::endl; + toml_node *node = new toml_node; + TOMLMap *mapObject = new TOMLMap; + bool completed = false; + while (!completed) + { + if (tokenizer.hasMoreTokens()) + { + s_token nextToken; + try { nextToken = tokenizer.getToken(); } + catch (std::logic_error e) + { + std::cerr << e.what() << std::endl; + break; + } + if (nextToken.type == MAPARRAY_DECL) + { + tokenizer.rollBackToken(); + break; + } + std::string key = nextToken.value; + std::cerr << key << std::endl; + if (tokenizer.getToken().type != ASSIGN) + throw std::logic_error("EXPECTED assign!"); + nextToken = tokenizer.getToken(); + switch (nextToken.type) + { + case STRING: + { + tokenizer.rollBackToken(); + (*mapObject)[key] = parseString(); + break; + } + case ARR_OPEN: + { + (*mapObject)[key] = parseArray(); + break; + } + case NUMBER: + { + tokenizer.rollBackToken(); + (*mapObject)[key] = parseNumber(); + break; + } + case BOOL: + { + tokenizer.rollBackToken(); + (*mapObject)[key] = parseBool(); + break; + } + case MAPARRAY_DECL: + { + std::cerr << "reached MAPARRAY_DECL in parseMap" << std::endl; + completed = true; + break; + } + default: + { + /* throw std::logic_error("jopa in parseMap"); */ + std::cerr << "Unknown token, marking as complete" << std::endl; + completed = true; + break; + } + } + if (tokenizer.hasMoreTokens()) + nextToken = tokenizer.getToken(); + else + break; + if (nextToken.type != NEWLINE) + { + throw std::logic_error("EXPECTED newline"); + } + } + else + { + throw std::logic_error("parseMap: no more tokens"); + } + } + node->setObject(mapObject); + return (node); + } + + toml_node *parseString(void) + { + /* toml_node *node; */ + toml_node *node = new toml_node; + std::string *sValue; + + std::cerr << "Parsing string" << std::endl; + s_token token = tokenizer.getToken(); + sValue = new std::string(token.value); + node->setString(sValue); + + return (node); + } + + toml_node *parseNumber(void) + { + toml_node *node = new toml_node; + int value; + + std::cerr << "Parsing number" << std::endl; + s_token token = tokenizer.getToken(); + value = std::atoi(token.value.c_str()); + node->setNumber(value); + + return (node); + } + + toml_node *parseArray(void) + { + std::cerr << "Parsing array" << std::endl; + toml_node *node; + toml_node *result = new toml_node; + TOMLArray *array = new TOMLArray; + bool completed = false; + s_token current; + + while (!completed) + { + if (!tokenizer.hasMoreTokens()) + throw std::logic_error("No more tokens"); + else + { + current = tokenizer.getToken(); + switch (current.type) + { + case ARR_OPEN: + { + node = parseArray(); + break; + } + case STRING: + { + tokenizer.rollBackToken(); + node = parseString(); + break; + } + case NUMBER: + { + tokenizer.rollBackToken(); + node = parseNumber(); + break; + } + case BOOL: + { + tokenizer.rollBackToken(); + node = parseBool(); + break; + } + case NIL: + { + node = parseNil(); + break; + } + default: + { + throw std::logic_error("unkown token in parseArray"); + } + + } + array->push_back(node); + current = tokenizer.getToken(); + if (current.type == COMMA) + continue; + else if (current.type == ARR_CLOSE) + completed = true; + else + throw std::invalid_argument("Unexpected token in array!"); + } + } + result->setArr(array); + return (result); + } + + toml_node *parseBool(void) + { + toml_node *node = new toml_node; + bool value; + + std::cerr << "Parsing bool" << std::endl; + s_token token = tokenizer.getToken(); + if (token.value == "true") + value = true; + else if (token.value == "false") + value = false; + else + throw std::invalid_argument("Unexpected bool value"); + node->setBool(value); + + return (node); + } + + toml_node *parseNil(void) + { + toml_node *node = new toml_node; + std::cerr << "Parsing NIL" << std::endl; + node->setNil(); + return (node); + } + }; + + /* parse tha root ! */ + TOMLMap *TOMLParser::parse(void) + { + std::cerr << "Parsing ROOT" << std::endl; + root = new TOMLMap; + bool completed = false; + while (!completed) + { + if (tokenizer.hasMoreTokens()) + { + s_token current; + try { current = tokenizer.getToken(); } + catch (std::logic_error e) + { + std::cerr << e.what() << std::endl; + break; + } + if (current.type == MAPARRAY_DECL) + { + /* parseMapArray(); */ + tokenizer.rollBackToken(); + std::cerr << "Parsing MapArray" << std::endl; + toml_node *map_node; + toml_node *maparr_node; + s_token current; + + try { current = tokenizer.getToken(); } + catch (std::logic_error e) + { + std::cerr << e.what() << std::endl; + break; + } + if (current.type == MAPARRAY_DECL) + { + if (tokenizer.getToken().type != NEWLINE) + throw std::logic_error("no newline after map_array declaration"); + map_node = parseMap(); + } + else + throw std::logic_error("unexpected token in parseMapArray"); + + TOMLMap::iterator it; + std::cout << current.value << std::endl; + std::string name = current.value; + std::vector full_name; + size_t dot; + + while (1) + { + dot = name.find('.'); + if (dot == std::string::npos) + break; + /* std::cout << dot << std::endl; */ + full_name.push_back(name.substr(0, dot)); + name.erase(0, dot + 1); + } + full_name.push_back(name); + + for (size_t i = 0; i < full_name.size(); i++) + std::cout << full_name[i] << ", "; + std::cout << std::endl; + + /* throw std::logic_error("tha end"); */ + TOMLMap *local_root; + + std::vector::iterator subname = full_name.begin(); + local_root = root; + while (1) + { + if (subname + 1 == full_name.end()) + { + it = local_root->find(*subname); + if (it == local_root->end()) + { + maparr_node = new toml_node; + TOMLMapArray *map_array = new TOMLMapArray; + map_array->push_back(map_node->getMap()); + maparr_node->setMapArray(map_array); + (*local_root)[*subname] = maparr_node; + } + else + (it->second)->getMapArray()->push_back(map_node->getMap()); + break; + } + else + { + it = local_root->find(*subname); + + toml_node *map_node2; + map_node2 = new toml_node; + TOMLMap *map = new TOMLMap; + map_node2->setObject(map); + /* subname not found in local_root */ + if (it == local_root->end()) + { + (*local_root)[*subname] = map_node2; + local_root = map; + } + /* subname found in local_root */ + else + { + local_root = *((it->second)->getMapArray()->end() - 1); + } + + } + ++subname; + } + + } + else + { + std::string key = current.value; + std::cerr << key << std::endl; + if (tokenizer.getToken().type != ASSIGN) + throw std::logic_error("EXPECTED assign!"); + current = tokenizer.getToken(); + switch (current.type) + { + case STRING: + { + tokenizer.rollBackToken(); + (*root)[key] = parseString(); + break; + } + case ARR_OPEN: + { + (*root)[key] = parseArray(); + break; + } + case NUMBER: + { + tokenizer.rollBackToken(); + (*root)[key] = parseNumber(); + break; + } + case BOOL: + { + tokenizer.rollBackToken(); + (*root)[key] = parseBool(); + break; + } + default: + { + /* throw std::logic_error("jopa in parseMap"); */ + std::cerr << "Unknown token, marking as complete" << std::endl; + completed = true; + break; + } + } + if (tokenizer.hasMoreTokens()) + current = tokenizer.getToken(); + else + break; + if (current.type != NEWLINE) + { + throw std::logic_error("EXPECTED newline"); + } + } + } + else + { + completed = true; + break; + } + } + return (root); + } +} +#endif diff --git a/src/config/Tokenizer.hpp b/src/config/Tokenizer.hpp new file mode 100644 index 0000000..791f8a8 --- /dev/null +++ b/src/config/Tokenizer.hpp @@ -0,0 +1,245 @@ +#ifndef TOKENIZER_HPP +#define TOKENIZER_HPP + +#include "webserv.hpp" +#include +#include +#include +#include +#include +#include + +namespace config +{ + enum e_token + { + KEY, + NEWLINE, + ASSIGN, + STRING, + NUMBER, + COMMA, + BOOL, + NIL, + ARR_OPEN, + ARR_CLOSE, + MAP_OPEN, + MAP_CLOSE, + MAPARRAY_DECL + }; + + struct s_token + { + std::string value; + e_token type; + /* std::string to_string(void); */ + }; + + bool isspace(char c) + { + if (c == ' ' || c == '\t') + return (true); + else + return (false); + } + + bool istomlkey(char c) + { + if (isalnum(c) || c == '-' || c == '_') + return (true); + else + return (false); + } + + class Tokenizer + { + private: + std::fstream file; + size_t prev_pos; + e_token last_token; + public: + Tokenizer(std::string filename) + { + file.open(filename.c_str(), std::ios::in); + if (!file.good()) + { + std::cerr << "file didn't open" << std::endl; + } + } + char getWithoutWhiteSpace(); + struct s_token getToken(); + bool hasMoreTokens(); + bool firstToken() + { + // doesn't account for indent! + if (file.tellg() == 0 || file.tellg() == 1 || (last_token == NEWLINE)) + return (true); + else + return (false); + } + void rollBackToken(); + }; + + char Tokenizer::getWithoutWhiteSpace(void) + { + char c = ' '; + while (config::isspace(c)) + { + file.get(c); + if ((c == ' ') && !file.good()) + { + throw std::logic_error("No more tokens!"); + } + else if (!file.good()) + return (c); + } + return (c); + } + + bool Tokenizer::hasMoreTokens(void) + { + return (!file.eof()); + } + + void Tokenizer::rollBackToken(void) + { + if (file.eof()) + file.clear(); + file.seekg(prev_pos); + } + + /* struct s_token Tokenizer::getKey(void) */ + /* { */ + /* char c; */ + /* struct s_token token; */ + /* if (file.eof()) */ + /* { */ + /* std::cout << "Tokens exhausted" << std::endl; */ + /* } */ + /* } */ + + struct s_token Tokenizer::getToken(void) + { + char c; + struct s_token token; + + if (file.eof()) + { + std::cout << "Tokens exhausted" << std::endl; + } + prev_pos = file.tellg(); + c = getWithoutWhiteSpace(); + + if (firstToken() && config::istomlkey(c)) + { + token.type = KEY; + while (config::istomlkey(c)) + { + token.value += c; + file.get(c); + } + } + else if (c == '"') + { + token.type = STRING; + token.value = ""; + /* TODO: maybe do-while? */ + file.get(c); + while (c != '"') + { + token.value += c; + file.get(c); + } + } + else if (c == '[') + { + std::streampos prev_pos = file.tellg(); + file.get(c); + if (c == '[') + { + token.type = MAPARRAY_DECL; + file.get(c); + while (c != ']') + { + token.value += c; + file.get(c); + } + if (c == ']') + file.get(c); + if (c != ']') + throw std::logic_error("error in MAPARRAY_DECL"); + } + else + { + token.type = ARR_OPEN; + file.seekg(prev_pos); + } + + } + else if (c == ']') + token.type = ARR_CLOSE; + else if (c == '=') + token.type = ASSIGN; + else if (c == '\n') + token.type = NEWLINE; + else if (c == '-' || isdigit(c)) + { + std::streampos prevCharPos; + token.type = NUMBER; + token.value = ""; + token.value += c; + /* prevCharPos = file.tellg(); */ + while (c == '-' || c == '.' || isdigit(c)) + { + prevCharPos = file.tellg(); + file.get(c); + if (file.eof()) + break; + else + { + if (c == '-' || c == '.' || isdigit(c)) + token.value += c; + else + file.seekg(prevCharPos); + + } + } + } + else if (c == 'f') + { + token.type = BOOL; + token.value = "false"; + file.seekg(4, std::ios_base::cur); + + /* token.value = ""; */ + /* while (std::isalpha(c)) */ + /* { */ + /* token.value += c; */ + /* file.get(c); */ + /* } */ + std::cerr << "value is: " << token.value << std::endl; + std::cerr << "c is: " << c << std::endl; + } + else if (c == 't') + { + token.type = BOOL; + while (std::isalpha(c)) + { + token.value += c; + file.get(c); + } + /* file.seekg(3, std::ios_base::cur); */ + } + else if (c == 'n') + { + token.type = NIL; + file.seekg(3, std::ios_base::cur); + } + else if (c == ',') + token.type = COMMA; + last_token = token.type; + return (token); + } +} + +#endif diff --git a/src/config/parse.cpp b/src/config/parse.cpp new file mode 100644 index 0000000..90e806d --- /dev/null +++ b/src/config/parse.cpp @@ -0,0 +1,50 @@ +#include "webserv.hpp" +#include +#include +#include +#include +#include +#include + +#include "TOMLNode.hpp" +#include "TOMLParser.hpp" +#include "Tokenizer.hpp" + +/* struct location */ +/* { */ +/* std::string location; */ +/* std::string root; */ + +/* } */ + +/* class config */ +/* { */ +/* std::string name; */ +/* } */ +namespace config +{ + + void display(TOMLMap *root_map) + { + std::cout << ">>> printing config: <<<" << std::endl; + + TOMLMap::iterator it; + + for (it = root_map->begin(); it != root_map->end(); ++it) + { + std::cout << it->first + << ": " + << *(it->second->toString()); + std::cout << ", " << std::endl; + /* << std::endl << "-------" << std::endl; */ + } + } +} + +void parse(void) +{ + std::string filename = "config/simple.toml"; + config::TOMLParser parser(filename); + TOMLMap *root = parser.parse(); + config::display(root); +} diff --git a/src/main.cpp b/src/main.cpp index d9079a3..44a1a6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,9 +7,10 @@ int main(int argc, char **argv) Server server; - server.readConfig(); - server.setupConfig(); - server.start(); + /* server.readConfig(); */ + /* server.setupConfig(); */ + /* server.start(); */ + parse();