# HG changeset patch # User Alain Mazy # Date 1676890574 -3600 # Node ID af44dce56328a87a2e8b2447bd33b5afefb90997 # Parent 1a13c4fbc9a138b62d4fb3dbcc763c740079ba6c new 'auth/user-profile' Rest API route diff -r 1a13c4fbc9a1 -r af44dce56328 CMakeLists.txt --- a/CMakeLists.txt Fri Feb 17 15:05:02 2023 +0100 +++ b/CMakeLists.txt Mon Feb 20 11:56:14 2023 +0100 @@ -38,7 +38,7 @@ # Advanced parameters to fine-tune linking against system libraries set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK") - +set(ORTHANC_SDK_VERSION "1.11.3" CACHE STRING "Version of the Orthanc plugin SDK to use, if not using the system version (can be \"1.3.1\" \"1.11.3\")") # Download and setup the Orthanc framework include(${CMAKE_SOURCE_DIR}/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake) @@ -64,6 +64,7 @@ set(ENABLE_MODULE_IMAGES OFF) set(ENABLE_MODULE_JOBS OFF) set(ENABLE_MODULE_DICOM OFF) + set(ENABLE_WEB_CLIENT ON) include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkConfiguration.cmake) include_directories(${ORTHANC_FRAMEWORK_ROOT}) @@ -75,7 +76,16 @@ # Check that the Orthanc SDK headers are available if (STATIC_BUILD OR NOT USE_SYSTEM_ORTHANC_SDK) - include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc//Sdk-1.3.1) + if (ORTHANC_SDK_VERSION STREQUAL "1.3.1") + include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.3.1) + elseif (ORTHANC_SDK_VERSION STREQUAL "1.11.3") + include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.11.3) + else() + message(FATAL_ERROR "Unsupported version of the Orthanc plugin SDK: ${ORTHANC_SDK_VERSION}") + endif() + + +include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc//Sdk-1.3.1) else () CHECK_INCLUDE_FILE_CXX(orthanc/OrthancCPlugin.h HAVE_ORTHANC_H) if (NOT HAVE_ORTHANC_H) diff -r 1a13c4fbc9a1 -r af44dce56328 NEWS --- a/NEWS Fri Feb 17 15:05:02 2023 +0100 +++ b/NEWS Mon Feb 20 11:56:14 2023 +0100 @@ -1,4 +1,5 @@ * new "orthanc-explorer-2" StandardConfigurations +* new "auth/user-profile" Rest API route 2022-11-16 - v 0.4.1 ==================== diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/AuthorizationWebService.cpp --- a/Plugin/AuthorizationWebService.cpp Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/AuthorizationWebService.cpp Mon Feb 20 11:56:14 2023 +0100 @@ -22,6 +22,7 @@ #include #include +#include namespace OrthancPlugins { @@ -170,9 +171,65 @@ password_ = password; } + void AuthorizationWebService::SetUserProfileUrl(const std::string& url) + { + userProfileUrl_ = url; + } + void AuthorizationWebService::SetIdentifier(const std::string& webServiceIdentifier) { identifier_ = webServiceIdentifier; } + bool AuthorizationWebService::GetUserProfile(Json::Value& profile /* out */, + const Token& token, + const std::string& tokenValue) + { + if (userProfileUrl_.empty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Can not get user profile if the 'WebServiceUserProfileUrl' is not configured"); + } + + Orthanc::WebServiceParameters authWebservice; + authWebservice.SetUrl(userProfileUrl_); + + if (!username_.empty()) + { + authWebservice.SetCredentials(username_, password_); + } + + Json::Value body; + + body["token-key"] = token.GetKey(); + body["token-value"] = tokenValue; + + if (!identifier_.empty()) + { + body["identifier"] = identifier_; + } + else + { + body["identifier"] = Json::nullValue; + } + + std::string bodyAsString; + Orthanc::Toolbox::WriteFastJson(bodyAsString, body); + + try + { + Orthanc::HttpClient authClient(authWebservice, ""); + authClient.AssignBody(bodyAsString); + authClient.SetMethod(Orthanc::HttpMethod_Post); + authClient.AddHeader("Content-Type", "application/json"); + authClient.AddHeader("Expect", ""); + + authClient.ApplyAndThrowException(profile); + return true; + } + catch (Orthanc::OrthancException& ex) + { + return false; + } + } + } diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/AuthorizationWebService.h --- a/Plugin/AuthorizationWebService.h Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/AuthorizationWebService.h Mon Feb 20 11:56:14 2023 +0100 @@ -29,6 +29,7 @@ std::string username_; std::string password_; std::string identifier_; + std::string userProfileUrl_; bool IsGrantedInternal(unsigned int& validity, OrthancPluginHttpMethod method, @@ -47,6 +48,8 @@ void SetIdentifier(const std::string& webServiceIdentifier); + void SetUserProfileUrl(const std::string& url); + virtual bool IsGranted(unsigned int& validity, OrthancPluginHttpMethod method, const AccessedResource& access, @@ -62,5 +65,10 @@ { return IsGrantedInternal(validity, method, access, NULL, ""); } + + virtual bool GetUserProfile(Json::Value& profile /* out */, + const Token& token, + const std::string& tokenValue); + }; } diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/CachedAuthorizationService.cpp --- a/Plugin/CachedAuthorizationService.cpp Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/CachedAuthorizationService.cpp Mon Feb 20 11:56:14 2023 +0100 @@ -96,4 +96,12 @@ // The cache is not used if no token is available return decorated_->IsGranted(validity, method, access); } + + bool CachedAuthorizationService::GetUserProfile(Json::Value& profile /* out */, + const Token& token, + const std::string& tokenValue) + { + return decorated_->GetUserProfile(profile, token, tokenValue); + } + } diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/CachedAuthorizationService.h --- a/Plugin/CachedAuthorizationService.h Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/CachedAuthorizationService.h Mon Feb 20 11:56:14 2023 +0100 @@ -54,5 +54,9 @@ virtual bool IsGranted(unsigned int& validity, OrthancPluginHttpMethod method, const AccessedResource& access) ORTHANC_OVERRIDE; - }; + + virtual bool GetUserProfile(Json::Value& profile /* out */, + const Token& token, + const std::string& tokenValue); + }; } diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/IAuthorizationService.h --- a/Plugin/IAuthorizationService.h Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/IAuthorizationService.h Mon Feb 20 11:56:14 2023 +0100 @@ -23,6 +23,7 @@ #include #include +#include namespace OrthancPlugins { @@ -43,5 +44,9 @@ virtual bool IsGranted(unsigned int& validity /* out */, OrthancPluginHttpMethod method, const AccessedResource& access) = 0; + + virtual bool GetUserProfile(Json::Value& profile /* out */, + const Token& token, + const std::string& tokenValue) = 0; }; } diff -r 1a13c4fbc9a1 -r af44dce56328 Plugin/Plugin.cpp --- a/Plugin/Plugin.cpp Fri Feb 17 15:05:02 2023 +0100 +++ b/Plugin/Plugin.cpp Mon Feb 20 11:56:14 2023 +0100 @@ -240,6 +240,61 @@ } } +void GetUserProfile(OrthancPluginRestOutput* output, + const char* /*url*/, + const OrthancPluginHttpRequest* request) +{ + OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); + + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context, output, "GET"); + } + else + { + OrthancPlugins::AssociativeArray headers + (request->headersCount, request->headersKeys, request->headersValues, false); + + OrthancPlugins::AssociativeArray getArguments + (request->getCount, request->getKeys, request->getValues, true); + + + // Loop over all the authorization tokens stored in the HTTP + // headers, until finding one that is granted + for (std::set::const_iterator + token = tokens_.begin(); token != tokens_.end(); ++token) + { + Json::Value profile; + + std::string value; + + bool hasValue = false; + switch (token->GetType()) + { + case OrthancPlugins::TokenType_HttpHeader: + hasValue = headers.GetValue(value, token->GetKey()); + break; + + case OrthancPlugins::TokenType_GetArgument: + hasValue = getArguments.GetValue(value, token->GetKey()); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (hasValue) + { + authorizationService_->GetUserProfile(profile, *token, value); + + OrthancPlugins::AnswerJson(profile, output); + break; + } + } + + } +} + extern "C" { @@ -364,6 +419,7 @@ { uncheckedFolders_.push_back("/ui/app/"); uncheckedResources_.insert("/ui/api/pre-login-configuration"); // for the UI to know, i.e. if Keycloak is enabled or not + uncheckedResources_.insert("/auth/user-profile"); tokens_.insert(OrthancPlugins::Token(OrthancPlugins::TokenType_HttpHeader, "Authorization")); // for basic-auth tokens_.insert(OrthancPlugins::Token(OrthancPlugins::TokenType_HttpHeader, "token")); // for keycloak @@ -433,11 +489,18 @@ webService->SetCredentials(webServiceUsername, webServicePassword); } + std::string webServiceUserProfileUrl; + if (configuration.LookupStringValue(webServiceUserProfileUrl, "WebServiceUserProfileUrl")) + { + webService->SetUserProfileUrl(webServiceUserProfileUrl); + } + authorizationService_.reset (new OrthancPlugins::CachedAuthorizationService (webService.release(), factory)); OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); + OrthancPlugins::RegisterRestCallback("/auth/user-profile", true); #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 1) OrthancPluginRegisterIncomingHttpRequestFilter2(context, FilterHttpRequests); diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/CMake/AutoGeneratedCode.cmake --- a/Resources/Orthanc/CMake/AutoGeneratedCode.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/CMake/AutoGeneratedCode.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/CMake/Compiler.cmake --- a/Resources/Orthanc/CMake/Compiler.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/CMake/Compiler.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/CMake/DownloadOrthancFramework.cmake --- a/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -71,7 +72,10 @@ if (NOT ORTHANC_FRAMEWORK_MAJOR MATCHES "^[0-9]+$" OR NOT ORTHANC_FRAMEWORK_MINOR MATCHES "^[0-9]+$" OR NOT ORTHANC_FRAMEWORK_REVISION MATCHES "^[0-9]+$") - message("Bad version of the Orthanc framework: ${ORTHANC_FRAMEWORK_VERSION}") + message("Bad version of the Orthanc framework, assuming a pre-release: ${ORTHANC_FRAMEWORK_VERSION}") + set(ORTHANC_FRAMEWORK_MAJOR 999) + set(ORTHANC_FRAMEWORK_MINOR 999) + set(ORTHANC_FRAMEWORK_REVISION 999) endif() if (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.3.1") @@ -130,6 +134,26 @@ set(ORTHANC_FRAMEWORK_MD5 "3ea66c09f64aca990016683b6375734e") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.3") set(ORTHANC_FRAMEWORK_MD5 "9b86e6f00e03278293cd15643cc0233f") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.4") + set(ORTHANC_FRAMEWORK_MD5 "6d5ca4a73ac7d42445041ca79de1624d") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.5") + set(ORTHANC_FRAMEWORK_MD5 "10fc64de1254a095e5d3ed3931f0cfbb") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.6") + set(ORTHANC_FRAMEWORK_MD5 "4b5d05683d747c29b2860ad79d11e62e") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.9.7") + set(ORTHANC_FRAMEWORK_MD5 "c912bbb860d640d3ae3003b5c9698205") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.10.0") + set(ORTHANC_FRAMEWORK_MD5 "8610c82d9153f22e929f2110f8f60279") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.10.1") + set(ORTHANC_FRAMEWORK_MD5 "caf667fc5ea452b3d0c2f70bfd02599c") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.0") + set(ORTHANC_FRAMEWORK_MD5 "962c4a4a706a2ef28b390d8515dd7091") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.1") + set(ORTHANC_FRAMEWORK_MD5 "a39661c406adf22cf574fde290cf4bbf") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.2") + set(ORTHANC_FRAMEWORK_MD5 "ede3de356493a8868545f8cb4b8bc8b5") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.3") + set(ORTHANC_FRAMEWORK_MD5 "5c1b11009d782f248739919db6bf7f7a") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc @@ -150,6 +174,9 @@ elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "23ad1b9c7800") # For "Toolbox::ReadJson()" and "Toolbox::Write{...}Json()" (pre-1.9.0) set(ORTHANC_FRAMEWORK_MD5 "9af92080e57c60dd288eba46ce606c00") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "b2e08d83e21d") + # WSI 1.1 (framework pre-1.10.0), to remove "-std=c++11" + set(ORTHANC_FRAMEWORK_MD5 "2eaa073cbb4b44ffba199ad93393b2b1") endif() endif() endif() @@ -492,35 +519,6 @@ message(FATAL_ERROR "Please install the libjsoncpp-dev package") endif() - # Switch to the C++11 standard if the version of JsonCpp is 1.y.z - # (same as variable JSONCPP_CXX11 in the source code of Orthanc) - if (EXISTS ${JSONCPP_INCLUDE_DIR}/json/version.h) - file(STRINGS - "${JSONCPP_INCLUDE_DIR}/json/version.h" - JSONCPP_VERSION_MAJOR1 REGEX - ".*define JSONCPP_VERSION_MAJOR.*") - - if (NOT JSONCPP_VERSION_MAJOR1) - message(FATAL_ERROR "Unable to extract the major version of JsonCpp") - endif() - - string(REGEX REPLACE - ".*JSONCPP_VERSION_MAJOR.*([0-9]+)$" "\\1" - JSONCPP_VERSION_MAJOR ${JSONCPP_VERSION_MAJOR1}) - message("JsonCpp major version: ${JSONCPP_VERSION_MAJOR}") - - if (JSONCPP_VERSION_MAJOR GREATER 0) - message("Switching to C++11 standard, as version of JsonCpp is >= 1.0.0") - if (CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif() - endif() - else() - message("Unable to detect the major version of JsonCpp, assuming < 1.0.0") - endif() - # Look for Orthanc framework shared library include(CheckCXXSymbolExists) diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/CMake/DownloadPackage.cmake --- a/Resources/Orthanc/CMake/DownloadPackage.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/CMake/DownloadPackage.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/CMake/GoogleTestConfiguration.cmake --- a/Resources/Orthanc/CMake/GoogleTestConfiguration.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/CMake/GoogleTestConfiguration.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp --- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp Mon Feb 20 11:56:14 2023 +0100 @@ -2,7 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -249,26 +250,52 @@ } } + // helper class to convert std::map of headers to the plugin SDK C structure + class PluginHttpHeaders + { + std::vector headersKeys_; + std::vector headersValues_; + public: + + PluginHttpHeaders(const std::map& httpHeaders) + { + for (std::map::const_iterator + it = httpHeaders.begin(); it != httpHeaders.end(); it++) + { + headersKeys_.push_back(it->first.c_str()); + headersValues_.push_back(it->second.c_str()); + } + } + + const char* const* GetKeys() + { + return (headersKeys_.empty() ? NULL : &headersKeys_[0]); + } + + const char* const* GetValues() + { + return (headersValues_.empty() ? NULL : &headersValues_[0]); + } + + uint32_t GetSize() + { + return static_cast(headersKeys_.size()); + } + }; + bool MemoryBuffer::RestApiGet(const std::string& uri, const std::map& httpHeaders, bool applyPlugins) { Clear(); - std::vector headersKeys; - std::vector headersValues; - - for (std::map::const_iterator - it = httpHeaders.begin(); it != httpHeaders.end(); it++) - { - headersKeys.push_back(it->first.c_str()); - headersValues.push_back(it->second.c_str()); - } + PluginHttpHeaders headers(httpHeaders); return CheckHttp(OrthancPluginRestApiGet2( - GetGlobalContext(), &buffer_, uri.c_str(), httpHeaders.size(), - (headersKeys.empty() ? NULL : &headersKeys[0]), - (headersValues.empty() ? NULL : &headersValues[0]), applyPlugins)); + GetGlobalContext(), &buffer_, uri.c_str(), + headers.GetSize(), + headers.GetKeys(), + headers.GetValues(), applyPlugins)); } bool MemoryBuffer::RestApiPost(const std::string& uri, @@ -291,6 +318,41 @@ } } +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + + bool MemoryBuffer::RestApiPost(const std::string& uri, + const void* body, + size_t bodySize, + const std::map& httpHeaders, + bool applyPlugins) + { + MemoryBuffer answerHeaders; + uint16_t httpStatus; + + PluginHttpHeaders headers(httpHeaders); + + return CheckHttp(OrthancPluginCallRestApi(GetGlobalContext(), + &buffer_, + *answerHeaders, + &httpStatus, + OrthancPluginHttpMethod_Post, + uri.c_str(), + headers.GetSize(), headers.GetKeys(), headers.GetValues(), + body, bodySize, + applyPlugins)); + } + + + bool MemoryBuffer::RestApiPost(const std::string& uri, + const Json::Value& body, + const std::map& httpHeaders, + bool applyPlugins) + { + std::string s; + WriteFastJson(s, body); + return RestApiPost(uri, s.c_str(), s.size(), httpHeaders, applyPlugins); + } +#endif bool MemoryBuffer::RestApiPut(const std::string& uri, const void* body, @@ -510,6 +572,22 @@ } + void OrthancString::ToJsonWithoutComments(Json::Value& target) const + { + if (str_ == NULL) + { + LogError("Cannot convert an empty memory buffer to JSON"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + + if (!ReadJsonWithoutComments(target, str_)) + { + LogError("Cannot convert some memory buffer to JSON"); + ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); + } + } + + void MemoryBuffer::DicomToJson(Json::Value& target, OrthancPluginDicomToJsonFormat format, OrthancPluginDicomToJsonFlags flags, @@ -539,6 +617,13 @@ const std::string& password) { Clear(); + + if (body.size() > 0xffffffffu) + { + LogError("Cannot handle body size > 4GB"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + return CheckHttp(OrthancPluginHttpPost(GetGlobalContext(), &buffer_, url.c_str(), body.c_str(), body.size(), username.empty() ? NULL : username.c_str(), @@ -552,6 +637,13 @@ const std::string& password) { Clear(); + + if (body.size() > 0xffffffffu) + { + LogError("Cannot handle body size > 4GB"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + return CheckHttp(OrthancPluginHttpPut(GetGlobalContext(), &buffer_, url.c_str(), body.empty() ? NULL : body.c_str(), body.size(), @@ -630,7 +722,7 @@ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); } - str.ToJson(configuration_); + str.ToJsonWithoutComments(configuration_); if (configuration_.type() != Json::objectValue) { @@ -1426,6 +1518,30 @@ } } +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(Json::Value& result, + const std::string& uri, + const Json::Value& body, + const std::map& httpHeaders, + bool applyPlugins) + { + MemoryBuffer answer; + + if (!answer.RestApiPost(uri, body, httpHeaders, applyPlugins)) + { + return false; + } + else + { + if (!answer.IsEmpty()) + { + answer.ToJson(result); + } + return true; + } + } +#endif + bool RestApiPost(Json::Value& result, const std::string& uri, @@ -1515,24 +1631,18 @@ " is required)"); } - - bool CheckMinimalOrthancVersion(unsigned int major, - unsigned int minor, - unsigned int revision) + bool CheckMinimalVersion(const char* version, + unsigned int major, + unsigned int minor, + unsigned int revision) { - if (!HasGlobalContext()) - { - LogError("Bad Orthanc context in the plugin"); - return false; - } - - if (!strcmp(GetGlobalContext()->orthancVersion, "mainline")) + if (!strcmp(version, "mainline")) { // Assume compatibility with the mainline return true; } - // Parse the version of the Orthanc core + // Parse the version int aa, bb, cc; if ( #ifdef _MSC_VER @@ -1540,7 +1650,7 @@ #else sscanf #endif - (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || + (version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || aa < 0 || bb < 0 || cc < 0) @@ -1564,7 +1674,6 @@ return false; } - // Check the minor version number assert(a == major); @@ -1592,6 +1701,21 @@ } + bool CheckMinimalOrthancVersion(unsigned int major, + unsigned int minor, + unsigned int revision) + { + if (!HasGlobalContext()) + { + LogError("Bad Orthanc context in the plugin"); + return false; + } + + return CheckMinimalVersion(GetGlobalContext()->orthancVersion, + major, minor, revision); + } + + #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0) const char* AutodetectMimeType(const std::string& path) { @@ -1761,7 +1885,8 @@ bool OrthancPeers::DoGet(MemoryBuffer& target, size_t index, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { if (index >= index_.size()) { @@ -1770,10 +1895,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast(index), OrthancPluginHttpMethod_Get, uri.c_str(), - 0, NULL, NULL, NULL, 0, timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1789,21 +1916,23 @@ bool OrthancPeers::DoGet(MemoryBuffer& target, const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { size_t index; return (LookupName(index, name) && - DoGet(target, index, uri)); + DoGet(target, index, uri, headers)); } bool OrthancPeers::DoGet(Json::Value& target, size_t index, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { MemoryBuffer buffer; - if (DoGet(buffer, index, uri)) + if (DoGet(buffer, index, uri, headers)) { buffer.ToJson(target); return true; @@ -1817,11 +1946,12 @@ bool OrthancPeers::DoGet(Json::Value& target, const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { MemoryBuffer buffer; - if (DoGet(buffer, name, uri)) + if (DoGet(buffer, name, uri, headers)) { buffer.ToJson(target); return true; @@ -1836,22 +1966,24 @@ bool OrthancPeers::DoPost(MemoryBuffer& target, const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { size_t index; return (LookupName(index, name) && - DoPost(target, index, uri, body)); + DoPost(target, index, uri, body, headers)); } bool OrthancPeers::DoPost(Json::Value& target, size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { MemoryBuffer buffer; - if (DoPost(buffer, index, uri, body)) + if (DoPost(buffer, index, uri, body, headers)) { buffer.ToJson(target); return true; @@ -1866,11 +1998,12 @@ bool OrthancPeers::DoPost(Json::Value& target, const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { MemoryBuffer buffer; - if (DoPost(buffer, name, uri, body)) + if (DoPost(buffer, name, uri, body, headers)) { buffer.ToJson(target); return true; @@ -1885,19 +2018,28 @@ bool OrthancPeers::DoPost(MemoryBuffer& target, size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { if (index >= index_.size()) { ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange); } + if (body.size() > 0xffffffffu) + { + LogError("Cannot handle body size > 4GB"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast(index), OrthancPluginHttpMethod_Post, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1913,19 +2055,28 @@ bool OrthancPeers::DoPut(size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { if (index >= index_.size()) { ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange); } + if (body.size() > 0xffffffffu) + { + LogError("Cannot handle body size > 4GB"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast(index), OrthancPluginHttpMethod_Put, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1940,16 +2091,18 @@ bool OrthancPeers::DoPut(const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map& headers) const { size_t index; return (LookupName(index, name) && - DoPut(index, uri, body)); + DoPut(index, uri, body, headers)); } bool OrthancPeers::DoDelete(size_t index, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { if (index >= index_.size()) { @@ -1958,10 +2111,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast(index), OrthancPluginHttpMethod_Delete, uri.c_str(), - 0, NULL, NULL, NULL, 0, timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1975,11 +2130,12 @@ bool OrthancPeers::DoDelete(const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map& headers) const { size_t index; return (LookupName(index, name) && - DoDelete(index, uri)); + DoDelete(index, uri, headers)); } #endif @@ -2016,6 +2172,36 @@ } +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static OrthancPluginErrorCode CopyStringToMemoryBuffer(OrthancPluginMemoryBuffer* target, + const std::string& source) + { + if (OrthancPluginCreateMemoryBuffer(globalContext_, target, source.size()) != OrthancPluginErrorCode_Success) + { + return OrthancPluginErrorCode_NotEnoughMemory; + } + else + { + if (!source.empty()) + { + memcpy(target->data, source.c_str(), source.size()); + } + + return OrthancPluginErrorCode_Success; + } + } +#endif + + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + OrthancPluginErrorCode OrthancJob::CallbackGetContent(OrthancPluginMemoryBuffer* target, + void* job) + { + assert(job != NULL); + OrthancJob& that = *reinterpret_cast(job); + return CopyStringToMemoryBuffer(target, that.content_); + } +#else const char* OrthancJob::CallbackGetContent(void* job) { assert(job != NULL); @@ -2029,8 +2215,33 @@ return 0; } } - - +#endif + + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + int32_t OrthancJob::CallbackGetSerialized(OrthancPluginMemoryBuffer* target, + void* job) + { + assert(job != NULL); + OrthancJob& that = *reinterpret_cast(job); + + if (that.hasSerialized_) + { + if (CopyStringToMemoryBuffer(target, that.serialized_) == OrthancPluginErrorCode_Success) + { + return 1; + } + else + { + return -1; + } + } + else + { + return 0; + } + } +#else const char* OrthancJob::CallbackGetSerialized(void* job) { assert(job != NULL); @@ -2053,6 +2264,7 @@ return 0; } } +#endif OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job) @@ -2184,10 +2396,15 @@ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer); } - OrthancPluginJob* orthanc = OrthancPluginCreateJob( - GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), - CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, - CallbackStep, CallbackStop, CallbackReset); + OrthancPluginJob* orthanc = +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + OrthancPluginCreateJob2 +#else + OrthancPluginCreateJob +#endif + (GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), + CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, + CallbackStep, CallbackStop, CallbackReset); if (orthanc == NULL) { @@ -2483,7 +2700,7 @@ } catch (...) { - return OrthancPluginErrorCode_InternalError; + return OrthancPluginErrorCode_Plugin; } } } @@ -2569,8 +2786,8 @@ void HttpClient::ClearCredentials() { - username_.empty(); - password_.empty(); + username_.clear(); + password_.clear(); } @@ -2883,6 +3100,12 @@ MemoryBuffer answerBodyBuffer, answerHeadersBuffer; + if (body.size() > 0xffffffffu) + { + LogError("Cannot handle body size > 4GB"); + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + OrthancPluginErrorCode error = OrthancPluginHttpClient( GetGlobalContext(), *answerBodyBuffer, @@ -3499,4 +3722,254 @@ } } #endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static std::vector WebDavConvertPath(uint32_t pathSize, + const char* const* pathItems) + { + std::vector result(pathSize); + + for (uint32_t i = 0; i < pathSize; i++) + { + result[i] = pathItems[i]; + } + + return result; + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavIsExistingFolder(uint8_t* isExisting, + uint32_t pathSize, + const char* const* pathItems, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + *isExisting = (that.IsExistingFolder(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavListFolder(uint8_t* isExisting, + OrthancPluginWebDavCollection* collection, + OrthancPluginWebDavAddFile addFile, + OrthancPluginWebDavAddFolder addFolder, + uint32_t pathSize, + const char* const* pathItems, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + std::list files; + std::list subfolders; + + if (!that.ListFolder(files, subfolders, WebDavConvertPath(pathSize, pathItems))) + { + *isExisting = 0; + } + else + { + *isExisting = 1; + + for (std::list::const_iterator + it = files.begin(); it != files.end(); ++it) + { + OrthancPluginErrorCode code = addFile( + collection, it->GetName().c_str(), it->GetContentSize(), + it->GetMimeType().c_str(), it->GetDateTime().c_str()); + + if (code != OrthancPluginErrorCode_Success) + { + return code; + } + } + + for (std::list::const_iterator it = + subfolders.begin(); it != subfolders.end(); ++it) + { + OrthancPluginErrorCode code = addFolder( + collection, it->GetName().c_str(), it->GetDateTime().c_str()); + + if (code != OrthancPluginErrorCode_Success) + { + return code; + } + } + } + + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavRetrieveFile(OrthancPluginWebDavCollection* collection, + OrthancPluginWebDavRetrieveFile retrieveFile, + uint32_t pathSize, + const char* const* pathItems, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + std::string content, mime, dateTime; + + if (that.GetFile(content, mime, dateTime, WebDavConvertPath(pathSize, pathItems))) + { + return retrieveFile(collection, content.empty() ? NULL : content.c_str(), + content.size(), mime.c_str(), dateTime.c_str()); + } + else + { + // Inexisting file + return OrthancPluginErrorCode_Success; + } + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_InternalError; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavStoreFileCallback(uint8_t* isReadOnly, /* out */ + uint32_t pathSize, + const char* const* pathItems, + const void* data, + uint64_t size, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + if (static_cast(static_cast(size)) != size) + { + ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory); + } + + *isReadOnly = (that.StoreFile(WebDavConvertPath(pathSize, pathItems), data, + static_cast(size)) ? 1 : 0); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_InternalError; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavCreateFolderCallback(uint8_t* isReadOnly, /* out */ + uint32_t pathSize, + const char* const* pathItems, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + *isReadOnly = (that.CreateFolder(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_InternalError; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + static OrthancPluginErrorCode WebDavDeleteItemCallback(uint8_t* isReadOnly, /* out */ + uint32_t pathSize, + const char* const* pathItems, + void* payload) + { + IWebDavCollection& that = *reinterpret_cast(payload); + + try + { + *isReadOnly = (that.DeleteItem(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_InternalError; + } + } +#endif + + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + void IWebDavCollection::Register(const std::string& uri, + IWebDavCollection& collection) + { + OrthancPluginErrorCode code = OrthancPluginRegisterWebDavCollection( + GetGlobalContext(), uri.c_str(), WebDavIsExistingFolder, WebDavListFolder, WebDavRetrieveFile, + WebDavStoreFileCallback, WebDavCreateFolderCallback, WebDavDeleteItemCallback, &collection); + + if (code != OrthancPluginErrorCode_Success) + { + ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code); + } + } +#endif + + void GetHttpHeaders(std::map& result, const OrthancPluginHttpRequest* request) + { + result.clear(); + + for (uint32_t i = 0; i < request->headersCount; ++i) + { + result[request->headersKeys[i]] = request->headersValues[i]; + } + } } diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h --- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h Mon Feb 20 11:56:14 2023 +0100 @@ -2,7 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -114,6 +115,18 @@ # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 0 #endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) +# define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 1 +#else +# define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 0 +#endif + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 1) +# define HAS_ORTHANC_PLUGIN_WEBDAV 1 +#else +# define HAS_ORTHANC_PLUGIN_WEBDAV 0 +#endif + namespace OrthancPlugins @@ -217,6 +230,19 @@ const Json::Value& body, bool applyPlugins); +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(const std::string& uri, + const Json::Value& body, + const std::map& httpHeaders, + bool applyPlugins); + + bool RestApiPost(const std::string& uri, + const void* body, + size_t bodySize, + const std::map& httpHeaders, + bool applyPlugins); +#endif + bool RestApiPut(const std::string& uri, const Json::Value& body, bool applyPlugins); @@ -296,10 +322,17 @@ return str_; } + bool IsNullOrEmpty() const + { + return str_ == NULL || str_[0] == 0; + } + void ToString(std::string& target) const; void ToJson(Json::Value& target) const; - }; + + void ToJsonWithoutComments(Json::Value& target) const; +}; class OrthancConfiguration : public boost::noncopyable @@ -521,6 +554,14 @@ size_t bodySize, bool applyPlugins); +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(Json::Value& result, + const std::string& uri, + const Json::Value& body, + const std::map& httpHeaders, + bool applyPlugins); +#endif + bool RestApiPost(Json::Value& result, const std::string& uri, const Json::Value& body, @@ -601,6 +642,10 @@ unsigned int minor, unsigned int revision); + bool CheckMinimalVersion(const char* version, + unsigned int major, + unsigned int minor, + unsigned int revision); namespace Internals { @@ -710,53 +755,65 @@ bool DoGet(MemoryBuffer& target, size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; bool DoGet(MemoryBuffer& target, const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; bool DoGet(Json::Value& target, size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; bool DoGet(Json::Value& target, const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; bool DoPost(MemoryBuffer& target, size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoPost(MemoryBuffer& target, const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoPost(Json::Value& target, size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoPost(Json::Value& target, const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoPut(size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoPut(const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map& headers) const; bool DoDelete(size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; bool DoDelete(const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map& headers) const; }; #endif @@ -776,9 +833,19 @@ static float CallbackGetProgress(void* job); +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static OrthancPluginErrorCode CallbackGetContent(OrthancPluginMemoryBuffer* target, + void* job); +#else static const char* CallbackGetContent(void* job); +#endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static int32_t CallbackGetSerialized(OrthancPluginMemoryBuffer* target, + void* job); +#else static const char* CallbackGetSerialized(void* job); +#endif static OrthancPluginJobStepStatus CallbackStep(void* job); @@ -1245,4 +1312,108 @@ const std::string& transferSyntax); #endif }; + +// helper method to convert Http headers from the plugin SDK to a std::map +void GetHttpHeaders(std::map& result, const OrthancPluginHttpRequest* request); + +#if HAS_ORTHANC_PLUGIN_WEBDAV == 1 + class IWebDavCollection : public boost::noncopyable + { + public: + class FileInfo + { + private: + std::string name_; + uint64_t contentSize_; + std::string mime_; + std::string dateTime_; + + public: + FileInfo(const std::string& name, + uint64_t contentSize, + const std::string& dateTime) : + name_(name), + contentSize_(contentSize), + dateTime_(dateTime) + { + } + + const std::string& GetName() const + { + return name_; + } + + uint64_t GetContentSize() const + { + return contentSize_; + } + + void SetMimeType(const std::string& mime) + { + mime_ = mime; + } + + const std::string& GetMimeType() const + { + return mime_; + } + + const std::string& GetDateTime() const + { + return dateTime_; + } + }; + + class FolderInfo + { + private: + std::string name_; + std::string dateTime_; + + public: + FolderInfo(const std::string& name, + const std::string& dateTime) : + name_(name), + dateTime_(dateTime) + { + } + + const std::string& GetName() const + { + return name_; + } + + const std::string& GetDateTime() const + { + return dateTime_; + } + }; + + virtual ~IWebDavCollection() + { + } + + virtual bool IsExistingFolder(const std::vector& path) = 0; + + virtual bool ListFolder(std::list& files, + std::list& subfolders, + const std::vector& path) = 0; + + virtual bool GetFile(std::string& content /* out */, + std::string& mime /* out */, + std::string& dateTime /* out */, + const std::vector& path) = 0; + + virtual bool StoreFile(const std::vector& path, + const void* data, + size_t size) = 0; + + virtual bool CreateFolder(const std::vector& path) = 0; + + virtual bool DeleteItem(const std::vector& path) = 0; + + static void Register(const std::string& uri, + IWebDavCollection& collection); + }; +#endif } diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/Plugins/OrthancPluginException.h --- a/Resources/Orthanc/Plugins/OrthancPluginException.h Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginException.h Mon Feb 20 11:56:14 2023 +0100 @@ -2,7 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/Orthanc/Plugins/OrthancPluginsExports.cmake --- a/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake Mon Feb 20 11:56:14 2023 +0100 @@ -1,7 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2017-2022 Osimis S.A., Belgium +# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as diff -r 1a13c4fbc9a1 -r af44dce56328 Resources/SyncOrthancFolder.py --- a/Resources/SyncOrthancFolder.py Fri Feb 17 15:05:02 2023 +0100 +++ b/Resources/SyncOrthancFolder.py Mon Feb 20 11:56:14 2023 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # This maintenance script updates the content of the "Orthanc" folder @@ -8,10 +8,10 @@ import multiprocessing import os import stat -import urllib2 +import urllib.request TARGET = os.path.join(os.path.dirname(__file__), 'Orthanc') -PLUGIN_SDK_VERSION = '1.3.1' +PLUGIN_SDK_VERSION = '1.11.3' REPOSITORY = 'https://hg.orthanc-server.com/orthanc/raw-file' FILES = [ @@ -20,10 +20,11 @@ ('OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake', 'CMake'), ('OrthancFramework/Resources/CMake/DownloadPackage.cmake', 'CMake'), ('OrthancFramework/Resources/CMake/GoogleTestConfiguration.cmake', 'CMake'), - ('OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake', '.'), - ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake', '.'), - ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain64.cmake', '.'), - ('OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake', '.'), + ('OrthancFramework/Resources/EmbedResources.py', 'CMake'), + ('OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake', 'Toolchains'), + ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake', 'Toolchains'), + ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain64.cmake', 'Toolchains'), + ('OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake', 'Toolchains'), ('OrthancServer/Plugins/Samples/Common/ExportedSymbolsPlugins.list', 'Plugins'), ('OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp', 'Plugins'), ('OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h', 'Plugins'), @@ -41,7 +42,7 @@ branch = x[0] source = x[1] target = os.path.join(TARGET, x[2]) - print target + print(target) try: os.makedirs(os.path.dirname(target)) @@ -50,8 +51,12 @@ url = '%s/%s/%s' % (REPOSITORY, branch, source) - with open(target, 'w') as f: - f.write(urllib2.urlopen(url).read()) + with open(target, 'wb') as f: + try: + f.write(urllib.request.urlopen(url).read()) + except: + print('ERROR %s' % url) + raise commands = [] @@ -64,7 +69,7 @@ for f in SDK: commands.append([ 'Orthanc-%s' % PLUGIN_SDK_VERSION, - 'Plugins/Include/%s' % f, + 'OrthancServer/Plugins/Include/%s' % f, 'Sdk-%s/%s' % (PLUGIN_SDK_VERSION, f) ])