Files
42_INT_12_webserv/srcs/config/parser.cpp
2022-08-12 20:08:49 +02:00

343 lines
9.0 KiB
C++

#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("#")) == NPOS)
{
// remove empty lines, i think...
if ((buf.find_first_not_of(" \t")) != 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);
}
*/
// const?
std::vector<ServerConfig> * ConfigParser::parse()
{
std::vector<ServerConfig> * ret = new std::vector<ServerConfig>();
size_t start = 0;
size_t curr = _content.find_first_not_of(" \t\n", 0);
if (curr == NPOS)
throw std::invalid_argument("empty config file");
while (curr != NPOS)
{
if ((start = _content.find_first_not_of(" \t\n", curr)) == NPOS)
throw std::invalid_argument("empty config file");
if ((curr = _content.find_first_of(" \t\n", start)) == 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 == NPOS || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax 1");
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
throw std::invalid_argument("bad config file syntax");
// are there other things to check for?
while (curr != 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 == NPOS || _content[curr] != '{')
throw std::invalid_argument("bad config file syntax 2");
if ((curr = _content.find_first_of(" \t\n", curr + 1)) == NPOS)
throw std::invalid_argument("bad config file syntax");
while (curr != 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<std::string> 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<std::string>::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(":") == NPOS)
{
if (!::isNumeric_btw(0, 65535, tmp_val[0]))
throw std::invalid_argument("bad port number");
server->host = "0.0.0.0";
server->port = tmp_val[0];
}
else
{
std::vector<std::string> tmp2 = ::split(tmp_val[0], ':');
std::vector<std::string> 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_btw(0, 65535, 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<std::string> 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");
}