diff --git a/.gitignore b/.gitignore index ed337c9..73c26f4 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ cmake-build-debug # cmake stuff CMakeLists.txt +request +webserv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5195c87 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +SRC = main.cpp src/response.cpp +CMP = clang++ -std=c++98 +FLAGS = -Wall -Wextra -Werror -g3 +NAME = a.out +all : $(NAME) + +$(NAME): + $(CMP) $(FLAGS) $(SRC) -o a.out + +clean: + rm a.out +re: clean all \ No newline at end of file diff --git a/README.md b/README.md index 87eeac7..d369726 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,86 @@ # webserv #### Serer Block Configurations + - A virtual server is defined by a server directive. - A server block is a subset of servers’s configuration that defines a virtual server used to handle requests of a defined type. - - A location block lives within a server block and is used to define how Nginx should handle requests for different resources and URIs for the parent server. + - + ``` + server { + # Server configuration + } + ``` + - It is possible to add multiple server directives into the http context to define multiple virtual servers. + ``` + server { + # Server configuration + } + + server { + # Other Server configuration + } + ``` + + - The server configuration block usually includes a listen directive to specify the IP address and port on which the server listens for requests. + ``` + server { + # Server configuration + listen 127.0.0.1:8080 + + # Or + host 127.0.0.1 + port 8080 + } + ``` + - If a port is omitted, the standard port is used. Likewise, if an address is omitted, the server listens on all addresses. + - If the listen directive is not included at all, the “standard” port is 80/tcp and the “default” port is 8000/tcp, depending on superuser privileges. + - If there are several servers that match the IP address and port of the request, the server tests the request’s Host header field against the server_name directives in the server blocks. + - If the Host header field does not match a server name, should routes the request to the default server for the port on which the request arrived. The default server is the first one listed in the config file, unless you include the default_server parameter to the listen directive to explicitly designate a server as the default. + + ### Serving Static Content + + An important web server task is serving out files (such as images or static HTML pages). You will implement an example where, depending on the request, files will be served from different local directories: /data/www (which may contain HTML files) and /data/images (containing images). This will require editing of the configuration file and setting up of a server block inside the http block with two location blocks. + + First, create the /data/www directory and put an index.html file with any text content into it and create the /data/images directory and place some images in it. + + Next, open the configuration file. The default configuration file already includes several examples of the server block, mostly commented out. For now comment out all such blocks and start a new server block: +``` +http { + server { + } +} +``` +Generally, the configuration file may include several server blocks distinguished by ports on which they listen to and by server names. Once nginx decides which server processes a request, it tests the URI specified in the request’s header against the parameters of the location directives defined inside the server block. + +Add the following location block to the server block: +``` +location / { + root /data/www; +} +``` +This location block specifies the “/” prefix compared with the URI from the request. For matching requests, the URI will be added to the path specified in the root directive, that is, to /data/www, to form the path to the requested file on the local file system. If there are several matching location blocks nginx selects the one with the longest prefix. The location block above provides the shortest prefix, of length one, and so only if all other location blocks fail to provide a match, this block will be used. + +Next, add the second location block: +``` +location /images/ { + root /data; +} +``` +It will be a match for requests starting with /images/ (location / also matches such requests, but has shorter prefix). + +The resulting configuration of the server block should look like this: +``` +server { + location / { + root /data/www; + } + + location /images/ { + root /data; + } +} +``` + +This is already a working configuration of a server that listens on the standard port 80 and is accessible on the local machine at http://localhost/. In response to requests with URIs starting with /images/, the server will send files from the /data/images directory. For example, in response to the http://localhost/images/example.png request nginx will send the /data/images/example.png file. If such file does not exist, nginx will send a response indicating the 404 error. Requests with URIs not starting with /images/ will be mapped onto the /data/www directory. For example, in response to the http://localhost/some/example.html request nginx will send the /data/www/some/example.html file. + #### How Nginx Decides Which Server Block Will Handle a Request - diff --git a/cmake-build-debug/Testing/Temporary/LastTest.log b/cmake-build-debug/Testing/Temporary/LastTest.log index 17a281c..fcd970b 100644 --- a/cmake-build-debug/Testing/Temporary/LastTest.log +++ b/cmake-build-debug/Testing/Temporary/LastTest.log @@ -1,3 +1,3 @@ -Start testing: Nov 15 14:55 +01 +Start testing: Nov 15 16:48 +01 ---------------------------------------------------------- -End testing: Nov 15 14:55 +01 +End testing: Nov 15 16:48 +01 diff --git a/error_pages/403.html b/error_pages/403.html new file mode 100644 index 0000000..c860cb4 --- /dev/null +++ b/error_pages/403.html @@ -0,0 +1,15 @@ + + + + 403 Forbidden + + + +
+

