|
1 | | -#include <unistd.h> |
2 | 1 | #include <cstring> |
3 | 2 | #include <fcntl.h> |
| 3 | +#include <unistd.h> |
4 | 4 | #include <linux/limits.h> |
5 | 5 |
|
6 | 6 | #include "DataHandler.h" |
@@ -36,45 +36,184 @@ bool DataHandler::verify_path(std::string path) { |
36 | 36 | return path_ok; |
37 | 37 | } |
38 | 38 |
|
39 | | -char * DataHandler::read_resource(std::string path) { |
| 39 | +bool is(char * mime, std::string type) { |
| 40 | + return (strstr(mime, type.c_str()) != NULL); |
| 41 | +} |
| 42 | + |
| 43 | +resource DataHandler::read_resource(std::string path) { |
40 | 44 | // prepend cwd() to path |
41 | 45 |
|
42 | 46 | if (!verify_path(path)) |
43 | 47 | path = "/index.html"; |
44 | 48 |
|
45 | | - path = get_working_path() + path; |
46 | | - this->logger->debug("Reading file at: " + path); |
| 49 | + std::string cwd = get_working_path(); |
| 50 | + path = cwd + path; |
| 51 | + this->logger->debug("Checking resource at: " + path); |
| 52 | + |
| 53 | + // check mime type of resource |
| 54 | + std::string args[2] = { "/usr/bin/file", path }; |
| 55 | + resource file_mime = run_command(args); |
| 56 | + char * mime = file_mime.data; |
| 57 | + |
| 58 | + resource output; |
| 59 | + // now check for known mime types |
| 60 | + if (is(mime, "executable")) { |
| 61 | + // run the script |
| 62 | + std::string args[2] = { path, "" }; // TODO: Fix me too! |
| 63 | + resource script_output = run_command(args); |
| 64 | + output.data = script_output.data; |
| 65 | + output.size = script_output.size; |
| 66 | + output.type = "executable"; |
| 67 | + } |
| 68 | + // TODO: Move this definition to some more reasonable place |
| 69 | + else if (is(mime, "HTML")) { output.type = "text/html; charset=UTF-8"; } |
| 70 | + else if (is(mime, "ERROR") || is(mime, "ASCII")) { output.type = "text/plain; charset=UTF-8"; } |
| 71 | + else if (is(mime, "JPEG")) { output.type = "image/jpeg"; } |
| 72 | + else if (is(mime, "PNG")) { output.type = "image/png"; } |
| 73 | + else if (is(mime, "MS Windows icon")) { output.type = "image/vnd.microsoft.icon"; } |
| 74 | + |
| 75 | + if (output.type.length() > 0 && output.type != "executable") { |
| 76 | + resource f = get_file(path); |
| 77 | + output.data = f.data; |
| 78 | + output.size = f.size; |
| 79 | + } |
| 80 | + else if (output.type.length() == 0){ |
| 81 | + std::string error_str = "Unsupported mime type: " + std::string(mime); |
| 82 | + // drop 'local' part of path |
| 83 | + size_t pos = 0; |
| 84 | + while ((pos = error_str.find(cwd)) != std::string::npos ) |
| 85 | + error_str.erase(pos, cwd.length()); |
| 86 | + |
| 87 | + throw DataHandler::Unsupported(error_str); |
| 88 | + } |
| 89 | + |
| 90 | + return output; |
| 91 | +} |
| 92 | + |
| 93 | +std::string DataHandler::get_working_path() { |
| 94 | + char temp[PATH_MAX]; |
| 95 | + return ( getcwd(temp, PATH_MAX) ? std::string( temp ) : std::string("") ); |
| 96 | +} |
47 | 97 |
|
| 98 | +resource DataHandler::get_file(std::string path) { |
| 99 | + this->logger->debug("Read file: " + path); |
48 | 100 | int fd = open(path.c_str(), O_RDONLY); |
49 | 101 | if (fd < 0) { |
50 | 102 | // TODO: replace with a proper HTTP CODE |
51 | 103 | char * err = std::strerror(errno); |
52 | 104 | throw DataHandler::FileNotFound("Error while reading file contents at " + path + ": " + std::string(err ? err : "unknown error")); |
53 | 105 | } |
54 | 106 |
|
55 | | - lseek(fd, 0, SEEK_END); |
56 | | - long fsize = lseek(fd, 0, SEEK_CUR); |
| 107 | + long fsize = lseek(fd, 0, SEEK_END); |
57 | 108 | lseek(fd, 0, SEEK_SET); |
58 | 109 |
|
59 | | - char * data = (char *) malloc(fsize + 1); |
60 | | - read(fd, data, fsize); |
| 110 | + resource data; |
| 111 | + data.data = (char *) malloc(fsize+1); |
| 112 | + read(fd, data.data, fsize); |
61 | 113 | close(fd); |
62 | | - data[fsize] = '\0'; |
63 | 114 |
|
64 | | - return data; |
65 | | -} |
| 115 | + data.data[fsize] = '\0'; |
| 116 | + data.size = fsize; |
66 | 117 |
|
67 | | -std::string DataHandler::get_working_path() { |
68 | | - char temp[PATH_MAX]; |
69 | | - return ( getcwd(temp, PATH_MAX) ? std::string( temp ) : std::string("") ); |
| 118 | + return data; |
70 | 119 | } |
71 | 120 |
|
72 | | -char * DataHandler::get_error_file(int error_code, std::string param) { |
| 121 | +resource DataHandler::get_error_file(int error_code, std::string param) { |
73 | 122 | // start by reading the error template |
74 | | - char * contents = read_resource("/errors/"+ std::to_string(error_code) +".html"); |
| 123 | + resource output = read_resource("/errors/"+ std::to_string(error_code) +".html"); |
75 | 124 | // now prepare a place to write the filled template |
76 | | - char * data = (char *) malloc(strlen(contents) + param.length() - 3); |
| 125 | + long new_size = output.size + param.length() - 3; |
| 126 | + char * data = (char *) malloc(new_size * sizeof(char)); |
77 | 127 | // and fill it |
78 | | - sprintf(data, contents, param.c_str()); |
79 | | - return data; |
| 128 | + sprintf(data, output.data, param.c_str()); |
| 129 | + |
| 130 | + // free old structure |
| 131 | + free(output.data); |
| 132 | + // replace with new pointer |
| 133 | + output.data = data; |
| 134 | + output.size = new_size; |
| 135 | + |
| 136 | + return output; |
| 137 | +} |
| 138 | + |
| 139 | +resource DataHandler::run_command(std::string args[]) { |
| 140 | + int comms[2]; |
| 141 | + // start with a small 1k buffer |
| 142 | + int buf_pos = 0, buf_max = 1024, buf_blocks = 1; |
| 143 | + resource output; |
| 144 | + output.data = (char *) malloc(buf_blocks * buf_max * sizeof(char)); |
| 145 | + |
| 146 | + if (pipe(comms) < 0) { |
| 147 | + char * err = std::strerror(errno); |
| 148 | + throw DataHandler::Exception("Cannot create pipe: " + std::string(err ? err : "unknown error")); |
| 149 | + } |
| 150 | + |
| 151 | + int pid = fork(); |
| 152 | + if (pid == 0) { |
| 153 | + // redirect STDOUT to our comms |
| 154 | + if (dup2(comms[1], STDOUT_FILENO) == -1) { |
| 155 | + char * err = std::strerror(errno); |
| 156 | + throw DataHandler::Exception("Cannot redirect STDOUT to pipe: " + std::string(err ? err : "unknown error")); |
| 157 | + } |
| 158 | + |
| 159 | + // same with STDERR |
| 160 | + if (dup2(comms[1], STDERR_FILENO) == -1) { |
| 161 | + char * err = std::strerror(errno); |
| 162 | + throw DataHandler::Exception("Cannot redirect STDERR to pipe: " + std::string(err ? err : "unknown error")); |
| 163 | + } |
| 164 | + |
| 165 | + // now close the pipe on our side |
| 166 | + close(comms[0]); |
| 167 | + |
| 168 | + // now run the target |
| 169 | + // TODO: Fix me! I'm ugly! |
| 170 | + if (execl(args[0].c_str(), args[0].c_str(), args[1].c_str(), (char *) 0) < 0) { |
| 171 | + char * err = std::strerror(errno); |
| 172 | + throw DataHandler::Exception( |
| 173 | + "Cannot exec '" + std::string(args[0]) + "' due to: " + std::string(err ? err : "unknown error") |
| 174 | + ); |
| 175 | + } |
| 176 | + } |
| 177 | + else if (pid > 0) { |
| 178 | + close(comms[1]); |
| 179 | + |
| 180 | + // Just a char by char read here, you can change it accordingly |
| 181 | + char rchar; |
| 182 | + while (read(comms[0], &rchar, 1) == 1) { |
| 183 | + output.data[buf_pos++] = rchar; |
| 184 | + if (buf_pos >= buf_max){ |
| 185 | + // extend the buffer by another 1k block |
| 186 | + buf_blocks++; |
| 187 | + // reset the curent position |
| 188 | + buf_pos = 0; |
| 189 | + // allocate more memory |
| 190 | + output.data = (char *) realloc(output.data, buf_blocks * buf_max * sizeof(char)); |
| 191 | + if (output.data == NULL) { |
| 192 | + char * err = std::strerror(errno); |
| 193 | + throw DataHandler::Exception( |
| 194 | + "Error while reading output from: " + std::string(args[0]) +", " |
| 195 | + + "because of: " + std::string(err ? err : "unknown error") |
| 196 | + ); |
| 197 | + } |
| 198 | + } |
| 199 | + } |
| 200 | + // add NULL termination |
| 201 | + output.data[buf_pos] = '\0'; |
| 202 | + output.size = (buf_blocks - 1) * buf_max + buf_pos + 1; |
| 203 | + |
| 204 | + close(comms[0]); |
| 205 | + } |
| 206 | + else { |
| 207 | + // fork failed |
| 208 | + char * err = std::strerror(errno); |
| 209 | + close(comms[0]); |
| 210 | + close(comms[1]); |
| 211 | + throw DataHandler::Exception("Cannot fork: " + std::string(err ? err : "unknown error")); |
| 212 | + } |
| 213 | + |
| 214 | + if (strstr(output.data, "DataHandler::Exception") != NULL) { |
| 215 | + throw DataHandler::Exception("Fork returned an exception: " + std::string(output.data)); |
| 216 | + } |
| 217 | + |
| 218 | + return output; |
80 | 219 | } |
0 commit comments