#include "Webserv.hpp" bool Webserv::_is_cgi(Client *client, std::string path) { std::string script_path; size_t file_type; size_t file_mode = client->status; size_t pos = 0; while (pos != NPOS) { pos = _cgi_pos(client, path, pos); if (pos == NPOS) break; client->fill_script_path(path, pos); script_path = "." + client->get_rq_script_path(); file_type = ::eval_file_type(script_path); if (file_type == IS_DIR) // but what if it's a symlink ? continue; if (file_type == IS_FILE) { file_mode = ::eval_file_mode( script_path, X_OK ); if (!file_mode) return true; } } client->clear_script(); client->status = file_mode; // 404 not_found OR 403 forbidden return false; } size_t Webserv::_cgi_pos(Client *client, std::string &path, size_t pos) { std::vector v_ext; std::vector::const_iterator it; std::vector::const_iterator it_end; size_t len; std::locale loc; // for isalpha() v_ext = client->assigned_location->cgi_ext; if (v_ext.empty()) return NPOS; it_end = client->assigned_location->cgi_ext.end(); while (pos < path.size()) { if (path.compare(pos, 2, "./") == 0) pos += 2; pos = path.find('.', pos); if (pos == NPOS) return pos; it = client->assigned_location->cgi_ext.begin(); for ( ; it != it_end; ++it) { len = (*it).size(); if (path.compare(pos + 1, len, *it) == 0) if ( !std::isalpha(path[pos + 1 + len], loc) ) return pos + 1 + len; } pos++; } return NPOS; } std::string Webserv::_exec_cgi(Client *client) { char** env; std::string script_output; env = _set_env(client); script_output = _exec_script(client, env); for (int i = 0; env[i]; i++) delete[] env[i]; delete[] env; return script_output; } char* Webserv::_dup_env(std::string var, std::string val = "") { std::string str; str = var + "=" + val; return ( strdup(str.c_str()) ); } char* Webserv::_dup_env(std::string var, int i) { std::string str; std::string val; val = ::itos(i); str = var + "=" + val; // TODO change strdup for something with new return ( strdup(str.c_str()) ); } char** Webserv::_set_env(Client *client) { char** env = new char*[19]; env[0] = _dup_env("AUTH_TYPE"); // authentification not supported env[1] = _dup_env("CONTENT_LENGTH" , client->get_rq_body().size()); env[2] = _dup_env("CONTENT_TYPE" , client->get_rq_headers("Content-Type")); env[3] = _dup_env("GATEWAY_INTERFACE" , "CGI/1.1"); // https://www.rfc-editor.org/rfc/rfc3875 env[4] = _dup_env("PATH_INFO" , client->get_rq_script_info()); env[5] = _dup_env("PATH_TRANSLATED"); // not supported env[6] = _dup_env("QUERY_STRING" , client->get_rq_query()); env[7] = _dup_env("REMOTE_ADDR" , client->get_cl_ip()); env[8] = _dup_env("REMOTE_HOST" , client->get_rq_headers("Host")); // just test env[9] = _dup_env("REMOTE_IDENT"); // authentification not supported env[10] = _dup_env("REMOTE_USER"); // authentification not supported env[11] = _dup_env("REQUEST_METHOD" , client->get_rq_method_str()); env[12] = _dup_env("SCRIPT_NAME" , client->get_rq_script_path()); env[13] = _dup_env("SERVER_NAME" , client->get_rq_hostname()); env[14] = _dup_env("SERVER_PORT" , client->get_rq_port()); env[15] = _dup_env("SERVER_PROTOCOL" , client->get_rq_version()); env[16] = _dup_env("SERVER_SOFTWARE" , "webser/1.0"); env[17] = _dup_env("REDIRECT_STATUS" , "200"); env[18] = NULL; return env; } std::string Webserv::_exec_script(Client *client, char **env) { #define RD 0 #define WR 1 #define CGI_BUF_SIZE 10 #define FD_WR_TO_CHLD fd_in[WR] #define FD_WR_TO_PRNT fd_out[WR] #define FD_RD_FR_CHLD fd_out[RD] #define FD_RD_FR_PRNT fd_in[RD] pid_t pid; char buf[CGI_BUF_SIZE]; // WIP define buffer char * const * nll = NULL; std::string script_output; std::string body = client->get_rq_body(); int fd_in[2]; int fd_out[2]; std::string path; pipe(fd_in); pipe(fd_out); pid = fork(); if (pid == -1) std::cerr << "fork crashed" << std::endl; else if (pid == 0) // child { close(FD_WR_TO_CHLD); close(FD_RD_FR_CHLD); dup2(FD_RD_FR_PRNT, STDIN_FILENO); dup2(FD_WR_TO_PRNT, STDOUT_FILENO); path = "." + client->get_rq_script_path(); /*DEBUG*/std::cerr << "execve:[" << path << "]\n"; execve(path.c_str(), nll, env); // for tests execve crash : //execve("wrong", nll, env); std::cerr << "execve crashed.\n"; // TODO HUGO : check errno } else //parent { close(FD_RD_FR_PRNT); close(FD_WR_TO_PRNT); write(FD_WR_TO_CHLD, body.c_str(), body.size()); close(FD_WR_TO_CHLD); waitpid(-1, NULL, 0); memset(buf, '\0', CGI_BUF_SIZE); while (read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE - 1) > 0) { script_output += buf; memset(buf, '\0', CGI_BUF_SIZE); } close(FD_RD_FR_CHLD); } if (script_output.empty()) script_output = "Status: 500\r\n\r\n"; return script_output; } void Webserv::_check_script_output(Client *client, std::string & output) { _check_script_status(client, output); _check_script_fields(client, output); _remove_body_leading_empty_lines(output); _add_script_body_length_header(output); // _check_script_empty_lines(client, output); // _check_script_space_colons(client, output); // _check_script_new_lines(client, output); } void Webserv::_check_script_status(Client *client, std::string & output) { size_t pos; int status_pos; pos = output.find("Status:"); if (pos != NPOS) { status_pos = pos + std::string("Status:").size(); client->status = std::strtoul(output.c_str() + status_pos, NULL, 10); ::extract_line(output, pos, CRLF); } else client->status = 200; } void Webserv::_check_script_fields(Client *client, std::string & output) { std::map srv_fld; // server_field std::map scr_fld; // script_field std::map::iterator it_srv; std::map::iterator it_scr; std::string tmp; size_t pos; // put server headers in map tmp = client->response; pos = tmp.find(CRLF CRLF); if (pos != NPOS) tmp.erase(pos); ::parse_http_headers(tmp, srv_fld); // put script headers in map tmp = output; pos = tmp.find(CRLF CRLF); if (pos != NPOS) tmp.erase(pos); ::parse_http_headers(tmp, scr_fld); // compare both map to supress duplicates for (it_srv = srv_fld.begin(); it_srv != srv_fld.end(); it_srv++) { for (it_scr = scr_fld.begin(); it_scr != scr_fld.end(); it_scr++) { if (str_tolower(it_srv->first) == str_tolower(it_scr->first)) { pos = client->response.find(it_srv->first); ::extract_line(client->response, pos, CRLF); } } } } void Webserv::_remove_body_leading_empty_lines(std::string & output) { size_t pos; size_t pos_empty; pos = output.find(CRLF CRLF); if (pos == NPOS) return; pos += CRLF_SIZE * 2; pos_empty = pos; while (pos_empty == pos) { pos = output.find(CRLF, pos); if (pos == pos_empty) extract_line(output, pos, CRLF); } } void Webserv::_add_script_body_length_header(std::string & output) { std::map field; std::map::iterator it; std::stringstream str_len; std::string tmp; size_t pos; size_t len; pos = output.find(CRLF CRLF); if (pos != NPOS) tmp = output.substr(pos + (CRLF_SIZE * 2)); len = tmp.size(); str_len << len; // put script headers in map tmp = output; pos = tmp.find(CRLF CRLF); if (pos != NPOS) tmp.erase(pos); ::parse_http_headers(tmp, field); // case insensitive search in map for "Content-Length" tmp = "Content-Length"; for (it = field.begin(); it != field.end(); ++it) { if (str_tolower(it->first) == str_tolower(tmp)) { pos = output.find(it->first); ::extract_line(output, pos, CRLF); } } tmp += ": "; tmp += str_len.str(); tmp += CRLF; output.insert(0, tmp); }