diff --git a/.github/workflows/compile-check.yml b/.github/workflows/compile-check.yml index 5bbe2a4..78a8038 100644 --- a/.github/workflows/compile-check.yml +++ b/.github/workflows/compile-check.yml @@ -5,6 +5,8 @@ on: branches: - main - server/* + - conf-parser + - CGI-Parser - handle_connection - parsing diff --git a/Makefile b/Makefile index 8428f77..5070daf 100644 --- a/Makefile +++ b/Makefile @@ -28,12 +28,13 @@ TESTS_DIR:= .tests PARSER_DIR:= $(SRCS_DIR)/parser HTTP_DIR:= $(SRCS_DIR)/http SERVER_DIR:= $(SRCS_DIR)/server +CGI_DIR:= $(SRCS_DIR)/CGI ### EXECUTABLE ### NAME:= webserv ### MODULES & INCLUDES ### -MODULES:= $(PARSER_DIR) $(HTTP_DIR) $(SERVER_DIR) +MODULES:= $(PARSER_DIR) $(HTTP_DIR) $(SERVER_DIR) $(CGI_DIR) INCLUDES:= -I./includes/ $(patsubst %,-I./%,$(MODULES)) ### SOURCES ### @@ -41,7 +42,7 @@ SRCS:= $(SRCS_DIR)/main.cpp ### OBJECTS & SUBDIRS ### include $(patsubst %,%/module.mk,$(MODULES)) -OBJS += $(patsubst $(SRCS_DIR)%.cpp,$(OBJS_DIR)/%.o,$(SRCS)) +OBJS += $(patsubst $(SRCS_DIR)%.cpp,$(OBJS_DIR)%.o,$(SRCS)) SUB_DIRS:= $(patsubst $(SRCS_DIR)%,$(OBJS_DIR)%,$(shell find $(SRCS_DIR) -type d)) all: $(NAME) @@ -70,8 +71,8 @@ clean: $(RM) $(OBJS_DIR); \ echo "$(RED)[ DELETE ]$(RESET) Removed object files."; \ fi - @if [ -f $(TEST_PARSER) ] || [ -f $(TEST_HTTP) ] || [ -f $(TEST_SOCKET) ]; then \ - $(RM) $(TEST_PARSER) $(TEST_HTTP) $(TEST_SOCKET); \ + @if [ -f $(TEST_PARSER) ] || [ -f $(TEST_HTTP) ] || [ -f $(TEST_SOCKET) ] || [ -f $(TEST_CGI) ]; then \ + $(RM) $(TEST_PARSER) $(TEST_HTTP) $(TEST_SOCKET) $(TEST_CGI); \ echo "$(GREEN)[ DELETE ]$(RESET) Removed testers."; \ fi @@ -98,6 +99,10 @@ test_http: # @$(CXX) $(CXXFLAGS) $(INCLUDES) $(DEBUGFLAGS) $(SOCKET_SRCS) $(TEST_SOCKET_SRC) -o $(TEST_SOCKET) # @echo "$(BLUE)[ TEST ]$(RESET) SOCKET ready for testing." +test_cgi: + @$(CXX) $(CXXFLAGS) $(INCLUDES) $(DEBUGFLAGS) $(SANITIZE) $(HTTP_SRCS) $(CGI_SRCS) $(TEST_CGI_SRC) -o $(TEST_CGI) + @echo "$(BLUE)[ TEST ]$(RESET) CGI ready for testing." + -include $(OBJS:.o=.d) -.PHONY: clean fclean all re debug run test_parser test_http test_socket +.PHONY: clean fclean all re debug run test_parser test_http test_socket test_cgi diff --git a/sources/CGI/.tests/file.py b/sources/CGI/.tests/file.py new file mode 100755 index 0000000..b263f32 --- /dev/null +++ b/sources/CGI/.tests/file.py @@ -0,0 +1,2 @@ +#!/usr/bin/python3 +print("hello worled") \ No newline at end of file diff --git a/sources/CGI/.tests/file.sh b/sources/CGI/.tests/file.sh new file mode 100755 index 0000000..349d4ae --- /dev/null +++ b/sources/CGI/.tests/file.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Hello, World Bash!" \ No newline at end of file diff --git a/sources/CGI/.tests/test_cgi.cpp b/sources/CGI/.tests/test_cgi.cpp new file mode 100644 index 0000000..387ecbe --- /dev/null +++ b/sources/CGI/.tests/test_cgi.cpp @@ -0,0 +1,96 @@ + +// #include +// # include +// # include +// # include +// # include +// # include +// # include +// # include +#include "../Cgi.hpp" + + +//using namespace webserv::http; + + +// int main(int ac, char **av, char **env) +// { +// (void)ac; +// //(void)av; +// int fd[2]; +// int id; + +// pipe(fd); +// id = fork(); +// if (id == 0) +// { +// //dup2(fd[0], STDIN_FILENO); +// close(fd[0]); +// close(fd[1]); +// if (execve("/bin/ls", ++av, env)) +// write(2, "error\n", 6); +// } +// close(fd[0]); +// close(fd[1]); + +// return (0); +// } + + + +int main(int argc, char** argv, char** envp) +{ + (void)argc; + (void)argv; + (void)envp; + vsp headers; + + // for (int i = 0; envp[i] != NULL; ++i) + // { + // std::cout << envp[i] << std::endl; + // } + // cout<< endl<first << ": " << it->second << endl; + // } + + Cgi cgi(request); + + cgi.execute("stds"); + } + catch (std::runtime_error &e) + { + cerr << e.what() << std::endl; + } + + // const char *pythonScriptPath = "/Users/hashim/Desktop/42curses/webserv/CGI/tester/file.sh"; + // const char *pythonInterpreterPath = "/Users/hashim/Desktop/42curses/webserv/CGI/tester/file.sh"; + + // // Check if the Python script exists + // if (access(pythonScriptPath, X_OK) == -1) + // { + // std::cerr << "Error: Python script not found or does not have execution permission." << std::endl; + // return 1; + // } + + // // Create an instance of the CGI class + // char* scriptArguments[] = { const_cast(pythonScriptPath), nullptr }; + // Cgi cgi(const_cast(pythonInterpreterPath), scriptArguments, envp); + + // // Execute the Python script + // cgi.execute("std"); + return 0; +} \ No newline at end of file diff --git a/sources/CGI/Cgi.cpp b/sources/CGI/Cgi.cpp new file mode 100644 index 0000000..06923ac --- /dev/null +++ b/sources/CGI/Cgi.cpp @@ -0,0 +1,280 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Cgi.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: hmohamed +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/02/23 12:44:51 by hmohamed #+# #+# */ +/* Updated: 2024/04/06 04:41:57 by hmohamed ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Cgi.hpp" + + +// Cgi::Cgi(char* filePath, char** arguments, char** environment): filePath(filePath), arguments(arguments), environment(environment) +// { + +// } + +char **headersToEnv(vsp &headers) +{ + std::vector envVector; + + // Iterate through headers + for (vsp::iterator it = headers.begin(); it != headers.end(); ++it) + { + size_t len = it->first.size() + it->second.size() + 2; + char *envEntry = new char[len]; + std::snprintf(envEntry, len, "%s=%s", it->first.c_str(), it->second.c_str()); + envVector.push_back(envEntry); + } + + // Allocate memory for char* array + char **envp = new char *[envVector.size() + 1]; + + // Copy pointers from vector to char* array + for (size_t i = 0; i < envVector.size(); ++i) + { + envp[i] = envVector[i]; + } + envp[envVector.size()] = NULL; + + return envp; +} + +// string *geturi(string res) +// { +// char *result; +// string *resn; +// size_t qu; + +// result = NULL; +// qu = res.find('?', 0); +// resn = new string(res.substr(0, qu)); +// //result = const_cast(res.substr(0,qu).c_str()); +// cout << *resn << endl; +// cout<< "test" << qu <(resn->c_str()); +// cout<< "result : " << result <(res.substr(0,qu).c_str()); + cout << resn << endl; + cout<< "test" << qu <(res.substr(0,qu).c_str()); + cout << resn << endl; + return (resn); +} + + +Cgi::Cgi(const Request &request) +{ + string res; + + res = const_cast(request.get_resource().c_str()); + headers = request.get_headers(); + environment = headersToEnv(headers); + //filePath = const_cast(geturi(request.get_resource())->c_str()); + filePath = (geturi(res)); + queryString = getStingQuery(res); + //filePath = const_cast (request.get_resource().c_str()); + // // Check if the Python script exists + // if (access(filePath, X_OK) == -1) + // { + // std::cerr << "Error: Python script not found or does not have execution permission." << std::endl; + // return ; + // } + cout<< "file path :" << filePath << endl; + arguments = new char *[2]; + arguments[0] = const_cast(filePath.c_str()); + arguments[1] = NULL; + + // Print out the environment variables + for (int i = 0; environment[i] != NULL; ++i) + { + std::cout << environment[i] << std::endl; + } +} + +Cgi::~Cgi() +{ + for (int i = 0; environment[i] != NULL; ++i) + { + delete[] environment[i]; + } + delete[] environment; + delete[] arguments; +} + +// void Cgi::execute() +// { +// int fd[2]; +// int id; +// char *res_body; +// int a; + +// res_body = (char *)malloc(100); +// res_body[99] = '\0'; +// pipe(fd); +// id = fork(); +// if (id == 0) +// { +// // Child process +// dup2(fd[1], STDOUT_FILENO); +// close(fd[0]); +// close(fd[1]); + +// if (execve(filePath, arguments, environment) == -1) +// { +// std::cerr << "Error executing execve: " << strerror(errno) << std::endl; +// _exit(EXIT_FAILURE); +// } +// } + +// // Wait for the child process to finish +// int status; +// waitpid(id, &status, 0); +// a = read(fd[0], res_body, 90); +// res_body[a] = '\0'; +// cout << res_body << endl; +// close(fd[0]); +// close(fd[1]); + + +// } + + +void Cgi::execute(const std::string& outputFile) +{ + int fd[2]; + int id; + std::string res_body; + + // Create a pipe for communication + if (pipe(fd) == -1) { + std::cerr << "Error creating pipe: " << strerror(errno) << std::endl; + return; + } + + // Fork the process + id = fork(); + if (id == -1) { + std::cerr << "Error forking process: " << strerror(errno) << std::endl; + return; + } else if (id == 0) { + // Child process + dup2(fd[1], STDOUT_FILENO); + close(fd[0]); + close(fd[1]); + + if (execve(const_cast(filePath.c_str()), arguments, environment) == -1) { + std::cerr << "Error executing execve: " << strerror(errno) << std::endl; + _exit(EXIT_FAILURE); + } + } + + // Parent process + close(fd[1]); // Close the write end of the pipe + + // Wait for the child process to finish + int status; + waitpid(id, &status, 0); + + // Read the response from the pipe + char buffer[91]; + ssize_t bytesRead; + while ((bytesRead = read(fd[0], buffer, 90)) > 0) { + res_body.append(buffer, bytesRead); + } + // Save the response to a file + std::ofstream outFile(outputFile.c_str()); + if (outFile.is_open()) { + outFile << res_body; + outFile.close(); + std::cout << "Response saved to: " << outputFile << std::endl; + } else { + std::cerr << "Error opening output file: " << strerror(errno) << std::endl; + } + + close(fd[0]); +} + +// std::string Cgi::execute(void) +// { +// int fd[2]; +// int id; +// std::string res_body; + +// // Create a pipe for communication +// if (pipe(fd) == -1) { +// std::cerr << "Error creating pipe: " << strerror(errno) << std::endl; +// return (NULL); +// } + +// // Fork the process +// id = fork(); +// if (id == -1) { +// std::cerr << "Error forking process: " << strerror(errno) << std::endl; +// return (NULL); +// } else if (id == 0) { +// // Child process +// dup2(fd[1], STDOUT_FILENO); +// close(fd[0]); +// close(fd[1]); + +// if (execve(filePath, arguments, environment) == -1) { +// std::cerr << "Error executing execve: " << strerror(errno) << std::endl; +// _exit(EXIT_FAILURE); +// } +// } + +// // Parent process +// close(fd[1]); // Close the write end of the pipe + +// // Wait for the child process to finish +// int status; +// waitpid(id, &status, 0); + +// // Read the response from the pipe +// char buffer[91]; +// ssize_t bytesRead; +// while ((bytesRead = read(fd[0], buffer, 90)) > 0) { +// res_body.append(buffer, bytesRead); +// } +// // Save the response to a file +// // std::ofstream outFile(outputFile); +// // if (outFile.is_open()) { +// // outFile << res_body; +// // outFile.close(); +// // std::cout << "Response saved to: " << outputFile << std::endl; +// // } else { +// // std::cerr << "Error opening output file: " << strerror(errno) << std::endl; +// // } + +// close(fd[0]); +// return(res_body); +// } \ No newline at end of file diff --git a/sources/CGI/Cgi.hpp b/sources/CGI/Cgi.hpp new file mode 100644 index 0000000..dc7d1df --- /dev/null +++ b/sources/CGI/Cgi.hpp @@ -0,0 +1,43 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Cgi.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: hmohamed +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/02/23 12:42:45 by hmohamed #+# #+# */ +/* Updated: 2024/04/06 04:41:25 by hmohamed ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CGI_HPP +#define CGI_HPP + +//#include "../../includes/webserv.hpp" +#include +#include +#include +#include +#include +#include +#include "webserv.hpp" +#include "../sources/http/request/Request.hpp" + +class Cgi { + public: + // Cgi(char* filePath, char** arguments, char** environment); + Cgi(const Request &request); + ~Cgi(); + void execute(const std::string &outputFile); + + private: + vsp headers; + string queryString; + string filePath; + char **arguments; + char **environment; + +}; + +#endif + diff --git a/sources/CGI/module.mk b/sources/CGI/module.mk new file mode 100644 index 0000000..40040e5 --- /dev/null +++ b/sources/CGI/module.mk @@ -0,0 +1,9 @@ +# CGI makefile + +CGI_SRCS:= $(CGI_DIR)/Cgi.cpp +CGI_OBJS:= $(CGI_SRCS:$(SRCS_DIR)/%.cpp=$(OBJS_DIR)/%.o) +OBJS += $(CGI_OBJS) + +# tester mains +TEST_CGI:= $(CGI_DIR)/test_cgi +TEST_CGI_SRC:= $(CGI_DIR)/$(TESTS_DIR)/test_cgi.cpp diff --git a/sources/CGI/stds b/sources/CGI/stds new file mode 100644 index 0000000..5e1b6f1 --- /dev/null +++ b/sources/CGI/stds @@ -0,0 +1 @@ +Hello, World Bash! diff --git a/sources/http/Message.hpp b/sources/http/Message.hpp index cb5bf78..7bec0c9 100644 --- a/sources/http/Message.hpp +++ b/sources/http/Message.hpp @@ -429,4 +429,27 @@ static const std::string sample_response = "

Hello, World!

This is a sample webpage.

\r\n" "\r\n"; +static const std::string sample_request_cgi = + "GET /Users/hashim/Desktop/42curses/webserv/sources/CGI/.tests/file.sh?name=hashim&lastname=mohamed HTTP/1.1\r\n" // request-line + "Host: Linode.com\r\n" // headers fields + "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.8) " + "Gecko/20091102 Firefox/3.5.5 \r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8\r\n" + "Cache-Control: no-cache\r\n" + "\r\n" // + "Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit\r\n" + "enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet.\r\n" + "Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem\r\n" + "est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud\r\n" + "officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat " + "reprehenderit\r\n" + "commodo officia dolor Lorem duis laboris cupidatat officia voluptate.\r\n" + "Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia " + "eiusmod.\r\n" + "Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim.\r\n" + "Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa " + "duis.\r\n" + "\r\n"; + #endif // MESSAGE_HPP diff --git a/sources/http/test_http.dSYM/Contents/Info.plist b/sources/http/test_http.dSYM/Contents/Info.plist new file mode 100644 index 0000000..1adf4b5 --- /dev/null +++ b/sources/http/test_http.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_http + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/sources/http/test_http.dSYM/Contents/Resources/DWARF/test_http b/sources/http/test_http.dSYM/Contents/Resources/DWARF/test_http new file mode 100644 index 0000000..2a6d5fa Binary files /dev/null and b/sources/http/test_http.dSYM/Contents/Resources/DWARF/test_http differ