# HG changeset patch # User Alain Mazy # Date 1639518779 -3600 # Node ID abb0e1c2b88d8dae82ac915c055a116b4fe0923a # Parent cab8d689a9a1f291e0e7de1f941aa8ded02d5fb1# Parent 5ec96ada5a539224634d5618df461f919c64e055 merge received-instance-callback diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -734,10 +734,15 @@ if (!(flags & DicomToJsonFlags_ConvertBinaryToNull)) { Uint8* data = NULL; + Uint16* data16 = NULL; if (element.getUint8Array(data) == EC_Normal) { return new DicomValue(reinterpret_cast(data), element.getLength(), true); } + else if (element.getUint16Array(data16) == EC_Normal) + { + return new DicomValue(reinterpret_cast(data16), element.getLength(), true); + } } return new DicomValue; @@ -1851,6 +1856,23 @@ break; } + case EVR_xs: // unsigned short, signed short or multiple values + { + if (decoded->find('\\') != std::string::npos) + { + ok = element.putString(decoded->c_str()).good(); + } + else if (decoded->find('-') != std::string::npos) + { + ok = element.putSint16(boost::lexical_cast(*decoded)).good(); + } + else + { + ok = element.putUint16(boost::lexical_cast(*decoded)).good(); + } + break; + } + case EVR_US: // unsigned short { ok = element.putUint16(boost::lexical_cast(*decoded)).good(); @@ -1902,7 +1924,6 @@ **/ case EVR_ox: // OB or OW depending on context - case EVR_xs: // SS or US depending on context case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) case EVR_na: // na="not applicable", for data which has no VR case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp --- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -1195,6 +1195,10 @@ EmbedImage(mime, content); break; + case MimeType_Binary: + EmbedImage(mime, content); + break; + case MimeType_Pdf: EmbedPdf(content); break; @@ -1254,6 +1258,12 @@ break; } + case MimeType_Binary: + { + EmbedRawPixelData(content); + break; + } + default: throw OrthancException(ErrorCode_NotImplemented); } @@ -1407,7 +1417,24 @@ } } - + void ParsedDicomFile::EmbedRawPixelData(const std::string& content) + { + DcmTag key(DICOM_TAG_PIXEL_DATA.GetGroup(), + DICOM_TAG_PIXEL_DATA.GetElement()); + + std::unique_ptr pixels(new DcmPixelData(key)); + + Uint8* target = NULL; + pixels->createUint8Array(content.size(), target); + memcpy(target, content.c_str(), content.size()); + + if (!GetDcmtkObject().getDataset()->insert(pixels.release(), false, false).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + } + + Encoding ParsedDicomFile::DetectEncoding(bool& hasCodeExtensions) const { return FromDcmtkBridge::DetectEncoding(hasCodeExtensions, diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h --- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Tue Dec 14 22:52:59 2021 +0100 @@ -212,6 +212,8 @@ void EmbedImage(MimeType mime, const std::string& content); + void EmbedRawPixelData(const std::string& content); + Encoding DetectEncoding(bool& hasCodeExtensions) const; // WARNING: This function only sets the encoding, it will not diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancFramework/UnitTestsSources/DicomMapTests.cpp --- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -756,6 +756,15 @@ ASSERT_FALSE(d > d); } +TEST(ParsedDicomFile, canIncludeXsVrTags) +{ + Json::Value tags; + tags["0028,0034"] = "1\\1"; // PixelAspectRatio + tags["0028,1101"] = "256\\0\\16"; // RedPaletteColorLookupTableDescriptor which is declared as xs VR in dicom.dic + + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(tags, DicomFromJsonFlags_DecodeDataUriScheme, "")); + // simply make sure it does not throw ! +} #if ORTHANC_SANDBOXED != 1 diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Plugins/Engine/OrthancPlugins.cpp --- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -1168,6 +1168,7 @@ typedef std::list IncomingHttpRequestFilters2; typedef std::list IncomingDicomInstanceFilters; typedef std::list IncomingCStoreInstanceFilters; + typedef std::list ReceivedInstanceCallbacks; typedef std::list DecodeImageCallbacks; typedef std::list TranscoderCallbacks; typedef std::list JobsUnserializers; @@ -1191,6 +1192,7 @@ IncomingHttpRequestFilters2 incomingHttpRequestFilters2_; IncomingDicomInstanceFilters incomingDicomInstanceFilters_; IncomingCStoreInstanceFilters incomingCStoreInstanceFilters_; // New in Orthanc 1.9.8 + ReceivedInstanceCallbacks receivedInstanceCallbacks_; // New in Orthanc 1.9.8 RefreshMetricsCallbacks refreshMetricsCallbacks_; StorageCommitmentScpCallbacks storageCommitmentScpCallbacks_; std::unique_ptr storageArea_; @@ -2295,6 +2297,74 @@ } + bool OrthancPlugins::ApplyReceivedInstanceCallbacks(const void* receivedDicom, + size_t receivedDicomSize, + void** modifiedDicomBufferData, + size_t& modifiedDicomBufferSize) + { + uint64_t modifiedDicomSize64 = 0; + *modifiedDicomBufferData = NULL; + + boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); + + for (PImpl::ReceivedInstanceCallbacks::const_iterator + callback = pimpl_->receivedInstanceCallbacks_.begin(); + callback != pimpl_->receivedInstanceCallbacks_.end(); ++callback) + { + OrthancPluginReceivedInstanceCallbackResult callbackResult = (*callback) (receivedDicom, + receivedDicomSize, + modifiedDicomBufferData, + &modifiedDicomSize64); + + if (callbackResult == OrthancPluginReceivedInstanceCallbackResult_Discard) + { + if (modifiedDicomSize64 > 0 || *modifiedDicomBufferData != NULL) + { + free(modifiedDicomBufferData); + throw OrthancException(ErrorCode_Plugin, "The ReceivedInstanceCallback plugin is returning a modified buffer while it has discarded the instance"); + } + + CLOG(INFO, PLUGINS) << "A plugin has discarded the instance in its ReceivedInstanceCallback"; + return false; + } + else if (callbackResult == OrthancPluginReceivedInstanceCallbackResult_KeepAsIs) + { + if (modifiedDicomSize64 > 0 || *modifiedDicomBufferData != NULL) + { + free(modifiedDicomBufferData); + throw OrthancException(ErrorCode_Plugin, "The ReceivedInstanceCallback plugin is returning a modified buffer while it has not modified the instance"); + } + return true; + } + else if (callbackResult == OrthancPluginReceivedInstanceCallbackResult_Modified) + { + if (modifiedDicomSize64 > 0 && modifiedDicomBufferData != NULL) + { + if (static_cast(modifiedDicomSize64) != modifiedDicomSize64) // Orthanc is running in 32bits and has received a > 4GB buffer + { + free(modifiedDicomBufferData); + throw OrthancException(ErrorCode_Plugin, "The Plugin has returned a > 4GB which is too large for Orthanc running in 32bits"); + } + + modifiedDicomBufferSize = static_cast(modifiedDicomSize64); + + CLOG(INFO, PLUGINS) << "A plugin has modified the instance in its ReceivedInstanceCallback"; + return true; + } + else + { + throw OrthancException(ErrorCode_Plugin, "The ReceivedInstanceCallback plugin is not returning a modified buffer while it has modified the instance"); + } + } + else + { + throw OrthancException(ErrorCode_Plugin, "The ReceivedInstanceCallback has returned an invalid value"); + } + } + + return STATUS_Success; + } + void OrthancPlugins::SignalChangeInternal(OrthancPluginChangeType changeType, OrthancPluginResourceType resourceType, const char* resource) @@ -2521,6 +2591,14 @@ pimpl_->incomingCStoreInstanceFilters_.push_back(p.callback); } + void OrthancPlugins::RegisterReceivedInstanceCallback(const void* parameters) + { + const _OrthancPluginReceivedInstanceCallback& p = + *reinterpret_cast(parameters); + + CLOG(INFO, PLUGINS) << "Plugin has registered a received instance callback"; + pimpl_->receivedInstanceCallbacks_.push_back(p.callback); + } void OrthancPlugins::RegisterRefreshMetricsCallback(const void* parameters) { @@ -5004,6 +5082,10 @@ RegisterIncomingCStoreInstanceFilter(parameters); return true; + case _OrthancPluginService_RegisterReceivedInstanceCallback: + RegisterReceivedInstanceCallback(parameters); + return true; + case _OrthancPluginService_RegisterRefreshMetricsCallback: RegisterRefreshMetricsCallback(parameters); return true; diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Plugins/Engine/OrthancPlugins.h --- a/OrthancServer/Plugins/Engine/OrthancPlugins.h Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h Tue Dec 14 22:52:59 2021 +0100 @@ -136,6 +136,8 @@ void RegisterIncomingCStoreInstanceFilter(const void* parameters); + void RegisterReceivedInstanceCallback(const void* parameters); + void RegisterRefreshMetricsCallback(const void* parameters); void RegisterStorageCommitmentScpCallback(const void* parameters); @@ -285,6 +287,11 @@ virtual uint16_t FilterIncomingCStoreInstance(const DicomInstanceToStore& instance, const Json::Value& simplified) ORTHANC_OVERRIDE; + virtual bool ApplyReceivedInstanceCallbacks(const void* receivedDicomBuffer, + size_t receivedDicomBufferSize, + void** modifiedDicomBufferData, + size_t& modifiedDicomBufferSize); + bool HasStorageArea() const; IStorageArea* CreateStorageArea(); // To be freed after use diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h --- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Dec 14 22:52:59 2021 +0100 @@ -463,6 +463,7 @@ _OrthancPluginService_RegisterTranscoderCallback = 1015, /* New in Orthanc 1.7.0 */ _OrthancPluginService_RegisterStorageArea2 = 1016, /* New in Orthanc 1.9.0 */ _OrthancPluginService_RegisterIncomingCStoreInstanceFilter = 1017, /* New in Orthanc 1.9.8 */ + _OrthancPluginService_RegisterReceivedInstanceCallback = 1018, /* New in Orthanc 1.9.8 */ /* Sending answers to REST calls */ _OrthancPluginService_AnswerBuffer = 2000, @@ -1001,7 +1002,19 @@ is already in use */ } OrthancPluginStorageCommitmentFailureReason; - + + /** + * The return value of ReceivedInstanceCallback + **/ + typedef enum + { + OrthancPluginReceivedInstanceCallbackResult_KeepAsIs = 1, /*!< Keep the instance as is */ + OrthancPluginReceivedInstanceCallbackResult_Modified = 2, /*!< Modified the instance */ + OrthancPluginReceivedInstanceCallbackResult_Discard = 3, /*!< Tell Orthanc to discard the instance */ + + _OrthancPluginReceivedInstanceCallbackResult_INTERNAL = 0x7fffffff + } OrthancPluginReceivedInstanceCallbackResult; + /** * @brief A 32-bit memory buffer allocated by the core system of Orthanc. @@ -7823,6 +7836,73 @@ } /** + * @brief Callback to possibly modify a DICOM instance received + * by Orthanc through any source (C-Store or Rest API) + * + * Signature of a callback function that is triggered whenever + * Orthanc receives a new DICOM instance (through DICOM protocol or + * Rest API), and that answers a possibly modified version of the + * DICOM that should be stored in Orthanc. + * + * This callback is called immediately after receiption: before + * transcoding and before filtering (FilterIncomingInstance). + * + * @param receivedDicomBuffer A buffer containing the received DICOM (input). + * @param receivedDicomBufferSize The size of the received DICOM (input) + * @param modifiedDicomBuffer A buffer containing the modified DICOM (output). + * This buffer will be freed by the Orthanc Core and must have + * been allocated by malloc in your plugin or by Orthanc core through + * a plugin method. + * @param modifiedDicomBufferSize The size of the modified DICOM (output) + * @return OrthancPluginReceivedInstanceCallbackResult_KeepAsIs to accept the instance as is + * OrthancPluginReceivedInstanceCallbackResult_Modified to store the modified DICOM + * OrthancPluginReceivedInstanceCallbackResult_Discard to tell Orthanc to discard the instance + * @ingroup Callback + **/ + typedef OrthancPluginReceivedInstanceCallbackResult (*OrthancPluginReceivedInstanceCallback) ( + const void* receivedDicomBuffer, + uint64_t receivedDicomBufferSize, + void** modifiedDicomBuffer, + uint64_t* modifiedDicomBufferSize + ); + + + typedef struct + { + OrthancPluginReceivedInstanceCallback callback; + } _OrthancPluginReceivedInstanceCallback; + + /** + * @brief Register a callback to possibly modify a DICOM instance received + * by Orthanc through any source (C-Store or Rest API) + * + * + * @warning Your callback function will be called synchronously with + * the core of Orthanc. This implies that deadlocks might emerge if + * you call other core primitives of Orthanc in your callback (such + * deadlocks are particular visible in the presence of other plugins + * or Lua scripts). It is thus strongly advised to avoid any call to + * the REST API of Orthanc in the callback. If you have to call + * other primitives of Orthanc, you should make these calls in a + * separate thread, passing the pending events to be processed + * through a message queue. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback. + * @return 0 if success, other value if error. + * @ingroup Callbacks + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterReceivedInstanceCallback( + OrthancPluginContext* context, + OrthancPluginReceivedInstanceCallback callback) + { + _OrthancPluginReceivedInstanceCallback params; + params.callback = callback; + + return context->InvokeService(context, _OrthancPluginService_RegisterReceivedInstanceCallback, ¶ms); + } + + /** * @brief Get the transfer syntax of a DICOM file. * * This function returns a pointer to a newly created string that diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Plugins/Samples/Sanitizer/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Samples/Sanitizer/CMakeLists.txt Tue Dec 14 22:52:59 2021 +0100 @@ -0,0 +1,59 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2021 Osimis S.A., Belgium +# Copyright (C) 2021-2021 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 +# 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +cmake_minimum_required(VERSION 2.8) + +project(Sanitizer) + +SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") +SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") + +SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp") +SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of boost") +SET(ORTHANC_FRAMEWORK_SOURCE path) +SET(ORTHANC_FRAMEWORK_ROOT ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources) + +include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake) +include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/JsonCppConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/BoostConfiguration.cmake) + +include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake) +include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkParameters.cmake) +set(ENABLE_LOCALE ON) +set(ENABLE_DCMTK ON) +include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkConfiguration.cmake) +include_directories(${ORTHANC_FRAMEWORK_ROOT}) + + +add_library(Sanitizer SHARED + ${CMAKE_SOURCE_DIR}/../Common/OrthancPluginCppWrapper.cpp + ${JSONCPP_SOURCES} + ${BOOST_SOURCES} + Plugin.cpp + ${ORTHANC_CORE_SOURCES} + ${ORTHANC_DICOM_SOURCES} + ) + + +install( + TARGETS Sanitizer + RUNTIME DESTINATION lib # Destination for Windows + LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux + ) diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Plugins/Samples/Sanitizer/Plugin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Samples/Sanitizer/Plugin.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -0,0 +1,100 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * Copyright (C) 2021-2021 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 + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../../../../OrthancFramework/Sources/Compatibility.h" +#include "../../../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" +#include "../../../../OrthancFramework/Sources/OrthancFramework.h" +#include "../Common/OrthancPluginCppWrapper.h" + +#include +#include +#include +#include + + + + +OrthancPluginReceivedInstanceCallbackResult ReceivedInstanceCallback(const void* receivedDicomBuffer, + uint64_t receivedDicomBufferSize, + void** modifiedDicomBuffer, + uint64_t* modifiedDicomBufferSize) +{ + Orthanc::ParsedDicomFile dicom(receivedDicomBuffer, receivedDicomBufferSize); + std::string institutionName = "My institution"; + + dicom.Replace(Orthanc::DICOM_TAG_INSTITUTION_NAME, institutionName, false, Orthanc::DicomReplaceMode_InsertIfAbsent, ""); + + std::string modifiedDicom; + dicom.SaveToMemoryBuffer(modifiedDicom); + + *modifiedDicomBuffer = malloc(modifiedDicom.size()); + *modifiedDicomBufferSize = modifiedDicom.size(); + memcpy(*modifiedDicomBuffer, modifiedDicom.c_str(), modifiedDicom.size()); + + return OrthancPluginReceivedInstanceCallbackResult_Modified; +} + + +extern "C" +{ + ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) + { + OrthancPlugins::SetGlobalContext(c); + + Orthanc::InitializeFramework("", true); + + /* Check the version of the Orthanc core */ + // if (OrthancPluginCheckVersion(c) == 0) + // { + // OrthancPlugins::ReportMinimalOrthancVersion(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, + // ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, + // ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); + // return -1; + // } + + OrthancPlugins::LogWarning("Sanitizer plugin is initializing"); + OrthancPluginSetDescription(c, "Sample plugin to sanitize incoming DICOM instances."); + + OrthancPluginRegisterReceivedInstanceCallback(c, ReceivedInstanceCallback); + + return 0; + } + + + ORTHANC_PLUGINS_API void OrthancPluginFinalize() + { + OrthancPlugins::LogWarning("Sanitizer plugin is finalizing"); + Orthanc::FinalizeFramework(); + } + + + ORTHANC_PLUGINS_API const char* OrthancPluginGetName() + { + return "sanitizer"; + } + + + ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() + { + return "0.1"; + } +} diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Tue Dec 14 22:52:59 2021 +0100 @@ -47,6 +47,7 @@ #include "../../OrthancFramework/Sources/HttpServer/HttpStreamTranscoder.h" #include "../../OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h" #include "../../OrthancFramework/Sources/Logging.h" +#include "../../OrthancFramework/Sources/MallocMemoryBuffer.h" #include "../../OrthancFramework/Sources/MetricsRegistry.h" #include "../Plugins/Engine/OrthancPlugins.h" @@ -694,13 +695,47 @@ ServerContext::StoreResult ServerContext::Store(std::string& resultPublicId, - DicomInstanceToStore& dicom, + DicomInstanceToStore& receivedDicom, StoreInstanceMode mode) - { + { + DicomInstanceToStore* dicom = &receivedDicom; + std::unique_ptr modifiedDicom; + + void* modifiedDicomBuffer = NULL; + size_t modifiedDicomBufferSize = 0; + + std::unique_ptr raii(new MallocMemoryBuffer); + +#if ORTHANC_ENABLE_PLUGINS == 1 + if (HasPlugins()) + { + + bool store = GetPlugins().ApplyReceivedInstanceCallbacks(receivedDicom.GetBufferData(), + receivedDicom.GetBufferSize(), + &modifiedDicomBuffer, + modifiedDicomBufferSize); + raii->Assign(modifiedDicomBuffer, modifiedDicomBufferSize, ::free); + + if (!store) + { + StoreResult result; + result.SetStatus(StoreStatus_FilteredOut); + return result; + } + + if (modifiedDicomBufferSize > 0 && modifiedDicomBuffer != NULL) + { + modifiedDicom.reset(DicomInstanceToStore::CreateFromBuffer(modifiedDicomBuffer, modifiedDicomBufferSize)); + modifiedDicom->SetOrigin(dicom->GetOrigin()); + dicom = modifiedDicom.get(); + } + } +#endif + if (!isIngestTranscoding_) { // No automated transcoding. This was the only path in Orthanc <= 1.6.1. - return StoreAfterTranscoding(resultPublicId, dicom, mode); + return StoreAfterTranscoding(resultPublicId, *dicom, mode); } else { @@ -709,7 +744,7 @@ bool transcode = false; DicomTransferSyntax sourceSyntax; - if (!dicom.LookupTransferSyntax(sourceSyntax) || + if (!dicom->LookupTransferSyntax(sourceSyntax) || sourceSyntax == ingestTransferSyntax_) { // Don't transcode if the incoming DICOM is already in the proper transfer syntax @@ -736,7 +771,7 @@ if (!transcode) { // No transcoding - return StoreAfterTranscoding(resultPublicId, dicom, mode); + return StoreAfterTranscoding(resultPublicId, *dicom, mode); } else { @@ -745,7 +780,7 @@ syntaxes.insert(ingestTransferSyntax_); IDicomTranscoder::DicomImage source; - source.SetExternalBuffer(dicom.GetBufferData(), dicom.GetBufferSize()); + source.SetExternalBuffer(dicom->GetBufferData(), dicom->GetBufferSize()); IDicomTranscoder::DicomImage transcoded; if (Transcode(transcoded, source, syntaxes, true /* allow new SOP instance UID */)) @@ -753,7 +788,7 @@ std::unique_ptr tmp(transcoded.ReleaseAsParsedDicomFile()); std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp)); - toStore->SetOrigin(dicom.GetOrigin()); + toStore->SetOrigin(dicom->GetOrigin()); StoreResult result = StoreAfterTranscoding(resultPublicId, *toStore, mode); assert(resultPublicId == tmp->GetHasher().HashInstance()); @@ -763,7 +798,7 @@ else { // Cannot transcode => store the original file - return StoreAfterTranscoding(resultPublicId, dicom, mode); + return StoreAfterTranscoding(resultPublicId, *dicom, mode); } } } diff -r cab8d689a9a1 -r abb0e1c2b88d OrthancServer/Sources/ServerEnumerations.h --- a/OrthancServer/Sources/ServerEnumerations.h Sat Dec 11 16:00:38 2021 +0100 +++ b/OrthancServer/Sources/ServerEnumerations.h Tue Dec 14 22:52:59 2021 +0100 @@ -61,7 +61,7 @@ StoreStatus_Success, StoreStatus_AlreadyStored, StoreStatus_Failure, - StoreStatus_FilteredOut // Removed by NewInstanceFilter + StoreStatus_FilteredOut // Removed by NewInstanceFilter or ReceivedInstanceCallback }; enum DicomTagType