403 Forbidden

+
+
+
nginx/1.21.4
+ + + \ No newline at end of file diff --git a/error_pages/404.html b/error_pages/404.html new file mode 100644 index 0000000..0d9dec6 --- /dev/null +++ b/error_pages/404.html @@ -0,0 +1,15 @@ + + + + 404 Not Found + + + +
+

404 Not Found

+
+
+
nginx/1.21.4
+ + + \ No newline at end of file diff --git a/error_pages/405.html b/error_pages/405.html new file mode 100644 index 0000000..7d04c1d --- /dev/null +++ b/error_pages/405.html @@ -0,0 +1,15 @@ + + + + 405 Not Allowed + + + +
+

405 Not Allowed

+
+
+
nginx/1.21.4
+ + + \ No newline at end of file diff --git a/error_pages/502.html b/error_pages/502.html new file mode 100644 index 0000000..3b829f9 --- /dev/null +++ b/error_pages/502.html @@ -0,0 +1,15 @@ + + + + 502 Bad Gateway + + + +
+

502 Bad Gateway

+
+
+
nginx/1.21.4
+ + + \ No newline at end of file diff --git a/file.html b/file.html new file mode 100755 index 0000000..e69de29 diff --git a/file.json b/file.json new file mode 100644 index 0000000..978d70e --- /dev/null +++ b/file.json @@ -0,0 +1 @@ +{"name":"John", "age":30, "car":null} \ No newline at end of file diff --git a/fork.cpp b/fork.cpp new file mode 100644 index 0000000..a762f3c --- /dev/null +++ b/fork.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + + +std::vector cgi_meta_var(void) +{ + std::vector meta_var; + std::string str; + /* + * SRC = Request (we will get this info from the request headers) + * The server MUST set this meta-variable if and only if the request is accompanied by a message body entity. + * The CONTENT_LENGTH value must reflect the length of the message-body + */ + str = std::string("CONTENT_LENGHT") + '\n'; + meta_var.push_back(str.c_str()); + // _cgi_meta_var += "CONTENT_LENGHT=" + '\n'; + /* + * SRC = Request (we will get this info from the request headers) + * The server MUST set this meta-variable if an HTTP Content-Type field is present in the client request header. + */ + str = std::string("CONTENT_TYPE=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Static (hard code it) + * It MUST be set to the dialect of CGI being used by the server to communicate with the script. Example: CGI/1.1 + */ + str = "GATEWAY_INTERFACE=CGI/1.1\n"; + meta_var.push_back(str.c_str()); + /* + * SRC = Request (we will get this info from the request headers) + * Extra "path" information. It's possible to pass extra info to your script in the URL, + * after the filename of the CGI script. For example, calling the + * URL http://www.myhost.com/mypath/myscript.cgi/path/info/here will set PATH_INFO to "/path/info/here". + * Commonly used for path-like data, but you can use it for anything. + */ + str = std::string("PATH_INFO=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request/Conf (we will get this info from the request headers but we should parse it as a local uri) + * the PATH_TRANSLATED variable is derived by taking the PATH_INFO value, parsing it as a local URI in its own right, + * and performing any virtual-to-physical translation appropriate to map it onto the server's document repository structure + */ + str = std::string("PATH_TRANSLATED=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * When information is sent using a method of GET, this variable contains the information in a query that follows the "?". + * The string is coded in the standard URL format of changing spaces to "+" and encoding special characters with "%xx" hexadecimal encoding. + * The CGI program must decode this information. + */ + str = std::string("QUERY_STRING=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * Contains the method (as specified with the METHOD attribute in an HTML form) that is + * used to send the request. Example: GET + */ + str = std::string("REQUEST_METHOD=GET") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * The path part of the URL that points to the script being executed. + * It should include the leading slash. Example: /cgi-bin/hello.pgm + */ + str = std::string("SCRIPT_NAME=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Conf + * Contains the server host name or IP address of the server. Example: 10.9.8.7 + */ + str = std::string("SERVER_NAME=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * Contains the port number to which the client request was sent. + */ + str = std::string("SERVER_PORT=") + '\n'; + meta_var.push_back(str.c_str()); + str = "SERVER_PROTOCOL=HTTP/1.1\n"; + meta_var.push_back(str.c_str()); + str = "SERVER_SOFTWARE=Webserv\n"; + meta_var.push_back(str.c_str()); + meta_var.push_back(NULL); + return meta_var; +} + +int main(void) +{ + int fd = open("index.php", O_RDONLY); + pid_t pid; + int pfd[2]; + + pipe(pfd); + if(!(pid = fork())) + { + std::vector meta_var = cgi_meta_var(); + std::vector args; + std::string path; + + args.push_back("/Users/mamoussa/Desktop/brew/bin/php-cgi"); + args.push_back(NULL); + path = "/Users/mamoussa/Desktop/brew/bin/php-cgi"; + close(pfd[0]); + dup2(fd, 0); + dup2(pfd[1], 1); + if (execve(path.c_str(), const_cast(&(*args.begin())) + , const_cast(&(*meta_var.begin()))) < 0) + { + meta_var.~vector(); + args.~vector(); + std::cout << strerror(errno) << std::endl; + exit(1); + } + } + wait(&pid); + close(pfd[1]); + std::string ans; + char buff[1024]; + int ret = 1; + + while ((ret = read(pfd[0], buff, 1024))) + ans += buff; + std::cout << ans << std::endl; + return 0; +} \ No newline at end of file diff --git a/hello.html b/hello.html new file mode 100755 index 0000000..bfd56b7 --- /dev/null +++ b/hello.html @@ -0,0 +1,6 @@ + + + + hello + + \ No newline at end of file diff --git a/includes/location.hpp b/includes/location.hpp new file mode 100644 index 0000000..5fc473c --- /dev/null +++ b/includes/location.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +class Location +{ + public: + Location(){} + Location(std::string const& path, std::vector const& index, std::vector const& allowed): + _path(path), _index(index), _allowed_methods(allowed){} + std::string const& getPath(void) const { return _path; } + std::vector getIndex(void) const { return _index; } + std::vector getAllowedMethods(void) const { return _allowed_methods; } + std::string & getAutoIndex(void) { return _autoIndex;} + private: + std::string _path; + std::vector _index; + std::vector _allowed_methods; + std::string _autoIndex; + std::string _cgi_path; +}; \ No newline at end of file diff --git a/includes/request.hpp b/includes/request.hpp index 0aab20d..3161d0b 100644 --- a/includes/request.hpp +++ b/includes/request.hpp @@ -10,13 +10,13 @@ #include #include +typedef enum transfer_type { + CHUNKED, + COMPLETED, +} transfer_type; class Request { private: - typedef enum transfer_type - { - CHUNKED, COMPLETED - } transfer_type; std::map > _RequestMap; std::stringstream _req; diff --git a/includes/response.hpp b/includes/response.hpp new file mode 100644 index 0000000..f14a781 --- /dev/null +++ b/includes/response.hpp @@ -0,0 +1,51 @@ +#pragma once +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include "location.hpp" + +class Response +{ + public: + Response(void); + Response(std::string const&, Location const&, std::string const&, std::string const&); + Response(Response const&); + Response& operator=(Response const&); + ~Response(void); + public: + void Get_request(void); + void Post_request(void); + void Delete_request(void); + std::string const& get_response(void) const; + private: + void _set_headers(size_t, std::string const&, size_t, std::string const&); + void _fill_response(std::string const&, size_t, std::string const&); + bool _file_is_good(bool); + bool _is_dir(std::string const&) const; + void _process_as_dir(void); + void _process_as_file(void); + void _process_post_delete(std::string const&); + void _cgi(void); + void _fill_cgi_response(std::string *, bool); + void _auto_index_list(void); + void _fill_auto_index_response(std::string *); + private: + std::string _response; + std::ifstream _file; + std::string _file_path; + Location _loc; + std::string _root; + std::string _uri; + std::string _error_pages; + std::map _type; +}; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100755 index 0000000..75c29b0 --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ + \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..f391a4d --- /dev/null +++ b/index.php @@ -0,0 +1,4 @@ +Hello World

'; +header("Pragma: no-cache"); +// header("Location: https://datatracker.ietf.org/doc/html/rfc3875#section-4.2"); +?> \ No newline at end of file diff --git a/main.cpp b/main.cpp index 095dea6..73c611a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,44 +1,43 @@ -#include -#include -#include -#include -#include -#include +#include "includes/response.hpp" +#include "includes/location.hpp" +#include +#include +#include #include -#include "parser/parser.hpp" -#include "includes/request.hpp" -#include "includes/utility.hpp" -#include "includes/server.hpp" - - -int main(int ac, char **av) { - if (ac != 2) - exit(1); - Request request; - std::ifstream ifs; - - // std::cout << performParsing().size() << std::endl; - Server serv(performParsing(av[1])); - // ifs.open("./request", std::ios_base::in); - // if (ifs.is_open()) { - // std::stringstream ss; - // std::string line; - // while (std::getline(ifs, line)) - // ss << line << std::endl; - - // request.parseRequest(ss); - // auto it = request.getMap().begin(); - - // for(; it != request.getMap().end(); ++it) { - // std::cout << it->first << " "; - // for (int i = 0; i < it->second.size(); ++i) - // std::cout << it->second[i] << " "; - // std::cout << std::endl; - // } +#include +int main() +{ + std::vector index; + std::vector allowed; + index.push_back("hello.html"); + index.push_back("file.html"); + index.push_back("index.html"); + allowed.push_back("POST"); + Location loc(".php", index, allowed); + loc.getAutoIndex() = "off"; + Response res("/Users/mamoussa/Desktop/42/webserv", loc, "user_login.php", "/Users/mamoussa/Desktop/42/webserv/error_pages"); + // res.Get_request(); + res.Post_request(); +// res.Delete_request(); + std::cout << res.get_response(); + // system("leaks a.out"); + // struct dirent *cur_dir; + + // DIR* dir = opendir("/Users/mamoussa/Desktop/42/webserv/src"); + // if (!dir) + // { + // std::cout << "permission denied" << std::endl; + // return 0; // } - serv.listen(); - - return EXIT_SUCCESS; + // while ((cur_dir = readdir(dir))) + // { + // std::cout << "d_ino \t" << cur_dir->d_ino << std::endl; + // std::cout << "d_name \t" << cur_dir->d_name << std::endl; + // std::cout << "d_namlen \t" << cur_dir->d_namlen << std::endl; + // std::cout << "d_reclen \t" << cur_dir->d_reclen << std::endl; + // std::cout << "d_seekoff \t" << cur_dir->d_seekoff << std::endl; + // std::cout << "d_type \t" << cur_dir->d_type << std::endl; + // std::cout << "---------------------------------------" << std::endl; + // } + return 0; } - - diff --git a/response.html b/response.html new file mode 100644 index 0000000..75c29b0 --- /dev/null +++ b/response.html @@ -0,0 +1,9 @@ + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ + \ No newline at end of file diff --git a/src/response.cpp b/src/response.cpp new file mode 100644 index 0000000..ad52cd7 --- /dev/null +++ b/src/response.cpp @@ -0,0 +1,482 @@ +#include "../includes/response.hpp" +#include "../includes/location.hpp" + +/* this is the implementation of the default, param, and copy constructors plus the operator=*/ +Response::Response(void): _response(""), _loc() +, _root(""), _uri(""), _error_pages(""){} + +Response::Response(std::string const& root, Location const& loc +, std::string const& uri, std::string const& error_pages): _loc(loc), _root(root) +, _uri(uri), _error_pages(error_pages) +{ + _type.insert(std::make_pair("json", "application")); + _type.insert(std::make_pair("html", "text")); + _type.insert(std::make_pair("php", "application/octet-stream")); +} + +Response::Response(Response const& x) { *this = x; } +Response::~Response(void) { _file.close(); } +Response& Response::operator=(Response const& x) +{ + _response = x._response; + return *this; +} +/*-------------------------------------------------------------------------------*/ + +/* those are the public methods to process the request @GET @POST @DELETE */ +void Response::Delete_request(void) { _process_post_delete("DELETE"); } + +void Response::Post_request(void) { _process_post_delete("POST"); } + +void Response::Get_request(void) +{ + std::vector allowed = _loc.getAllowedMethods(); + std::string const loc_path = _loc.getPath(); + + // lets first check for alowed methods in this location + if (find(allowed.begin(), allowed.end(), "GET") == allowed.end()) + { + _fill_response(".html", 403, "Forbiden"); + return; + } + // now lets check if we have to pass the file to the cgi (when we have a .php location), or process it as a static file otherwise + if (_uri.substr(_uri.find_last_of(".") + 1) == "php" && loc_path.substr(loc_path.find_last_of(".") + 1) == "php") + { + _cgi(); + return; + } + // first we have to check if the location is a dir or just a file + if (loc_path == "/") + _process_as_dir(); + else if (_is_dir(_root + '/' + _loc.getPath())) + _process_as_dir(); + else + _process_as_file(); +} +/*----------------------------------------------------------------------------*/ +/* this part is for the cgi (fill the meta_var and excute it) */ +std::vector cgi_meta_var(void) +{ + std::vector meta_var; + std::string str; + /* + * SRC = Request (we will get this info from the request headers) + * The server MUST set this meta-variable if and only if the request is accompanied by a message body entity. + * The CONTENT_LENGTH value must reflect the length of the message-body + */ + str = std::string("CONTENT_LENGHT") + '\n'; + meta_var.push_back(str.c_str()); + // _cgi_meta_var += "CONTENT_LENGHT=" + '\n'; + /* + * SRC = Request (we will get this info from the request headers) + * The server MUST set this meta-variable if an HTTP Content-Type field is present in the client request header. + */ + str = std::string("CONTENT_TYPE=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Static (hard code it) + * It MUST be set to the dialect of CGI being used by the server to communicate with the script. Example: CGI/1.1 + */ + str = "GATEWAY_INTERFACE=CGI/1.1\n"; + meta_var.push_back(str.c_str()); + /* + * SRC = Request (we will get this info from the request headers) + * Extra "path" information. It's possible to pass extra info to your script in the URL, + * after the filename of the CGI script. For example, calling the + * URL http://www.myhost.com/mypath/myscript.cgi/path/info/here will set PATH_INFO to "/path/info/here". + * Commonly used for path-like data, but you can use it for anything. + */ + str = std::string("PATH_INFO=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request/Conf (we will get this info from the request headers but we should parse it as a local uri) + * the PATH_TRANSLATED variable is derived by taking the PATH_INFO value, parsing it as a local URI in its own right, + * and performing any virtual-to-physical translation appropriate to map it onto the server's document repository structure + */ + str = std::string("PATH_TRANSLATED=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * When information is sent using a method of GET, this variable contains the information in a query that follows the "?". + * The string is coded in the standard URL format of changing spaces to "+" and encoding special characters with "%xx" hexadecimal encoding. + * The CGI program must decode this information. + */ + str = std::string("QUERY_STRING=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * Contains the method (as specified with the METHOD attribute in an HTML form) that is + * used to send the request. Example: GET + */ + str = std::string("REQUEST_METHOD=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Request + * The path part of the URL that points to the script being executed. + * It should include the leading slash. Example: /cgi-bin/hello.pgm + */ + str = std::string("SCRIPT_NAME=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * SRC = Conf + * Contains the server host name or IP address of the server. Example: 10.9.8.7 + */ + str = std::string("SERVER_NAME=") + '\n'; + meta_var.push_back(str.c_str()); + /* + * Contains the port number to which the client request was sent. + */ + str = std::string("SERVER_PORT=") + '\n'; + meta_var.push_back(str.c_str()); + str = "SERVER_PROTOCOL=HTTP/1.1\n"; + meta_var.push_back(str.c_str()); + str = "SERVER_SOFTWARE=Webserv\n"; + meta_var.push_back(str.c_str()); + meta_var.push_back(NULL); + return meta_var; +} + +std::string *get_res(int fd) +{ + std::string *ans = new std::string(); + char buff[1024]; + int ret; + + while ((ret = read(fd, buff, 1024))) + *ans += buff; + return ans; +} + +void Response::_fill_cgi_response(std::string *tmp_res, bool is_red) +{ + time_t rawtime; + + time (&rawtime); + if(is_red) + _response += "HTTP/1.1 302 Found\r\n"; + else + _response += "HTTP/1.1 200 OK\r\n"; + _response += "Date: " + std::string(ctime(&rawtime)); + _response += "Server: webserver\r\n"; + _response += "Transfer-Encoding: chunked\r\n"; + _response += "Connection: close\r\n"; + _response += *tmp_res; +} + +void Response::_cgi(void) +{ + int fd = open("user_login.php", O_RDONLY); + int pfd[2]; + std::string *tmp_res; + size_t index; + int status; + + pipe(pfd); + if(!(fork())) + { + std::vector meta_var = cgi_meta_var(); + std::vector args; + std::string path; + + args.push_back("/Users/mamoussa/Desktop/brew/bin/php-cgi"); + args.push_back(NULL); + path = "/Users/mamoussa/Desktop/brew/bin/php-cgi"; + close(pfd[0]); + dup2(fd, 0); + dup2(pfd[1], 1); + if (execve(path.c_str(), const_cast(&(*args.begin())) + , const_cast(&(*meta_var.begin()))) < 0) + { + close(pfd[1]); + args.~vector(); + // delete meta_var; + exit(1); + } + exit(0); + } + wait(&status); + close(pfd[1]); + // first lets check if the cgi path is correct + if (WEXITSTATUS(status) == 1) + { + close(pfd[0]); + close(fd); + _fill_response(".html", 502, "Bad Gateway"); + return; + } + tmp_res = get_res(pfd[0]); + // if the tmp_res contains the Status header field then we should erase it, because it will be added + if ((index = tmp_res->find("Status")) != std::string::npos) + tmp_res->erase(tmp_res->begin() + index, tmp_res->begin() + tmp_res->find_first_of('\n', index) + 1); + if (tmp_res->find("Location") != std::string::npos) + _fill_cgi_response(tmp_res, true); + else + _fill_cgi_response(tmp_res, false); + close(pfd[0]); + close(fd); + delete tmp_res; +} +/*---------------------------------------------------------------------------------------------------*/ +/* this part is for the files and subdirectoreis listing e.i when we have the auto indexing */ +void Response::_fill_auto_index_response(std::string* tmp_res) +{ + time_t rawtime; + + time (&rawtime); + _response += "HTTP/1.1 200 OK\r\n"; + _response += "Date: " + std::string(ctime(&rawtime)); + _response.erase(--_response.end()); + _response += "\r\n"; + _response += "Server: webserver\r\n"; + _response += "Transfer-Encoding: chunked\r\n"; + _response += "Connection: close\r\n"; + _response += "\r\n"; + _response += *tmp_res; +} + +void Response::_auto_index_list(void) +{ + std::string *tmp_res = new std::string(); + DIR* dir; + struct dirent *dir_info; + + dir = opendir(_root.c_str()); + if (!dir) + { + _fill_response(".html", 403, "Forbidden"); + return; + } + *tmp_res += std::string("\r\n\r\nIndex of " + _uri + "\r\n"); + *tmp_res += "\r\n\r\n"; + *tmp_res += std::string("

Index of " + _uri + "

\r\n"); + *tmp_res += "
\r\n
\r\n";
+	while ((dir_info = readdir(dir)))
+	{
+		if (std::string(dir_info->d_name) == ".")
+			continue;
+		if (std::string(dir_info->d_name) == "..")
+		{
+			*tmp_res += "..\r\n");
+			continue;
+		}
+		*tmp_res += std::string("d_name) + "\">" + std::string(dir_info->d_name) + "\r\n";
+	}
+	*tmp_res += "
\r\n
\r\n\r\n\r\n"; + _fill_auto_index_response(tmp_res); + delete tmp_res; + closedir(dir); +} +/*-----------------------------------------------------------------------------------------------------*/ +std::string *error_page(std::string const& message) +{ + std::string *error_body = new std::string(); + + *error_body += std::string("\r\n\r\n"); + *error_body += std::string("") + message; + *error_body += std::string("\r\n\r\n\r\n
\r\n

") + message; + *error_body += std::string("

\r\n
\r\n
\r\n
webserver
\r\n\r\n\r\n"); + return error_body; +} +void Response::_process_post_delete(std::string const& req_method) +{ + std::vector const allowed = _loc.getAllowedMethods(); + std::vector const index = _loc.getIndex(); + std::string const loc_path = _loc.getPath(); + bool found(false); + + // lets first check for alowed methods in this location + if (find(allowed.begin(), allowed.end(), req_method) == allowed.end()) + { + _fill_response(".html", 403, "Forbiden"); + return; + } + // now lets check if we have to pass the file to the cgi (when we have a .php location), or process it as a static file otherwise + if (_uri.substr(_uri.find_last_of(".") + 1) == "php" && loc_path.substr(loc_path.find_last_of(".") + 1) == "php") + { + _cgi(); + return; + } + // otherwise if the request method is delete then we should return a Not Allowed message + if (req_method == "DELETE") + { + _fill_response(".html", 405, "Not Allowed"); + return; + } + // now if we have an other file extension than php, then we should return an error + if (!_is_dir(_root + '/' + _uri)) + { + if (!_file_is_good(true)) // if the file doesn't exist then we should return a not found message + return; + _fill_response(".html", 405, "Not Allowed"); + return; + } + else // if the _uri is a dir, then it behavios as get request, if the file not found we should return a 403 error + { + _root += '/' + _uri; + for (size_t i = 0; i < index.size(); ++i) + { + _file_path = _root + '/' + index[i]; + if (!(found = _file_is_good(false)) && errno != 2) // if the file exists but we don't have the permissions to read from it + { + _fill_response(".html", 403, "Forbiden"); + return; + } + else if (found) + { + if (loc_path == "/") + { + _fill_response(".html", 405, "Not Allowed"); + return; + } + _fill_response(_file_path, 200, "OK"); + return; + } + } + if (!found && _loc.getAutoIndex() != "on") + { + _fill_response(".html", 403, "Forbiden"); + return; + } + } + // if we are here then we have a dir in the _uri, and the auto index is set to on, so we should list all the files in the dir + _auto_index_list(); +} + +void Response::_process_as_dir(void) +{ + std::vector const index = _loc.getIndex(); + bool found(false); + + _root += '/' + _uri; + if (_uri.empty() || _is_dir(_root)) + { + for (size_t i = 0; i < index.size(); ++i) + { + _file_path = _root + '/' + index[i]; + if (!(found = _file_is_good(false)) && errno != 2) // if the file exists but we don't have the permissions to read from it + { + _fill_response(".html", 403, "Forbiden"); + return; + } + else if (found) + { + if (!_file_is_good(true)) + return; + _fill_response(_file_path, 200, "OK"); + return; + } + } + if (!found && _loc.getAutoIndex() != "on") + { + _fill_response(".html", 404, "Not found"); + return; + } + } + else + { + _file_path = _root; + if (!_file_is_good(true)) + return; + _fill_response(_file_path, 200, "OK"); + return; + } + // if we are here than we didn't found the file we are seaching on, and we have a intoindex set to on, so we should fill the template for + // autoindex on to list all the files in the dir + _auto_index_list(); +} + +void Response::_process_as_file(void) +{ + _file_path = _root + '/' + _uri; + if (!_file_is_good(true)) + return; + _fill_response(_file_path, 200, "OK"); + return; +} + +bool Response::_is_dir(std::string const& path) const +{ + struct stat s; + + if (!lstat(path.c_str(), &s)) + { + if (S_ISDIR(s.st_mode)) + return true; + else + return false; + } + return false; +} + +void Response::_set_headers(size_t status_code, std::string const& message, size_t content_length, std::string const& path) +{ + time_t rawtime; + std::string const extention = path.substr(path.find_last_of(".") + 1); + std::stringstream ss, ss_content; + + ss << status_code; + time (&rawtime); + _response += "HTTP/1.1 " + ss.str() + " " + message + "\r\n"; + _response += "Date: " + std::string(ctime(&rawtime)); + _response.erase(--_response.end()); + _response += "\r\n"; + _response += "Server: webserver\r\n"; + ss_content << content_length; + _response += "Content-Length: " + ss_content.str() + "\r\n"; + if (extention == "php") + _response += "Content-Type: " + _type[extention] + "\r\n"; + else + _response += "Content-Type: " + _type[extention] + '/' + extention + "\r\n"; + _response += "Connection: close\r\n"; + _response += "\r\n"; +} + +void Response::_fill_response(std::string const& path, size_t status_code, std::string const& message) +{ + std::string line; + std::string *tmp_resp = new std::string(); + + if (status_code == 200) + { + _file.open(path); + while (!_file.eof()) + { + std::getline(_file, line); + if (!_file.eof()) + line += "\r\n"; + *tmp_resp += line; + } + *tmp_resp += "\r\n"; + } + else + { + std::stringstream ss; + std::string buff; + ss << status_code; + ss >> buff; + delete tmp_resp; + tmp_resp = error_page(buff + ' ' + message); + } + // set all the needed response header + _set_headers(status_code, message, tmp_resp->length(), path); + _response += *tmp_resp; + delete tmp_resp; +} + +bool Response::_file_is_good(bool fill_resp) +{ + if (_file_path.empty()) + _file_path = _root + '/' + _uri; + if (open(_file_path.c_str(), O_RDONLY) < 0) + { + if (errno == 2 && fill_resp) + _fill_response(".html", 404, "Not Found"); + else if (fill_resp) + _fill_response(".html", 403, "Forbidden"); + return false; + } + return true; +} +std::string const& Response::get_response(void) const { return _response; } \ No newline at end of file diff --git a/user_login.php b/user_login.php new file mode 100644 index 0000000..924a755 --- /dev/null +++ b/user_login.php @@ -0,0 +1,16 @@ + \ No newline at end of file