Mercurial > hg > orthanc-education
view Sources/HttpToolbox.cpp @ 77:80b663d5f8fe default tip
replaced boost::math::iround() by Orthanc::Math::llround()
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 27 Jan 2026 17:05:03 +0100 |
| parents | 0f8c46d755e2 |
| children |
line wrap: on
line source
/** * SPDX-FileCopyrightText: 2024-2026 Sebastien Jodogne, EPL UCLouvain, Belgium * SPDX-License-Identifier: AGPL-3.0-or-later */ /** * Orthanc for Education * Copyright (C) 2024-2026 Sebastien Jodogne, EPL UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "HttpToolbox.h" #include <OrthancPluginCppWrapper.h> #include <ChunkedBuffer.h> #include <OrthancException.h> #include <SystemToolbox.h> #include <Toolbox.h> #if ORTHANC_STANDALONE == 1 # include <EmbeddedResources.h> #endif #include <boost/algorithm/string/predicate.hpp> namespace HttpToolbox { bool LookupJsonObject(Json::Value& target, const Json::Value& source, const std::string& field) { if (source.isMember(field) && source[field].type() == Json::objectValue) { target = source[field]; return true; } else { return false; } } std::string ReadMandatoryString(const std::map<std::string, std::string>& dictionary, const std::string& field) { std::map<std::string, std::string>::const_iterator found = dictionary.find(field); if (found == dictionary.end()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Missing field: \"" + field + "\""); } else { return found->second; } } std::string ReadOptionalString(const std::map<std::string, std::string>& dictionary, const std::string& field, const std::string& defaultValue) { std::map<std::string, std::string>::const_iterator found = dictionary.find(field); if (found == dictionary.end()) { return defaultValue; } else { return found->second; } } void EncodeBase64Url(std::string& base64, const std::string& source) { Orthanc::Toolbox::EncodeBase64(base64, source); // https://en.wikipedia.org/wiki/Base64#URL_applications for (size_t i = 0; i < base64.size(); i++) { if (base64[i] == '+') { base64[i] = '-'; } else if (base64[i] == '/') { base64[i] = '_'; } else if (base64[i] == '=') // The end of the base64 has been reached { base64.resize(i); return; } } } void DecodeBase64Url(std::string& decoded, const std::string& base64) { // https://en.wikipedia.org/wiki/Base64#URL_applications std::string s; s.resize(base64.size()); for (size_t i = 0; i < base64.size(); i++) { if (base64[i] == '-') { s[i] = '+'; } else if (base64[i] == '_') { s[i] = '/'; } else { s[i] = base64[i]; } } Orthanc::Toolbox::DecodeBase64(decoded, s); } void ConvertDictionaryFromC(std::map<std::string, std::string>& target, bool toLowerCase, uint32_t count, const char *const *keys, const char *const *values) { target.clear(); for (uint32_t i = 0; i < count; i++) { std::string s; if (toLowerCase) { Orthanc::Toolbox::ToLowerCase(s, keys[i]); } else { s = keys[i]; } target[s] = values[i]; } } bool LookupCDictionary(std::string& target, const std::string& key, bool toLowerCase, uint32_t count, const char *const *keys, const char *const *values) { for (uint32_t i = 0; i < count; i++) { std::string s; if (toLowerCase) { Orthanc::Toolbox::ToLowerCase(s, keys[i]); } else { s = keys[i]; } if (s == key) { target = values[i]; return true; } } return false; } bool LookupHttpHeader(std::string& value, const std::map<std::string, std::string>& headers, const std::string& header) { std::map<std::string, std::string>::const_iterator found = headers.find(header); if (found == headers.end()) { return false; } else { value = found->second; return true; } } bool ParseAuthorizationHeader(std::string& type, std::string& authorization, const std::string& authorizationHeader) { std::size_t pos = authorizationHeader.find(' '); if (pos == std::string::npos) { return false; } else { type = authorizationHeader.substr(0, pos); authorization = Orthanc::Toolbox::StripSpaces(authorizationHeader.substr(pos + 1)); return true; } } bool LookupAuthorizationHeader(std::string& type, std::string& authorization, const std::map<std::string, std::string>& headers) { std::string value; if (LookupHttpHeader(value, headers, "authorization")) { return ParseAuthorizationHeader(type, authorization, value); } else { return false; } } void EncodeFormUrl(std::string& target, const std::map<std::string, std::string>& source) { Orthanc::ChunkedBuffer buffer; bool first = true; for (std::map<std::string, std::string>::const_iterator it = source.begin(); it != source.end(); ++it) { if (!it->first.empty()) { if (first) { first = false; } else { buffer.AddChunk("&"); } std::string key; Orthanc::Toolbox::UriEncode(key, it->first); buffer.AddChunk(key); if (!it->second.empty()) { std::string value; Orthanc::Toolbox::UriEncode(value, it->second); buffer.AddChunk("="); buffer.AddChunk(value); } } } buffer.Flatten(target); } void ParseFormUrlEncoded(std::map<std::string, std::string>& target, const void* body, size_t bodySize) { target.clear(); std::string decoded; decoded.assign(reinterpret_cast<const char*>(body), bodySize); std::vector<std::string> parameters; Orthanc::Toolbox::TokenizeString(parameters, decoded, '&'); for (size_t i = 0; i < parameters.size(); i++) { std::vector<std::string> tokens; Orthanc::Toolbox::TokenizeString(tokens, parameters[i], '='); if (!tokens.empty()) { std::string& key = tokens[0]; Orthanc::Toolbox::UrlDecode(key); if (tokens.size() == 1) { target[key] = ""; } else if (tokens.size() == 2) { std::string& value = tokens[1]; Orthanc::Toolbox::UrlDecode(value); target[key] = value; } } } } void ParseCookies(std::list<Cookie>& target, const std::string& cookieHeader) { target.clear(); std::vector<std::string> cookies; Orthanc::Toolbox::TokenizeString(cookies, cookieHeader, ';'); for (size_t i = 0; i < cookies.size(); i++) { std::vector<std::string> tokens; Orthanc::Toolbox::TokenizeString(tokens, cookies[i], '='); if (!tokens.empty()) { std::string key = Orthanc::Toolbox::StripSpaces(tokens[0]); if (tokens.size() == 1) { target.push_back(Cookie(key)); } else if (tokens.size() == 2) { target.push_back(Cookie(key, Orthanc::Toolbox::StripSpaces(tokens[1]))); } } } } void FormatRedirectionUrl(std::string& target, const std::string& base, const std::map<std::string, std::string>& arguments) { std::string form; EncodeFormUrl(form, arguments); if (form.empty()) { target = base; } else { target = base + "?" + form; } } void SetCookie(OrthancPluginRestOutput* output, const std::string& cookie, const std::string& value, CookieSameSite sameSite, bool secure) { std::string s = EnumerationToString(sameSite); if (secure) { s += "; Secure"; } // NB: This is a session cookie, as it does not have "Expires" or "Max-Age" std::string setCookie = cookie + "=" + value + "; HttpOnly; SameSite=" + s + "; Path=/"; OrthancPluginSetHttpHeader(OrthancPlugins::GetGlobalContext(), output, "Set-Cookie", setCookie.c_str()); } void ClearCookie(OrthancPluginRestOutput* output, const std::string& cookie, CookieSameSite sameSite, bool secure) { std::string s = EnumerationToString(sameSite); if (secure) { s += "; Secure"; } // Deleting the cookie by setting its expiration date in the past // https://stackoverflow.com/a/53573622 std::string setCookie = cookie + "=; HttpOnly; SameSite=" + s + "; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"; OrthancPluginSetHttpHeader(OrthancPlugins::GetGlobalContext(), output, "Set-Cookie", setCookie.c_str()); } void AnswerJson(OrthancPluginRestOutput* output, const Json::Value& value) { const std::string s = value.toStyledString(); OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Json)); } void AnswerBuffer(OrthancPluginRestOutput* output, const std::string& value, Orthanc::MimeType type) { OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, value.c_str(), value.size(), Orthanc::EnumerationToString(type)); } void GetWebApplicationResource(std::string& target, const std::string& path) { #if ORTHANC_STANDALONE == 0 Orthanc::SystemToolbox::ReadFile(target, Orthanc::SystemToolbox::InterpretRelativePath(WEB_APPLICATION_PATH, path)); #else const std::string s = "/" + path; Orthanc::EmbeddedResources::GetDirectoryResource(target, Orthanc::EmbeddedResources::WEB_APPLICATION, s.c_str()); #endif } void CheckUrlScheme(const std::string& url) { if (!boost::starts_with(url, "http://") && !boost::starts_with(url, "https://")) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Not a valid HTTP or HTTPS URL: " + url); } } std::string RemoveTrailingSlashes(const std::string& url) { size_t slash = url.size(); while (slash > 0 && url[slash - 1] == '/') { slash--; } if (slash == std::string::npos) { return url; } else { return url.substr(0, slash); } } void CopySetOfStrings(Json::Value& target, const std::set<std::string>& source) { target = Json::arrayValue; for (std::set<std::string>::const_iterator it = source.begin(); it != source.end(); ++it) { target.append(*it); } } void FormatViewers(Json::Value& target, const std::set<ViewerType>& source) { target = Json::arrayValue; for (std::set<ViewerType>::const_iterator it = source.begin(); it != source.end(); ++it) { Json::Value item; item["id"] = EnumerationToString(*it); item["description"] = GetViewerDescription(*it); target.append(item); } } }
