#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()) { 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) { // check for 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("failed to 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) { if ((start = _content.find_first_not_of(" \t\n", curr)) == std::string::npos) throw std::invalid_argument("empty config file"); if ((curr = _content.find_first_of(" \t\n", start)) == std::string::npos) throw std::invalid_argument("empty config file"); 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)); } _post_processing(ret); return (ret); } ServerConfig ConfigParser::_parse_server(size_t *start) { ServerConfig ret; size_t curr = _content.find_first_not_of(" \t\n", *start); ret.client_body_limit = 0; if (curr == std::string::npos || _content[curr] != '{') throw std::invalid_argument("bad config file syntax 1"); if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos) throw std::invalid_argument("bad config file syntax"); // are there other things to check for? 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.autoindex = false; ret.redirect_status = 0; ret.allow_methods = 0; ret.path = _get_first_word(&curr); if (ret.path[0] != '/') throw std::invalid_argument("Location path require a leading /"); // ret.path.insert(0, "/"); // 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"); if ((curr = _content.find_first_of(" \t\n", curr + 1)) == std::string::npos) 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); } // should i be sending pointers or references? void ConfigParser::_set_server_values(ServerConfig *server, \ const std::string key, std::string value) { // should i be sending pointers or references? 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" && server->server_name.empty()) { for (std::vector::iterator it = server->server_name.begin(); \ it < server->server_name.end(); it++) { if (it->compare(tmp_val[0]) == 0) throw std::invalid_argument("server_name already exists"); } server->server_name.push_back(tmp_val[0]); } else if (key == "listen" && size == 1 && server->host == "" \ && server->port == "") { if (tmp_val[0].find_first_of(":") == std::string::npos) { if (!::isNumeric(tmp_val[0])) throw std::invalid_argument("bad port number"); server->host = "0.0.0.0"; server->port = tmp_val[0]; } else { std::vector tmp2 = ::split(tmp_val[0], ':'); std::vector ip = ::split(tmp2[0], '.'); if (ip.size() != 4) throw std::invalid_argument("bad host ip"); for (size_t i = 0; i < ip.size(); i++) { if (!::isNumeric_btw(0, 255, ip[i])) throw std::invalid_argument("bad host ip"); } if (!::isNumeric(tmp2[1])) throw std::invalid_argument("bad port number"); server->host = tmp2[0]; server->port = tmp2[1]; } } else if (key == "root" && size == 1 && server->root == "") { // remove trailing / if (tmp_val[0][tmp_val[0].size() - 1] == '/') tmp_val[0].erase(tmp_val[0].size() - 1, 1); // tmp_val[0].push_back('/'); server->root = tmp_val[0]; } else if (key == "client_body_limit" && size == 1 \ && server->client_body_limit == 0) { if (!::isNumeric(tmp_val[0])) throw std::invalid_argument("client_body_limit not a number"); server->client_body_limit = std::strtoul(tmp_val[0].c_str(), NULL, 10); } else if (key == "index") { for (unsigned long i = 0; i != tmp_val.size(); i++) server->index.push_back(tmp_val[i]); } else if (key == "error_page") { std::string path = tmp_val[tmp_val.size() - 1]; for (unsigned long i = 0; i != tmp_val.size() - 1; i++) { if (!(isNumeric_btw(400, 599, tmp_val[i]))) throw std::invalid_argument("invalid error code"); int status_code = std::strtoul(tmp_val[i].c_str(), NULL, 10); if (server->error_pages.find(status_code) != server->error_pages.end()) throw std::invalid_argument("redeclaring error page"); server->error_pages[status_code] = path; } } else throw std::invalid_argument("bad key value pair"); } // should i be sending pointers or references? void ConfigParser::_set_location_values(LocationConfig *location, \ const std::string key, std::string value) { // should i be sending pointers or references? 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 == "") { // remove trailing / if (tmp_val[0][tmp_val[0].size() - 1] == '/') tmp_val[0].erase(tmp_val[0].size() - 1, 1); // tmp_val[0].push_back('/'); location->root = tmp_val[0]; } else if (key == "autoindex" && size == 1) location->autoindex = (tmp_val[0] == "on" ? true : false); 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" && location->allow_methods == 0) { for (unsigned long i = 0; i != tmp_val.size(); i++) { http_method m = ::str_to_http_method(tmp_val[i]); if (m == UNKNOWN) throw std::invalid_argument("not a valid method"); location->allow_methods |= m; } } else if (key == "cgi_ext") { for (size_t i = 0; i < tmp_val.size(); i++) { if (tmp_val[i][0] == '.') throw std::invalid_argument("cgi_ext should not have a leading '.'"); location->cgi_ext.push_back(tmp_val[i]); } } else if (key == "redirect" && location->redirect_status == 0 \ && location->redirect_uri == "") { if (tmp_val.size() != 2) throw std::invalid_argument("wrong number of values"); if (tmp_val[0] != "301" && tmp_val[0] != "302" && tmp_val[0] != "303" && tmp_val[0] != "307" && tmp_val[0] != "308") throw std::invalid_argument("bad redirect status"); std::cout << tmp_val[1] << '\n'; if (tmp_val[1].compare(0, 7, "http://") && tmp_val[1].compare(0, 8, "https://")) throw std::invalid_argument("bad redirect uri"); location->redirect_status = std::strtoul(tmp_val[0].c_str(), NULL, 10); location->redirect_uri = tmp_val[1]; } else if (key == "upload_dir" && size == 1 && location->upload_dir == "") { // what checks to do? // add trailing / if (tmp_val[0][tmp_val[0].size() - 1] != '/') tmp_val[0].push_back('/'); location->upload_dir = tmp_val[0]; } else throw std::invalid_argument("bad key value pair"); }