diff --git a/srcs/ConfigParser.cpp b/srcs/ConfigParser.cpp new file mode 100644 index 0000000..1d748b6 --- /dev/null +++ b/srcs/ConfigParser.cpp @@ -0,0 +1,352 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConfigParser.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/13 22:11:17 by me #+# #+# */ +/* Updated: 2022/07/25 02:56:26 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ConfigParser.hpp" + + +/***** Stuf to rework + +I don't love the use of EMPTY and FAILURE and those + +I would rather throw exceptions everywhere... + + + + +*/ + + + + +// Default +ConfigParser::ConfigParser() +{ + std::cout << "Default Constructor\n"; + // don't use yet, you have no idea what the defaults are +} + +//ConfigParser::ConfigParser(std::string &path) +//ConfigParser::ConfigParser(char & path) +ConfigParser::ConfigParser(const char* path) +{ + std::cout << "Param Constructor\n"; + + std::ifstream file; + std::string buf; +// std::string tmp; // maybe there's a reason we redeclare vars... + size_t comment; // just a number so fine to reset + + _content.clear(); + file.open(path); + if (file.is_open()) + { + while (!file.eof()) + { + getline(file, buf); + // remove # comments here. + if ((comment = buf.find_first_of("#")) == std::string::npos) + { + _content.append(buf + '\n'); + } +// else if (comment > 0 && (buf.find_first_not_of(" \t")) != std::string::npos) + // i think this works cuz it will find # no matter what, so it will find + // something else first if there is non comment stuff. + else if (comment > 0 && (buf.find_first_not_of(" \t")) < comment) + { + // is there a comment at the end of the line + std::string tmp = buf.substr(0, comment - 1); + _content.append(tmp + '\n'); + } + } + file.close(); + } + else + throw std::invalid_argument("open config"); +} + +ConfigParser::~ConfigParser() +{ + // do i need to destroy anything, won't it handle itself? +} + +/* +ConfigParser & ConfigParser::operator=(const ConfigParser& rhs) +{ + if (this == rhs) // * & ? + return (*this); // * ? + + // make some stuff equal + return (*this); +} +*/ + + +std::vector * ConfigParser::parse() +{ + // is this the best way to do this? new? + std::vector *ret = new std::vector(); + + size_t prev = 0; + size_t curr = _content.find_first_not_of(" \t\n", prev); + + + if (curr == std::string::npos) + throw std::invalid_argument("empty config file"); + while (cur != std::string::npos) + { + // ok i don't love her way of scoping out the "server" strings... + prev = _content.find_first_not_of(" \t\n", curr); + curr = _content.find_first_of(" \t\n", prev); + std::string key = _conent.substr(prev, curr - prev); + if (key != "server") + throw std::invalid_argument("bad config file arguments"); +// Server server = parse_server(&curr); +// ret->push_back(server); + // why not this? + ret->push_back(parse_server(&curr); + } + return (ret); +} + +// could we return a ref instead? + // i think no +Server ConfigParser::parse_server(size_t *start) +{ + Server ret; + size_t key_start; + size_t value_end; + size_t prev = _content.find_first_not_of(" \t\n", *start); + + if (prev == std::string::npos || _content[prev] != '{') + throw std::invalid_argument("bad config file syntax"); + + size_t curr = _content.find_first_of(" \t\n", ++prev); + while (curr != std::string::npos) + { + _check_proper_line_end(&prev, &curr); // nope, not an option i think... +/* if ((prev = _content.find_first_not_of(" \t\n", curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + if ((curr = _content.find_first_of(" \t\n", prev)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); +*/ key_start = prev; + + std::string key = _content.substr(prev, curr - prev); + switch (key) + { + case "}": + *start = _content.find_first_not_of(" \t\n" curr + 1); + break ; + case "location": + // this does assume we have locations in Server... we could change + // the name but it's so clear... + ret.location.push_back(parse_location(&curr)); + default: + _check_proper_line_end(&prev, &curr); + /* if ((prev = _content.find_first_not_of(" \t\n", curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + if ((curr = _content.find_first_of(" \t\n", prev)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + */ + // why bother with the if.. why not throw exception in check_line... + if ((value_end = check_line_syntax(_content.substr(key_start, curr - key_start))) == FAILED) + throw std::invalid_argument("bad config file arguments"); + if ((int)value_end == EMPTY) + continue ; + std::string value = _content.substr(prev, value_end - prev + key_start + 1) + // I might need to catch here, or i could do nothing and trow all the way to the top! + if (_set_server_value(&ret, key, value) == FAILED) + throw std::invalid_argument("bad config file arguments"); + } + } + return (ret); +} + + + +Location ConfigParser::parse_location(size_t *start) +{ + Location ret; + size_t key_start; + size_t value_end; + size_t prev = _content.find_first_not_of(" \t\n", *start); + + if (prev == std::string::npos || _content[prev] != '{') + throw std::invalid_argument("bad config file syntax"); + + size_t curr = _content.find_first_of(" \t\n", ++prev); + while (curr != std::string::npos) + { + _check_proper_line_end(&prev, &curr); + key_start = prev; + + std::string key = _content.substr(prev, curr - prev); + switch (key) + { + case "}": + *start = curr; + break ; + default: + _check_proper_line_end(&prev, &curr); + // why bother with the if.. why not throw exception in check_line... + if ((value_end = check_line_syntax(_content.substr(key_start, curr - key_start))) == FAILED) + throw std::invalid_argument("bad config file arguments"); + if ((int)value_end == EMPTY) + continue ; + std::string value = _content.substr(prev, value_end - prev + key_start + 1) + if (_set_location_value(&ret, key, value) == FAILED) + throw std::invalid_argument("bad config file arguments"); + } + } + return (ret); +} + +// not convinced i want to return an int... +// but could be helpful to get more feed back +// maybe use a throw... but catch in parse_server... +int ConfigParser::_set_server_value(Server *server, const std::string key, const std::string value) +{ + switch (key) + { + case "server_name": + server->server_name = value; + case "listen": + if (value.find_first_of(":") == std::string::npos) + { + // why not store as vector [4] ? + server->host = "0.0.0.0"; + server->value = value; + } + else + { + // maybe do this differently? + std::vector tmp = split(value, ':'); + // i might take issue with this, will see + if (server->host != "" && server->host != tmp[0]) + throw std::invalid_argument("bad config file arguments"); + server->host = tmp[0]; + server->port = tmp[1]; + } + case "root": + server->root = value; + case "index": + std::vector tmp = split(value, ' '); + for (unsigned long i = 0; i != tmp.size(); i++) + server->index.push_back(tmp[i]); + case "allow_methods": + std::vector tmp = split(value, ' '); + // might do something different here + // like change how methods are stored? + for (unsigned long i = 0; i != tmp.size(); i++) + server->allow_methods.push_back(str_to_method_type(tmp[i])); + case "autoindex": + server->autoindex = (value == "on" ? true : false); + case "client_body_limit": + server->client_body_limit = atoi(value.c_str()); + case "recv_timeout": + // what is tv_sec and do i need it? + server->recv_timeout.tv_sec = atoi(value.c_str()); + case "send_timeout": + server->send_timeout.tv_sec = atoi(value.c_str()); + case "return": + std::vector tmp = split(value, ' '); + server->redirect_status = atoi(tmp[0].c_str()); + server->redirect_uri = tmp[1]; + case "error_page": + std::vector tmp = split(value, ' '); + std::string path = tmp[tmp.size() - 1]; + for (unsigned long i = 0; i != tmp.size() - 1; i++) + { + int status_code = atoi(tmp[i].c_str()); + // yea IDK i might not want to store this like that... + if (server->error_pages.find(status_code) != server->error_pages.end()) + continue ; + server->error_pages[status_code] = path; + } + default : + throw std::invalid_argument("bad config file arguments"); + } + return (1); // for now but prolly will change... +} + +// again not sure i want an int ret +int ConfigParser::_set_location_value(Location *location, const std::string key, const std::string value) +{ + switch (key) + { + case "root": + location->root = value; + case "index": + std::vector tmp = split(value, ' '); + for (unsigned long i = 0; i != tmp.size(); i++) + location->index.push_back(tmp[i]); + case "allow_methods": + std::vector tmp = split(value, ' '); + for (unsigned long i = 0; i != tmp.size(); i++) + location->allow_methods.push_back(str_to_methodtype(tmp[i])); + case "cgi_info": + // ok wtf is all this even doing, figure that out + unsigned long i = value.find_first_of(" "); + if (i == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + // ok why an int now, we gotta be more consistent! + int j = value.find_first_not_of(" ", i); + location->cgi_info[value.substr(0, i)] = value.substr(j, value.length()); + case "client_body_limit": + location->client_body_limit = atoi(value.c_str()); + default : + throw std::invalid_argument("bad config file arguments"); + } + return (1); // again prolly want to change this... +} + + +// shit, this doesn't work... + // wait it might... +void ConfigParser::_check_proper_line_end(size_t *prev, size_t *curr) +{ + if ((*prev = _content.find_first_not_of(" \t\n", *curr)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); + if ((*curr = _content.find_first_of(" \t\n", *prev)) == std::string::npos) + throw std::invalid_argument("bad config file arguments"); +} + +// i'm not sure i like this... + // and it needs to be _check_... +// rework this whole thing... +int ConfigParser::check_line_syntax(std::string line) +{ + + // line must be end with semicolon + size_t semicol; + size_t find; + + semicol = line.find_first_of(";"); + if (semicol == std::string::npos) + return FAILED; + find = line.find_first_not_of(" \t\n", semicol + 1, line.length() - semicol - 1); + if (find != std::string::npos) + return FAILED; + find = line.find_last_not_of(" \t", semicol - 1); + return find; +} + + + +// I might need to make my own Exceptions to throw... + + + + + + + + diff --git a/srcs/ConfigParser.hpp b/srcs/ConfigParser.hpp new file mode 100644 index 0000000..b9ce28b --- /dev/null +++ b/srcs/ConfigParser.hpp @@ -0,0 +1,59 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConfigParser.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/11 23:01:41 by me #+# #+# */ +/* Updated: 2022/07/23 15:53:19 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONFIGPARSER_HPP +# define CONFIGPARSER_HPP + +# include "Webserv.hpp" // easier to just do this? + + +class ConfigParser { + +public: + + // canonical + + ConfigParser(const char* path); // a string? + ~ConfigParser(); + + // ideally i wouldn't have one cuz it makes no sense, when would i use it? +// ConfigParser & operator=(const ConfigParser& rhs); + +// void parse(); // return void cuz throw exceptions. + std::vector * parse(); // const? + + // other parses? + + +private: + std::string _content; + + // explicit? + // what exaclty does explicit do again? + ConfigParser(); // might need a path as arg? + // should this be in private since it always needs a path? + + + void _check_proper_line_end(size_t prev, size_t curr); // const? + + +}; + + + + + +#endif + + + + diff --git a/srcs/LocationConfig.hpp b/srcs/LocationConfig.hpp new file mode 100644 index 0000000..db45ddb --- /dev/null +++ b/srcs/LocationConfig.hpp @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* LocationConfig.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/23 16:08:00 by me #+# #+# */ +/* Updated: 2022/07/23 16:14:01 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef LOCATIONCONFIG_HPP +# define LOCATIONCONFIG_HPP + +// includes +# include "Webserv.hpp" + +// again, struct instead? +class LocationConfig +{ +public: + // canonic stuff? + + + int client_body_limit; + std::string path; + std::string root; + std::vector index; + std::vector allow_methods; + std::map cgi_info; + + + + + + +}; + + + + +#endif + + + + + + + + diff --git a/srcs/ServerConfig.hpp b/srcs/ServerConfig.hpp new file mode 100644 index 0000000..6935720 --- /dev/null +++ b/srcs/ServerConfig.hpp @@ -0,0 +1,66 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ServerConfig.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: me +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2022/07/23 15:55:16 by me #+# #+# */ +/* Updated: 2022/07/23 16:19:43 by me ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SERVERCONFIG_HPP +# define SERVERCONFIG_HPP + +# include "LocationConfig.hpp" +//other includes + +// a class that's all public? just so we have options? +class ServerConfig +{ +public: + + // i mean i guess i need some canonic stuff? + // although maybe if i make it a struct i can barebones it? + + + std::string server_name; + std::string root; + + std::vector index; + std::map error_pages; + + // i'm tempted to do something diff for storing method types... + std::vector allow_methods; + + std::vector locations; + + // might do something diff + struct timeval send_timeout; + struct timeval recv_timeout; + + int client_body_limit; + bool autoindex; + + // not sure what these look like in config file + int redirect_status; + std::string redirect_uri; + + // is this the best way? + std::string host; + std::string port; + +}; + + + + +#endif + + + + + + + diff --git a/srcs/Webserv.hpp b/srcs/Webserv.hpp index c9acb5f..5076f8e 100644 --- a/srcs/Webserv.hpp +++ b/srcs/Webserv.hpp @@ -11,6 +11,8 @@ # include // close # include // cout, cin # include // memset +# include +# include # include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname # include // sockaddr_in @@ -39,6 +41,7 @@ class Webserv private: int _socket_fd; // TODO: replace with vector of "Server" struct + std::vector _servers; int _epfd; // WIP global buffer. Need one variable set per "Client"