/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* ConfigParser.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: lperrey +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2022/07/13 22:11:17 by me #+# #+# */ /* Updated: 2022/07/31 13:18:14 by simplonco ### ########.fr */ /* */ /* ************************************************************************** */ #include "ConfigParser.hpp" // Default ConfigParser::ConfigParser() { std::cout << "Default Constructor\n"; // don't use yet, you have no idea what the defaults are } ConfigParser::ConfigParser(const char* path) { std::cout << "Param Constructor\n"; std::ifstream file; std::string buf; size_t comment; _content.clear(); file.open(path); if (file.is_open()) { // are there more throws i need to add in case of errors, what would // those errors be? while (!file.eof()) { getline(file, buf); // remove # comments here. if ((comment = buf.find_first_of("#")) == std::string::npos) { // remove empty lines, i think... if ((buf.find_first_not_of(" \t")) != std::string::npos) _content.append(buf + '\n'); } 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() { std::vector * ret = new std::vector(); // std::vector ret; size_t start = 0; size_t curr = _content.find_first_not_of(" \t\n", 0); if (curr == std::string::npos) throw std::invalid_argument("empty config file"); while (curr != std::string::npos) { // why no checks here // if not here do i need them elsewhere? start = _content.find_first_not_of(" \t\n", curr); curr = _content.find_first_of(" \t\n", start); std::string key = _content.substr(start, curr - start); if (key != "server") throw std::invalid_argument("bad config file arguments 1"); ret->push_back(_parse_server(&curr)); } return (ret); } ServerConfig ConfigParser::_parse_server(size_t *start) { ServerConfig ret; size_t curr = _content.find_first_not_of(" \t\n", *start); if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 1"); curr = _content.find_first_of(" \t\n", curr + 1); // if (curr == std::string::npos) // are there other things to check for? // throw std::invalid_argument("bad config file syntax"); while (curr != std::string::npos) // here curr == { + 1 { // so this moves curr to past the word... std::string key = _get_first_word(&curr); // now curr is on space after 1st word. if (key == "}") { // why +1 curr is already after it no? *start = _content.find_first_not_of(" \t\n", curr + 1); break ; } else if (key == "location") ret.locations.push_back(_parse_location(&curr)); else { std::string values = _get_rest_of_line(&curr); // curr now should be \n _set_server_values(&ret, key, values); } } return (ret); } LocationConfig ConfigParser::_parse_location(size_t *start) { LocationConfig ret; size_t curr = *start; // start is after the 1st word aka "location" ret.path = _get_first_word(&curr); // in theory now curr should be right after the "path" curr = _content.find_first_not_of(" \t\n", curr); if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 2"); curr = _content.find_first_of(" \t\n", curr + 1); // if (curr == std::string::npos) // are there other things to check for? // throw std::invalid_argument("bad config file syntax"); while (curr != std::string::npos) { // so this moves curr to past the word... std::string key = _get_first_word(&curr); // now curr is on space after 1st word. if (key == "}") { *start = curr; break ; } else { std::string values = _get_rest_of_line(&curr); // curr now should be \n _set_location_values(&ret, key, values); } } return (ret); } void ConfigParser::_set_server_values(ServerConfig *server, \ const std::string key, std::string value) { /* // check key for ; // check values for ; at end and right number of words depending on key if (key.find_first_of(";") != std::string::npos) throw std::invalid_argument("bad config file arguments 2"); // there shouldn't be any tabs, right? not between values... if (value.find_first_of("\t") != std::string::npos) { std::cout << value << "\n"; throw std::invalid_argument("bad config file arguments 3"); } size_t i = value.find_first_of(";"); // so you can't have no ; // you can't have just ; // and you can't have a ; not at the end or several ; // in theory value_find_last_of should find the only ; if (i == std::string::npos || (value.find_last_not_of(" \n")) != i \ || value.compare(";") == 0) throw std::invalid_argument("bad config file arguments 4"); // we Trim value. // is this valid? // would it be better to shove the result directly in tmp_val? // like call substr in split? //value = value.substr(0, i - 1); value = value.substr(0, i); */ value = _pre_set_val_check(key, value); std::vector tmp_val = ::split(value, ' '); size_t size = tmp_val.size(); if (size < 1) throw std::invalid_argument("missing value"); else if (key == "server_name" && size == 1) { // should i be checking if the field is already filled server->server_name = tmp_val[0]; } else if (key == "listen" && size == 1) { // should i be checking if field already filled? // are we saying only 1 possible? if (tmp_val[0].find_first_of(":") == std::string::npos) { if (!(isNumeric(tmp_val[0]))) throw std::invalid_argument("value not a number"); server->host = "0.0.0.0"; server->port = tmp_val[0]; } else { std::vector tmp2 = ::split(tmp_val[0], ':'); if (!(::isNumeric(tmp2[1]))) throw std::invalid_argument("value not a number"); // not sure if this is what we want, means there's only 1 host per // server... if (server->host != "" && server->host != tmp2[0]) throw std::invalid_argument("bad listen"); server->host = tmp2[0]; server->port = tmp2[1]; } } else if (key == "root" && size == 1) { server->root = tmp_val[0]; } else if (key == "autoindex" && size == 1) { server->autoindex = (tmp_val[0] == "on" ? true : false); } else if (key == "client_body_limit" && size == 1) { //std::cout << "made it\n"; if (!(::isNumeric(tmp_val[0]))) throw std::invalid_argument("value not a number"); server->client_body_limit = atoi(tmp_val[0].c_str()); } else if (key == "recv_timeout" && size == 1) { // what is tv_sec and do i need it? // ok so i don't fully understand this part but ok, keep for now... server->recv_timeout.tv_sec = atoi(tmp_val[0].c_str()); } else if (key == "send_timeout" && size == 1) { server->send_timeout.tv_sec = atoi(tmp_val[0].c_str()); } else if (key == "index") { // should i be doing an access? for (unsigned long i = 0; i != tmp_val.size(); i++) server->index.push_back(tmp_val[i]); } else if (key == "allow_methods") { // you need to throw if it's a bad method type // or should we skip? and see if any others are good? for (unsigned long i = 0; i != tmp_val.size(); i++) { MethodType m = _str_to_method_type(tmp_val[i]); if (m == 3) throw std::invalid_argument("not a valid method"); server->allow_methods.push_back(m); } } else if (key == "return") { if (tmp_val.size() != 2) throw std::invalid_argument("wrong number of values"); // and tmp_val[0] should be a number and tmp_val[1] a string? if (!(::isNumeric(tmp_val[0]))) throw std::invalid_argument("value not a number"); // something about using access() to see if server->redirect_status = atoi(tmp_val[0].c_str()); server->redirect_uri = tmp_val[1]; } else if (key == "error_page") { // something more complicated? // like make sure ints then 1 string? std::string path = tmp_val[tmp_val.size() - 1]; for (unsigned long i = 0; i != tmp_val.size() - 1; i++) { // what are the bounds for Error codes? if (!(isNumeric_btw(0, 600, tmp_val[i]))) throw std::invalid_argument("value not a valid number"); int status_code = atoi(tmp_val[i].c_str()); // yea cuz here we continue.. why suddenly flexible not throw ? if (server->error_pages.find(status_code) != server->error_pages.end()) continue ; server->error_pages[status_code] = path; } } else { throw std::invalid_argument("wrong number of values"); } // std::cout << "End set\n"; } void ConfigParser::_set_location_values(LocationConfig *location, \ const std::string key, std::string value) { value = _pre_set_val_check(key, value); std::vector tmp_val = ::split(value, ' '); size_t size = tmp_val.size(); if (size < 1) throw std::invalid_argument("missing value"); else if (key == "root" && size == 1) { location->root = tmp_val[0]; } else if (key == "client_body_limit" && size == 1) { location->client_body_limit = atoi(tmp_val[0].c_str()); } else if (key == "index") { for (unsigned long i = 0; i != tmp_val.size(); i++) location->index.push_back(tmp_val[i]); } else if (key == "allow_methods") { for (unsigned long i = 0; i != tmp_val.size(); i++) location->allow_methods.push_back(_str_to_method_type(tmp_val[i])); } else if (key == "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 8"); // 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()); } else { throw std::invalid_argument("bad config file arguments 9"); } }