# HG changeset patch # User Alain Mazy # Date 1695193351 -7200 # Node ID c026301eee9e15c3f62cb194c6584f7b6153ebf8 # Parent 579f541fb9ceb90ec401dc32475f7b71bbbcaf36# Parent b074943fca35d4e2ebe68f7c985f388b77ada7cd merged Orthanc-1.12.1 back to mainline (todo file was commited in the wrong branch) diff -r b074943fca35 -r c026301eee9e NEWS --- a/NEWS Tue Sep 19 10:56:09 2023 +0200 +++ b/NEWS Wed Sep 20 09:02:31 2023 +0200 @@ -1,6 +1,29 @@ Pending changes in the mainline =============================== +General +------- + +* 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. + +REST API +-------- + +* API version upgraded to 22 +* Added a route to delete completed jobs from history: DELETE /jobs/{id} + +Maintenance +----------- + +* Fix unit test PngWriter.Color16Pattern on big-endian architectures, + as suggested by Etienne Mollier: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1041813 +* 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 + Version 1.12.1 (2023-07-04) =========================== diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CMake/Compiler.cmake --- a/OrthancFramework/Resources/CMake/Compiler.cmake Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CMake/Compiler.cmake Wed Sep 20 09:02:31 2023 +0200 @@ -263,3 +263,24 @@ # preceding batches. https://cmake.org/Bug/view.php?id=14874 set(CMAKE_CXX_ARCHIVE_APPEND " q ") endif() + + +# This function defines macro "__ORTHANC_FILE__" as a replacement to +# macro "__FILE__", as the latter leaks the full path of the source +# files in the binaries +# https://stackoverflow.com/questions/8487986/file-macro-shows-full-path +# https://twitter.com/wget42/status/1676877802375634944?s=20 +function(DefineSourceBasenameForTarget targetname) + # Microsoft Visual Studio is extremely slow if using + # "set_property()", we only enable this feature for gcc and clang + if (CMAKE_COMPILER_IS_GNUCXX OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + get_target_property(source_files "${targetname}" SOURCES) + foreach(sourcefile ${source_files}) + get_filename_component(basename "${sourcefile}" NAME) + set_property( + SOURCE "${sourcefile}" APPEND + PROPERTY COMPILE_DEFINITIONS "__ORTHANC_FILE__=\"${basename}\"") + endforeach() + endif() +endfunction() diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake --- a/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Wed Sep 20 09:02:31 2023 +0200 @@ -153,9 +153,11 @@ elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.2") set(ORTHANC_FRAMEWORK_MD5 "ede3de356493a8868545f8cb4b8bc8b5") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.3") - set(ORTHANC_FRAMEWORK_MD5 "5c1b11009d782f248739919db6bf7f7a") + set(ORTHANC_FRAMEWORK_MD5 "f941c0f5771db7616e7b7961026a60e2") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.0") set(ORTHANC_FRAMEWORK_MD5 "d32a0cde03b6eb603d8dd2b33d38bf1b") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.1") + set(ORTHANC_FRAMEWORK_MD5 "8a435140efc8ff4a01d8242f092f21de") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc @@ -179,6 +181,9 @@ elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "b2e08d83e21d") # WSI 1.1 (framework pre-1.10.0), to remove "-std=c++11" set(ORTHANC_FRAMEWORK_MD5 "2eaa073cbb4b44ffba199ad93393b2b1") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "daf4807631c5") + # DICOMweb 1.15 (framework pre-1.12.2) + set(ORTHANC_FRAMEWORK_MD5 "c644aff2817306b3207c98c92e43f35f") endif() endif() endif() diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CMake/LibCurlConfiguration.cmake --- a/OrthancFramework/Resources/CMake/LibCurlConfiguration.cmake Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CMake/LibCurlConfiguration.cmake Wed Sep 20 09:02:31 2023 +0200 @@ -109,7 +109,7 @@ endif() set_property( - SOURCE ${CURL_SOURCES} + SOURCE ${CURL_SOURCES} APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H=1;OS=\"${TMP_OS}\"" ) diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake --- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Wed Sep 20 09:02:31 2023 +0200 @@ -24,7 +24,7 @@ ##################################################################### # Version of the build, should always be "mainline" except in release branches -set(ORTHANC_VERSION "1.12.1") +set(ORTHANC_VERSION "mainline") # Version of the database schema. History: # * Orthanc 0.1.0 -> Orthanc 0.3.0 = no versioning @@ -38,7 +38,7 @@ # Version of the Orthanc API, can be retrieved from "/system" URI in # order to check whether new URI endpoints are available even if using # the mainline version of Orthanc -set(ORTHANC_API_VERSION "21") +set(ORTHANC_API_VERSION "22") ##################################################################### diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CodeGeneration/ErrorCodes.json --- a/OrthancFramework/Resources/CodeGeneration/ErrorCodes.json Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CodeGeneration/ErrorCodes.json Wed Sep 20 09:02:31 2023 +0200 @@ -250,6 +250,12 @@ "Name": "MainDicomTagsMultiplyDefined", "Description": "A main DICOM Tag has been defined multiple times for the same resource level" }, + { + "Code": 45, + "HttpStatus": 403, + "Name": "ForbiddenAccess", + "Description": "Access to a resource is forbidden" + }, diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py --- a/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Wed Sep 20 09:02:31 2023 +0200 @@ -122,7 +122,7 @@ with open(path, 'r') as f: a = f.read() -e = filter(lambda x: 'SQLite' in x and x['SQLite'], ERRORS) +e = list(filter(lambda x: 'SQLite' in x and x['SQLite'], ERRORS)) s = ',\n'.join(map(lambda x: ' ErrorCode_%s' % x['Name'], e)) a = re.sub('(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL) diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/Patches/civetweb-1.14.patch --- a/OrthancFramework/Resources/Patches/civetweb-1.14.patch Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/Patches/civetweb-1.14.patch Wed Sep 20 09:02:31 2023 +0200 @@ -1,6 +1,38 @@ diff -urEb civetweb-1.14.orig/src/civetweb.c civetweb-1.14/src/civetweb.c ---- civetweb-1.14.orig/src/civetweb.c 2021-06-21 17:42:52.343136123 +0200 -+++ civetweb-1.14/src/civetweb.c 2021-06-21 17:43:11.623158128 +0200 +--- civetweb-1.14.orig/src/civetweb.c 2023-07-06 15:48:01.163703913 +0200 ++++ civetweb-1.14/src/civetweb.c 2023-07-06 15:48:51.207843938 +0200 +@@ -567,7 +567,7 @@ + #if (_MSC_VER < 1300) + #define STRX(x) #x + #define STR(x) STRX(x) +-#define __func__ __FILE__ ":" STR(__LINE__) ++#define __func__ __ORTHANC_FILE__ ":" STR(__LINE__) + #define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) + #define strtoll(x, y, z) (_atoi64(x)) + #else +@@ -1450,14 +1450,14 @@ + } + + +-#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__) +-#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__) +-#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__) +-#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) +- +-#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__) +-#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__) +-#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__) ++#define mg_malloc(a) mg_malloc_ex(a, NULL, __ORTHANC_FILE__, __LINE__) ++#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __ORTHANC_FILE__, __LINE__) ++#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __ORTHANC_FILE__, __LINE__) ++#define mg_free(a) mg_free_ex(a, __ORTHANC_FILE__, __LINE__) ++ ++#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __ORTHANC_FILE__, __LINE__) ++#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __ORTHANC_FILE__, __LINE__) ++#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __ORTHANC_FILE__, __LINE__) + + + #else /* USE_SERVER_STATS */ @@ -1774,6 +1774,7 @@ #if !defined(OPENSSL_API_3_0) #define OPENSSL_API_3_0 diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/ProtocolBuffers/CMakeLists.txt --- a/OrthancFramework/Resources/ProtocolBuffers/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/ProtocolBuffers/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -133,7 +133,7 @@ if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property( - SOURCE ${PROTOBUF_COMPILER_SOURCES} + SOURCE ${PROTOBUF_COMPILER_SOURCES} APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_PTHREAD=1" ) endif() diff -r b074943fca35 -r c026301eee9e OrthancFramework/Resources/ProtocolBuffers/ProtobufLibrary.cmake --- a/OrthancFramework/Resources/ProtocolBuffers/ProtobufLibrary.cmake Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Resources/ProtocolBuffers/ProtobufLibrary.cmake Wed Sep 20 09:02:31 2023 +0200 @@ -138,7 +138,7 @@ if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property( - SOURCE ${PROTOBUF_LIBRARY_SOURCES} + SOURCE ${PROTOBUF_LIBRARY_SOURCES} APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_PTHREAD=1" ) endif() diff -r b074943fca35 -r c026301eee9e OrthancFramework/SharedLibrary/CMakeLists.txt --- a/OrthancFramework/SharedLibrary/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/SharedLibrary/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -257,6 +257,8 @@ ${ORTHANC_DICOM_SOURCES} ) + DefineSourceBasenameForTarget(OrthancFramework) + # CMake does not natively handle SIDE_MODULE, and believes that # Emscripten produces a ".js" file (whereas it creates only the # ".wasm"). Create a dummy ".js" for target to work. @@ -273,6 +275,8 @@ DllMain.cpp ) + DefineSourceBasenameForTarget(OrthancFramework) + # By default, hide all the symbols set_target_properties(OrthancFramework PROPERTIES C_VISIBILITY_PRESET hidden) set_target_properties(OrthancFramework PROPERTIES CXX_VISIBILITY_PRESET hidden) @@ -301,10 +305,14 @@ ${ORTHANC_DICOM_SOURCES} ) + DefineSourceBasenameForTarget(OrthancFramework) + # Add the "-fPIC" option to use the static library from Orthanc # plugins (the latter being shared libraries) set_property(TARGET OrthancFramework PROPERTY POSITION_INDEPENDENT_CODE ON) endif() + + DefineSourceBasenameForTarget(OrthancFramework) endif() diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/Enumerations.cpp --- a/OrthancFramework/Sources/Enumerations.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/Enumerations.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -199,6 +199,9 @@ case ErrorCode_MainDicomTagsMultiplyDefined: return "A main DICOM Tag has been defined multiple times for the same resource level"; + case ErrorCode_ForbiddenAccess: + return "Access to a resource is forbidden"; + case ErrorCode_SQLiteNotOpened: return "SQLite: The database is not opened"; @@ -2278,6 +2281,9 @@ case ErrorCode_Revision: return HttpStatus_409_Conflict; + case ErrorCode_ForbiddenAccess: + return HttpStatus_403_Forbidden; + case ErrorCode_CreateDicomNotString: return HttpStatus_400_BadRequest; diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/Enumerations.h --- a/OrthancFramework/Sources/Enumerations.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/Enumerations.h Wed Sep 20 09:02:31 2023 +0200 @@ -148,6 +148,7 @@ ErrorCode_DatabaseCannotSerialize = 42 /*!< Database could not serialize access due to concurrent update, the transaction should be retried */, ErrorCode_Revision = 43 /*!< A bad revision number was provided, which might indicate conflict between multiple writers */, ErrorCode_MainDicomTagsMultiplyDefined = 44 /*!< A main DICOM Tag has been defined multiple times for the same resource level */, + ErrorCode_ForbiddenAccess = 45 /*!< Access to a resource is forbidden */, ErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, ErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, ErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp --- a/OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -122,8 +122,16 @@ size_t size, FileContentType type) { - LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) - << "\" type (size: " << (size / (1024 * 1024) + 1) << "MB)"; + if (size > (1024 * 1024)) + { + LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) + << "\" type (size: " << (size / (1024 * 1024) + 1) << "MB)"; + } + else + { + LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) + << "\" type (size: " << (size / 1024 + 1) << "KB)"; + } boost::filesystem::path path; diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/JobsEngine/IJob.h --- a/OrthancFramework/Sources/JobsEngine/IJob.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/JobsEngine/IJob.h Wed Sep 20 09:02:31 2023 +0200 @@ -66,5 +66,9 @@ // This function can only be called if the job has reached its // "success" state virtual bool DeleteOutput(const std::string& key) = 0; + + // This function can only be called if the job has reached its + // "success" state + virtual void DeleteAllOutputs() {} }; } diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp --- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -649,6 +649,42 @@ } + bool JobsRegistry::DeleteJobInfo(const std::string& id) + { + LOG(INFO) << "Deleting job: " << id; + + boost::mutex::scoped_lock lock(mutex_); + CheckInvariants(); + + JobsIndex::iterator found = jobsIndex_.find(id); + + if (found == jobsIndex_.end()) + { + LOG(WARNING) << "Unknown job to delete: " << id; + return false; + } + else + { + for (CompletedJobs::iterator it = completedJobs_.begin(); + it != completedJobs_.end(); ++it) + { + if (*it == found->second) + { + found->second->GetJob().DeleteAllOutputs(); + delete found->second; + + completedJobs_.erase(it); + jobsIndex_.erase(id); + return true; + } + } + + LOG(WARNING) << "Can not delete a job that is not complete: " << id; + return false; + } + } + + bool JobsRegistry::GetJobOutput(std::string& output, MimeType& mime, std::string& filename, diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/JobsEngine/JobsRegistry.h --- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Wed Sep 20 09:02:31 2023 +0200 @@ -147,6 +147,8 @@ bool GetJobInfo(JobInfo& target, const std::string& id); + bool DeleteJobInfo(const std::string& id); + bool GetJobOutput(std::string& output, MimeType& mime, std::string& filename, diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/Logging.h --- a/OrthancFramework/Sources/Logging.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/Logging.h Wed Sep 20 09:02:31 2023 +0200 @@ -157,15 +157,25 @@ # define VLOG(unused) ::Orthanc::Logging::NullStream() # define CLOG(level, category) ::Orthanc::Logging::NullStream() #else /* ORTHANC_ENABLE_LOGGING == 1 */ -# define LOG(level) ::Orthanc::Logging::InternalLogger \ - (::Orthanc::Logging::LogLevel_ ## level, \ - ::Orthanc::Logging::LogCategory_GENERIC, __FILE__, __LINE__) -# define VLOG(unused) ::Orthanc::Logging::InternalLogger \ - (::Orthanc::Logging::LogLevel_TRACE, \ - ::Orthanc::Logging::LogCategory_GENERIC, __FILE__, __LINE__) + +#if !defined(__ORTHANC_FILE__) +# if defined(_MSC_VER) +# pragma message("Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries") +# else +# warning Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries +# endif +# define __ORTHANC_FILE__ __FILE__ +#endif + +# define LOG(level) ::Orthanc::Logging::InternalLogger \ + (::Orthanc::Logging::LogLevel_ ## level, \ + ::Orthanc::Logging::LogCategory_GENERIC, __ORTHANC_FILE__, __LINE__) +# define VLOG(unused) ::Orthanc::Logging::InternalLogger \ + (::Orthanc::Logging::LogLevel_TRACE, \ + ::Orthanc::Logging::LogCategory_GENERIC, __ORTHANC_FILE__, __LINE__) # define CLOG(level, category) ::Orthanc::Logging::InternalLogger \ (::Orthanc::Logging::LogLevel_ ## level, \ - ::Orthanc::Logging::LogCategory_ ## category, __FILE__, __LINE__) + ::Orthanc::Logging::LogCategory_ ## category, __ORTHANC_FILE__, __LINE__) #endif diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/SQLite/Connection.h --- a/OrthancFramework/Sources/SQLite/Connection.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/SQLite/Connection.h Wed Sep 20 09:02:31 2023 +0200 @@ -46,7 +46,16 @@ #include #include -#define SQLITE_FROM_HERE ::Orthanc::SQLite::StatementId(__FILE__, __LINE__) +#if !defined(__ORTHANC_FILE__) +# if defined(_MSC_VER) +# pragma message("Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries") +# else +# warning Warning: Macro __ORTHANC_FILE__ is not defined, this will leak the full path of the source files in the binaries +# endif +# define __ORTHANC_FILE__ __FILE__ +#endif + +#define SQLITE_FROM_HERE ::Orthanc::SQLite::StatementId(__ORTHANC_FILE__, __LINE__) namespace Orthanc { diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/SerializationToolbox.cpp --- a/OrthancFramework/Sources/SerializationToolbox.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/SerializationToolbox.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -170,31 +170,52 @@ void SerializationToolbox::ReadArrayOfStrings(std::vector& target, - const Json::Value& value, + const Json::Value& valueObject, const std::string& field) { - if (value.type() != Json::objectValue || - !value.isMember(field.c_str()) || - value[field.c_str()].type() != Json::arrayValue) + if (valueObject.type() != Json::objectValue || + !valueObject.isMember(field.c_str()) || + valueObject[field.c_str()].type() != Json::arrayValue) { throw OrthancException(ErrorCode_BadFileFormat, - "List of strings expected in field: " + field); + "List of strings expected in field: " + field); } - const Json::Value& arr = value[field.c_str()]; + const Json::Value& arr = valueObject[field.c_str()]; - target.resize(arr.size()); + try + { + ReadArrayOfStrings(target, arr); + } + catch (OrthancException& ex) + { // more detailed error + throw OrthancException(ErrorCode_BadFileFormat, + "List of strings expected in field: " + field); + } + } + - for (Json::Value::ArrayIndex i = 0; i < arr.size(); i++) + void SerializationToolbox::ReadArrayOfStrings(std::vector& target, + const Json::Value& valueArray) + { + if (valueArray.type() != Json::arrayValue) { - if (arr[i].type() != Json::stringValue) + throw OrthancException(ErrorCode_BadFileFormat, + "List of strings expected"); + } + + target.resize(valueArray.size()); + + for (Json::Value::ArrayIndex i = 0; i < valueArray.size(); i++) + { + if (valueArray[i].type() != Json::stringValue) { throw OrthancException(ErrorCode_BadFileFormat, - "List of strings expected in field: " + field); + "List of strings expected"); } else { - target[i] = arr[i].asString(); + target[i] = valueArray[i].asString(); } } } @@ -216,11 +237,25 @@ void SerializationToolbox::ReadSetOfStrings(std::set& target, - const Json::Value& value, + const Json::Value& valueObject, const std::string& field) { std::vector tmp; - ReadArrayOfStrings(tmp, value, field); + ReadArrayOfStrings(tmp, valueObject, field); + + target.clear(); + for (size_t i = 0; i < tmp.size(); i++) + { + target.insert(tmp[i]); + } + } + + + void SerializationToolbox::ReadSetOfStrings(std::set& target, + const Json::Value& valueArray) + { + std::vector tmp; + ReadArrayOfStrings(tmp, valueArray); target.clear(); for (size_t i = 0; i < tmp.size(); i++) @@ -379,24 +414,38 @@ } - void SerializationToolbox::WriteSetOfStrings(Json::Value& target, + void SerializationToolbox::WriteSetOfStrings(Json::Value& targetObject, const std::set& values, const std::string& field) { - if (target.type() != Json::objectValue || - target.isMember(field.c_str())) + if (targetObject.type() != Json::objectValue || + targetObject.isMember(field.c_str())) { throw OrthancException(ErrorCode_BadFileFormat); } - Json::Value& value = target[field]; + Json::Value& targetArray = targetObject[field]; + + targetArray = Json::arrayValue; + + WriteSetOfStrings(targetArray, values); + } + - value = Json::arrayValue; + void SerializationToolbox::WriteSetOfStrings(Json::Value& targetArray, + const std::set& values) + { + if (targetArray.type() != Json::arrayValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + targetArray.clear(); for (std::set::const_iterator it = values.begin(); it != values.end(); ++it) { - value.append(*it); + targetArray.append(*it); } } diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/SerializationToolbox.h --- a/OrthancFramework/Sources/SerializationToolbox.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/SerializationToolbox.h Wed Sep 20 09:02:31 2023 +0200 @@ -60,17 +60,23 @@ const std::string& field); static void ReadArrayOfStrings(std::vector& target, - const Json::Value& value, + const Json::Value& valueObject, const std::string& field); + static void ReadArrayOfStrings(std::vector& target, + const Json::Value& valueArray); + static void ReadListOfStrings(std::list& target, const Json::Value& value, const std::string& field); static void ReadSetOfStrings(std::set& target, - const Json::Value& value, + const Json::Value& valueObject, const std::string& field); + static void ReadSetOfStrings(std::set& target, + const Json::Value& valueArray); + static void ReadSetOfTags(std::set& target, const Json::Value& value, const std::string& field); @@ -91,10 +97,13 @@ const std::list& values, const std::string& field); - static void WriteSetOfStrings(Json::Value& target, + static void WriteSetOfStrings(Json::Value& targetObject, const std::set& values, const std::string& field); + static void WriteSetOfStrings(Json::Value& targetArray, + const std::set& values); + static void WriteSetOfTags(Json::Value& target, const std::set& tags, const std::string& field); diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/Toolbox.cpp --- a/OrthancFramework/Sources/Toolbox.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/Toolbox.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -496,6 +496,20 @@ result[2 * i + 1] = GetHexadecimalCharacter(static_cast(actualHash[i] % 16)); } } + + void Toolbox::ComputeMD5(std::string& result, + const std::set& data) + { + std::string s; + + for (std::set::const_iterator it = data.begin(); it != data.end(); ++it) + { + s += *it; + } + + ComputeMD5(result, s); + } + #endif @@ -1035,10 +1049,10 @@ return result; } - - void Toolbox::TokenizeString(std::vector& result, + static void TokenizeStringInternal(std::vector& result, const std::string& value, - char separator) + char separator, + bool includeEmptyStrings) { size_t countSeparators = 0; @@ -1068,7 +1082,41 @@ } } - result.push_back(currentItem); + if (includeEmptyStrings || !currentItem.empty()) + { + result.push_back(currentItem); + } + } + + + void Toolbox::TokenizeString(std::vector& result, + const std::string& value, + char separator) + { + TokenizeStringInternal(result, value, separator, true); + } + + + void Toolbox::SplitString(std::set& result, + const std::string& value, + char separator) + { + result.clear(); + + std::vector temp; + TokenizeStringInternal(temp, value, separator, false); + for (size_t i = 0; i < temp.size(); ++i) + { + result.insert(temp[i]); + } + } + + + void Toolbox::SplitString(std::vector& result, + const std::string& value, + char separator) + { + TokenizeStringInternal(result, value, separator, false); } diff -r b074943fca35 -r c026301eee9e OrthancFramework/Sources/Toolbox.h --- a/OrthancFramework/Sources/Toolbox.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/Sources/Toolbox.h Wed Sep 20 09:02:31 2023 +0200 @@ -128,6 +128,9 @@ static void ComputeMD5(std::string& result, const void* data, size_t size); + + static void ComputeMD5(std::string& result, + const std::set& data); #endif static void ComputeSHA1(std::string& result, @@ -183,10 +186,21 @@ static std::string WildcardToRegularExpression(const std::string& s); + // TokenizeString result might contain empty strings (not SplitString) static void TokenizeString(std::vector& result, const std::string& source, char separator); + // SplitString result won't contain empty strings (compared to TokenizeString) + static void SplitString(std::vector& result, + const std::string& source, + char separator); + + // SplitString result won't contain empty strings (compared to TokenizeString) + static void SplitString(std::set& result, + const std::string& source, + char separator); + static void JoinStrings(std::string& result, const std::set& source, const char* separator); @@ -245,6 +259,22 @@ } } + // returns true if all element of 'needles' are found in 'haystack' + template static void GetIntersection(std::set& target, const std::set& a, const std::set& b) + { + target.clear(); + + for (typename std::set::const_iterator it = a.begin(); + it != a.end(); ++it) + { + if (b.count(*it) > 0) + { + target.insert(*it); + } + } + } + + #if ORTHANC_ENABLE_PUGIXML == 1 static void JsonToXml(std::string& target, const Json::Value& source, diff -r b074943fca35 -r c026301eee9e OrthancFramework/UnitTestsSources/CMakeLists.txt --- a/OrthancFramework/UnitTestsSources/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -109,6 +109,8 @@ ${GOOGLE_TEST_SOURCES} ) +DefineSourceBasenameForTarget(UnitTests) + target_link_libraries(UnitTests ${ORTHANC_FRAMEWORK_LIBRARIES}) install(TARGETS UnitTests diff -r b074943fca35 -r c026301eee9e OrthancFramework/UnitTestsSources/FrameworkTests.cpp --- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -381,6 +381,21 @@ ASSERT_EQ("8b1a9953c4611296a827abf8c47804d7", s); Toolbox::ComputeMD5(s, ""); ASSERT_EQ("d41d8cd98f00b204e9800998ecf8427e", s); + + Toolbox::ComputeMD5(s, "aaabbbccc"); + ASSERT_EQ("d1aaf4767a3c10a473407a4e47b02da6", s); + + std::set set; + + Toolbox::ComputeMD5(s, set); + ASSERT_EQ("d41d8cd98f00b204e9800998ecf8427e", s); // empty set same as empty string + + set.insert("bbb"); + set.insert("ccc"); + set.insert("aaa"); + + Toolbox::ComputeMD5(s, set); + ASSERT_EQ("d1aaf4767a3c10a473407a4e47b02da6", s); // set md5 same as string with the values sorted } TEST(Toolbox, ComputeSHA1) @@ -706,6 +721,82 @@ ASSERT_EQ("", t[3]); } +TEST(Toolbox, SplitString) +{ + { + std::set result; + Toolbox::SplitString(result, "", ';'); + ASSERT_EQ(0u, result.size()); + } + + { + std::set result; + Toolbox::SplitString(result, "a", ';'); + ASSERT_EQ(1u, result.size()); + ASSERT_TRUE(result.end() != result.find("a")); + } + + { + std::set result; + Toolbox::SplitString(result, "a;b", ';'); + ASSERT_EQ(2u, result.size()); + ASSERT_TRUE(result.end() != result.find("a")); + ASSERT_TRUE(result.end() != result.find("b")); + } + + { + std::set result; + Toolbox::SplitString(result, "a;b;", ';'); + ASSERT_EQ(2u, result.size()); + ASSERT_TRUE(result.end() != result.find("a")); + ASSERT_TRUE(result.end() != result.find("b")); + } + + { + std::set result; + Toolbox::SplitString(result, "a;a", ';'); + ASSERT_EQ(1u, result.size()); + ASSERT_TRUE(result.end() != result.find("a")); + } + + { + std::vector result; + Toolbox::SplitString(result, "", ';'); + ASSERT_EQ(0u, result.size()); + } + + { + std::vector result; + Toolbox::SplitString(result, "a", ';'); + ASSERT_EQ(1u, result.size()); + ASSERT_EQ("a", result[0]); + } + + { + std::vector result; + Toolbox::SplitString(result, "a;b", ';'); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ("a", result[0]); + ASSERT_EQ("b", result[1]); + } + + { + std::vector result; + Toolbox::SplitString(result, "a;b;", ';'); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ("a", result[0]); + ASSERT_EQ("b", result[1]); + } + + { + std::vector result; + Toolbox::TokenizeString(result, "a;a", ';'); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ("a", result[0]); + ASSERT_EQ("a", result[1]); + } +} + TEST(Toolbox, Enumerations) { ASSERT_EQ(Encoding_Utf8, StringToEncoding(EnumerationToString(Encoding_Utf8))); diff -r b074943fca35 -r c026301eee9e OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp --- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -3479,6 +3479,7 @@ } +#if ORTHANC_SANDBOXED != 1 TEST(ParsedDicomFile, DISABLED_InjectEmptyPixelData2) { static const char* PIXEL_DATA = "7FE00010"; @@ -3526,7 +3527,7 @@ } } } - +#endif diff -r b074943fca35 -r c026301eee9e OrthancFramework/UnitTestsSources/ImageTests.cpp --- a/OrthancFramework/UnitTestsSources/ImageTests.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/ImageTests.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -33,10 +33,11 @@ #include "../Sources/Images/ImageProcessing.h" #include "../Sources/Images/JpegReader.h" #include "../Sources/Images/JpegWriter.h" +#include "../Sources/Images/PamReader.h" +#include "../Sources/Images/PamWriter.h" #include "../Sources/Images/PngReader.h" #include "../Sources/Images/PngWriter.h" -#include "../Sources/Images/PamReader.h" -#include "../Sources/Images/PamWriter.h" +#include "../Sources/OrthancException.h" #include "../Sources/Toolbox.h" #if ORTHANC_SANDBOXED != 1 @@ -96,14 +97,33 @@ uint8_t *p = &image[0] + y * pitch; for (unsigned int x = 0; x < width; x++, p += 8) { - p[0] = (y % 8 == 0) ? 255 : 0; - p[1] = (y % 8 == 1) ? 255 : 0; - p[2] = (y % 8 == 2) ? 255 : 0; - p[3] = (y % 8 == 3) ? 255 : 0; - p[4] = (y % 8 == 4) ? 255 : 0; - p[5] = (y % 8 == 5) ? 255 : 0; - p[6] = (y % 8 == 6) ? 255 : 0; - p[7] = (y % 8 == 7) ? 255 : 0; + switch (Orthanc::Toolbox::DetectEndianness()) + { + case Orthanc::Endianness_Little: + p[0] = (y % 8 == 0) ? 255 : 0; + p[1] = (y % 8 == 1) ? 255 : 0; + p[2] = (y % 8 == 2) ? 255 : 0; + p[3] = (y % 8 == 3) ? 255 : 0; + p[4] = (y % 8 == 4) ? 255 : 0; + p[5] = (y % 8 == 5) ? 255 : 0; + p[6] = (y % 8 == 6) ? 255 : 0; + p[7] = (y % 8 == 7) ? 255 : 0; + break; + + case Orthanc::Endianness_Big: + p[0] = (y % 8 == 1) ? 255 : 0; + p[1] = (y % 8 == 0) ? 255 : 0; + p[2] = (y % 8 == 3) ? 255 : 0; + p[3] = (y % 8 == 2) ? 255 : 0; + p[4] = (y % 8 == 5) ? 255 : 0; + p[5] = (y % 8 == 4) ? 255 : 0; + p[6] = (y % 8 == 7) ? 255 : 0; + p[7] = (y % 8 == 6) ? 255 : 0; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } } } diff -r b074943fca35 -r c026301eee9e OrthancFramework/UnitTestsSources/ToolboxTests.cpp --- a/OrthancFramework/UnitTestsSources/ToolboxTests.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/ToolboxTests.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -280,6 +280,48 @@ } } +TEST(Toolbox, GetSetIntersection) +{ + { + std::set target; + std::set a; + std::set b; + + Toolbox::GetIntersection(target, a, b); + ASSERT_EQ(0u, target.size()); + } + + { + std::set target; + std::set a; + std::set b; + + a.insert(1); + b.insert(1); + + Toolbox::GetIntersection(target, a, b); + ASSERT_EQ(1u, target.size()); + ASSERT_EQ(1u, target.count(1)); + } + + { + std::set target; + std::set a; + std::set b; + + a.insert(1); + a.insert(2); + b.insert(2); + + Toolbox::GetIntersection(target, a, b); + ASSERT_EQ(1u, target.size()); + ASSERT_EQ(0u, target.count(1)); + ASSERT_EQ(1u, target.count(2)); + } + +} + + TEST(Toolbox, JoinStrings) { { diff -r b074943fca35 -r c026301eee9e OrthancServer/CMakeLists.txt --- a/OrthancServer/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -379,6 +379,8 @@ ${AUTOGENERATED_SOURCES} ) +DefineSourceBasenameForTarget(CoreLibrary) + add_dependencies(CoreLibrary AutogeneratedTarget) if (LIBICU_LIBRARIES) @@ -394,8 +396,11 @@ add_custom_command( COMMAND ${PROTOC_EXECUTABLE} ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --cpp_out=${AUTOGENERATED_DIR} -I${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc + COMMAND + ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/Resources/PreventProtobufDirectoryLeaks.py ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc DEPENDS ProtobufCompiler + ${CMAKE_SOURCE_DIR}/Resources/PreventProtobufDirectoryLeaks.py ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancDatabasePlugin.proto OUTPUT ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc @@ -420,6 +425,8 @@ ${ORTHANC_SERVER_SOURCES} ) +DefineSourceBasenameForTarget(ServerLibrary) + # Ensure autogenerated code is built before building ServerLibrary add_dependencies(ServerLibrary CoreLibrary OrthancDatabaseProtobuf) @@ -428,6 +435,8 @@ ${ORTHANC_RESOURCES} ) +DefineSourceBasenameForTarget(Orthanc) + target_link_libraries(Orthanc ServerLibrary CoreLibrary ${DCMTK_LIBRARIES}) if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") @@ -458,6 +467,8 @@ ${BOOST_EXTENDED_SOURCES} ) +DefineSourceBasenameForTarget(UnitTests) + target_link_libraries(UnitTests ServerLibrary CoreLibrary @@ -506,6 +517,8 @@ ${PLUGINS_DEPENDENCIES_SOURCES} ) + DefineSourceBasenameForTarget(PluginsDependencies) + # Add the "-fPIC" option as this static library must be embedded # inside shared libraries (important on UNIX) set_target_properties( @@ -546,6 +559,8 @@ ${SERVE_FOLDERS_RESOURCES} ) + DefineSourceBasenameForTarget(ServeFolders) + target_link_libraries(ServeFolders PluginsDependencies) set_target_properties( @@ -594,6 +609,8 @@ ${MODALITY_WORKLISTS_RESOURCES} ) + DefineSourceBasenameForTarget(ModalityWorklists) + target_link_libraries(ModalityWorklists PluginsDependencies) set_target_properties( @@ -655,7 +672,9 @@ ${CMAKE_SOURCE_DIR}/Plugins/Samples/ConnectivityChecks/OrthancFrameworkDependencies.cpp ${CONNECTIVITY_CHECKS_RESOURCES} ) - + + DefineSourceBasenameForTarget(ConnectivityChecks) + target_link_libraries(ConnectivityChecks PluginsDependencies) set_target_properties( @@ -708,6 +727,8 @@ ${DELAYED_DELETION_RESOURCES} ) + DefineSourceBasenameForTarget(DelayedDeletion) + target_link_libraries(DelayedDeletion PluginsDependencies) set_target_properties( @@ -755,6 +776,8 @@ ${HOUSEKEEPER_RESOURCES} ) + DefineSourceBasenameForTarget(Housekeeper) + target_link_libraries(Housekeeper PluginsDependencies) set_target_properties( @@ -820,6 +843,8 @@ ${MULTITENANT_DICOM_RESOURCES} ) + DefineSourceBasenameForTarget(MultitenantDicom) + target_link_libraries(MultitenantDicom PluginsDependencies ${DCMTK_LIBRARIES}) set_target_properties( @@ -865,6 +890,7 @@ endif() add_executable(OrthancRecoverCompressedFile ${RECOVER_COMPRESSED_SOURCES}) + DefineSourceBasenameForTarget(OrthancRecoverCompressedFile) target_link_libraries(OrthancRecoverCompressedFile CoreLibrary) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h --- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Wed Sep 20 09:02:31 2023 +0200 @@ -245,6 +245,7 @@ OrthancPluginErrorCode_DatabaseCannotSerialize = 42 /*!< Database could not serialize access due to concurrent update, the transaction should be retried */, OrthancPluginErrorCode_Revision = 43 /*!< A bad revision number was provided, which might indicate conflict between multiple writers */, OrthancPluginErrorCode_MainDicomTagsMultiplyDefined = 44 /*!< A main DICOM Tag has been defined multiple times for the same resource level */, + OrthancPluginErrorCode_ForbiddenAccess = 45 /*!< Access to a resource is forbidden */, OrthancPluginErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, OrthancPluginErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, OrthancPluginErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/AutomatedJpeg2kCompression/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/AutomatedJpeg2kCompression/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/AutomatedJpeg2kCompression/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -25,3 +25,5 @@ include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake) add_library(AutomatedJpeg2kCompression SHARED Plugin.cpp) + +DefineSourceBasenameForTarget(AutomatedJpeg2kCompression) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/Basic/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/Basic/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/Basic/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -25,3 +25,5 @@ include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake) add_library(PluginTest SHARED Plugin.c) + +DefineSourceBasenameForTarget(PluginTest) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp --- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -1670,15 +1670,16 @@ return true; } +#ifdef _MSC_VER +#define ORTHANC_SCANF sscanf_s +#else +#define ORTHANC_SCANF sscanf +#endif + // Parse the version - int aa, bb, cc; - if ( -#ifdef _MSC_VER - sscanf_s -#else - sscanf -#endif - (version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || + int aa, bb, cc = 0; + if ((ORTHANC_SCANF(version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 && + ORTHANC_SCANF(version, "%4d.%4d", &aa, &bb) != 2) || aa < 0 || bb < 0 || cc < 0) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/ConnectivityChecks/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/ConnectivityChecks/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/ConnectivityChecks/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -77,6 +77,8 @@ Plugin.cpp ) +DefineSourceBasenameForTarget(ConnectivityChecks) + set_target_properties( ConnectivityChecks PROPERTIES VERSION ${PLUGIN_VERSION} diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/CustomImageDecoder/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/CustomImageDecoder/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/CustomImageDecoder/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -25,3 +25,5 @@ include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake) add_library(PluginTest SHARED Plugin.cpp) + +DefineSourceBasenameForTarget(PluginTest) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/DelayedDeletion/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/DelayedDeletion/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/DelayedDeletion/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -78,6 +78,8 @@ Plugin.cpp ) +DefineSourceBasenameForTarget(DelayedDeletion) + set_target_properties( DelayedDeletion PROPERTIES VERSION ${PLUGIN_VERSION} diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp --- a/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -45,7 +45,7 @@ static bool triggerOnMainDicomTagsChange_ = true; static bool triggerOnUnnecessaryDicomAsJsonFiles_ = true; static bool triggerOnIngestTranscodingChange_ = true; - +static bool triggerOnDicomWebCacheChange_ = true; struct RunningPeriod { @@ -166,6 +166,7 @@ std::string seriesMainDicomTagsSignature; std::string instancesMainDicomTagsSignature; std::string ingestTranscoding; + std::string dicomWebVersion; bool storageCompressionEnabled; DbConfiguration() @@ -186,6 +187,7 @@ seriesMainDicomTagsSignature.clear(); instancesMainDicomTagsSignature.clear(); ingestTranscoding.clear(); + dicomWebVersion.clear(); } void ToJson(Json::Value& target) @@ -210,6 +212,7 @@ target["OrthancVersion"] = orthancVersion; target["StorageCompressionEnabled"] = storageCompressionEnabled; target["IngestTranscoding"] = ingestTranscoding; + target["DicomWebVersion"] = dicomWebVersion; } } @@ -218,6 +221,14 @@ if (!source.isNull()) { orthancVersion = source["OrthancVersion"].asString(); + if (source.isMember("DicomWebVersion")) + { + dicomWebVersion = source["DicomWebVersion"].asString(); + } + else + { + dicomWebVersion = "1.14"; // the first change that requires processing has been introduced between 1.14 & 1.15 + } const Json::Value& signatures = source["MainDicomTagsSignature"]; patientsMainDicomTagsSignature = signatures["Patient"].asString(); @@ -321,6 +332,7 @@ pluginStatus_.lastTimeStarted = boost::date_time::not_a_date_time; pluginStatus_.lastProcessedConfiguration.orthancVersion = "1.9.0"; // when we don't know, we assume some files were stored with Orthanc 1.9.0 (last version saving the dicom-as-json files) + pluginStatus_.lastProcessedConfiguration.dicomWebVersion = "1.14"; // the first change that requires processing has been introduced between 1.14 & 1.15 // default main dicom tags signature are the one from Orthanc 1.4.2 (last time the list was changed): pluginStatus_.lastProcessedConfiguration.patientsMainDicomTagsSignature = "0010,0010;0010,0020;0010,0030;0010,0040;0010,1000"; @@ -360,12 +372,19 @@ configuration.ingestTranscoding = systemInfo["IngestTranscoding"].asString(); configuration.orthancVersion = OrthancPlugins::GetGlobalContext()->orthancVersion; + + Json::Value pluginInfo; + if (OrthancPlugins::RestApiGet(pluginInfo, "/plugins/dicom-web", false)) + { + configuration.dicomWebVersion = pluginInfo["Version"].asString(); + } } -static void CheckNeedsProcessing(bool& needsReconstruct, bool& needsReingest, const DbConfiguration& current, const DbConfiguration& last) +static void CheckNeedsProcessing(bool& needsReconstruct, bool& needsReingest, bool& needsDicomWebCaching, const DbConfiguration& current, const DbConfiguration& last) { needsReconstruct = false; needsReingest = false; + needsDicomWebCaching = false; if (!last.IsDefined()) { @@ -474,9 +493,37 @@ } } + if (!current.dicomWebVersion.empty()) + { + if (last.dicomWebVersion.empty()) + { + if (triggerOnDicomWebCacheChange_) + { + OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin is enabled and the housekeeper has never run, you might miss series metadata cache -> will perform housekeeping"); + } + needsDicomWebCaching = triggerOnDicomWebCacheChange_; + } + else + { + const char* lastDicomWebVersion = last.dicomWebVersion.c_str(); + + if (!OrthancPlugins::CheckMinimalVersion(lastDicomWebVersion, 1, 15, 0)) + { + if (triggerOnDicomWebCacheChange_) + { + OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache -> will perform housekeeping"); + needsDicomWebCaching = true; + } + else + { + OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache but the trigger has been disabled"); + } + } + } + } } -static bool ProcessChanges(bool needsReconstruct, bool needsReingest, const DbConfiguration& currentDbConfiguration) +static bool ProcessChanges(bool needsReconstruct, bool needsReingest, bool needsDicomWebCaching, const DbConfiguration& currentDbConfiguration) { Json::Value changes; @@ -498,12 +545,22 @@ if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed { Json::Value result; - Json::Value request; - if (needsReingest) + + if (needsReconstruct) { - request["ReconstructFiles"] = true; + Json::Value request; + if (needsReingest) + { + request["ReconstructFiles"] = true; + } + OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); } - OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); + + if (needsDicomWebCaching) + { + Json::Value request; + OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/update-dicomweb-cache", request, true); + } } { @@ -554,13 +611,14 @@ bool needsReingest = false; bool needsFullProcessing = false; bool needsProcessing = false; + bool needsDicomWebCaching = false; { boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); // compare with last full processed configuration - CheckNeedsProcessing(needsReconstruct, needsReingest, currentDbConfiguration, pluginStatus_.lastProcessedConfiguration); - needsFullProcessing = needsReconstruct || needsReingest; + CheckNeedsProcessing(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration, pluginStatus_.lastProcessedConfiguration); + needsFullProcessing = needsReconstruct || needsReingest || needsDicomWebCaching; needsProcessing = needsFullProcessing; // if a processing was in progress, check if the config has changed since @@ -570,9 +628,10 @@ bool needsReconstruct2 = false; bool needsReingest2 = false; + bool needsDicomWebCaching2 = false; - CheckNeedsProcessing(needsReconstruct2, needsReingest2, currentDbConfiguration, pluginStatus_.currentlyProcessingConfiguration); - needsFullProcessing = needsReconstruct2 || needsReingest2; // if the configuration has changed compared to the config being processed, we need a full processing again + CheckNeedsProcessing(needsReconstruct2, needsReingest2, needsDicomWebCaching2, currentDbConfiguration, pluginStatus_.currentlyProcessingConfiguration); + needsFullProcessing = needsReconstruct2 || needsReingest2 || needsDicomWebCaching2; // if the configuration has changed compared to the config being processed, we need a full processing again } } @@ -621,7 +680,7 @@ { if (runningPeriods_.isInPeriod()) { - completed = ProcessChanges(needsReconstruct, needsReingest, currentDbConfiguration); + completed = ProcessChanges(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration); SaveStatusInDb(); if (!completed) @@ -775,7 +834,8 @@ "Triggers" : { "StorageCompressionChange": true, "MainDicomTagsChange": true, - "UnnecessaryDicomAsJsonFiles": true + "UnnecessaryDicomAsJsonFiles": true, + "DicomWebCacheChange": true // new in 1.12.2 } } @@ -789,11 +849,14 @@ if (housekeeper.GetJson().isMember("Triggers")) { - triggerOnStorageCompressionChange_ = housekeeper.GetBooleanValue("StorageCompressionChange", true); + OrthancPlugins::OrthancConfiguration triggers; + housekeeper.GetSection(triggers, "Triggers"); + triggerOnStorageCompressionChange_ = triggers.GetBooleanValue("StorageCompressionChange", true); - triggerOnMainDicomTagsChange_ = housekeeper.GetBooleanValue("MainDicomTagsChange", true); - triggerOnUnnecessaryDicomAsJsonFiles_ = housekeeper.GetBooleanValue("UnnecessaryDicomAsJsonFiles", true); - triggerOnIngestTranscodingChange_ = housekeeper.GetBooleanValue("IngestTranscodingChange", true); + triggerOnMainDicomTagsChange_ = triggers.GetBooleanValue("MainDicomTagsChange", true); + triggerOnUnnecessaryDicomAsJsonFiles_ = triggers.GetBooleanValue("UnnecessaryDicomAsJsonFiles", true); + triggerOnIngestTranscodingChange_ = triggers.GetBooleanValue("IngestTranscodingChange", true); + triggerOnDicomWebCacheChange_ = triggers.GetBooleanValue("DicomWebCacheChange", true); } if (housekeeper.GetJson().isMember("Schedule")) @@ -802,7 +865,7 @@ } OrthancPluginRegisterOnChangeCallback(c, OnChangeCallback); - OrthancPluginRegisterRestCallback(c, "/housekeeper/status", GetPluginStatus); // for bacward compatiblity with version 1.11.0 + OrthancPluginRegisterRestCallback(c, "/housekeeper/status", GetPluginStatus); // for backward compatiblity with version 1.11.0 OrthancPluginRegisterRestCallback(c, "/plugins/housekeeper/status", GetPluginStatus); } else diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/MultitenantDicom/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -55,6 +55,8 @@ ${AUTOGENERATED_SOURCES} ) +DefineSourceBasenameForTarget(MultitenantDicom) + target_link_libraries(MultitenantDicom ${DCMTK_LIBRARIES}) message("Setting the version of the plugin to ${ORTHANC_PLUGIN_VERSION}") diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/FindRequestHandler.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/MultitenantDicomServer.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ @@ -120,6 +119,10 @@ return -1; } + // For static builds, this is not required - but does not harm - because the dictionary seems to be shared between the Orthanc Core and the plugin. + // For dynamic builds, this is however required. See https://discourse.orthanc-server.org/t/dimse-failure-using-multitenant-plugin/3665 + Orthanc::FromDcmtkBridge::InitializeDictionary(false /* loadPrivateDictionary */); + /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */ dcmDisableGethostbyaddr.set(OFTrue); diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/PluginEnumerations.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/PluginEnumerations.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/PluginEnumerations.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.h Wed Sep 20 09:02:31 2023 +0200 @@ -6,18 +6,17 @@ * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . **/ diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/Sanitizer/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/Sanitizer/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/Sanitizer/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -51,6 +51,8 @@ ${ORTHANC_DICOM_SOURCES} ) +DefineSourceBasenameForTarget(Sanitizer) + target_link_libraries(Sanitizer ${DCMTK_LIBRARIES}) diff -r b074943fca35 -r c026301eee9e OrthancServer/Plugins/Samples/WebSkeleton/CMakeLists.txt --- a/OrthancServer/Plugins/Samples/WebSkeleton/CMakeLists.txt Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Plugins/Samples/WebSkeleton/CMakeLists.txt Wed Sep 20 09:02:31 2023 +0200 @@ -32,3 +32,5 @@ add_library(WebSkeleton SHARED ${AUTOGENERATED_SOURCES} ) + +DefineSourceBasenameForTarget(WebSkeleton) diff -r b074943fca35 -r c026301eee9e OrthancServer/Resources/PreventProtobufDirectoryLeaks.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Resources/PreventProtobufDirectoryLeaks.py Wed Sep 20 09:02:31 2023 +0200 @@ -0,0 +1,40 @@ +#!/usr/bin/python + +# 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) 2021-2023 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 . + + +import sys + +if len(sys.argv) != 2: + raise Exception('Bad number of arguments in %s' % sys.argv[0]) + +with open(sys.argv[1], 'r') as f: + s = f.read() + +s = s.replace('__FILE__', '__ORTHANC_FILE__') + +s = """ +#if !defined(__ORTHANC_FILE__) +# define __ORTHANC_FILE__ __FILE__ +#endif +""" + s + +with open(sys.argv[1], 'w') as f: + f.write(s) diff -r b074943fca35 -r c026301eee9e OrthancServer/Sources/OrthancInitialization.cpp --- a/OrthancServer/Sources/OrthancInitialization.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Sources/OrthancInitialization.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -52,6 +52,12 @@ #include // For DCM_dcmnetLogger #if ORTHANC_ENABLE_PLUGINS == 1 +# if defined(__ORTHANC_FILE__) +// Prevents the system-wide Google Protobuf library from leaking the +// full path of this source file +# undef __FILE__ +# define __FILE__ __ORTHANC_FILE__ +# endif # include #endif diff -r b074943fca35 -r c026301eee9e OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -729,6 +729,34 @@ } } + static void DeleteJobInfo(RestApiDeleteCall& call) + { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Jobs") + .SetSummary("Delete a job from history") + .SetDescription("Delete the job from the jobs history. Only a completed job can be deleted. " + "If the job has not run or not completed yet, you must cancel it first. " + "If the job has outputs, all outputs will be deleted as well. ") + .SetUriArgument("id", "Identifier of the job of interest"); + return; + } + + std::string job = call.GetUriComponent("id", ""); + + if (OrthancRestApi::GetContext(call).GetJobsEngine(). + GetRegistry().DeleteJobInfo(job)) + { + call.GetOutput().AnswerBuffer("", MimeType_PlainText); + } + else + { + throw OrthancException(ErrorCode_InexistentItem, + "No job found with this id: " + job); + } + } + static void GetJobOutput(RestApiGetCall& call) { @@ -1138,6 +1166,7 @@ Register("/jobs", ListJobs); Register("/jobs/{id}", GetJobInfo); + Register("/jobs/{id}", DeleteJobInfo); Register("/jobs/{id}/cancel", ApplyJobAction); Register("/jobs/{id}/pause", ApplyJobAction); Register("/jobs/{id}/resubmit", ApplyJobAction); diff -r b074943fca35 -r c026301eee9e OrthancServer/Sources/ServerJobs/ArchiveJob.cpp --- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -1482,4 +1482,9 @@ return false; } } + + void ArchiveJob::DeleteAllOutputs() + { + DeleteOutput("archive"); + } } diff -r b074943fca35 -r c026301eee9e OrthancServer/Sources/ServerJobs/ArchiveJob.h --- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h Wed Sep 20 09:02:31 2023 +0200 @@ -122,5 +122,7 @@ const std::string& key) ORTHANC_OVERRIDE; virtual bool DeleteOutput(const std::string& key) ORTHANC_OVERRIDE; + + virtual void DeleteAllOutputs() ORTHANC_OVERRIDE; }; } diff -r b074943fca35 -r c026301eee9e OrthancServer/Sources/main.cpp --- a/OrthancServer/Sources/main.cpp Tue Sep 19 10:56:09 2023 +0200 +++ b/OrthancServer/Sources/main.cpp Wed Sep 20 09:02:31 2023 +0200 @@ -819,6 +819,7 @@ PrintErrorCode(ErrorCode_DatabaseCannotSerialize, "Database could not serialize access due to concurrent update, the transaction should be retried"); PrintErrorCode(ErrorCode_Revision, "A bad revision number was provided, which might indicate conflict between multiple writers"); PrintErrorCode(ErrorCode_MainDicomTagsMultiplyDefined, "A main DICOM Tag has been defined multiple times for the same resource level"); + PrintErrorCode(ErrorCode_ForbiddenAccess, "Access to a resource is forbidden"); PrintErrorCode(ErrorCode_SQLiteNotOpened, "SQLite: The database is not opened"); PrintErrorCode(ErrorCode_SQLiteAlreadyOpened, "SQLite: Connection is already open"); PrintErrorCode(ErrorCode_SQLiteCannotOpen, "SQLite: Unable to open the database"); diff -r b074943fca35 -r c026301eee9e README --- a/README Tue Sep 19 10:56:09 2023 +0200 +++ b/README Wed Sep 20 09:02:31 2023 +0200 @@ -38,6 +38,14 @@ * Cross-compilation for Windows under GNU/Linux, with MinGW. +Contributing +------------ + +Instructions for contributing to the Orthanc project are included in +the Orthanc Book: +https://book.orthanc-server.com/developers/repositories.html + + Licensing --------- diff -r b074943fca35 -r c026301eee9e TODO --- a/TODO Tue Sep 19 10:56:09 2023 +0200 +++ b/TODO Wed Sep 20 09:02:31 2023 +0200 @@ -115,15 +115,20 @@ https://groups.google.com/g/orthanc-users/c/r20kDb0axms/m/2tzbQzYJAgAJ * save more details in jobs e.g: the resources being sent/exported ... https://groups.google.com/g/orthanc-users/c/rDDusFG5Lco/m/TzTUjWXLAQAJ + https://discourse.orthanc-server.org/t/some-confusion-about-jobs-function/3887 * allow filtering/ordering on the /jobs route: 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. 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 + --------- Long-term @@ -173,6 +178,7 @@ * Support multiple specific character sets (cf. "SCSH32" in orthanc-tests) - http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.12.html#sect_C.12.1.1.2 - Japanese test: http://dicom.nema.org/MEDICAL/dicom/2017c/output/chtml/part05/sect_H.3.2.html + https://discourse.orthanc-server.org/t/garbled-characters-when-i-uploaded-japanese-patient-name/3204/5 * Support Supplementary Kanji set (ISO 2022 IR 159) * Create DICOM files with multibyte encodings (Korean, JapaneseKanji, SimplifiedChinese) @@ -191,8 +197,6 @@ - ModalitiesInStudy - all computed counters at series/study/patient level - RequestAttributesSequence (sequence that must be included in all DicomWeb QIDO-RS for series) -* Investigate increase of CPU consumption while idle after anonymize/delete - (https://discourse.orthanc-server.org/t/onchange-callbacks-and-cpu-loads/3534) * Long-shot & not sure it is even feasible at all: try to reduce memory usage by implementing streaming when receiving DICOM instances from the Rest API or from DICOM and store files directly to disk as they @@ -274,8 +278,6 @@ Code refactoring ================ -* Use Semaphore::Locker everywhere (instead of explicit - Release() and Acquire()) * Avoid direct calls to FromDcmtkBridge (make most of its methods private), go through ParsedDicomFile wherever possible