Mercurial > hg > orthanc
changeset 6444:31197da4a994 queues-timeout
integration mainline->queues-timeout
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 18 Nov 2025 17:50:39 +0100 |
| parents | 36aff9f84264 (current diff) f288ceb2a131 (diff) |
| children | 68de7e892eaa |
| files | NEWS OrthancServer/CMakeLists.txt OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h |
| diffstat | 37 files changed, 575 insertions(+), 998 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Sat Nov 15 12:28:27 2025 +0100 +++ b/NEWS Tue Nov 18 17:50:39 2025 +0100 @@ -53,29 +53,6 @@ - civetweb 1.16, including patch for CVE-2025-55763 - SQLite 3.50.4 -Plugins -------- - -* Worklists plugin: - - The Worklists plugin now provides a REST API to: - - create worklists through POST at /worklists/create - - list the worklists through GET at /worklists - - view a single worklist through GET at /worklists/{uuid} - - modify a worklist through PUT at /worklists/{uuid} - - delete a worklist through DELETE at /worklists/{uuid} - All details are available in the Orthanc book: - https://orthanc.uclouvain.be/book/plugins/worklists-plugin.html - - New configuration options: - - "SaveInOrthancDatabase" to store the worklists in the Orthanc DB (provided that you are using SQLite or PostgreSQL). - - "DeleteWorklistsOnStableStudy" to delete the worklist once its related study has been received and is stable. - - "SetStudyInstanceUidIfMissing" to add a StudyInstanceUID if the REST API request does not include one. - - "DeleteWorklistsDelay" to delete a worklist N hours after it has been created - (only available if using the "SaveInOrthancDatabase" mode). - - "HousekeepingInterval" to define the delay between 2 executions of the Worklist Housekeeping thread - that deletes the worklists when required. - - Note: the previous "Database" configuration has now been renamed in "Directory" to better differentiate - the "File" or "DB modes. - Plugin SDK ----------
--- a/OrthancFramework/Resources/CMake/Compiler.cmake Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Resources/CMake/Compiler.cmake Tue Nov 18 17:50:39 2025 +0100 @@ -246,6 +246,9 @@ # fix this error that appears with recent compilers on MacOS: boost/mpl/aux_/integral_wrapper.hpp:73:31: error: integer value -1 is outside the valid range of values [0, 3] for this enumeration type [-Wenum-constexpr-conversion] SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-enum-constexpr-conversion") + # it seems that some recent MacOS compilers don't set these flags correctly which prevents zlib from building correctly + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64") + add_definitions( -D_XOPEN_SOURCE=1 )
--- a/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Tue Nov 18 17:50:39 2025 +0100 @@ -215,6 +215,10 @@ # Advanced storage 0.2.2 (framework pre-1.12.10) set(ORTHANC_FRAMEWORK_PRE_RELEASE ON) set(ORTHANC_FRAMEWORK_MD5 "bd5ba2cec329010b912209345acbdeaf") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "0ebe8cfd9bf7") + # Worklists plugin 0.9.0 (framework pre-1.12.10) + set(ORTHANC_FRAMEWORK_PRE_RELEASE ON) + set(ORTHANC_FRAMEWORK_MD5 "17a5ca9254e881ab89c93d052d4655cb") endif() endif() endif()
--- a/OrthancFramework/Resources/CMake/ZlibConfiguration.cmake Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Resources/CMake/ZlibConfiguration.cmake Tue Nov 18 17:50:39 2025 +0100 @@ -25,8 +25,28 @@ SET(ZLIB_URL "https://orthanc.uclouvain.be/downloads/third-party-downloads/zlib-1.3.1.tar.gz") SET(ZLIB_MD5 "9855b6d802d7fe5b7bd5b196a2271655") + if (IS_DIRECTORY "${ZLIB_SOURCES_DIR}") + set(FirstRun OFF) + else() + set(FirstRun ON) + endif() + DownloadPackage(${ZLIB_MD5} ${ZLIB_URL} "${ZLIB_SOURCES_DIR}") + if (FirstRun) + # fix https://github.com/madler/zlib/issues/1044 + execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i + ${CMAKE_CURRENT_LIST_DIR}/../Patches/zlib-1.3.1-zconf.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) + + if (Failure) + message(FATAL_ERROR "Error while patching a file") + endif() + endif() + include_directories( ${ZLIB_SOURCES_DIR} )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/Patches/zlib-1.3.1-zconf.patch Tue Nov 18 17:50:39 2025 +0100 @@ -0,0 +1,12 @@ +diff -ruN zlib-1.3.1-orig/zlib.h zlib-1.3.1/zlib.h +--- zlib-1.3.1-orig/zlib.h 2025-11-18 10:09:54.402921485 +0100 ++++ zlib-1.3.1/zlib.h 2025-11-18 10:10:33.743470112 +0100 +@@ -31,7 +31,7 @@ + #ifndef ZLIB_H + #define ZLIB_H + +-#include "zconf.h" ++#include <zconf.h> + + #ifdef __cplusplus + extern "C" {
--- a/OrthancFramework/Sources/Cache/MemoryCache.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryCache.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -88,9 +88,19 @@ while (!index_.IsEmpty()) { Page* element = NULL; - index_.RemoveOldest(element); - assert(element != NULL); - delete element; + + try + { + index_.RemoveOldest(element); + + assert(element != NULL); + delete element; + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } }
--- a/OrthancFramework/Sources/Cache/MemoryObjectCache.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryObjectCache.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -26,6 +26,7 @@ #include "MemoryObjectCache.h" #include "../Compatibility.h" +#include "../Logging.h" namespace Orthanc { @@ -96,7 +97,16 @@ MemoryObjectCache::~MemoryObjectCache() { - Recycle(0); + try + { + Recycle(0); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } + assert(content_.IsEmpty()); }
--- a/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -25,6 +25,8 @@ #include "../PrecompiledHeaders.h" #include "MemoryStringCache.h" +#include "../Logging.h" + namespace Orthanc { class MemoryStringCache::StringValue : public ICacheable @@ -118,7 +120,16 @@ MemoryStringCache::~MemoryStringCache() { - Recycle(0); + try + { + Recycle(0); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } + assert(content_.IsEmpty()); }
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -1696,15 +1696,20 @@ { DcmDataset* dcmDataset = GetDcmtkObjectConst().getDataset(); - if (!this->HasTag(DICOM_TAG_PIXEL_DATA) && !DicomImageDecoder::IsPsmctRle1(*dcmDataset)) + if (dcmDataset == NULL) { - throw OrthancException(ErrorCode_BadRequest, "Cannot extract a frame from a DIOCM file that does not have pixel data."); + throw OrthancException(ErrorCode_InternalError); + } + + if (!this->HasTag(DICOM_TAG_PIXEL_DATA) && + !DicomImageDecoder::IsPsmctRle1(*dcmDataset)) + { + throw OrthancException(ErrorCode_BadRequest, "Cannot extract a frame from a DICOM file that does not have pixel data."); } if (pimpl_->frameIndex_.get() == NULL) { - assert(pimpl_->file_ != NULL && dcmDataset != NULL); - + assert(pimpl_->file_ != NULL); pimpl_->frameIndex_.reset(new DicomFrameIndex(*dcmDataset)); }
--- a/OrthancFramework/Sources/Enumerations.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/Enumerations.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -1199,6 +1199,9 @@ case ErrorPayloadType_Dimse: return "Dimse"; + case ErrorPayloadType_RetrieveJob: + return "RetrieveJob"; + default: throw OrthancException(ErrorCode_ParameterOutOfRange); } @@ -1932,6 +1935,10 @@ { return ErrorPayloadType_Dimse; } + else if (type == "RetrieveJob") + { + return ErrorPayloadType_RetrieveJob; + } else { throw OrthancException(ErrorCode_ParameterOutOfRange);
--- a/OrthancFramework/Sources/Enumerations.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/Enumerations.h Tue Nov 18 17:50:39 2025 +0100 @@ -805,7 +805,8 @@ enum ErrorPayloadType { ErrorPayloadType_None = 1, - ErrorPayloadType_Dimse = 2 + ErrorPayloadType_Dimse = 2, + ErrorPayloadType_RetrieveJob = 3, };
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -369,7 +369,7 @@ serialized.isMember(ERROR_PAYLOAD)) { const std::string type = SerializationToolbox::ReadString(serialized, ERROR_PAYLOAD_TYPE); - lastStatus_.GetErrorPayload().SetContent(StringToErrorPayloadType(type.c_str()), serialized[ERROR_PAYLOAD]); + lastStatus_.GetErrorPayload().SetContent(StringToErrorPayloadType(type), serialized[ERROR_PAYLOAD]); } job_->Start(); @@ -1300,27 +1300,35 @@ { boost::mutex::scoped_lock lock(registry_.mutex_); - switch (targetState_) + try { - case JobState_Failure: - registry_.MarkRunningAsCompleted - (*handler_, canceled_ ? CompletedReason_Canceled : CompletedReason_Failure); - break; + switch (targetState_) + { + case JobState_Failure: + registry_.MarkRunningAsCompleted + (*handler_, canceled_ ? CompletedReason_Canceled : CompletedReason_Failure); + break; - case JobState_Success: - registry_.MarkRunningAsCompleted(*handler_, CompletedReason_Success); - break; + case JobState_Success: + registry_.MarkRunningAsCompleted(*handler_, CompletedReason_Success); + break; - case JobState_Paused: - registry_.MarkRunningAsPaused(*handler_); - break; + case JobState_Paused: + registry_.MarkRunningAsPaused(*handler_); + break; + + case JobState_Retry: + registry_.MarkRunningAsRetry(*handler_, targetRetryTimeout_); + break; - case JobState_Retry: - registry_.MarkRunningAsRetry(*handler_, targetRetryTimeout_); - break; - - default: - assert(0); + default: + assert(0); + } + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); } } }
--- a/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -191,12 +191,6 @@ } - void SetOfInstancesJob::Start() - { - SetOfCommandsJob::Start(); - } - - void SetOfInstancesJob::Reset() { SetOfCommandsJob::Reset();
--- a/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h Tue Nov 18 17:50:39 2025 +0100 @@ -73,8 +73,6 @@ bool IsFailedInstance(const std::string& instance) const; - virtual void Start() ORTHANC_OVERRIDE; - virtual void Reset() ORTHANC_OVERRIDE; virtual void GetPublicContent(Json::Value& target) const ORTHANC_OVERRIDE;
--- a/OrthancFramework/Sources/MallocMemoryBuffer.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/MallocMemoryBuffer.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -25,6 +25,7 @@ #include "PrecompiledHeaders.h" #include "MallocMemoryBuffer.h" +#include "Logging.h" #include "OrthancException.h" #include <string.h> @@ -40,6 +41,20 @@ } + MallocMemoryBuffer::~MallocMemoryBuffer() + { + try + { + Clear(); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } + } + + void MallocMemoryBuffer::Clear() { if (size_ != 0)
--- a/OrthancFramework/Sources/MallocMemoryBuffer.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/MallocMemoryBuffer.h Tue Nov 18 17:50:39 2025 +0100 @@ -45,10 +45,7 @@ public: MallocMemoryBuffer(); - virtual ~MallocMemoryBuffer() ORTHANC_OVERRIDE - { - Clear(); - } + virtual ~MallocMemoryBuffer() ORTHANC_OVERRIDE; void Clear();
--- a/OrthancFramework/Sources/MetricsRegistry.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/MetricsRegistry.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -27,6 +27,7 @@ #include "ChunkedBuffer.h" #include "Compatibility.h" +#include "Logging.h" #include "OrthancException.h" #include <boost/math/special_functions/round.hpp> @@ -617,7 +618,15 @@ MetricsRegistry::ActiveCounter::~ActiveCounter() { - metrics_.Add(-1); + try + { + metrics_.Add(-1); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } @@ -629,7 +638,15 @@ MetricsRegistry::AvailableResourcesDecounter::~AvailableResourcesDecounter() { - metrics_.Add(1); + try + { + metrics_.Add(1); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } @@ -673,7 +690,16 @@ if (active_) { boost::posix_time::time_duration diff = GetNow() - start_; - registry_.SetIntegerValue(name_, static_cast<int64_t>(diff.total_milliseconds()), policy_); + + try + { + registry_.SetIntegerValue(name_, static_cast<int64_t>(diff.total_milliseconds()), policy_); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } } }
--- a/OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h Tue Nov 18 17:50:39 2025 +0100 @@ -32,10 +32,6 @@ class IRunnableBySteps : public IDynamicObject { public: - virtual ~IRunnableBySteps() ORTHANC_OVERRIDE - { - } - // Must return "true" if the runnable wishes to continue. Must // return "false" if the runnable has not finished its job. virtual bool Step() = 0;
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -1562,7 +1562,7 @@ if (components.size() != 2) { - throw; + throw OrthancException(ErrorCode_InternalError); } int a = boost::lexical_cast<int>(components[0]); @@ -1571,7 +1571,7 @@ b < 0 || b > 15 || (a == 0 && b == 0)) { - throw; + throw OrthancException(ErrorCode_InternalError); } result[i] = static_cast<uint8_t>(a * 16 + b);
--- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -1316,7 +1316,7 @@ { if (b[i] != ('0' + i % 7)) { - throw; + throw OrthancException(ErrorCode_InternalError); } }
--- a/OrthancServer/CMakeLists.txt Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/CMakeLists.txt Tue Nov 18 17:50:39 2025 +0100 @@ -637,14 +637,6 @@ list(APPEND MODALITY_WORKLISTS_RESOURCES ${AUTOGENERATED_DIR}/ModalityWorklists.rc) endif() - EmbedResources( - --target=ModalityWorklistsResources - --namespace=Orthanc.FrameworkResources - --framework-path=${CMAKE_SOURCE_DIR}/../OrthancFramework/Sources - ${LIBICU_RESOURCES} - ${DCMTK_DICTIONARIES} - ) - set_source_files_properties( ${CMAKE_SOURCE_DIR}/Plugins/Samples/ModalityWorklists/Plugin.cpp PROPERTIES COMPILE_DEFINITIONS "MODALITY_WORKLISTS_VERSION=\"${ORTHANC_VERSION}\"" @@ -652,14 +644,12 @@ add_library(ModalityWorklists SHARED ${CMAKE_SOURCE_DIR}/Plugins/Samples/ModalityWorklists/Plugin.cpp - ${CMAKE_SOURCE_DIR}/Plugins/Samples/ModalityWorklists/OrthancFrameworkDependencies.cpp - ${AUTOGENERATED_DIR}/ModalityWorklistsResources.cpp ${MODALITY_WORKLISTS_RESOURCES} ) DefineSourceBasenameForTarget(ModalityWorklists) - target_link_libraries(ModalityWorklists PluginsDependencies ${DCMTK_LIBRARIES}) + target_link_libraries(ModalityWorklists PluginsDependencies) set_target_properties( ModalityWorklists PROPERTIES
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Nov 18 17:50:39 2025 +0100 @@ -2180,7 +2180,7 @@ **/ ORTHANC_PLUGIN_SINCE_SDK("1.4.0") ORTHANC_PLUGIN_INLINE int32_t OrthancPluginCheckVersionAdvanced( - OrthancPluginContext* context, + const OrthancPluginContext* context, int32_t expectedMajor, int32_t expectedMinor, int32_t expectedRevision) @@ -2295,7 +2295,7 @@ * @ingroup Callbacks **/ ORTHANC_PLUGIN_INLINE int32_t OrthancPluginCheckVersion( - OrthancPluginContext* context) + const OrthancPluginContext* context) { return OrthancPluginCheckVersionAdvanced( context,
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -221,6 +221,19 @@ } + MemoryBuffer::~MemoryBuffer() + { + try + { + Clear(); + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) + { + // Don't throw exceptions in destructors + } + } + + void MemoryBuffer::Clear() { if (buffer_.data != NULL) @@ -652,6 +665,19 @@ } + OrthancString::~OrthancString() + { + try + { + Clear(); + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) + { + // Don't throw exceptions in destructors + } + } + + void OrthancString::Assign(char* str) { Clear(); @@ -1301,6 +1327,20 @@ } } + + OrthancImage::~OrthancImage() + { + try + { + Clear(); + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) + { + // Don't throw exceptions in destructors + } + } + + void OrthancImage::UncompressPngImage(const void* data, size_t size) {
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Tue Nov 18 17:50:39 2025 +0100 @@ -219,10 +219,7 @@ public: MemoryBuffer(); - ~MemoryBuffer() - { - Clear(); - } + ~MemoryBuffer(); OrthancPluginMemoryBuffer* operator*() { @@ -377,10 +374,7 @@ { } - ~OrthancString() - { - Clear(); - } + ~OrthancString(); // This transfers ownership, warning: The string must have been // allocated by the Orthanc core @@ -497,10 +491,7 @@ uint32_t pitch, void* buffer); - ~OrthancImage() - { - Clear(); - } + ~OrthancImage(); void UncompressPngImage(const void* data, size_t size);
--- a/OrthancServer/Plugins/Samples/ModalityWorklists/CMakeLists.txt Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/ModalityWorklists/CMakeLists.txt Tue Nov 18 17:50:39 2025 +0100 @@ -27,31 +27,22 @@ 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") -include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake) -include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake) +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(ENABLE_JPEG ON CACHE INTERNAL "") -set(ENABLE_PNG ON CACHE INTERNAL "") -set(ENABLE_ZLIB ON CACHE INTERNAL "") -set(ENABLE_LOCALE ON CACHE INTERNAL "") -set(ENABLE_DCMTK ON CACHE INTERNAL "") -set(ENABLE_MODULE_DICOM ON CACHE INTERNAL "") -set(ENABLE_MODULE_JOBS OFF CACHE INTERNAL "") - -include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake) +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) add_library(ModalityWorklists SHARED Plugin.cpp ${CMAKE_SOURCE_DIR}/../Common/OrthancPluginCppWrapper.cpp - ${ORTHANC_CORE_SOURCES} - ${ORTHANC_DICOM_SOURCES} - ${AUTOGENERATED_SOURCES} + ${JSONCPP_SOURCES} + ${BOOST_SOURCES} ) DefineSourceBasenameForTarget(ModalityWorklists) -target_link_libraries(ModalityWorklists ${DCMTK_LIBRARIES}) - message("Setting the version of the plugin to ${MODALITY_WORKLISTS_VERSION}") add_definitions( -DMODALITY_WORKLISTS_VERSION="${MODALITY_WORKLISTS_VERSION}"
--- a/OrthancServer/Plugins/Samples/ModalityWorklists/OrthancFrameworkDependencies.cpp Sat Nov 15 12:28:27 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/** - * 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) 2024-2025 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2025 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 <http://www.gnu.org/licenses/>. - **/ - - -/** - * Remove the dependency upon ICU in plugins, as this greatly increase - * the size of the resulting binaries, since they must embed the ICU - * dictionary. - **/ - -#if BOOST_LOCALE_WITH_ICU == 1 -# undef BOOST_LOCALE_WITH_ICU -# if ORTHANC_STATIC_ICU == 1 -# include <unicode/udata.h> - -// Define an empty ICU dictionary for static builds -extern "C" -{ - struct - { - double bogus; - uint8_t *bytes; - } U_ICUDATA_ENTRY_POINT = { 0.0, NULL }; -} - -# endif -#endif - -#include "../../../../OrthancFramework/Sources/ChunkedBuffer.cpp" -#include "../../../../OrthancFramework/Sources/Compression/DeflateBaseCompressor.cpp" -#include "../../../../OrthancFramework/Sources/Compression/GzipCompressor.cpp" -#include "../../../../OrthancFramework/Sources/Compression/ZlibCompressor.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomArray.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomElement.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomInstanceHasher.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomPath.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomTag.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomValue.cpp" -#include "../../../../OrthancFramework/Sources/DicomFormat/Window.cpp" -#include "../../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp" -#include "../../../../OrthancFramework/Sources/DicomParsing/Internals/DicomFrameIndex.cpp" -#include "../../../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp" -#include "../../../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp" -#include "../../../../OrthancFramework/Sources/DicomParsing/ToDcmtkBridge.cpp" -#include "../../../../OrthancFramework/Sources/Enumerations.cpp" -#include "../../../../OrthancFramework/Sources/HttpServer/HttpOutput.cpp" -#include "../../../../OrthancFramework/Sources/Images/IImageWriter.cpp" -#include "../../../../OrthancFramework/Sources/Images/Image.cpp" -#include "../../../../OrthancFramework/Sources/Images/ImageAccessor.cpp" -#include "../../../../OrthancFramework/Sources/Images/ImageBuffer.cpp" -#include "../../../../OrthancFramework/Sources/Images/ImageProcessing.cpp" -#include "../../../../OrthancFramework/Sources/Images/JpegErrorManager.cpp" -#include "../../../../OrthancFramework/Sources/Images/JpegReader.cpp" -#include "../../../../OrthancFramework/Sources/Images/JpegWriter.cpp" -#include "../../../../OrthancFramework/Sources/Images/PamReader.cpp" -#include "../../../../OrthancFramework/Sources/Images/PamWriter.cpp" -#include "../../../../OrthancFramework/Sources/Images/PngReader.cpp" -#include "../../../../OrthancFramework/Sources/Images/PngWriter.cpp" -#include "../../../../OrthancFramework/Sources/Logging.cpp" -#include "../../../../OrthancFramework/Sources/MetricsRegistry.cpp" -#include "../../../../OrthancFramework/Sources/MultiThreading/RunnableWorkersPool.cpp" -#include "../../../../OrthancFramework/Sources/MultiThreading/SharedMessageQueue.cpp" -#include "../../../../OrthancFramework/Sources/OrthancException.cpp" -#include "../../../../OrthancFramework/Sources/OrthancFramework.cpp" -#include "../../../../OrthancFramework/Sources/RestApi/RestApiOutput.cpp" -#include "../../../../OrthancFramework/Sources/SerializationToolbox.cpp" -#include "../../../../OrthancFramework/Sources/SystemToolbox.cpp" -#include "../../../../OrthancFramework/Sources/TemporaryFile.cpp" -#include "../../../../OrthancFramework/Sources/Toolbox.cpp" - -namespace Orthanc -{ - void HttpClient::GlobalInitialize() - { - } - - void HttpClient::GlobalFinalize() - { - } -}
--- a/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -24,84 +24,17 @@ #define MODALITY_WORKLISTS_NAME "worklists" #include "../../../../OrthancFramework/Sources/Compatibility.h" -#include "../../../../OrthancFramework/Sources/OrthancException.h" -#include "../../../../OrthancFramework/Sources/Logging.h" -#include "../../../../OrthancFramework/Sources/Toolbox.h" -#include "../../../../OrthancFramework/Sources/SystemToolbox.h" -#include "../../../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomPath.h" -#include "../../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomInstanceHasher.h" #include "../Common/OrthancPluginCppWrapper.h" -#include <boost/thread.hpp> #include <boost/filesystem.hpp> #include <json/value.h> #include <string.h> #include <iostream> #include <algorithm> -static boost::filesystem::path worklistDirectory_; +static std::string folder_; static bool filterIssuerAet_ = false; static unsigned int limitAnswers_ = 0; -static std::unique_ptr<boost::thread> worklistHousekeeperThread_; -static bool worklistHousekeeperThreadShouldStop_ = false; -static bool deleteWorklistsOnStableStudy_ = true; -static unsigned int hkIntervalInSeconds_ = 60; -static unsigned int deleteDelayInHours_ = 24; -static std::unique_ptr<OrthancPlugins::KeyValueStore> worklistsStore_; -static bool setStudyInstanceUidIfMissing_ = true; - -enum WorklistStorageType -{ - WorklistStorageType_Folder = 1, - WorklistStorageType_OrthancDb = 2 -}; - -static WorklistStorageType worklistStorage_ = WorklistStorageType_Folder; - -struct Worklist -{ - std::string id_; - std::string createdAt_; - std::string dicomContent_; - - Worklist(const std::string& id, - const std::string& dicomContent) : - id_(id), - createdAt_(Orthanc::SystemToolbox::GetNowIsoString(true)), - dicomContent_(dicomContent) - { - } - - - Worklist(const std::string& id, const Json::Value& jsonWl) : - id_(id), - createdAt_(jsonWl["CreatedAt"].asString()) - { - std::string b64DicomContent = jsonWl["Dicom"].asString(); - Orthanc::Toolbox::DecodeBase64(dicomContent_, b64DicomContent); - } - - void Serialize(std::string& target) const - { - Json::Value t; - t["CreatedAt"] = createdAt_; - std::string b64DicomContent; - Orthanc::Toolbox::EncodeBase64(b64DicomContent, dicomContent_); - t["Dicom"] = b64DicomContent; - - Orthanc::Toolbox::WriteFastJson(target, t); - } - - bool IsOlderThan(unsigned int delayInHours) const - { - boost::posix_time::ptime now(boost::posix_time::from_iso_string(Orthanc::SystemToolbox::GetNowIsoString(true))); - boost::posix_time::ptime creationDate(boost::posix_time::from_iso_string(createdAt_)); - - return (now - creationDate).total_seconds() > (3600 * deleteDelayInHours_); - } -}; /** * This is the main function for matching a DICOM worklist against a query. @@ -109,10 +42,10 @@ static bool MatchWorklist(OrthancPluginWorklistAnswers* answers, const OrthancPluginWorklistQuery* query, const OrthancPlugins::FindMatcher& matcher, - const std::string& dicomContent) + const std::string& path) { OrthancPlugins::MemoryBuffer dicom; - dicom.Assign(dicomContent); + dicom.ReadFile(path); if (matcher.IsMatch(dicom)) { @@ -204,116 +137,70 @@ } } -static void ListWorklistsFromDb(std::vector<Worklist>& target) -{ - assert(worklistStorage_ == WorklistStorageType_OrthancDb); - assert(worklistsStore_); - - std::unique_ptr<OrthancPlugins::KeyValueStore::Iterator> it(worklistsStore_->CreateIterator()); - - while (it->Next()) - { - std::string serialized; - it->GetValue(serialized); - - Json::Value jsonWl; - if (Orthanc::Toolbox::ReadJson(jsonWl, serialized)) - { - Worklist wl(it->GetKey(), jsonWl); - target.push_back(wl); - } - }; - -} -static void ListWorklistsFromFolder(std::vector<Worklist>& target) -{ - assert(worklistStorage_ == WorklistStorageType_Folder); - - // Loop over the regular files in the database folder - namespace fs = boost::filesystem; - - fs::path source = worklistDirectory_; - fs::directory_iterator end; - - try - { - for (fs::directory_iterator it(source); it != end; ++it) - { - fs::file_type type(it->status().type()); - - if (type == fs::regular_file || - type == fs::reparse_file) // cf. BitBucket issue #11 - { - std::string extension = it->path().extension().string(); - std::transform(extension.begin(), extension.end(), extension.begin(), tolower); // Convert to lowercase - - if (extension == ".wl") - { - std::string worklistId = Orthanc::SystemToolbox::PathToUtf8(it->path().filename().replace_extension("")); - std::string dicomContent; - Orthanc::SystemToolbox::ReadFile(dicomContent, it->path()); - - Worklist wl(worklistId, dicomContent); - target.push_back(wl); - } - } - } - } - catch (fs::filesystem_error&) - { - LOG(ERROR) << "Inexistent folder while scanning for worklists: " + source.string(); - } -} - - -OrthancPluginErrorCode WorklistCallback(OrthancPluginWorklistAnswers* answers, - const OrthancPluginWorklistQuery* query, - const char* issuerAet, - const char* calledAet) +OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers* answers, + const OrthancPluginWorklistQuery* query, + const char* issuerAet, + const char* calledAet) { try { - unsigned int parsedFilesCount = 0; - unsigned int matchedWorklistCount = 0; - // Construct an object to match the worklists in the database against the C-Find query std::unique_ptr<OrthancPlugins::FindMatcher> matcher(CreateMatcher(query, issuerAet)); - std::vector<Worklist> worklists; + // Loop over the regular files in the database folder + namespace fs = boost::filesystem; - if (worklistStorage_ == WorklistStorageType_Folder) + fs::path source(folder_); + fs::directory_iterator end; + + try { - ListWorklistsFromFolder(worklists); - } - else if (worklistStorage_ == WorklistStorageType_OrthancDb) - { - ListWorklistsFromDb(worklists); - } - - for (std::vector<Worklist>::const_iterator it = worklists.begin(); it != worklists.end(); ++it) - { - if (MatchWorklist(answers, query, *matcher, it->dicomContent_)) + unsigned int parsedFilesCount = 0; + unsigned int matchedWorklistCount = 0; + + for (fs::directory_iterator it(source); it != end; ++it) { - if (limitAnswers_ != 0 && - matchedWorklistCount >= limitAnswers_) + fs::file_type type(it->status().type()); + + if (type == fs::regular_file || + type == fs::reparse_file) // cf. BitBucket issue #11 { - // Too many answers are to be returned wrt. the - // "LimitAnswers" configuration parameter. Mark the - // C-FIND result as incomplete. - OrthancPluginWorklistMarkIncomplete(OrthancPlugins::GetGlobalContext(), answers); - return OrthancPluginErrorCode_Success; + std::string extension = it->path().extension().string(); + std::transform(extension.begin(), extension.end(), extension.begin(), tolower); // Convert to lowercase + + if (extension == ".wl") + { + parsedFilesCount++; + // We found a worklist (i.e. a DICOM find with extension ".wl"), match it against the query + if (MatchWorklist(answers, query, *matcher, it->path().string())) + { + if (limitAnswers_ != 0 && + matchedWorklistCount >= limitAnswers_) + { + // Too many answers are to be returned wrt. the + // "LimitAnswers" configuration parameter. Mark the + // C-FIND result as incomplete. + OrthancPluginWorklistMarkIncomplete(OrthancPlugins::GetGlobalContext(), answers); + return OrthancPluginErrorCode_Success; + } + + ORTHANC_PLUGINS_LOG_INFO("Worklist matched: " + it->path().string()); + matchedWorklistCount++; + } + } } - - LOG(INFO) << "Worklist matched: " << it->id_; - matchedWorklistCount++; } + + ORTHANC_PLUGINS_LOG_INFO("Worklist C-Find: parsed " + boost::lexical_cast<std::string>(parsedFilesCount) + + " files, found " + boost::lexical_cast<std::string>(matchedWorklistCount) + " match(es)"); } - - LOG(INFO) << "Worklist C-Find: parsed " << boost::lexical_cast<std::string>(parsedFilesCount) << - " worklists, found " << boost::lexical_cast<std::string>(matchedWorklistCount) << " match(es)"; - + catch (fs::filesystem_error&) + { + ORTHANC_PLUGINS_LOG_ERROR("Inexistent folder while scanning for worklists: " + source.string()); + return OrthancPluginErrorCode_DirectoryExpected; + } return OrthancPluginErrorCode_Success; } @@ -323,411 +210,13 @@ } } -static void DeleteWorklist(const std::string& worklistId) -{ - switch (worklistStorage_) - { - case WorklistStorageType_Folder: - { - boost::filesystem::path path = worklistDirectory_ / (worklistId + ".wl"); - if (!Orthanc::SystemToolbox::IsRegularFile(path)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - Orthanc::SystemToolbox::RemoveFile(path); - break; - } - - case WorklistStorageType_OrthancDb: - if (worklistsStore_.get() != NULL) - { - std::string notUsed; - if (!worklistsStore_->GetValue(notUsed, worklistId)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - worklistsStore_->DeleteKey(worklistId); - } - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - -} - -static void WorklistHkWorkerThread() -{ - OrthancPluginSetCurrentThreadName(OrthancPlugins::GetGlobalContext(), "WL HOUSEKEEPER"); - - OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), "Starting Worklist Housekeeper worker thread"); - Orthanc::Toolbox::ElapsedTimer timer; - - while (!worklistHousekeeperThreadShouldStop_) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - - if (timer.GetElapsedMilliseconds() > (hkIntervalInSeconds_ * 1000)) - { - timer.Restart(); - LOG(INFO) << "Performing Worklist Housekeeping"; - - std::vector<Worklist> worklists; - - if (worklistStorage_ == WorklistStorageType_OrthancDb) - { - ListWorklistsFromDb(worklists); - } - else if (worklistStorage_ == WorklistStorageType_Folder) - { - ListWorklistsFromFolder(worklists); - } - - for (std::vector<Worklist>::const_iterator it = worklists.begin(); it != worklists.end(); ++it) - { - if (deleteDelayInHours_ > 0 && it->IsOlderThan(deleteDelayInHours_)) - { - LOG(INFO) << "Deleting worklist " << it->id_ << " " << deleteDelayInHours_ << " hours after its creation"; - DeleteWorklist(it->id_); - } - else if (deleteWorklistsOnStableStudy_) - { - std::string studyInstanceUid; - std::string patientId; - - Orthanc::ParsedDicomFile parsed(it->dicomContent_); - - if (parsed.GetTagValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID) && - parsed.GetTagValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID)) - { - Orthanc::DicomInstanceHasher hasher(patientId, studyInstanceUid, "fake-id", "fake-id"); - const std::string& studyOrthancId = hasher.HashStudy(); - - Json::Value studyInfo; - if (OrthancPlugins::RestApiGet(studyInfo, "/studies/" + studyOrthancId, false)) - { - if (studyInfo["IsStable"].asBool()) - { - LOG(INFO) << "Deleting worklist " << it->id_ << " because its study is now stable"; - DeleteWorklist(it->id_); - } - } - } - } - } - } - } -} - - -static Orthanc::DicomToJsonFormat GetFormat(const OrthancPluginHttpRequest* request) -{ - std::map<std::string, std::string> getArguments; - OrthancPlugins::GetGetArguments(getArguments, request); - - Orthanc::DicomToJsonFormat format = Orthanc::DicomToJsonFormat_Human; - - if (getArguments.find("format") != getArguments.end()) - { - format = Orthanc::StringToDicomToJsonFormat(getArguments["format"]); - } - - return format; -} - -static Orthanc::ParsedDicomFile* GetWorklist(const std::string& id) -{ - std::string fileContent; - - switch (worklistStorage_) - { - case WorklistStorageType_Folder: - { - boost::filesystem::path path = worklistDirectory_ / Orthanc::SystemToolbox::PathFromUtf8(id + ".wl"); // the id might be a filename from a file that was pushed by an external program (therefore, it can contain fancy characters) - if (!Orthanc::SystemToolbox::IsRegularFile(path)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource, "Worklist not found"); - } - - Orthanc::SystemToolbox::ReadFile(fileContent, path); - break; - } - - case WorklistStorageType_OrthancDb: - { - std::string serializedWl; - if (!worklistsStore_->GetValue(serializedWl, id)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource, "Worklist not found"); - } - - Json::Value jsonWl; - if (Orthanc::Toolbox::ReadJson(jsonWl, serializedWl)) - { - Worklist wl(id, jsonWl); - fileContent = wl.dicomContent_; - } - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - return new Orthanc::ParsedDicomFile(fileContent); -} - -static void SerializeWorklistForApi(Json::Value& target, const std::string& id, const Orthanc::ParsedDicomFile& parsed, Orthanc::DicomToJsonFormat format) -{ - target["ID"] = id; - target["Tags"] = Json::objectValue; - parsed.DatasetToJson(target["Tags"], format, Orthanc::DicomToJsonFlags_None, 0); -} extern "C" { - - OrthancPluginErrorCode ListWorklists(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) - { - if (request->method != OrthancPluginHttpMethod_Get) - { - OrthancPlugins::AnswerMethodNotAllowed(output, "GET"); - } - else - { - Json::Value response = Json::arrayValue; - Orthanc::DicomToJsonFormat format = GetFormat(request); - - std::vector<Worklist> worklists; - - if (worklistStorage_ == WorklistStorageType_Folder) - { - ListWorklistsFromFolder(worklists); - } - else if (worklistStorage_ == WorklistStorageType_OrthancDb) - { - ListWorklistsFromDb(worklists); - } - - for (std::vector<Worklist>::const_iterator it = worklists.begin(); it != worklists.end(); ++it) - { - Orthanc::ParsedDicomFile parsed(it->dicomContent_); - Json::Value jsonWl; - SerializeWorklistForApi(jsonWl, it->id_, parsed, format); - - if (worklistStorage_ == WorklistStorageType_OrthancDb) - { - jsonWl["CreationDate"] = it->createdAt_; - } - - response.append(jsonWl); - } - - OrthancPlugins::AnswerJson(response, output); - } - - return OrthancPluginErrorCode_Success; - } - - - void CreateOrUpdateWorklist(std::string& worklistId, - bool defaultForceValue, - OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) - { - Json::Value body; - - if (!OrthancPlugins::ReadJson(body, request->body, request->bodySize)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "A JSON payload was expected"); - } - - if (!body.isMember("Tags") || !body["Tags"].isObject()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "'Tags' field is missing or not a JSON object"); - } - - bool force = defaultForceValue; - if (body.isMember("Force")) { - force = body["Force"].asBool(); - } - - Json::Value& jsonWorklist = body["Tags"]; - - if (!jsonWorklist.isMember("SpecificCharacterSet")) - { - jsonWorklist["SpecificCharacterSet"] = Orthanc::GetDicomSpecificCharacterSet(Orthanc::Encoding_Utf8); - } - - std::unique_ptr<Orthanc::ParsedDicomFile> dicom(Orthanc::ParsedDicomFile::CreateFromJson(jsonWorklist, Orthanc::DicomFromJsonFlags_None, "")); - - if (!force) - { - if (!dicom->HasTag(Orthanc::DICOM_TAG_SCHEDULED_PROCEDURE_STEP_SEQUENCE)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "'Tags' is missing a 'ScheduledProcedureStepSequence'. Use 'Force': true to bypass this check."); - } - Orthanc::DicomMap step; - if (!dicom->LookupSequenceItem(step, Orthanc::DicomPath::Parse("ScheduledProcedureStepSequence"), 0) || !step.HasTag(Orthanc::DICOM_TAG_MODALITY)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "'ScheduledProcedureStepSequence' is missing a 'Modality' Use 'Force': true to bypass this check."); - } - if (!dicom->LookupSequenceItem(step, Orthanc::DicomPath::Parse("ScheduledProcedureStepSequence"), 0) || !step.HasTag(Orthanc::DICOM_TAG_SCHEDULED_PROCEDURE_STEP_START_DATE)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "'ScheduledProcedureStepSequence' is missing a 'ScheduledProcedureStepStartDate' Use 'Force': true to bypass this check."); - } - } - - dicom->SetIfAbsent(Orthanc::DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, "1.2.276.0.7230010.3.1.0.1"); - - if (setStudyInstanceUidIfMissing_) - { - dicom->SetIfAbsent(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Study)); - } - - if (worklistId.empty()) - { - worklistId = Orthanc::Toolbox::GenerateUuid(); - } - - std::string dicomContent; - dicom->SaveToMemoryBuffer(dicomContent); - - switch (worklistStorage_) - { - case WorklistStorageType_Folder: - Orthanc::SystemToolbox::WriteFile(dicomContent.empty() ? NULL : dicomContent.c_str(), dicomContent.size(), - worklistDirectory_ / Orthanc::SystemToolbox::PathFromUtf8(worklistId + ".wl"), true); - break; - - case WorklistStorageType_OrthancDb: - { - Worklist wl(worklistId, dicomContent); - std::string serializedWl; - wl.Serialize(serializedWl); - - worklistsStore_->Store(worklistId, serializedWl); - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - Json::Value response; - - response["ID"] = worklistId; - response["Path"] = "/worklists/" + worklistId; - - OrthancPlugins::AnswerJson(response, output); - } - - - OrthancPluginErrorCode GetPutDeleteWorklist(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) - { - std::string worklistId = std::string(request->groups[0]); - - if (request->method == OrthancPluginHttpMethod_Delete) - { - DeleteWorklist(worklistId); - - OrthancPlugins::AnswerString("{}", "application/json", output); - } - else if (request->method == OrthancPluginHttpMethod_Get) - { - Orthanc::DicomToJsonFormat format = GetFormat(request); - std::unique_ptr<Orthanc::ParsedDicomFile> parsed(GetWorklist(worklistId)); - - Json::Value jsonWl; - SerializeWorklistForApi(jsonWl, worklistId, *parsed, format); - - OrthancPlugins::AnswerJson(jsonWl, output); - } - else if (request->method == OrthancPluginHttpMethod_Put) - { - CreateOrUpdateWorklist(worklistId, true, output, url, request); - } - else - { - OrthancPlugins::AnswerMethodNotAllowed(output, "DELETE,GET"); - } - - return OrthancPluginErrorCode_Success; - } - - - OrthancPluginErrorCode PostCreateWorklist(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) - { - if (request->method != OrthancPluginHttpMethod_Post) - { - OrthancPlugins::AnswerMethodNotAllowed(output, "POST"); - } - else - { - std::string worklistId; - CreateOrUpdateWorklist(worklistId, false, output, url, request); - } - return OrthancPluginErrorCode_Success; - } - - static OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType, - OrthancPluginResourceType resourceType, - const char *resourceId) - { - try - { - if (changeType == OrthancPluginChangeType_OrthancStarted) - { - Json::Value system; - bool hasKeyValueStores = (OrthancPlugins::RestApiGet(system, "/system", false) && system.isMember("Capabilities") && - system["Capabilities"].isMember("HasKeyValueStores") && system["Capabilities"]["HasKeyValueStores"].asBool()); - - if (worklistStorage_ == WorklistStorageType_OrthancDb && !hasKeyValueStores) - { - LOG(ERROR) << "The Orthanc DB plugin does not support Key Value Stores. It is therefore impossible to store the worklists in Orthanc Database"; - return OrthancPluginErrorCode_IncompatibleConfigurations; - } - - if (deleteDelayInHours_ > 0 && !hasKeyValueStores) - { - LOG(ERROR) << "The Orthanc DB plugin does not support Key Value Stores. It is therefore impossible to use the \"DeleteWorklistsDelay\" option"; - return OrthancPluginErrorCode_IncompatibleConfigurations; - } - - if (worklistStorage_ == WorklistStorageType_OrthancDb) - { - worklistsStore_.reset(new OrthancPlugins::KeyValueStore("worklists")); - } - - if (deleteDelayInHours_ > 0 || deleteWorklistsOnStableStudy_) - { - worklistHousekeeperThread_.reset(new boost::thread(WorklistHkWorkerThread)); - } - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception: " << e.What(); - } - catch (...) - { - LOG(ERROR) << "Uncatched native exception"; - } - return OrthancPluginErrorCode_Success; - } - ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) { OrthancPlugins::SetGlobalContext(c, MODALITY_WORKLISTS_NAME); - Orthanc::Logging::InitializePluginContext(c, MODALITY_WORKLISTS_NAME); - + /* Check the version of the Orthanc core */ if (OrthancPluginCheckVersion(c) == 0) { @@ -737,29 +226,6 @@ return -1; } - { // init the OrthancFramework - static const char* const LOCALE = "Locale"; - static const char* const DEFAULT_ENCODING = "DefaultEncoding"; - - /** - * This function is a simplified version of function - * "Orthanc::OrthancInitialize()" that is executed when starting the - * Orthanc server. - **/ - OrthancPlugins::OrthancConfiguration globalConfig; - Orthanc::InitializeFramework(globalConfig.GetStringValue(LOCALE, ""), false /* loadPrivateDictionary */); - - std::string encoding; - if (globalConfig.LookupStringValue(encoding, DEFAULT_ENCODING)) - { - Orthanc::SetDefaultDicomEncoding(Orthanc::StringToEncoding(encoding.c_str())); - } - else - { - Orthanc::SetDefaultDicomEncoding(Orthanc::ORTHANC_DEFAULT_DICOM_ENCODING); - } - } - ORTHANC_PLUGINS_LOG_WARNING("Sample worklist plugin is initializing"); OrthancPluginSetDescription2(c, MODALITY_WORKLISTS_NAME, "Serve DICOM modality worklists from a folder with Orthanc."); @@ -771,52 +237,19 @@ bool enabled = worklists.GetBooleanValue("Enable", false); if (enabled) { - std::string folder; - if (worklists.LookupStringValue(folder, "Database") || worklists.LookupStringValue(folder, "Directory")) + if (worklists.LookupStringValue(folder_, "Database")) { - if (worklists.GetBooleanValue("SaveInOrthancDatabase", false)) - { - LOG(ERROR) << "Worklists plugin: you can not set the \"SaveInOrthancDatabase\" configuration to \"true\" once you have configured the \"Directory\" (or former \"Database\") configuration."; - return -1; - } - - worklistStorage_ = WorklistStorageType_Folder; - worklistDirectory_ = folder; //Orthanc::SystemToolbox::PathFromUtf8(folder); - LOG(WARNING) << "The database of worklists will be read from folder: " << folder; - } - else if (worklists.GetBooleanValue("SaveInOrthancDatabase", false)) - { - - worklistStorage_ = WorklistStorageType_OrthancDb; - ORTHANC_PLUGINS_LOG_WARNING("The database of worklists will be read from Orthanc Database"); + ORTHANC_PLUGINS_LOG_WARNING("The database of worklists will be read from folder: " + folder_); + OrthancPluginRegisterWorklistCallback(OrthancPlugins::GetGlobalContext(), Callback); } else { - LOG(ERROR) << "The configuration option \"Worklists.Directory\" must contain a path"; + ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"Worklists.Database\" must contain a path"); return -1; } - OrthancPluginRegisterWorklistCallback(OrthancPlugins::GetGlobalContext(), WorklistCallback); - filterIssuerAet_ = worklists.GetBooleanValue("FilterIssuerAet", false); limitAnswers_ = worklists.GetUnsignedIntegerValue("LimitAnswers", 0); - - deleteWorklistsOnStableStudy_ = worklists.GetBooleanValue("DeleteWorklistsOnStableStudy", true); - hkIntervalInSeconds_ = worklists.GetUnsignedIntegerValue("HousekeepingInterval", 60); - deleteDelayInHours_ = worklists.GetUnsignedIntegerValue("DeleteWorklistsDelay", 0); - setStudyInstanceUidIfMissing_ = worklists.GetBooleanValue("SetStudyInstanceUidIfMissing", true); - - if (deleteDelayInHours_ > 0 && worklistStorage_ == WorklistStorageType_Folder) - { - LOG(ERROR) << "Worklists plugin: you can not set the \"DeleteWorklistsDelay\" configuration once you have configured the \"Directory\" (or former \"Database\") configuration. This feature only works once \"SaveInOrthancDatabase\" is set to true."; - return -1; - } - - OrthancPluginRegisterOnChangeCallback(OrthancPlugins::GetGlobalContext(), OnChangeCallback); - - OrthancPluginRegisterRestCallback(OrthancPlugins::GetGlobalContext(), "/worklists/create", PostCreateWorklist); - OrthancPluginRegisterRestCallback(OrthancPlugins::GetGlobalContext(), "/worklists/([^/]+)", GetPutDeleteWorklist); - OrthancPluginRegisterRestCallback(OrthancPlugins::GetGlobalContext(), "/worklists", ListWorklists); } else { @@ -830,13 +263,6 @@ ORTHANC_PLUGINS_API void OrthancPluginFinalize() { ORTHANC_PLUGINS_LOG_WARNING("Sample worklist plugin is finalizing"); - - worklistHousekeeperThreadShouldStop_ = true; - if (worklistHousekeeperThread_.get() != NULL && worklistHousekeeperThread_->joinable()) - { - worklistHousekeeperThread_->join(); - } - worklistHousekeeperThread_.reset(NULL); }
--- a/OrthancServer/Plugins/Samples/WebDavFilesystem/Plugin.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/WebDavFilesystem/Plugin.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -23,6 +23,8 @@ #include "../Common/OrthancPluginCppWrapper.h" +#include "../../../../OrthancFramework/Sources/Compatibility.h" + #include <boost/thread/mutex.hpp> @@ -69,12 +71,12 @@ return content_; } - virtual bool IsFolder() const + virtual bool IsFolder() const ORTHANC_OVERRIDE { return false; } - virtual Resource* LookupPath(const std::vector<std::string>& path) + virtual Resource* LookupPath(const std::vector<std::string>& path) ORTHANC_OVERRIDE { if (path.empty()) { @@ -96,7 +98,7 @@ Content content_; public: - virtual ~Folder() + virtual ~Folder() ORTHANC_OVERRIDE { for (Content::iterator it = content_.begin(); it != content_.end(); ++it) { @@ -105,12 +107,12 @@ } } - virtual bool IsFolder() const + virtual bool IsFolder() const ORTHANC_OVERRIDE { return true; } - virtual Resource* LookupPath(const std::vector<std::string>& path) + virtual Resource* LookupPath(const std::vector<std::string>& path) ORTHANC_OVERRIDE { if (path.empty()) { @@ -242,7 +244,7 @@ { boost::mutex::scoped_lock lock(mutex_); - Resource* resource = root_->LookupPath(path); + const Resource* resource = root_->LookupPath(path); return (resource != NULL && resource->IsFolder()); }
--- a/OrthancServer/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -194,8 +194,8 @@ } else { - std::string s = "Unknown static resource in plugin: " + std::string(request->groups[0]); - OrthancPluginLogError(context, s.c_str()); + const std::string t = "Unknown static resource in plugin: " + std::string(request->groups[0]); + OrthancPluginLogError(context, t.c_str()); OrthancPluginSendHttpStatusCode(context, output, 404); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Resources/RunCppCheck-2.1.sh Tue Nov 18 17:50:39 2025 +0100 @@ -0,0 +1,111 @@ +#!/bin/bash +# Note: This script is tuned to run with cppcheck v2.1 + +set -ex + +CPPCHECK=cppcheck + +if [ $# -ge 1 ]; then + CPPCHECK=$1 +fi + +cat <<EOF > /tmp/cppcheck-suppressions.txt +constParameter:../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp +knownArgument:../../OrthancFramework/UnitTestsSources/ImageTests.cpp +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp +nullPointer:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp:322 +stlFindInsert:../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp:1535 +stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:166 +stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:74 +stlFindInsert:../../OrthancServer/Sources/Database/MainDicomTagsRegistry.cpp:65 +stlFindInsert:../../OrthancServer/Sources/OrthancWebDav.cpp:328 +stlFindInsert:../../OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp:41 +stlFindInsert:../../OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp:191 +stlFindInsert:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:361 +syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53 +syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74 +syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133 +syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:325 +unreadVariable:../../OrthancFramework/Sources/FileStorage/StorageAccessor.cpp +unreadVariable:../../OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp:1173 +useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91 +useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99 +useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275 +assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:277 +assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:1026 +assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:292 +assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:391 +assertWithSideEffect:../../OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp:3068 +assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286 +assertWithSideEffect:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:454 +EOF + +CPPCHECK_BUILD_DIR=/tmp/cppcheck-build-dir-2.1/ +mkdir -p ${CPPCHECK_BUILD_DIR} + +${CPPCHECK} -j8 --enable=all --quiet --std=c++11 \ + --cppcheck-build-dir=${CPPCHECK_BUILD_DIR} \ + --suppressions-list=/tmp/cppcheck-suppressions.txt \ + --suppress=unusedFunction \ + -DBOOST_HAS_DATE_TIME=1 \ + -DBOOST_HAS_FILESYSTEM_V3=1 \ + -DBOOST_HAS_REGEX=1 \ + -DCIVETWEB_HAS_DISABLE_KEEP_ALIVE=1 \ + -DCIVETWEB_HAS_WEBDAV_WRITING=1 \ + -DDCMTK_VERSION_NUMBER=365 \ + -DHAVE_MALLOPT=1 \ + -DHAVE_MALLOC_TRIM=1 \ + -DMONGOOSE_USE_CALLBACKS=1 \ + -DJSONCPP_VERSION_MAJOR=1 \ + -DJSONCPP_VERSION_MINOR=0 \ + -DORTHANC_BUILDING_FRAMEWORK_LIBRARY=0 \ + -DORTHANC_BUILD_UNIT_TESTS=1 \ + -DORTHANC_ENABLE_BASE64=1 \ + -DORTHANC_ENABLE_CIVETWEB=1 \ + -DORTHANC_ENABLE_CURL=1 \ + -DORTHANC_ENABLE_DCMTK=1 \ + -DORTHANC_ENABLE_DCMTK_JPEG=1 \ + -DORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS=1 \ + -DORTHANC_ENABLE_DCMTK_NETWORKING=1 \ + -DORTHANC_ENABLE_DCMTK_TRANSCODING=1 \ + -DORTHANC_ENABLE_JPEG=1 \ + -DORTHANC_ENABLE_LOCALE=1 \ + -DORTHANC_ENABLE_LOGGING=1 \ + -DORTHANC_ENABLE_LOGGING_STDIO=1 \ + -DORTHANC_ENABLE_LUA=1 \ + -DORTHANC_ENABLE_MD5=1 \ + -DORTHANC_ENABLE_MONGOOSE=0 \ + -DORTHANC_ENABLE_PKCS11=1 \ + -DORTHANC_ENABLE_PLUGINS=1 \ + -DORTHANC_ENABLE_PNG=1 \ + -DORTHANC_ENABLE_PUGIXML=1 \ + -DORTHANC_ENABLE_SQLITE=1 \ + -DORTHANC_ENABLE_SSL=1 \ + -DORTHANC_ENABLE_ZLIB=1 \ + -DORTHANC_SANDBOXED=0 \ + -DORTHANC_SQLITE_VERSION=3027001 \ + -DORTHANC_UNIT_TESTS_LINK_FRAMEWORK=1 \ + -DPUGIXML_VERSION=150 \ + -DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1 \ + -D__BYTE_ORDER=__LITTLE_ENDIAN \ + -D__GNUC__ \ + -D__cplusplus=201103 \ + -D__linux__ \ + -UNDEBUG \ + -DHAS_ORTHANC_EXCEPTION=1 \ + \ + ../../OrthancFramework/Sources \ + ../../OrthancFramework/UnitTestsSources \ + ../../OrthancServer/Plugins/Engine \ + ../../OrthancServer/Plugins/Include \ + ../../OrthancServer/Sources \ + ../../OrthancServer/UnitTestsSources \ + ../../OrthancServer/Plugins/Samples/Common \ + ../../OrthancServer/Plugins/Samples/ConnectivityChecks \ + ../../OrthancServer/Plugins/Samples/DelayedDeletion \ + ../../OrthancServer/Plugins/Samples/Housekeeper \ + ../../OrthancServer/Plugins/Samples/ModalityWorklists \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom \ + ../../OrthancServer/Plugins/Samples/AdoptDicomInstance \ + \ + 2>&1
--- a/OrthancServer/Resources/RunCppCheck-2.17.0.sh Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Resources/RunCppCheck-2.17.0.sh Tue Nov 18 17:50:39 2025 +0100 @@ -1,4 +1,5 @@ #!/bin/bash +# Note: This script is tuned to run with cppcheck v2.17.0 set -ex @@ -36,7 +37,11 @@ assertWithSideEffect:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:454 EOF -${CPPCHECK} --enable=all --std=gnu++11 --library=boost \ +CPPCHECK_BUILD_DIR=/tmp/cppcheck-build-dir-2.7.0/ +mkdir -p ${CPPCHECK_BUILD_DIR} + +${CPPCHECK} -j8 --enable=all --std=gnu++11 --library=boost \ + --cppcheck-build-dir=${CPPCHECK_BUILD_DIR} \ --suppressions-list=/tmp/cppcheck-suppressions.txt \ -I/usr/include/ \ -I/usr/include/jsoncpp/ \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Resources/RunCppCheck-2.17.1.sh Tue Nov 18 17:50:39 2025 +0100 @@ -0,0 +1,147 @@ +#!/bin/bash +# Note: This script is tuned to run with cppcheck v2.17.1 + +set -ex + +CPPCHECK=cppcheck + +if [ $# -ge 1 ]; then + CPPCHECK=$1 +fi + +cat <<EOF > /tmp/cppcheck-suppressions.txt +assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:292 +assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:391 +assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286 +constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp:50 +constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp:112 +constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp:113 +constParameterCallback:../../OrthancFramework/Sources/Pkcs11.cpp:125 +constParameterCallback:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp:3489 +constParameterCallback:../../OrthancServer/Sources/OrthancGetRequestHandler.cpp:47 +constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:447 +constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:451 +constParameterPointer:../../OrthancFramework/Sources/Toolbox.cpp:3046 +cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:108 +cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:124 +cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:140 +cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:85 +knownConditionTrueFalse:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:114 +knownConditionTrueFalse:../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp:425 +knownConditionTrueFalse:../../OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp:345 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2291 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2292 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2293 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2294 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2295 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2296 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2297 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2298 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2299 +knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp:2300 +nullPointer:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp:322 +stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:166 +stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:74 +syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53 +syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74 +syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133 +syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:325 +useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91 +useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99 +useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275 +variableScope:../../OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp:228 +variableScope:../../OrthancServer/Sources/ServerJobs/OrthancPeerStoreJob.cpp:94 +EOF + +CPPCHECK_BUILD_DIR=/tmp/cppcheck-build-dir-2.7.1/ +mkdir -p ${CPPCHECK_BUILD_DIR} + +${CPPCHECK} -j8 --enable=all --quiet --std=c++11 \ + --cppcheck-build-dir=${CPPCHECK_BUILD_DIR} \ + --suppress=unusedFunction \ + --suppress=missingIncludeSystem \ + --suppress=missingInclude \ + --suppress=useStlAlgorithm \ + --check-level=exhaustive \ + --suppressions-list=/tmp/cppcheck-suppressions.txt \ + -DBOOST_HAS_DATE_TIME=1 \ + -DBOOST_HAS_FILESYSTEM_V3=1 \ + -DBOOST_HAS_REGEX=1 \ + -DCIVETWEB_HAS_DISABLE_KEEP_ALIVE=1 \ + -DCIVETWEB_HAS_WEBDAV_WRITING=1 \ + -DDCMTK_VERSION_NUMBER=365 \ + -DHAVE_MALLOPT=1 \ + -DHAVE_MALLOC_TRIM=1 \ + -DMONGOOSE_USE_CALLBACKS=1 \ + -DJSONCPP_VERSION_MAJOR=1 \ + -DJSONCPP_VERSION_MINOR=0 \ + -DORTHANC_BUILDING_FRAMEWORK_LIBRARY=0 \ + -DORTHANC_BUILD_UNIT_TESTS=1 \ + -DORTHANC_ENABLE_BASE64=1 \ + -DORTHANC_ENABLE_CIVETWEB=1 \ + -DORTHANC_ENABLE_CURL=1 \ + -DORTHANC_ENABLE_DCMTK=1 \ + -DORTHANC_ENABLE_DCMTK_JPEG=1 \ + -DORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS=1 \ + -DORTHANC_ENABLE_DCMTK_NETWORKING=1 \ + -DORTHANC_ENABLE_DCMTK_TRANSCODING=1 \ + -DORTHANC_ENABLE_JPEG=1 \ + -DORTHANC_ENABLE_LOCALE=1 \ + -DORTHANC_ENABLE_LOGGING=1 \ + -DORTHANC_ENABLE_LOGGING_STDIO=1 \ + -DORTHANC_ENABLE_LUA=1 \ + -DORTHANC_ENABLE_MD5=1 \ + -DORTHANC_ENABLE_MONGOOSE=0 \ + -DORTHANC_ENABLE_PKCS11=1 \ + -DORTHANC_ENABLE_PLUGINS=1 \ + -DORTHANC_ENABLE_PNG=1 \ + -DORTHANC_ENABLE_PUGIXML=1 \ + -DORTHANC_ENABLE_SQLITE=1 \ + -DORTHANC_ENABLE_SSL=1 \ + -DORTHANC_ENABLE_ZLIB=1 \ + -DORTHANC_SANDBOXED=0 \ + -DORTHANC_SQLITE_VERSION=3027001 \ + -DORTHANC_UNIT_TESTS_LINK_FRAMEWORK=1 \ + -DORTHANC_VERSION="\"mainline\"" \ + -DPUGIXML_VERSION=150 \ + -DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1 \ + -D__BYTE_ORDER=__LITTLE_ENDIAN \ + -D__GNUC__ \ + -D__cplusplus=201103 \ + -D__linux__ \ + -UNDEBUG \ + -DHAS_ORTHANC_EXCEPTION=1 \ + \ + ../../OrthancFramework/Sources \ + ../../OrthancFramework/UnitTestsSources \ + ../../OrthancServer/Plugins/Engine \ + ../../OrthancServer/Plugins/Include \ + ../../OrthancServer/Sources \ + ../../OrthancServer/UnitTestsSources \ + ../../OrthancServer/Plugins/Samples/Common \ + \ + ../../OrthancServer/Plugins/Samples/AdoptDicomInstance/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/AutomatedJpeg2kCompression/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp \ + ../../OrthancServer/Plugins/Samples/ConnectivityChecks/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/CppSkeleton/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/CustomImageDecoder/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/DelayedDeletion/LargeDeleteJob.cpp \ + ../../OrthancServer/Plugins/Samples/DelayedDeletion/PendingDeletionsDatabase.cpp \ + ../../OrthancServer/Plugins/Samples/DelayedDeletion/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.cpp \ + ../../OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp \ + ../../OrthancServer/Plugins/Samples/Sanitizer/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/StorageArea/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/StorageCommitmentScp/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/WebDavFilesystem/Plugin.cpp \ + ../../OrthancServer/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp \ + \ + 2>&1
--- a/OrthancServer/Resources/RunCppCheck.sh Sat Nov 15 12:28:27 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -#!/bin/bash -# note: this script was last tuned to run with cppcheck v2.17.1 -set -ex - -CPPCHECK=cppcheck - -if [ $# -ge 1 ]; then - CPPCHECK=$1 -fi - -cat <<EOF > /tmp/cppcheck-suppressions.txt -knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp -nullPointer:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp:322 -stlFindInsert:../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp:1525 -stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:166 -stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:74 -syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53 -syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74 -syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133 -syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:325 -uninitMemberVar:../../OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp:419 -useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91 -useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99 -useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275 -assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:292 -assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:391 -assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286 -variableScope:../../OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp:228 -variableScope:../../OrthancServer/Sources/ServerJobs/OrthancPeerStoreJob.cpp:94 -uselessOverride:../../OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h:35 -uselessOverride:../../OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h:76 -cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:85 -cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:108 -cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:124 -cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:140 -constParameterPointer:../../OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h -constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:447 -constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:451 -constParameterPointer:../../OrthancFramework/Sources/Toolbox.cpp:3053 -knownConditionTrueFalse:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:114 -knownConditionTrueFalse:../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp:425 -knownConditionTrueFalse:../../OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp:345 -throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryStringCache.cpp:121 -throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryCache.cpp:91 -throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryObjectCache.cpp:99 -throwInNoexceptFunction:../../OrthancFramework/Sources/MallocMemoryBuffer.h:50 -throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:620 -throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:632 -throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:676 -throwInNoexceptFunction:../../OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp:1296 -throwInNoexceptFunction:../../OrthancServer/Sources/LuaScripting.cpp:830 -throwInNoexceptFunction:../../OrthancServer/Sources/StorageCommitmentReports.cpp:187 -throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:218 -throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:376 -throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:496 -rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp -rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp -constParameterPointer:../../OrthancFramework/Sources/Toolbox.cpp:3046 -constParameterCallback:../../OrthancServer/Sources/OrthancGetRequestHandler.cpp -constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp -constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp -constParameterCallback:../../OrthancFramework/Sources/Pkcs11.cpp -constParameterCallback:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp:3449 -unknownMacro:../../OrthancFramework/Sources/DicomParsing/DicomModification.cpp:39 -EOF - - -${CPPCHECK} -j 8 --enable=all --quiet --std=c++11 \ - --suppress=missingIncludeSystem \ - --suppress=missingInclude \ - --suppress=useStlAlgorithm \ - --check-level=exhaustive \ - --suppressions-list=/tmp/cppcheck-suppressions.txt \ - -DBOOST_HAS_DATE_TIME=1 \ - -DBOOST_HAS_FILESYSTEM_V3=1 \ - -DBOOST_HAS_REGEX=1 \ - -DCIVETWEB_HAS_DISABLE_KEEP_ALIVE=1 \ - -DCIVETWEB_HAS_WEBDAV_WRITING=1 \ - -DDCMTK_VERSION_NUMBER=365 \ - -DHAVE_MALLOPT=1 \ - -DHAVE_MALLOC_TRIM=1 \ - -DMONGOOSE_USE_CALLBACKS=1 \ - -DJSONCPP_VERSION_MAJOR=1 \ - -DJSONCPP_VERSION_MINOR=0 \ - -DORTHANC_BUILDING_FRAMEWORK_LIBRARY=0 \ - -DORTHANC_BUILD_UNIT_TESTS=1 \ - -DORTHANC_ENABLE_BASE64=1 \ - -DORTHANC_ENABLE_CIVETWEB=1 \ - -DORTHANC_ENABLE_CURL=1 \ - -DORTHANC_ENABLE_DCMTK=1 \ - -DORTHANC_ENABLE_DCMTK_JPEG=1 \ - -DORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS=1 \ - -DORTHANC_ENABLE_DCMTK_NETWORKING=1 \ - -DORTHANC_ENABLE_DCMTK_TRANSCODING=1 \ - -DORTHANC_ENABLE_JPEG=1 \ - -DORTHANC_ENABLE_LOCALE=1 \ - -DORTHANC_ENABLE_LOGGING=1 \ - -DORTHANC_ENABLE_LOGGING_STDIO=1 \ - -DORTHANC_ENABLE_LUA=1 \ - -DORTHANC_ENABLE_MD5=1 \ - -DORTHANC_ENABLE_MONGOOSE=0 \ - -DORTHANC_ENABLE_PKCS11=1 \ - -DORTHANC_ENABLE_PLUGINS=1 \ - -DORTHANC_ENABLE_PNG=1 \ - -DORTHANC_ENABLE_PUGIXML=1 \ - -DORTHANC_ENABLE_SQLITE=1 \ - -DORTHANC_ENABLE_SSL=1 \ - -DORTHANC_ENABLE_ZLIB=1 \ - -DORTHANC_SANDBOXED=0 \ - -DORTHANC_SQLITE_VERSION=3027001 \ - -DORTHANC_UNIT_TESTS_LINK_FRAMEWORK=1 \ - -DPUGIXML_VERSION=150 \ - -DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1 \ - -D__BYTE_ORDER=__LITTLE_ENDIAN \ - -D__GNUC__ \ - -D__cplusplus=201103 \ - -D__linux__ \ - -UNDEBUG \ - -DHAS_ORTHANC_EXCEPTION=1 \ - \ - ../../OrthancFramework/Sources \ - ../../OrthancFramework/UnitTestsSources \ - ../../OrthancServer/Plugins/Engine \ - ../../OrthancServer/Plugins/Include \ - ../../OrthancServer/Sources \ - ../../OrthancServer/UnitTestsSources \ - ../../OrthancServer/Plugins/Samples/Common \ - ../../OrthancServer/Plugins/Samples/ConnectivityChecks \ - ../../OrthancServer/Plugins/Samples/DelayedDeletion \ - ../../OrthancServer/Plugins/Samples/Housekeeper \ - ../../OrthancServer/Plugins/Samples/ModalityWorklists \ - ../../OrthancServer/Plugins/Samples/MultitenantDicom \ - ../../OrthancServer/Plugins/Samples/AdoptDicomInstance \ - \ - 2>&1
--- a/OrthancServer/Sources/LuaScripting.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Sources/LuaScripting.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -827,7 +827,16 @@ if (state_ == State_Running) { LOG(ERROR) << "INTERNAL ERROR: LuaScripting::Stop() should be invoked manually to avoid mess in the destruction order!"; - Stop(); + + try + { + Stop(); + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } }
--- a/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -380,6 +380,6 @@ Json::Value publicContent; GetPublicContent(publicContent); - payload.SetContent(ErrorPayloadType_Dimse, publicContent["Details"]); + payload.SetContent(ErrorPayloadType_RetrieveJob, publicContent["Details"]); } }
--- a/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -187,7 +187,6 @@ explicit Unserializer(StorageCommitmentScpJob& that) : that_(that) { - that_.ready_ = false; } virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE @@ -420,9 +419,9 @@ const Json::Value& serialized) : SetOfCommandsJob(new Unserializer(*this), serialized), context_(context), + ready_(false), transactionUid_(SerializationToolbox::ReadString(serialized, TRANSACTION_UID)), calledAet_(SerializationToolbox::ReadString(serialized, CALLED_AET)) - // "ready_" is initialized by the unserializer { if (serialized.type() != Json::objectValue || !serialized.isMember(REMOTE_MODALITY))
--- a/OrthancServer/Sources/StorageCommitmentReports.cpp Sat Nov 15 12:28:27 2025 +0100 +++ b/OrthancServer/Sources/StorageCommitmentReports.cpp Tue Nov 18 17:50:39 2025 +0100 @@ -24,6 +24,7 @@ #include "PrecompiledHeadersServer.h" #include "StorageCommitmentReports.h" +#include "../../OrthancFramework/Sources/Logging.h" #include "../../OrthancFramework/Sources/OrthancException.h" namespace Orthanc @@ -184,10 +185,18 @@ while (!content_.IsEmpty()) { Report* report = NULL; - content_.RemoveOldest(report); - assert(report != NULL); - delete report; + try + { + content_.RemoveOldest(report); + assert(report != NULL); + delete report; + } + catch (OrthancException& e) + { + // Don't throw exceptions in destructors + LOG(ERROR) << "Exception in destructor: " << e.What(); + } } }
