# HG changeset patch # User Alain Mazy # Date 1703171580 -3600 # Node ID 4b51cf06b69706c5ce2fa6ded8887466a0f6c35e # Parent dceed5e3d6a9226eaf9d366b3c46f291a7bb2bce# Parent f7ac06300f862f173c9d777dd9de184552870002 merge mainline -> pg-transactions diff -r dceed5e3d6a9 -r 4b51cf06b697 NEWS --- a/NEWS Fri Dec 15 17:15:43 2023 +0100 +++ b/NEWS Thu Dec 21 16:13:00 2023 +0100 @@ -1,73 +1,67 @@ Pending changes in the mainline =============================== + +Version 1.12.2 (2023-12-19) +=========================== + General ------- * Performance: - Allow multiple plugins to use the plugin SDK at the same time. In previous versions, functions like instance transcoding or instance reading where mutually exclusive. - This can bring some significant improvements particularly in viewers. + This can bring some significant improvements, especially in viewers. - Optimized the StorageCache to prevent loading the same file multiple times if multiple users request the same file at the same time. - The StorageCache is now also storing transcoded instances that have been requested by /file?transcode=... that is now used by the DICOMweb plugin. This speeds up retrieval of transcoded frames through WADO-RS. - Now displaying timings when reading from/writing to disk in the verbose logs. -* Housekeeper plugin: - - Update to rebuild the cache of the DICOMweb plugin when updating to DICOMweb 1.15. - - New trigger configuration: "DicomWebCacheChange" - - Fixed reading the triggers configuration. -* HTTP Compression: +* HTTP compression: - The default value of the "HttpCompressionEnabled" is now false by default. This reduces the Orthanc overall CPU usage and latency. This is suitable for setups with large bandwidth network like LAN. - When "HttpCompressionEnabled" is true, only the content that is clearly identified as compressible is compressed (JSON, XML, HTML, text, ...). DICOM files are never compressed over HTTP. In prior versions, all content types were compressed. - This notably greatly improve loading time of large DICOM + This notably greatly improves loading time of large DICOM files through WADO-RS e.g in StoneViewer when working on large bandwidth networks. - When "HttpCompressionEnabled" is true, content < 2KB are never compressed. * Logs: - Each line of log now contains the name of the thread that is logging the message. - A new '--logs-no-thread' command line option can be used to get back to the previous behavior to + A new "--logs-no-thread" command line option can be used to get back to the previous behavior to keep backward compatibility. - -Bug Fixes ---------- - -* Solved a deadlock related to the Job Engine events and plugins. Job events are now pushed - into a queue to be handled asynchronously by plugins. -* Zip of studies whose PatientName and PatientID did not contain any ASCII character are now valid. - - REST API -------- * API version upgraded to 22 * Added a route to delete completed jobs from history: DELETE /jobs/{id} -* added a "transcode" option to the /file route: +* Added a "transcode" option to the /file route: e.g: /instances/../file?transcode=1.2.840.10008.1.2.4.80 * now accepting GET requests on these 3 routes to create archive/media: /tools/create-archive?resources=..,..2&transcode=1.2.840.10008.1.2.4.80 /tools/create-media?resources=..,..2&transcode=1.2.840.10008.1.2.4.80 /tools/create-media-extended?resources=..,..2&transcode=1.2.840.10008.1.2.4.80 -* All 'expand' GET arguments now accepts expand=true and expand=false values. +* All "expand" GET arguments now accepts "expand=true" and "expand=false" values. The /studies/../instances and sibling routes are the only whose expand is true if not specified. - These routes now accepts expand=false to simply list the child resources ids. + These routes now accepts "expand=false" to simply list the child resources ids. * In /tools/metrics-prometheus: - - 'orthanc_dicom_cache_size' renamed into 'orthanc_dicom_cache_size_mb' - - added 'orthanc_storage_cache_count' and 'orthanc_storage_cache_size_mb' - + - "orthanc_dicom_cache_size" renamed as "orthanc_dicom_cache_size_mb" + - added "orthanc_storage_cache_count" and "orthanc_storage_cache_size_mb" Plugins ------- +* Housekeeper plugin: + - Update to rebuild the cache of the DICOMweb plugin when updating to DICOMweb 1.15. + - New trigger configuration: "DicomWebCacheChange" + - Fixed reading the triggers configuration. + - Introduced a "sleep" to lower CPU usage when idle. * Plugins are now allowed to modify/delete private metadata/attachments (i.e. whose identifiers are < 1024) * Added "OrthancPluginSetCurrentThreadName()" in the plugin SDK. - Maintenance ----------- @@ -76,24 +70,26 @@ * Prevent the leak of the full path of the source files in the binaries * Fix loading of DCMTK dictionary in the MultitenantDicom plugin when built dynamically: https://discourse.orthanc-server.org/t/dimse-failure-using-multitenant-plugin/3665 -* Housekeeper: Introduced a 'sleep' to lower CPU usage when idle. * Support multiple values in SpecificCharacterSet in C-Find answers: https://discourse.orthanc-server.org/t/c-find-fails-on-unknown-specific-character-set-iso-2022-ir-6-iso-2022-ir-100/3947 * When exporting a study archive, make sure to use the PatientName from the study and not from the patient in case of PatientID collision. * DICOM C-Store: - - Avoid some unneccessary renegotiation of DICOM association. + - Avoid some unnecessary renegotiation of DICOM association. - Force renegotiation in case no presentation context were accepted in previous association (we have observed PACS that were not consistent in the accepted presentation contexts) - Improved logging -* Upgraded dependencies for static builds: - - boost 1.83.0 -* Upgraded minizip library to stay away from CVE-2023-45853 although Orthanc is likely not affected since zip +* Solved a deadlock related to the Job Engine events and plugins. Job events are now pushed + into a queue to be handled asynchronously by plugins. +* ZIP of studies whose PatientName and PatientID did not contain any ASCII character are now valid. +* Upgraded minizip library to stay away from CVE-2023-45853 although Orthanc is likely not affected since ZIP filenames are based on DICOM Tag values whose length is limited in size. Great thanks to James Addison for notifying us about the vulnerability and patch to apply ! * Fix XSS in Orthanc error reporting (as reported by Sébastien Doria, Vumetric Cybersecurity) by: - - always including a 'Content-Type' header in HTTP responses with a body. - - always including 'X-Content-Type-Options: nosniff' + - always including a "Content-Type" header in HTTP responses with a body. + - always including "X-Content-Type-Options: nosniff" +* Upgraded dependencies for static builds: + - boost 1.83.0 Version 1.12.1 (2023-07-04) diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Resources/CMake/Compiler.cmake --- a/OrthancFramework/Resources/CMake/Compiler.cmake Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Resources/CMake/Compiler.cmake Thu Dec 21 16:13:00 2023 +0100 @@ -124,12 +124,15 @@ ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") - if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND + if (# NOT ${CMAKE_SYSTEM_VERSION} STREQUAL "LinuxStandardBase" AND + NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") # The "--no-undefined" linker flag makes the shared libraries # (plugins ModalityWorklists and ServeFolders) fail to compile on # OpenBSD, and make the PostgreSQL plugin complain about missing - # "environ" global variable in FreeBSD + # "environ" global variable in FreeBSD. Furthermore, on Linux + # Standard Base running on Debian 12, the "-Wl,--no-undefined" + # breaks the compilation (added after Orthanc 1.12.2). set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") endif() diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake --- a/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Thu Dec 21 16:13:00 2023 +0100 @@ -158,6 +158,8 @@ set(ORTHANC_FRAMEWORK_MD5 "d32a0cde03b6eb603d8dd2b33d38bf1b") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.1") set(ORTHANC_FRAMEWORK_MD5 "8a435140efc8ff4a01d8242f092f21de") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.2") + set(ORTHANC_FRAMEWORK_MD5 "d2476b9e796e339ac320b5333489bdb3") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/Cache/MemoryStringCache.h --- a/OrthancFramework/Sources/Cache/MemoryStringCache.h Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.h Thu Dec 21 16:13:00 2023 +0100 @@ -48,7 +48,7 @@ class ORTHANC_PUBLIC MemoryStringCache : public boost::noncopyable { public: - class Accessor : public boost::noncopyable + class ORTHANC_PUBLIC Accessor : public boost::noncopyable { protected: MemoryStringCache& cache_; diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp --- a/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -37,6 +37,7 @@ #include "FromDcmtkBridge.h" #include "../Logging.h" #include "../OrthancException.h" +#include "../Toolbox.h" #include #include // for DJ_RPLossy @@ -83,12 +84,33 @@ return lossyQuality_; } + bool TryTranscode(std::vector& failureReasons, /* out */ + DicomTransferSyntax& selectedSyntax, /* out*/ + DcmFileFormat& dicom, /* in/out */ + const std::set& allowedSyntaxes, + DicomTransferSyntax trySyntax) + { + if (allowedSyntaxes.find(trySyntax) != allowedSyntaxes.end()) + { + if (FromDcmtkBridge::Transcode(dicom, trySyntax, NULL)) + { + selectedSyntax = trySyntax; + return true; + } + + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(trySyntax)); + } + return false; + } bool DcmtkTranscoder::InplaceTranscode(DicomTransferSyntax& selectedSyntax /* out */, - DcmFileFormat& dicom, + std::string& failureReason /* out */, + DcmFileFormat& dicom, /* in/out */ const std::set& allowedSyntaxes, bool allowNewSopInstanceUid) { + std::vector failureReasons; + if (dicom.getDataset() == NULL) { throw OrthancException(ErrorCode_InternalError); @@ -109,62 +131,75 @@ // No transcoding is needed return true; } - - if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianImplicit) != allowedSyntaxes.end() && - FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianImplicit, NULL)) + + if (TryTranscode(failureReasons, selectedSyntax, dicom, allowedSyntaxes, DicomTransferSyntax_LittleEndianImplicit)) + { + return true; + } + + if (TryTranscode(failureReasons, selectedSyntax, dicom, allowedSyntaxes, DicomTransferSyntax_LittleEndianExplicit)) { - selectedSyntax = DicomTransferSyntax_LittleEndianImplicit; + return true; + } + + if (TryTranscode(failureReasons, selectedSyntax, dicom, allowedSyntaxes, DicomTransferSyntax_BigEndianExplicit)) + { + return true; + } + + if (TryTranscode(failureReasons, selectedSyntax, dicom, allowedSyntaxes, DicomTransferSyntax_DeflatedLittleEndianExplicit)) + { return true; } - if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianExplicit) != allowedSyntaxes.end() && - FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianExplicit, NULL)) - { - selectedSyntax = DicomTransferSyntax_LittleEndianExplicit; - return true; - } - - if (allowedSyntaxes.find(DicomTransferSyntax_BigEndianExplicit) != allowedSyntaxes.end() && - FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_BigEndianExplicit, NULL)) - { - selectedSyntax = DicomTransferSyntax_BigEndianExplicit; - return true; - } - - if (allowedSyntaxes.find(DicomTransferSyntax_DeflatedLittleEndianExplicit) != allowedSyntaxes.end() && - FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL)) - { - selectedSyntax = DicomTransferSyntax_DeflatedLittleEndianExplicit; - return true; - } #if ORTHANC_ENABLE_DCMTK_JPEG == 1 - if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess1) != allowedSyntaxes.end() && - allowNewSopInstanceUid && - (!hasBitsStored || bitsStored == 8)) + if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess1) != allowedSyntaxes.end()) { - // Check out "dcmjpeg/apps/dcmcjpeg.cc" - DJ_RPLossy parameters(lossyQuality_); - - if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, ¶meters)) + if (!allowNewSopInstanceUid) + { + failureReasons.push_back(std::string("Can not transcode to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess1) + " without generating new SOPInstanceUID"); + } + else if (hasBitsStored && bitsStored != 8) + { + failureReasons.push_back(std::string("Can not transcode to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess1) + " if BitsStored != 8"); + } + else { - selectedSyntax = DicomTransferSyntax_JPEGProcess1; - return true; + // Check out "dcmjpeg/apps/dcmcjpeg.cc" + DJ_RPLossy parameters(lossyQuality_); + + if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, ¶meters)) + { + selectedSyntax = DicomTransferSyntax_JPEGProcess1; + return true; + } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess1)); } } #endif #if ORTHANC_ENABLE_DCMTK_JPEG == 1 - if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess2_4) != allowedSyntaxes.end() && - allowNewSopInstanceUid && - (!hasBitsStored || bitsStored <= 12)) + if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess2_4) != allowedSyntaxes.end()) { - // Check out "dcmjpeg/apps/dcmcjpeg.cc" - DJ_RPLossy parameters(lossyQuality_); - if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, ¶meters)) + if (!allowNewSopInstanceUid) + { + failureReasons.push_back(std::string("Can not transcode to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess2_4) + " without generating new SOPInstanceUID"); + } + else if (hasBitsStored && bitsStored > 12) { - selectedSyntax = DicomTransferSyntax_JPEGProcess2_4; - return true; + failureReasons.push_back(std::string("Can not transcode to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess2_4) + " if BitsStored != 8"); + } + else + { + // Check out "dcmjpeg/apps/dcmcjpeg.cc" + DJ_RPLossy parameters(lossyQuality_); + if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, ¶meters)) + { + selectedSyntax = DicomTransferSyntax_JPEGProcess2_4; + return true; + } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess2_4)); } } #endif @@ -180,6 +215,7 @@ selectedSyntax = DicomTransferSyntax_JPEGProcess14; return true; } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess14)); } #endif @@ -194,6 +230,7 @@ selectedSyntax = DicomTransferSyntax_JPEGProcess14SV1; return true; } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGProcess14SV1)); } #endif @@ -213,6 +250,7 @@ selectedSyntax = DicomTransferSyntax_JPEGLSLossless; return true; } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGLSLossless)); } #endif @@ -233,9 +271,11 @@ selectedSyntax = DicomTransferSyntax_JPEGLSLossy; return true; } + failureReasons.push_back(std::string("Internal error while transcoding to ") + GetTransferSyntaxUid(DicomTransferSyntax_JPEGLSLossy)); } #endif + Orthanc::Toolbox::JoinStrings(failureReason, failureReasons, ", "); return false; } @@ -285,28 +325,27 @@ return false; } + std::string failureReason; + std::string s; + for (std::set::const_iterator + it = allowedSyntaxes.begin(); it != allowedSyntaxes.end(); ++it) { - std::string s; - for (std::set::const_iterator - it = allowedSyntaxes.begin(); it != allowedSyntaxes.end(); ++it) + if (!s.empty()) { - if (!s.empty()) - { - s += ", "; - } - - s += GetTransferSyntaxUid(*it); + s += ", "; } - if (s.empty()) - { - s = ""; - } - - LOG(INFO) << "DCMTK transcoding from " << GetTransferSyntaxUid(sourceSyntax) - << " to one of: " << s; + s += GetTransferSyntaxUid(*it); } + if (s.empty()) + { + s = ""; + } + + LOG(INFO) << "DCMTK transcoding from " << GetTransferSyntaxUid(sourceSyntax) + << " to one of: " << s; + #if !defined(NDEBUG) const std::string sourceSopInstanceUid = GetSopInstanceUid(source.GetParsed()); #endif @@ -319,7 +358,7 @@ target.AcquireBuffer(source); return true; } - else if (InplaceTranscode(targetSyntax, source.GetParsed(), + else if (InplaceTranscode(targetSyntax, failureReason, source.GetParsed(), allowedSyntaxes, allowNewSopInstanceUid)) { // Sanity check @@ -347,6 +386,8 @@ else { // Cannot transcode + LOG(WARNING) << "DCMTK was unable to transcode from " << GetTransferSyntaxUid(sourceSyntax) + << " to one of: " << s << " " << failureReason; return false; } } diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h --- a/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h Thu Dec 21 16:13:00 2023 +0100 @@ -41,6 +41,7 @@ unsigned int lossyQuality_; bool InplaceTranscode(DicomTransferSyntax& selectedSyntax /* out */, + std::string& failureReason /* out */, DcmFileFormat& dicom, const std::set& allowedSyntaxes, bool allowNewSopInstanceUid); diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/HttpServer/HttpOutput.cpp --- a/OrthancFramework/Sources/HttpServer/HttpOutput.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpOutput.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -44,6 +44,8 @@ # endif #endif +static const std::string X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; + namespace Orthanc { @@ -58,7 +60,8 @@ contentLength_(0), contentPosition_(0), keepAlive_(isKeepAlive), - keepAliveTimeout_(keepAliveTimeout) + keepAliveTimeout_(keepAliveTimeout), + hasXContentTypeOptions_(false) { } @@ -142,6 +145,11 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls); } + if (header == X_CONTENT_TYPE_OPTIONS) + { + hasXContentTypeOptions_ = true; + } + headers_.push_back(header + ": " + value + "\r\n"); } @@ -178,9 +186,6 @@ if (state_ == State_WritingHeader) { - // always include this header to prevent MIME Confusion attacks: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options - AddHeader("X-Content-Type-Options", "nosniff"); - // Send the HTTP header before writing the body stream_.OnHttpStatusReceived(status_); @@ -220,6 +225,13 @@ s += *it; } + if (!hasXContentTypeOptions_) + { + // Always include this header to prevent MIME Confusion attacks: + // https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options + s += X_CONTENT_TYPE_OPTIONS + ": nosniff\r\n"; + } + if (status_ != HttpStatus_200_Ok) { hasContentLength_ = false; @@ -369,8 +381,8 @@ if (messageSize > 0) { - // we assume that the body always contains a json description of the error - stateMachine_.SetContentType("application/json"); + // Assume that the body always contains a textual description of the error + stateMachine_.SetContentType("text/plain"); } stateMachine_.SendBody(message, messageSize); diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/HttpServer/HttpOutput.h --- a/OrthancFramework/Sources/HttpServer/HttpOutput.h Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpOutput.h Thu Dec 21 16:13:00 2023 +0100 @@ -64,6 +64,7 @@ bool keepAlive_; unsigned int keepAliveTimeout_; std::list headers_; + bool hasXContentTypeOptions_; std::string multipartBoundary_; std::string multipartContentType_; diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/Images/Font.cpp --- a/OrthancFramework/Sources/Images/Font.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/Images/Font.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -37,6 +37,7 @@ #include "Image.h" #include "ImageProcessing.h" +#include #include #include #include diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancFramework/Sources/Images/PamWriter.cpp --- a/OrthancFramework/Sources/Images/PamWriter.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancFramework/Sources/Images/PamWriter.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -28,6 +28,7 @@ #include "../OrthancException.h" #include "../Toolbox.h" +#include #include diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h --- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Thu Dec 21 16:13:00 2023 +0100 @@ -120,7 +120,7 @@ #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 #define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 12 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 1 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 2 #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) @@ -9359,21 +9359,22 @@ } -/** - * @brief Sets the name of the current thread - * - * This function sets the name of the thread that is calling it. - * This name is used in the logs. This function shall be called only from threads that - * the plugin has created itself. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param threadName The name of the current thread. A Thread name can not be larger than 16 characters. + /** + * @brief Set the name of the current thread. + * + * This function gives a name to the thread that is calling this + * function. This name is used in the Orthanc logs. This function + * must only be called from threads that the plugin has created + * itself. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param threadName The name of the current thread. A thread name cannot be longer than 16 characters. * @return 0 if success, other value if error. * @ingroup Toolbox **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginSetCurrentThreadName( - OrthancPluginContext* context, - const char* threadName) + OrthancPluginContext* context, + const char* threadName) { return context->InvokeService(context, _OrthancPluginService_SetCurrentThreadName, threadName); } diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py --- a/OrthancServer/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py Thu Dec 21 16:13:00 2023 +0100 @@ -224,7 +224,7 @@ cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w') -print os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +print(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) cpp.write(""" #include "%s.h" diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Plugins/Samples/WebSkeleton/Framework/Framework.cmake --- a/OrthancServer/Plugins/Samples/WebSkeleton/Framework/Framework.cmake Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Plugins/Samples/WebSkeleton/Framework/Framework.cmake Thu Dec 21 16:13:00 2023 +0100 @@ -33,7 +33,7 @@ "${AUTOGENERATED_DIR}/EmbeddedResources.h" "${AUTOGENERATED_DIR}/EmbeddedResources.cpp" COMMAND - python + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/Framework/EmbedResources.py" "${AUTOGENERATED_DIR}/EmbeddedResources" STATIC_RESOURCES diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Resources/Configuration.json Thu Dec 21 16:13:00 2023 +0100 @@ -51,7 +51,9 @@ // In "Reject" mode, the sender will receive a 0xA700 DIMSE status code // if the instance was sent through C-Store, a 507 HTTP status code // if using the REST API and a 0xA700 Failure reason when using - // DicomWeb Stow-RS + // DicomWeb Stow-RS. + // Note: this value is taken into account only if you have set + // a MaximumStorageSize != 0 or a MaximumPatientCount != 0 // Allowed values: "Recycle", "Reject" // (new in Orthanc 1.11.2) "MaximumStorageMode" : "Recycle", diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py --- a/OrthancServer/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py Thu Dec 21 16:13:00 2023 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Resources/Samples/ImportDicomFiles/OrthancImport.py --- a/OrthancServer/Resources/Samples/ImportDicomFiles/OrthancImport.py Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Resources/Samples/ImportDicomFiles/OrthancImport.py Thu Dec 21 16:13:00 2023 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -342,8 +342,10 @@ { call.GetDocumentation() .SetTag("Patients") - .SetSummary("Protect one patient against recycling") - .SetDescription("Check out configuration options `MaximumStorageSize` and `MaximumPatientCount`") + .SetSummary("Protect/Unprotect a patient against recycling") + .SetDescription("Protects a patient by sending `1` or `true` in the payload request. " + "Unprotects a patient by sending `0` or `false` in the payload requests. " + "More info: https://orthanc.uclouvain.be/book/faq/features.html#recycling-protection") .SetUriArgument("id", "Orthanc identifier of the patient of interest"); return; } diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Sources/Search/ISqlLookupFormatter.cpp --- a/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -40,6 +40,7 @@ #include "DatabaseConstraint.h" +#include #include #include diff -r dceed5e3d6a9 -r 4b51cf06b697 OrthancServer/Sources/ServerIndex.cpp --- a/OrthancServer/Sources/ServerIndex.cpp Fri Dec 15 17:15:43 2023 +0100 +++ b/OrthancServer/Sources/ServerIndex.cpp Thu Dec 21 16:13:00 2023 +0100 @@ -417,11 +417,17 @@ if (mode == MaxStorageMode_Recycle) { - LOG(WARNING) << "Maximum Storage mode: Recycle"; + if (maximumStorageSize_ > 0 || maximumPatients_ > 0) + { + LOG(WARNING) << "Maximum Storage mode: Recycle"; + } } else { - LOG(WARNING) << "Maximum Storage mode: Reject"; + if (maximumStorageSize_ > 0 || maximumPatients_ > 0) + { + LOG(WARNING) << "Maximum Storage mode: Reject"; + } } } diff -r dceed5e3d6a9 -r 4b51cf06b697 TODO --- a/TODO Fri Dec 15 17:15:43 2023 +0100 +++ b/TODO Thu Dec 21 16:13:00 2023 +0100 @@ -138,11 +138,9 @@ https://groups.google.com/g/orthanc-users/c/hsZ1jng5rIg/m/8xZL2C1VBgAJ * add an "AutoDeleteIfSuccessful": false option when creating jobs https://discourse.orthanc-server.org/t/job-history-combined-with-auto-forwarding/3729/10 -* Also implement a GET variant of /tools/create-archive + sibling routes - in which resources & transcode options are provided as get arguments. - https://groups.google.com/g/orthanc-users/c/PmaRZ609ztA/m/JdwXvIBKAQAJ * Allow skiping automatic conversion of color-space in transcoding/decoding. The patch that was initialy provided was breaking the IngestTranscoding. + This might require a DCMTK decoding plugin ? https://discourse.orthanc-server.org/t/orthanc-convert-ybr-to-rgb-but-does-not-change-metadata/3533/9 * Implement a 'commit' route to force the Stable status earlier. https://discourse.orthanc-server.org/t/expediting-stability-of-a-dicom-study-new-api-endpoint/1684