diff options
-rw-r--r-- | cfws.c | 2 | ||||
-rw-r--r-- | file.c | 87 | ||||
-rw-r--r-- | file.h | 2 |
3 files changed, 78 insertions, 13 deletions
@@ -69,7 +69,7 @@ static void handle_request(const struct http_request *req, int sockfd) file_read(filepath, sockfd); break; case SERVE_METHOD_PHP: - file_read_php(filepath, req->query_str, sockfd); + file_read_php(filepath, req, sockfd); break; case SERVE_METHOD_ERROR: { const char *errmsg = "Content-Type: text/plain\r\n\r\nEpic fail"; @@ -92,28 +92,93 @@ int file_read(const char *filepath, int sockfd) return 0; } -int file_read_php(const char *filepath, const char *query_str, int sockfd) +static void cgi_setup_env(const char *filepath, const struct http_request *req) { - FILE *fp; + if (req->method == HTTP_METHOD_GET) { + setenv("REQUEST_METHOD", "GET", 1); + } else if (req->method == HTTP_METHOD_POST) { + setenv("REQUEST_METHOD", "POST", 1); + } + + setenv("SCRIPT_FILENAME", filepath, 1); + if (req->query_str) + setenv("QUERY_STRING", req->query_str, 1); + + if (req->body) { + static char intbuf[20]; + static char *content_type = "application/x-www-form-urlencoded"; + sprintf(intbuf, "%ld", strlen(req->body)); + setenv("CONTENT_LENGTH", intbuf, 1); + setenv("CONTENT_TYPE", content_type, strlen(content_type)); + } +} + +int file_read_php(const char *filepath, const struct http_request *req, int sockfd) +{ + int readfds[2]; + int writefds[2]; + pid_t pid; + char buffer[FILE_READBUF_SIZE]; size_t bytes_read; - setenv("REQUEST_METHOD", "GET", 1); - setenv("SCRIPT_FILENAME", filepath, 1); - if (query_str) - setenv("QUERY_STRING", query_str, 1); + cgi_setup_env(filepath, req); - fp = popen("php-cgi", "r"); - if (fp == NULL) { - perror("Failed to read command"); + /* Create pipes for reading from and writing to the child process. */ + if (pipe(readfds) == -1) { + perror("read pipe"); return 1; } - while ((bytes_read = fread(buffer, 1, FILE_READBUF_SIZE, fp)) > 0) + if (req->body && pipe(writefds) == -1) { + perror("write pipe"); + return 1; + } + + pid = fork(); + if (pid == -1) { + perror("fork"); + return 1; + } + + if (pid == 0) { + close(readfds[0]); + if (readfds[1] != STDOUT_FILENO) { + dup2(readfds[1], STDOUT_FILENO); + close(readfds[1]); + } + + if (req->body != NULL) { + close(writefds[1]); + if (writefds[0] != STDIN_FILENO) { + dup2(writefds[0], STDIN_FILENO); + close(writefds[0]); + } + } + + execl("/usr/bin/php-cgi", "php-cgi", NULL); + /* We should only end up here if there's an error. */ + perror("exec"); + exit(1); + } + + close(readfds[1]); + if (req->body != NULL) { + /* Write to stdin of child process. */ + close(writefds[0]); + write(writefds[1], req->body, strlen(req->body) + 1); + } + + /* Read its output from stdout. */ + while ((bytes_read = read(readfds[0], buffer, FILE_READBUF_SIZE)) > 0) write(sockfd, buffer, bytes_read); unsetenv("QUERY_STRING"); + unsetenv("CONTENT"); + unsetenv("CONTENT_LENGTH"); + + close(readfds[0]); + close(writefds[1]); - pclose(fp); return 0; } @@ -16,6 +16,6 @@ const char *file_path_for_uri(const char *); enum serve_method file_method_for_path(const char *, enum http_res_code *); int file_read(const char *, int); -int file_read_php(const char *, const char *, int); +int file_read_php(const char *, const struct http_request *, int); #endif |