int Webserver::process(FILE* f) { char buf[4096]; char *method; char *path; char *protocol; if (!fgets(buf, sizeof(buf), f)) return -1; printf("%s", buf); char* bufp = buf; method = strsep(&bufp, " "); path = strsep(&bufp, " "); protocol = strsep(&bufp, "\r"); if (!method || !path || !protocol) return -1; // Extract request HTTPRequest request; request.method = string(method); request.path = string(path); request.readHeaders(f); request.writeHeaders(stdout); request.readParams(f); request.setBody(f); // Find and execute action Action* action = actions[path]; if (action == NULL) { action = file_action; } HTTPResponse response = action->execute(request); response.addHeader("Server", string("RayGay Renderslave ") + string(VERSION)); request.addHeader("Date", WebUtil::formatDate(time(NULL))); // Send response fseek(f, 0, SEEK_CUR); // Force change of stream direction fprintf(f, "HTTP/1.0 %d %s\r\n", response.status, response.statusString().c_str()); response.writeHeaders(f); response.writeBody(f); return 0; }
HTTPResponse FileAction::execute(const HTTPRequest& request) { HTTPResponse response; struct stat statbuf; char md5_hex[33]; string path = document_root + request.path; // TODO: Sanitize path, ie. remove ".." and leading "/" if (request.method == "GET" || request.method == "HEAD") { if (stat(path.c_str(), &statbuf) >= 0) { ostringstream os; os << statbuf.st_size; response = HTTPResponse(200, WebUtil::pathToMimetype(path)); response.addHeader("Content-Length",os.str()); response.addHeader("Last-Modified",WebUtil::formatDate(statbuf.st_mtime)); FILE* f = fopen(path.c_str(), "r"); md5_stream_hex(f,md5_hex); md5_hex[32] = '\0'; response.addHeader("Content-MD5",string(md5_hex)); if (request.method == "GET") { rewind(f); response.setBody(f); } else { fclose(f); } } else { response = HTTPResponse(404, "text/plain"); } } else if (request.method == "DELETE") { // This method isn't strictly HTTP1.1, but we need it // and I don't want to implement a complete WebDAV stack. unlink(path.c_str()); response = HTTPResponse(200, "text/plain"); } else if (request.method == "PUT") { long size = atoi(request.getHeader("Content-length").c_str()); // TODO: If no content-header is sent, return "411 Length required" as per the RFC. bool exists = stat(path.c_str(), &statbuf) != -1; FILE* f = fopen(path.c_str(), "w"); WebUtil::copy(request.bodyFILE, f, size); fclose(f); if (exists) { response = HTTPResponse(200, "text/plain"); response.setBody("File modified"); } else { response = HTTPResponse(201, "text/plain"); response.setBody("File created"); } } else if (request.method == "MKCOL") { // This method isn't strictly HTTP1.1, but we need it // and I don't want to implement a complete WebDAV stack. if (mkdir(path.c_str(), 0777) == -1) { response = HTTPResponse(201, "text/plain"); response.setBody("Dir not created"); } else { response = HTTPResponse(201, "text/plain"); response.setBody("Dir created"); printf("Dir %s created\n", path.c_str()); } } else { cout << "Unknown method " << request.method << endl; response = HTTPResponse(405, "text/plain"); response.addHeader("Allow", "PUT, GET, HEAD, DELETE, MKCOL"); } return response; }