void HTMLForm::load(const HTTPRequest& request, std::istream& requestBody, PartHandler& handler) { clear(); URI uri(request.getURI()); const std::string& query = uri.getRawQuery(); if (!query.empty()) { std::istringstream istr(query); readUrl(istr); } if (request.getMethod() == HTTPRequest::HTTP_POST || request.getMethod() == HTTPRequest::HTTP_PUT) { std::string mediaType; NameValueCollection params; MessageHeader::splitParameters(request.getContentType(), mediaType, params); _encoding = mediaType; if (_encoding == ENCODING_MULTIPART) { _boundary = params["boundary"]; readMultipart(requestBody, handler); } else { readUrl(requestBody); } } }
void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request) { MD5Engine engine; const std::string& qop = _requestAuthParams.get(QOP_PARAM, DEFAULT_QOP); const std::string& realm = _requestAuthParams.getRealm(); const std::string& nonce = _requestAuthParams.get(NONCE_PARAM); _requestAuthParams.set(URI_PARAM, request.getURI()); if (qop.empty()) { const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, ha2)); } else if (icompare(qop, AUTH_PARAM) == 0) { const std::string& cnonce = _requestAuthParams.get(CNONCE_PARAM); const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); const std::string nc = formatNonceCounter(updateNonceCounter(nonce)); _requestAuthParams.set(NC_PARAM, nc); _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, nc, cnonce, qop, ha2)); } }
HTTPServer::HTTPServer(const std::string &addr, const std::string &port, int maxClientsCount, std::function<HTTPResponse(HTTPRequest &request)> onGet, std::function<HTTPResponse(HTTPRequest &request)> onPost, EpollHandler &epoll) { std::function<void(TCPSocket &)> onAccept = [&onGet, &onPost, this](TCPSocket &sock) { const int BUF_MAX_SIZE = 4096; char buf[BUF_MAX_SIZE]; int len; while (true) { len = sock.recieveMsg(buf, BUF_MAX_SIZE); if (len <= 0) break; addBufToString(currentRequest[sock.sockfd], buf, len); } std::cerr << " ======\n" << currentRequest[sock.sockfd] << "\n===========\n"; HTTPRequest httpRequest; HTTPResponse httpResponse; try { //std::cout << currentRequest << "\n"; httpRequest = HTTPRequest(currentRequest[sock.sockfd]); //std::cout << "Request from soket " << sock.sockfd << ": " << currentRequest << "\n"; if (httpRequest.getMethod() == "GET") { httpResponse = onGet(httpRequest); } else if (httpRequest.getMethod() == "POST") { httpResponse = onPost(httpRequest); } std::cout << "Response for socket " << sock.sockfd << ": \n"; sock.sendMsg(httpResponse.buildResponse().c_str()); currentRequest[sock.sockfd] = ""; } catch (NotFullRequestException &e) { std::cerr << "Not full request: " << e.getMessage() << "\n"; } catch (HTTPException &e) { std::cerr << "Bad HTTP Request: " << e.getMessage() << "\n"; httpResponse.setHttpVersion("HTTP/1.1"); httpResponse.setStatusCode(400); httpResponse.setReasonPhrase("Bad Request"); httpResponse.addEntityHeader("Content-Type", "*/*"); std::cout << "Response for socket " << sock.sockfd << ": \n"; sock.sendMsg(httpResponse.buildResponse().c_str()); currentRequest[sock.sockfd] = ""; } }; tcpServer = new TCPServer(addr.c_str(), port.c_str(), maxClientsCount * 2, onAccept, epoll); }
CGIParser::CGIParser(const HTTPRequest& request, bool queryOnly) { parse(request.getQueryString()); if (!queryOnly && !strcmp(request.getMethod(),"POST")) { if (request.getContentType().find("application/x-www-form-urlencoded") != string::npos) parse(request.getRequestBody()); } }
void HTMLForm::prepareSubmit(HTTPRequest& request) { if (request.getMethod() == HTTPRequest::HTTP_POST || request.getMethod() == HTTPRequest::HTTP_PUT) { if (_encoding == ENCODING_URL) { request.setContentType(_encoding); request.setChunkedTransferEncoding(false); Poco::CountingOutputStream ostr; writeUrl(ostr); request.setContentLength(ostr.chars()); } else { _boundary = MultipartWriter::createBoundary(); std::string ct(_encoding); ct.append("; boundary=\""); ct.append(_boundary); ct.append("\""); request.setContentType(ct); } if (request.getVersion() == HTTPMessage::HTTP_1_0) { request.setKeepAlive(false); request.setChunkedTransferEncoding(false); } else if (_encoding != ENCODING_URL) { request.setChunkedTransferEncoding(true); } } else { std::string uri = request.getURI(); std::ostringstream ostr; writeUrl(ostr); uri.append("?"); uri.append(ostr.str()); request.setURI(uri); } }
void HTTPRequestTest::testRead1() { std::string s("GET / HTTP/1.0\r\n\r\n"); std::istringstream istr(s); HTTPRequest request; request.read(istr); assert (request.getMethod() == HTTPRequest::HTTP_GET); assert (request.getURI() == "/"); assert (request.getVersion() == HTTPMessage::HTTP_1_0); assert (request.empty()); assert (istr.get() == -1); }
TEST(HTTPRequest, testRead1) { std::string s("GET / HTTP/1.0\r\n\r\n"); std::istringstream istr(s); HTTPRequest request; request.read(istr); EXPECT_TRUE (request.getMethod() == HTTPRequest::HTTP_GET); EXPECT_TRUE (request.getURI() == "/"); EXPECT_TRUE (request.getVersion() == HTTPMessage::HTTP_1_0); EXPECT_TRUE (request.empty()); EXPECT_TRUE (istr.get() == -1); }
bool HTTPDigestCredentials::verifyAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& params) const { const std::string& nonce = params.get(NONCE_PARAM); const std::string& realm = params.getRealm(); const std::string& qop = params.get(QOP_PARAM, DEFAULT_QOP); std::string response; MD5Engine engine; if (qop.empty()) { const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); response = digest(engine, ha1, nonce, ha2); } else if (icompare(qop, AUTH_PARAM) == 0) { const std::string& cnonce = params.get(CNONCE_PARAM); const std::string& nc = params.get(NC_PARAM); const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); response = digest(engine, ha1, nonce, nc, cnonce, qop, ha2); } return response == params.get(RESPONSE_PARAM); }
void HTTPRequestTest::testRead2() { std::string s("HEAD /index.html HTTP/1.1\r\nConnection: Keep-Alive\r\nHost: localhost\r\nUser-Agent: Poco\r\n\r\n"); std::istringstream istr(s); HTTPRequest request; request.read(istr); assert (request.getMethod() == HTTPRequest::HTTP_HEAD); assert (request.getURI() == "/index.html"); assert (request.getVersion() == HTTPMessage::HTTP_1_1); assert (request.size() == 3); assert (request["Connection"] == "Keep-Alive"); assert (request["Host"] == "localhost"); assert (request["User-Agent"] == "Poco"); assert (istr.get() == -1); }
std::string OAuth::createSignatureBaseString(HTTPRequest& r) { HTTPURL url = r.getURL(); std::string method = (r.getMethod() == HTTP_METHOD_POST) ? "POST" : "GET"; // Get the percent encoded parameters HTTPParameters collected_parameters = collectParameters(r); collected_parameters.percentEncode(); // We need to encode the parameter string (again) PercentEncode encoder; std::string parameter_string = collected_parameters.getQueryString(); std::string normalized_url = createNormalizedURL(r.getURL()); std::string signature_string = method +"&" +encoder.encode(normalized_url) +"&" +encoder.encode(parameter_string); return signature_string; }
void HTTPRequestTest::testRead4() { std::string s("POST /test.cgi HTTP/1.1\r\nConnection: Close\r\nContent-Length: 100 \r\nContent-Type: text/plain\r\nHost: localhost:8000\r\nUser-Agent: Poco\r\n\r\n"); std::istringstream istr(s); HTTPRequest request; request.read(istr); assert (request.getMethod() == HTTPRequest::HTTP_POST); assert (request.getURI() == "/test.cgi"); assert (request.getVersion() == HTTPMessage::HTTP_1_1); assert (request.size() == 5); assert (request["Connection"] == "Close"); assert (request["Host"] == "localhost:8000"); assert (request["User-Agent"] == "Poco"); assert (request.getContentType() == "text/plain"); assert (request.getContentLength() == 100); assert (istr.get() == -1); }
CGIParser::CGIParser(const HTTPRequest& request) { const char* pch=NULL; if (!strcmp(request.getMethod(),"POST")) pch=request.getRequestBody(); else pch=request.getQueryString(); size_t cl=pch ? strlen(pch) : 0; const URLEncoder* dec = XMLToolingConfig::getConfig().getURLEncoder(); while (cl && pch) { char *name; char *value; value=fmakeword('&',&cl,&pch); plustospace(value); dec->decode(value); name=makeword(value,'='); kvp_map.insert(pair<const string,char*>(name,value)); free(name); } }
void AbstractHandler::preservePostData( const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState ) const { #ifdef HAVE_STRCASECMP if (strcasecmp(request.getMethod(), "POST")) return; #else if (stricmp(request.getMethod(), "POST")) return; #endif // No specs mean no save. const PropertySet* props=application.getPropertySet("Sessions"); pair<bool,const char*> mech = props->getString("postData"); if (!mech.first) { m_log.info("postData property not supplied, form data will not be preserved across SSO"); return; } DDF postData = getPostData(application, request); if (postData.isnull()) return; if (strstr(mech.second,"ss:") == mech.second) { mech.second+=3; if (!*mech.second) { postData.destroy(); throw ConfigurationException("Unsupported postData mechanism ($1).", params(1, mech.second - 3)); } string postkey; if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { DDFJanitor postjan(postData); #ifndef SHIBSP_LITE StorageService* storage = application.getServiceProvider().getStorageService(mech.second); if (storage) { // Use a random key string rsKey; SAMLConfig::getConfig().generateRandomBytes(rsKey,20); rsKey = SAMLArtifact::toHex(rsKey); ostringstream out; out << postData; if (!storage->createString("PostData", rsKey.c_str(), out.str().c_str(), time(NULL) + 600)) throw IOException("Attempted to insert duplicate storage key."); postkey = string(mech.second-3) + ':' + rsKey; } else { m_log.error("storage-backed PostData mechanism with invalid StorageService ID (%s)", mech.second); } #endif } else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { DDF out,in = DDF("set::PostData").structure(); DDFJanitor jin(in),jout(out); in.addmember("id").string(mech.second); in.add(postData); out = application.getServiceProvider().getListenerService()->send(in); if (!out.isstring()) throw IOException("StorageService-backed PostData mechanism did not return a state key."); postkey = string(mech.second-3) + ':' + out.string(); } // Set a cookie with key info. pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState); postkey += shib_cookie.second; response.setCookie(shib_cookie.first.c_str(), postkey.c_str()); } else { postData.destroy(); throw ConfigurationException("Unsupported postData mechanism ($1).", params(1,mech.second)); } }
void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::MemoryInputStream& message, const std::shared_ptr<StreamSocket>& socket) { try { bool noCache = false; #if ENABLE_DEBUG noCache = true; #endif Poco::Net::HTTPResponse response; Poco::URI requestUri(request.getURI()); LOG_TRC("Fileserver request: " << requestUri.toString()); requestUri.normalize(); // avoid .'s and ..'s std::string path(requestUri.getPath()); if (path.find("loleaflet/" LOOLWSD_VERSION_HASH "/") == std::string::npos) { LOG_WRN("client - server version mismatch, disabling browser cache."); noCache = true; } std::vector<std::string> requestSegments; requestUri.getPathSegments(requestSegments); const std::string relPath = getRequestPathname(request); // Is this a file we read at startup - if not; its not for serving. if (requestSegments.size() < 1 || FileHash.find(relPath) == FileHash.end()) throw Poco::FileNotFoundException("Invalid URI request: [" + requestUri.toString() + "]."); const auto& config = Application::instance().config(); const std::string loleafletHtml = config.getString("loleaflet_html", "loleaflet.html"); const std::string endPoint = requestSegments[requestSegments.size() - 1]; if (endPoint == loleafletHtml) { preprocessFile(request, message, socket); return; } if (request.getMethod() == HTTPRequest::HTTP_GET) { if (endPoint == "admin.html" || endPoint == "adminSettings.html" || endPoint == "adminHistory.html" || endPoint == "adminAnalytics.html") { preprocessAdminFile(request, socket); return; } if (endPoint == "admin-bundle.js" || endPoint == "admin-localizations.js") { noCache = true; if (!LOOLWSD::AdminEnabled) throw Poco::FileAccessDeniedException("Admin console disabled"); if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) throw Poco::Net::NotAuthenticatedException("Invalid admin login"); // Ask UAs to block if they detect any XSS attempt response.add("X-XSS-Protection", "1; mode=block"); // No referrer-policy response.add("Referrer-Policy", "no-referrer"); } // Do we have an extension. const std::size_t extPoint = endPoint.find_last_of('.'); if (extPoint == std::string::npos) throw Poco::FileNotFoundException("Invalid file."); const std::string fileType = endPoint.substr(extPoint + 1); std::string mimeType; if (fileType == "js") mimeType = "application/javascript"; else if (fileType == "css") mimeType = "text/css"; else if (fileType == "html") mimeType = "text/html"; else if (fileType == "png") mimeType = "image/png"; else if (fileType == "svg") mimeType = "image/svg+xml"; else mimeType = "text/plain"; auto it = request.find("If-None-Match"); if (it != request.end()) { // if ETags match avoid re-sending the file. if (!noCache && it->second == "\"" LOOLWSD_VERSION_HASH "\"") { // TESTME: harder ... - do we even want ETag support ? std::ostringstream oss; Poco::DateTime now; Poco::DateTime later(now.utcTime(), int64_t(1000)*1000 * 60 * 60 * 24 * 128); oss << "HTTP/1.1 304 Not Modified\r\n" << "Date: " << Poco::DateTimeFormatter::format( now, Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" << "Expires: " << Poco::DateTimeFormatter::format( later, Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" << "Cache-Control: max-age=11059200\r\n" << "\r\n"; socket->send(oss.str()); socket->shutdown(); return; } } response.set("User-Agent", HTTP_AGENT_STRING); response.set("Date", Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT)); bool gzip = request.hasToken("Accept-Encoding", "gzip"); const std::string *content; #if ENABLE_DEBUG if (std::getenv("LOOL_SERVE_FROM_FS")) { // Useful to not serve from memory sometimes especially during loleaflet development // Avoids having to restart loolwsd everytime you make a change in loleaflet const std::string filePath = Poco::Path(LOOLWSD::FileServerRoot, relPath).absolute().toString(); HttpHelper::sendFile(socket, filePath, mimeType, response, noCache); return; } #endif if (gzip) { response.set("Content-Encoding", "gzip"); content = getCompressedFile(relPath); } else content = getUncompressedFile(relPath); if (!noCache) { // 60 * 60 * 24 * 128 (days) = 11059200 response.set("Cache-Control", "max-age=11059200"); response.set("ETag", "\"" LOOLWSD_VERSION_HASH "\""); } response.setContentType(mimeType); response.add("X-Content-Type-Options", "nosniff"); std::ostringstream oss; response.write(oss); const std::string header = oss.str(); LOG_TRC("#" << socket->getFD() << ": Sending " << (!gzip ? "un":"") << "compressed : file [" << relPath << "]: " << header); socket->send(header); socket->send(*content); } } catch (const Poco::Net::NotAuthenticatedException& exc) { LOG_ERR("FileServerRequestHandler::NotAuthenticated: " << exc.displayText()); sendError(401, request, socket, "", "", "WWW-authenticate: Basic realm=\"online\"\r\n"); } catch (const Poco::FileAccessDeniedException& exc) { LOG_ERR("FileServerRequestHandler: " << exc.displayText()); sendError(403, request, socket, "403 - Access denied!", "You are unable to access"); } catch (const Poco::FileNotFoundException& exc) { LOG_WRN("FileServerRequestHandler: " << exc.displayText()); sendError(404, request, socket, "404 - file not found!", "There seems to be a problem locating"); } }