# HG changeset patch # User Alain Mazy # Date 1684911401 -7200 # Node ID f26ed26a7793d117b5a3e9d37566eac6d4f80e22 # Parent 7d913ee2f6657bf761c4a6911864ff0e5ab298f2# Parent 76dc541c5ddaf862e534504a2f23bc997044ba3a merge diff -r 7d913ee2f665 -r f26ed26a7793 LinuxCompilation.txt --- a/LinuxCompilation.txt Wed May 24 08:54:54 2023 +0200 +++ b/LinuxCompilation.txt Wed May 24 08:56:41 2023 +0200 @@ -161,7 +161,7 @@ uuid-dev libcurl4-openssl-dev liblua5.3-dev \ libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \ zlib1g-dev libdcmtk-dev libboost-all-dev libwrap0-dev \ - libcharls-dev libjsoncpp-dev libpugixml-dev locales + libcharls-dev libjsoncpp-dev libpugixml-dev locales protobuf-compiler # cd ./Build # cmake -DALLOW_DOWNLOADS=ON \ diff -r 7d913ee2f665 -r f26ed26a7793 NEWS --- a/NEWS Wed May 24 08:54:54 2023 +0200 +++ b/NEWS Wed May 24 08:56:41 2023 +0200 @@ -11,6 +11,14 @@ * Fix decoding of YBR_FULL RLE images for which the "Planar Configuration" tag (0028,0006) equals 1 +* Made Orthanc more resilient to common spelling errors in SpecificCharacterSet +* Modality worklists plugin: allow searching on private tags (exact match only) +* Upgraded dependencies for static builds: + - boost 1.82.0 +* Fix orphan files remaining in storage when working with MaximumStorageSize + (https://discourse.orthanc-server.org/t/issue-with-deleting-incoming-dicoms-when-maximumstoragesize-is-reached/3510) +* When deleting a resource, its parents LastUpdate metadata are now updated. + * WIP: new dicomWeb Json format for some of the Rest API routes. * WIP: new 'include' get arguments for some of the Rest API routes to define the content of the response. Useful since the dicomWeb format is very slow diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/CMake/BoostConfiguration.cmake --- a/OrthancFramework/Resources/CMake/BoostConfiguration.cmake Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/Resources/CMake/BoostConfiguration.cmake Wed May 24 08:56:41 2023 +0200 @@ -90,10 +90,10 @@ ## Parameters for static compilation of Boost ## - set(BOOST_NAME boost_1_80_0) - set(BOOST_VERSION 1.80.0) - set(BOOST_BCP_SUFFIX bcpdigest-1.11.2) - set(BOOST_MD5 "7734e19f9a39a4411b807a9913e4a5ff") + set(BOOST_NAME boost_1_82_0) + set(BOOST_VERSION 1.82.0) + set(BOOST_BCP_SUFFIX bcpdigest-1.12.1) + set(BOOST_MD5 "9d02d026c61870b1838b53293692326f") set(BOOST_URL "https://orthanc.uclouvain.be/third-party-downloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz") set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME}) @@ -310,12 +310,14 @@ ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/date_time.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/formatting.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/generator.cpp + ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/iconv_codecvt.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/ids.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/localization_backend.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/message.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/shared/mo_lambda.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/codecvt_converter.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/default_locale.cpp + ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/encoding.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/gregorian.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/info.cpp ${BOOST_SOURCES_DIR}/libs/locale/src/boost/locale/util/locale_data.cpp diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/CMake/BoostConfiguration.sh --- a/OrthancFramework/Resources/CMake/BoostConfiguration.sh Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/Resources/CMake/BoostConfiguration.sh Wed May 24 08:56:41 2023 +0200 @@ -22,10 +22,11 @@ ## - Orthanc between 1.4.0 and 1.4.2: Boost 1.67.0 ## - Orthanc between 1.5.0 and 1.5.4: Boost 1.68.0 ## - Orthanc between 1.5.5 and 1.11.1: Boost 1.69.0 -## - Orthanc >= 1.11.2: Boost 1.80.0 +## - Orthanc between 1.11.2 and 1.12.0: Boost 1.80.0 +## - Orthanc >= 1.12.1: Boost 1.82.0 -BOOST_VERSION=1_80_0 -ORTHANC_VERSION=1.11.2 +BOOST_VERSION=1_82_0 +ORTHANC_VERSION=1.12.1 rm -rf /tmp/boost_${BOOST_VERSION} rm -rf /tmp/bcp/boost_${BOOST_VERSION} diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/boost-1.65.1-linux-standard-base.patch --- a/OrthancFramework/Resources/Patches/boost-1.65.1-linux-standard-base.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -diff -urEb boost_1_65_1.orig/boost/move/adl_move_swap.hpp boost_1_65_1/boost/move/adl_move_swap.hpp ---- boost_1_65_1.orig/boost/move/adl_move_swap.hpp 2017-11-08 17:43:20.000000000 +0100 -+++ boost_1_65_1/boost/move/adl_move_swap.hpp 2018-01-02 15:34:48.829052917 +0100 -@@ -28,6 +28,8 @@ - //Try to avoid including , as it's quite big - #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB) - #include //Dinkum libraries define std::swap in utility which is lighter than algorithm -+#elif defined(__LSB_VERSION__) -+# include - #elif defined(BOOST_GNU_STDLIB) - //For non-GCC compilers, where GNUC version is not very reliable, or old GCC versions - //use the good old stl_algobase header, which is quite lightweight diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/boost-1.66.0-linux-standard-base.patch --- a/OrthancFramework/Resources/Patches/boost-1.66.0-linux-standard-base.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -diff -urEb boost_1_66_0.orig/boost/move/adl_move_swap.hpp boost_1_66_0/boost/move/adl_move_swap.hpp ---- boost_1_66_0.orig/boost/move/adl_move_swap.hpp 2018-04-11 11:56:16.761768726 +0200 -+++ boost_1_66_0/boost/move/adl_move_swap.hpp 2018-04-11 11:57:01.073881330 +0200 -@@ -28,6 +28,8 @@ - //Try to avoid including , as it's quite big - #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB) - #include //Dinkum libraries define std::swap in utility which is lighter than algorithm -+#elif defined(__LSB_VERSION__) -+# include - #elif defined(BOOST_GNU_STDLIB) - //For non-GCC compilers, where GNUC version is not very reliable, or old GCC versions - //use the good old stl_algobase header, which is quite lightweight -Only in boost_1_66_0/boost/move: adl_move_swap.hpp~ diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/boost-1.67.0-linux-standard-base.patch --- a/OrthancFramework/Resources/Patches/boost-1.67.0-linux-standard-base.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -diff -urEb boost_1_67_0.orig/boost/move/adl_move_swap.hpp boost_1_67_0/boost/move/adl_move_swap.hpp ---- boost_1_67_0.orig/boost/move/adl_move_swap.hpp 2018-06-20 17:42:27.000000000 +0200 -+++ boost_1_67_0/boost/move/adl_move_swap.hpp 2018-10-12 14:27:41.368076902 +0200 -@@ -28,6 +28,8 @@ - //Try to avoid including , as it's quite big - #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB) - #include //Dinkum libraries define std::swap in utility which is lighter than algorithm -+#elif defined(__LSB_VERSION__) -+# include - #elif defined(BOOST_GNU_STDLIB) - //For non-GCC compilers, where GNUC version is not very reliable, or old GCC versions - //use the good old stl_algobase header, which is quite lightweight -diff -urEb boost_1_67_0.orig/boost/thread/detail/config.hpp boost_1_67_0/boost/thread/detail/config.hpp ---- boost_1_67_0.orig/boost/thread/detail/config.hpp 2018-06-20 17:42:27.000000000 +0200 -+++ boost_1_67_0/boost/thread/detail/config.hpp 2018-10-12 14:27:41.372076898 +0200 -@@ -417,6 +417,8 @@ - #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO - #elif defined(BOOST_THREAD_CHRONO_MAC_API) - #define BOOST_THREAD_HAS_MONO_CLOCK -+#elif defined(__LSB_VERSION__) || defined(__ANDROID__) -+ #define BOOST_THREAD_HAS_MONO_CLOCK - #else - #include // check for CLOCK_MONOTONIC - #if defined(CLOCK_MONOTONIC) -diff -urEb boost_1_67_0.orig/boost/type_traits/detail/has_postfix_operator.hpp boost_1_67_0/boost/type_traits/detail/has_postfix_operator.hpp ---- boost_1_67_0.orig/boost/type_traits/detail/has_postfix_operator.hpp 2018-06-20 17:42:27.000000000 +0200 -+++ boost_1_67_0/boost/type_traits/detail/has_postfix_operator.hpp 2018-10-12 14:31:27.539874170 +0200 -@@ -32,8 +32,11 @@ - namespace boost { - namespace detail { - -+// https://stackoverflow.com/a/15474269 -+#ifndef Q_MOC_RUN - // This namespace ensures that argument-dependent name lookup does not mess things up. - namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { -+#endif - - // 1. a function to have an instance of type T without requiring T to be default - // constructible -@@ -181,7 +184,9 @@ - BOOST_STATIC_CONSTANT(bool, value = (trait_impl1 < Lhs_noref, Ret, BOOST_TT_FORBIDDEN_IF >::value)); - }; - -+#ifndef Q_MOC_RUN - } // namespace impl -+#endif - } // namespace detail - - // this is the accessible definition of the trait to end user -diff -urEb boost_1_67_0.orig/boost/type_traits/detail/has_prefix_operator.hpp boost_1_67_0/boost/type_traits/detail/has_prefix_operator.hpp ---- boost_1_67_0.orig/boost/type_traits/detail/has_prefix_operator.hpp 2018-06-20 17:42:27.000000000 +0200 -+++ boost_1_67_0/boost/type_traits/detail/has_prefix_operator.hpp 2018-10-12 14:31:40.991862281 +0200 -@@ -45,8 +45,11 @@ - namespace boost { - namespace detail { - -+// https://stackoverflow.com/a/15474269 -+#ifndef Q_MOC_RUN - // This namespace ensures that argument-dependent name lookup does not mess things up. - namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { -+#endif - - // 1. a function to have an instance of type T without requiring T to be default - // constructible -@@ -194,7 +197,9 @@ - BOOST_STATIC_CONSTANT(bool, value = (trait_impl1 < Rhs_noref, Ret, BOOST_TT_FORBIDDEN_IF >::value)); - }; - -+#ifndef Q_MOC_RUN - } // namespace impl -+#endif - } // namespace detail - - // this is the accessible definition of the trait to end user diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/boost-1.68.0-linux-standard-base.patch --- a/OrthancFramework/Resources/Patches/boost-1.68.0-linux-standard-base.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -diff -urEb boost_1_68_0.orig/boost/move/adl_move_swap.hpp boost_1_68_0/boost/move/adl_move_swap.hpp ---- boost_1_68_0.orig/boost/move/adl_move_swap.hpp 2018-11-13 16:08:32.214434915 +0100 -+++ boost_1_68_0/boost/move/adl_move_swap.hpp 2018-11-13 16:09:03.558399048 +0100 -@@ -28,6 +28,8 @@ - //Try to avoid including , as it's quite big - #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB) - #include //Dinkum libraries define std::swap in utility which is lighter than algorithm -+#elif defined(__LSB_VERSION__) -+# include - #elif defined(BOOST_GNU_STDLIB) - //For non-GCC compilers, where GNUC version is not very reliable, or old GCC versions - //use the good old stl_algobase header, which is quite lightweight -diff -urEb boost_1_68_0.orig/boost/thread/detail/config.hpp boost_1_68_0/boost/thread/detail/config.hpp ---- boost_1_68_0.orig/boost/thread/detail/config.hpp 2018-11-13 16:08:32.210434920 +0100 -+++ boost_1_68_0/boost/thread/detail/config.hpp 2018-11-13 16:10:03.386329911 +0100 -@@ -417,7 +417,7 @@ - #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO - #elif defined(BOOST_THREAD_CHRONO_MAC_API) - #define BOOST_THREAD_HAS_MONO_CLOCK --#elif defined(__ANDROID__) -+#elif defined(__LSB_VERSION__) || defined(__ANDROID__) - #define BOOST_THREAD_HAS_MONO_CLOCK - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21 - #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO -diff -urEb boost_1_68_0.orig/boost/type_traits/detail/has_postfix_operator.hpp boost_1_68_0/boost/type_traits/detail/has_postfix_operator.hpp ---- boost_1_68_0.orig/boost/type_traits/detail/has_postfix_operator.hpp 2018-11-13 16:08:32.206434924 +0100 -+++ boost_1_68_0/boost/type_traits/detail/has_postfix_operator.hpp 2018-11-13 16:11:08.374253901 +0100 -@@ -32,8 +32,11 @@ - namespace boost { - namespace detail { - -+// https://stackoverflow.com/a/15474269 -+#ifndef Q_MOC_RUN - // This namespace ensures that argument-dependent name lookup does not mess things up. - namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { -+#endif - - // 1. a function to have an instance of type T without requiring T to be default - // constructible -@@ -181,7 +184,9 @@ - BOOST_STATIC_CONSTANT(bool, value = (trait_impl1 < Lhs_noref, Ret, BOOST_TT_FORBIDDEN_IF >::value)); - }; - -+#ifndef Q_MOC_RUN - } // namespace impl -+#endif - } // namespace detail - - // this is the accessible definition of the trait to end user -diff -urEb boost_1_68_0.orig/boost/type_traits/detail/has_prefix_operator.hpp boost_1_68_0/boost/type_traits/detail/has_prefix_operator.hpp ---- boost_1_68_0.orig/boost/type_traits/detail/has_prefix_operator.hpp 2018-11-13 16:08:32.206434924 +0100 -+++ boost_1_68_0/boost/type_traits/detail/has_prefix_operator.hpp 2018-11-13 16:14:30.278012856 +0100 -@@ -45,8 +45,11 @@ - namespace boost { - namespace detail { - -+// https://stackoverflow.com/a/15474269 -+#ifndef Q_MOC_RUN - // This namespace ensures that argument-dependent name lookup does not mess things up. - namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { -+#endif - - // 1. a function to have an instance of type T without requiring T to be default - // constructible -@@ -194,7 +197,10 @@ - BOOST_STATIC_CONSTANT(bool, value = (trait_impl1 < Rhs_noref, Ret, BOOST_TT_FORBIDDEN_IF >::value)); - }; - -+ -+#ifndef Q_MOC_RUN - } // namespace impl -+#endif - } // namespace detail - - // this is the accessible definition of the trait to end user -Only in boost_1_68_0/boost/type_traits/detail: has_prefix_operator.hpp~ diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/civetweb-1.11.patch --- a/OrthancFramework/Resources/Patches/civetweb-1.11.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -diff -urEb civetweb-1.11.orig/include/civetweb.h civetweb-1.11/include/civetweb.h ---- civetweb-1.11.orig/include/civetweb.h 2019-01-17 21:09:41.844888908 +0100 -+++ civetweb-1.11/include/civetweb.h 2019-01-21 12:05:08.138998659 +0100 -@@ -1507,6 +1507,10 @@ - #endif - - -+// Added by SJ -+CIVETWEB_API void mg_disable_keep_alive(struct mg_connection *conn); -+ -+ - #ifdef __cplusplus - } - #endif /* __cplusplus */ -diff -urEb civetweb-1.11.orig/src/civetweb.c civetweb-1.11/src/civetweb.c ---- civetweb-1.11.orig/src/civetweb.c 2019-01-17 21:09:41.852888857 +0100 -+++ civetweb-1.11/src/civetweb.c 2019-01-21 12:06:35.826868284 +0100 -@@ -59,6 +59,9 @@ - #if defined(__linux__) && !defined(_XOPEN_SOURCE) - #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ - #endif -+#if defined(__LSB_VERSION__) -+#define NEED_TIMEGM -+#endif - #if !defined(_LARGEFILE_SOURCE) - #define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ - #endif -@@ -129,6 +132,12 @@ - - - /* Alternative queue is well tested and should be the new default */ -+#if defined(__LSB_VERSION__) -+/* Function "eventfd()" is not available in Linux Standard Base, can't -+ * use the alternative queue */ -+#define NO_ALTERNATIVE_QUEUE -+#endif -+ - #if defined(NO_ALTERNATIVE_QUEUE) - #if defined(ALTERNATIVE_QUEUE) - #error "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE or none, but not both" -@@ -536,6 +545,10 @@ - #if !defined(EWOULDBLOCK) - #define EWOULDBLOCK WSAEWOULDBLOCK - #endif /* !EWOULDBLOCK */ -+#if !defined(ECONNRESET) -+/* This macro is not defined e.g. in Visual Studio 2008 */ -+#define ECONNRESET WSAECONNRESET -+#endif /* !ECONNRESET */ - #define _POSIX_ - #define INT64_FMT "I64d" - #define UINT64_FMT "I64u" -@@ -2939,6 +2952,13 @@ - #endif - - -+#if defined(__LSB_VERSION__) -+static void -+mg_set_thread_name(const char *threadName) -+{ -+ /* prctl() does not seem to be available in Linux Standard Base */ -+} -+#else - static void - mg_set_thread_name(const char *name) - { -@@ -2980,6 +3000,7 @@ - (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); - #endif - } -+#endif - #else /* !defined(NO_THREAD_NAME) */ - void - mg_set_thread_name(const char *threadName) -@@ -16919,6 +16940,10 @@ - /* Message is a valid request */ - - /* Is there a "host" ? */ -+ /* https://github.com/civetweb/civetweb/pull/675/commits/96e3e8c50acb4b8e0c946d02b5f880a3e62986e1 */ -+ if (conn->host!=NULL) { -+ mg_free((void *)conn->host); -+ } - conn->host = alloc_get_host(conn); - if (!conn->host) { - mg_snprintf(conn, -@@ -19857,4 +19882,13 @@ - } - - -+// Added by SJ -+void mg_disable_keep_alive(struct mg_connection *conn) -+{ -+ if (conn != NULL) { -+ conn->must_close = 1; -+ } -+} -+ -+ - /* End of civetweb.c */ diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Resources/Patches/civetweb-1.12.patch --- a/OrthancFramework/Resources/Patches/civetweb-1.12.patch Wed May 24 08:54:54 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -diff -urEb civetweb-1.12.orig/include/civetweb.h civetweb-1.12/include/civetweb.h ---- civetweb-1.12.orig/include/civetweb.h 2020-10-06 12:39:10.634902843 +0200 -+++ civetweb-1.12/include/civetweb.h 2020-10-06 12:39:30.630872089 +0200 -@@ -1614,6 +1614,9 @@ - struct mg_error_data *error); - #endif - -+// Added by SJ -+CIVETWEB_API void mg_disable_keep_alive(struct mg_connection *conn); -+ - #ifdef __cplusplus - } - #endif /* __cplusplus */ -diff -urEb civetweb-1.12.orig/src/civetweb.c civetweb-1.12/src/civetweb.c ---- civetweb-1.12.orig/src/civetweb.c 2020-10-06 12:39:10.638902837 +0200 -+++ civetweb-1.12/src/civetweb.c 2020-10-06 12:41:40.110671929 +0200 -@@ -10525,6 +10525,11 @@ - /* + MicroSoft extensions - * https://msdn.microsoft.com/en-us/library/aa142917.aspx */ - -+ /* Added by SJ, for write access to WebDAV on Windows >= 7 */ -+ {"LOCK", 1, 1, 0, 0, 0}, -+ {"UNLOCK", 1, 0, 0, 0, 0}, -+ {"PROPPATCH", 1, 1, 0, 0, 0}, -+ - /* REPORT method (RFC 3253) */ - {"REPORT", 1, 1, 1, 1, 1}, - /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */ -@@ -20704,5 +20709,12 @@ - return 1; - } - -+// Added by SJ -+void mg_disable_keep_alive(struct mg_connection *conn) -+{ -+ if (conn != NULL) { -+ conn->must_close = 1; -+ } -+} - - /* End of civetweb.c */ diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Sources/Enumerations.cpp --- a/OrthancFramework/Sources/Enumerations.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/Sources/Enumerations.cpp Wed May 24 08:56:41 2023 +0200 @@ -31,6 +31,7 @@ #include #include #include +#include namespace Orthanc { @@ -1940,6 +1941,11 @@ std::string s = Toolbox::StripSpaces(specificCharacterSet); Toolbox::ToUpperCase(s); + // handle common spelling mistakes + boost::replace_all(s, "ISO_IR_", "ISO_IR "); + boost::replace_all(s, "ISO_2022_IR_", "ISO 2022 IR "); + + // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2 // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java if (s == "ISO_IR 6" || diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Sources/Images/NumpyWriter.cpp --- a/OrthancFramework/Sources/Images/NumpyWriter.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/Sources/Images/NumpyWriter.cpp Wed May 24 08:56:41 2023 +0200 @@ -154,7 +154,7 @@ { if (compress) { -#if ORTHANC_ENABLE_ZLIB == 1 +#if (ORTHANC_ENABLE_ZLIB == 1) && (ORTHANC_SANDBOXED == 0) // This is the default name of the first array if arrays are // specified as positional arguments in "numpy.savez()" // https://numpy.org/doc/stable/reference/generated/numpy.savez.html @@ -172,7 +172,7 @@ writer.Write(uncompressed); writer.Close(); #else - throw OrthancException(ErrorCode_InternalError, "Orthanc was compiled without support for zlib"); + throw OrthancException(ErrorCode_InternalError, "Orthanc was compiled without support for ZIP"); #endif } else diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/Sources/Lua/LuaContext.cpp --- a/OrthancFramework/Sources/Lua/LuaContext.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/Sources/Lua/LuaContext.cpp Wed May 24 08:56:41 2023 +0200 @@ -396,10 +396,6 @@ const std::string s = value.asString(); lua_pushlstring(lua_, s.c_str(), s.size()); } - else if (value.isDouble()) - { - lua_pushnumber(lua_, value.asDouble()); - } else if (value.isInt()) { lua_pushinteger(lua_, value.asInt()); @@ -408,6 +404,10 @@ { lua_pushinteger(lua_, value.asUInt()); } + else if (value.isDouble()) + { + lua_pushnumber(lua_, value.asDouble()); + } else if (value.isBool()) { lua_pushboolean(lua_, value.asBool()); diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/UnitTestsSources/BitbucketCACertificates.h --- a/OrthancFramework/UnitTestsSources/BitbucketCACertificates.h Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/BitbucketCACertificates.h Wed May 24 08:56:41 2023 +0200 @@ -1,26 +1,30 @@ #define BITBUCKET_CERTIFICATES \ "-----BEGIN CERTIFICATE-----\n" \ -"MIIEFzCCAv+gAwIBAgIQB/LzXIeod6967+lHmTUlvTANBgkqhkiG9w0BAQwFADBh\n" \ +"MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs\n" \ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ -"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \ -"QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFYxCzAJBgNVBAYTAlVT\n" \ -"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMDAuBgNVBAMTJ0RpZ2lDZXJ0IFRMUyBI\n" \ -"eWJyaWQgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA\n" \ -"BMEbxppbmNmkKaDp1AS12+umsmxVwP/tmMZJLwYnUcu/cMEFesOxnYeJuq20ExfJ\n" \ -"qLSDyLiQ0cx0NTY8g3KwtdD3ImnI8YDEe0CPz2iHJlw5ifFNkU3aiYvkA8ND5b8v\n" \ -"c6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAq8CCkXjKU5\n" \ -"bXoOzjPHLrPt+8N6MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4G\n" \ -"A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI\n" \ -"KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j\n" \ -"b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp\n" \ -"Q2VydEdsb2JhbFJvb3RDQS5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny\n" \ -"bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAE\n" \ -"NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG\n" \ -"BmeBDAECAzANBgkqhkiG9w0BAQwFAAOCAQEAR1mBf9QbH7Bx9phdGLqYR5iwfnYr\n" \ -"6v8ai6wms0KNMeZK6BnQ79oU59cUkqGS8qcuLa/7Hfb7U7CKP/zYFgrpsC62pQsY\n" \ -"kDUmotr2qLcy/JUjS8ZFucTP5Hzu5sn4kL1y45nDHQsFfGqXbbKrAjbYwrwsAZI/\n" \ -"BKOLdRHHuSm8EdCGupK8JvllyDfNJvaGEwwEqonleLHBTnm8dqMLUeTF0J5q/hos\n" \ -"Vq4GNiejcxwIfZMy0MJEGdqN9A57HSgDKwmKdsp33Id6rHtSJlWncg+d0ohP/rEh\n" \ -"xRqhqjn1VtvChMQ1H3Dau0bwhr9kAMQ+959GG50jBbl9s08PqUU643QwmA==\n" \ +"d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" \ +"ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL\n" \ +"MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" \ +"LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW\n" \ +"YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" \ +"ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY\n" \ +"uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/\n" \ +"LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy\n" \ +"/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh\n" \ +"cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k\n" \ +"8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB\n" \ +"Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF\n" \ +"BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp\n" \ +"Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy\n" \ +"dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2\n" \ +"MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j\n" \ +"b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW\n" \ +"gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh\n" \ +"hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg\n" \ +"4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa\n" \ +"2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs\n" \ +"1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1\n" \ +"oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn\n" \ +"8TUoE6smftX3eg==\n" \ "-----END CERTIFICATE-----\n" \ "\n" diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp --- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Wed May 24 08:56:41 2023 +0200 @@ -314,6 +314,10 @@ ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 192")); ASSERT_EQ(Encoding_Utf8, e); ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e); ASSERT_TRUE(GetDicomEncoding(e, "GBK")); ASSERT_EQ(Encoding_Chinese, e); + + // common spelling mistakes + ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR_100")); ASSERT_EQ(Encoding_Latin1, e); + ASSERT_TRUE(GetDicomEncoding(e, "ISO_2022_IR_6")); ASSERT_EQ(Encoding_Ascii, e); } diff -r 7d913ee2f665 -r f26ed26a7793 OrthancFramework/UnitTestsSources/RestApiTests.cpp --- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp Wed May 24 08:56:41 2023 +0200 @@ -95,22 +95,15 @@ /** The HTTPS CA certificates for BitBucket were extracted as follows: - (1) We retrieve the certification chain of BitBucket: - - # echo | openssl s_client -showcerts -connect www.bitbucket.org:443 + (1) We retrieve the URI of the root CA of BitBucket: - (2) We see that the certification authority (CA) is - "www.digicert.com", and the root certificate is "DigiCert High - Assurance EV Root CA". As a consequence, we navigate to DigiCert to - find the URL to this CA certificate: + # echo | openssl s_client -servername bitbucket.org -connect bitbucket.org:443 2>/dev/null | openssl x509 -text | grep "CA Issuers" - firefox https://www.digicert.com/digicert-root-certificates.htm - - (3) Once we get the URL to the CA certificate, we convert it to a C + (2) Once we get the URL to the CA certificate, we convert it to a C macro that can be used by libcurl: # cd UnitTestsSources - # ../Resources/RetrieveCACertificates.py BITBUCKET_CERTIFICATES https://cacerts.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crt > BitbucketCACertificates.h + # python2 ../Resources/RetrieveCACertificates.py BITBUCKET_CERTIFICATES http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt > BitbucketCACertificates.h **/ #include "BitbucketCACertificates.h" diff -r 7d913ee2f665 -r f26ed26a7793 OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed May 24 08:56:41 2023 +0200 @@ -2037,6 +2037,24 @@ remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId; + + { // update the LastUpdate metadata of all parents + std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); + ResourcesContent content(true); + + int64_t parentId = 0; + if (transaction.LookupResource(parentId, remainingLevel, remainingPublicId)) + { + + do + { + content.AddMetadata(parentId, MetadataType_LastUpdate, now); + } + while (transaction.LookupParent(parentId, parentId)); + + transaction.SetResourcesContent(content); + } + } } else { @@ -3099,317 +3117,295 @@ virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE { - try + IDatabaseWrapper::CreateInstanceResult status; + int64_t instanceId; + + bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, + hashStudy_, hashSeries_, hashInstance_); + + if (isReconstruct_ && isNewInstance) { - IDatabaseWrapper::CreateInstanceResult status; - int64_t instanceId; - - bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, - hashStudy_, hashSeries_, hashInstance_); - - if (isReconstruct_ && isNewInstance) - { - // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex - // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance - throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); - } - - // Check whether this instance is already stored - if (!isNewInstance && !isReconstruct_) + // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex + // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance + throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); + } + + // Check whether this instance is already stored + if (!isNewInstance && !isReconstruct_) + { + // The instance already exists + if (overwrite_) { - // The instance already exists - if (overwrite_) + // Overwrite the old instance + LOG(INFO) << "Overwriting instance: " << hashInstance_; + transaction.DeleteResource(instanceId); + + // Re-create the instance, now that the old one is removed + if (!transaction.CreateInstance(status, instanceId, hashPatient_, + hashStudy_, hashSeries_, hashInstance_)) { - // Overwrite the old instance - LOG(INFO) << "Overwriting instance: " << hashInstance_; - transaction.DeleteResource(instanceId); - - // Re-create the instance, now that the old one is removed - if (!transaction.CreateInstance(status, instanceId, hashPatient_, - hashStudy_, hashSeries_, hashInstance_)) - { - throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); - } - } - else - { - // Do nothing if the instance already exists and overwriting is disabled - transaction.GetAllMetadata(instanceMetadata_, instanceId); - storeStatus_ = StoreStatus_AlreadyStored; - return; + throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); } } - - - if (!isReconstruct_) // don't signal new resources if this is a reconstruction + else + { + // Do nothing if the instance already exists and overwriting is disabled + transaction.GetAllMetadata(instanceMetadata_, instanceId); + storeStatus_ = StoreStatus_AlreadyStored; + return; + } + } + + + if (!isReconstruct_) // don't signal new resources if this is a reconstruction + { + // Warn about the creation of new resources. The order must be + // from instance to patient. + + // NB: In theory, could be sped up by grouping the underlying + // calls to "transaction.LogChange()". However, this would only have an + // impact when new patient/study/series get created, which + // occurs far less often that creating new instances. The + // positive impact looks marginal in practice. + transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); + + if (status.isNewSeries_) { - // Warn about the creation of new resources. The order must be - // from instance to patient. - - // NB: In theory, could be sped up by grouping the underlying - // calls to "transaction.LogChange()". However, this would only have an - // impact when new patient/study/series get created, which - // occurs far less often that creating new instances. The - // positive impact looks marginal in practice. - transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); - - if (status.isNewSeries_) - { - transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); - } - - if (status.isNewStudy_) - { - transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); - } - - if (status.isNewPatient_) - { - transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); - } - } + transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); + } + + if (status.isNewStudy_) + { + transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); + } - // Ensure there is enough room in the storage for the new instance - uint64_t instanceSize = 0; - for (Attachments::const_iterator it = attachments_.begin(); - it != attachments_.end(); ++it) + if (status.isNewPatient_) + { + transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); + } + } + + // Ensure there is enough room in the storage for the new instance + uint64_t instanceSize = 0; + for (Attachments::const_iterator it = attachments_.begin(); + it != attachments_.end(); ++it) + { + instanceSize += it->GetCompressedSize(); + } + + if (!isReconstruct_) // reconstruction should not affect recycling + { + if (maximumStorageMode_ == MaxStorageMode_Reject) { - instanceSize += it->GetCompressedSize(); - } - - if (!isReconstruct_) // reconstruction should not affect recycling - { - if (maximumStorageMode_ == MaxStorageMode_Reject) + if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) { - if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) - { - storeStatus_ = StoreStatus_StorageFull; - throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction - } - if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) - { - storeStatus_ = StoreStatus_StorageFull; - throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction - } + storeStatus_ = StoreStatus_StorageFull; + throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction + } + if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) + { + storeStatus_ = StoreStatus_StorageFull; + throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction } - else + } + else + { + transaction.Recycle(maximumStorageSize_, maximumPatientCount_, + instanceSize, hashPatient_ /* don't consider the current patient for recycling */); + } + } + + // Attach the files to the newly created instance + for (Attachments::const_iterator it = attachments_.begin(); + it != attachments_.end(); ++it) + { + if (isReconstruct_) + { + // we are replacing attachments during a reconstruction + transaction.DeleteAttachment(instanceId, it->GetContentType()); + } + + transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); + } + + if (!isReconstruct_) + { + ResourcesContent content(true /* new resource, metadata can be set */); + + // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) + for (MetadataMap::const_iterator + it = metadata_.begin(); it != metadata_.end(); ++it) + { + switch (it->first.first) { - transaction.Recycle(maximumStorageSize_, maximumPatientCount_, - instanceSize, hashPatient_ /* don't consider the current patient for recycling */); + case ResourceType_Patient: + content.AddMetadata(status.patientId_, it->first.second, it->second); + break; + + case ResourceType_Study: + content.AddMetadata(status.studyId_, it->first.second, it->second); + break; + + case ResourceType_Series: + content.AddMetadata(status.seriesId_, it->first.second, it->second); + break; + + case ResourceType_Instance: + SetInstanceMetadata(content, instanceMetadata_, instanceId, + it->first.second, it->second); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } - } - - // Attach the files to the newly created instance - for (Attachments::const_iterator it = attachments_.begin(); - it != attachments_.end(); ++it) - { - if (isReconstruct_) - { - // we are replacing attachments during a reconstruction - transaction.DeleteAttachment(instanceId, it->GetContentType()); - } - - transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); } if (!isReconstruct_) { - ResourcesContent content(true /* new resource, metadata can be set */); - - // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) - for (MetadataMap::const_iterator - it = metadata_.begin(); it != metadata_.end(); ++it) + // Populate the tags of the newly-created resources + content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); + SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 + SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 + + if (status.isNewSeries_) { - switch (it->first.first) - { - case ResourceType_Patient: - content.AddMetadata(status.patientId_, it->first.second, it->second); - break; - - case ResourceType_Study: - content.AddMetadata(status.studyId_, it->first.second, it->second); - break; - - case ResourceType_Series: - content.AddMetadata(status.seriesId_, it->first.second, it->second); - break; - - case ResourceType_Instance: - SetInstanceMetadata(content, instanceMetadata_, instanceId, - it->first.second, it->second); - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } + content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); + content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 + SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 } - if (!isReconstruct_) + if (status.isNewStudy_) { - // Populate the tags of the newly-created resources - content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); - SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 - SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 - - if (status.isNewSeries_) - { - content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); - content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 - SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 - } - - if (status.isNewStudy_) - { - content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); - content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 - SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 - } - - if (status.isNewPatient_) - { - content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); - content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 - SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 - } - - // Attach the auto-computed metadata for the patient/study/series levels - std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); - content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); - content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); - content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); - - if (status.isNewSeries_) + content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); + content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 + SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 + } + + if (status.isNewPatient_) + { + content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); + content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 + SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 + } + + // Attach the auto-computed metadata for the patient/study/series levels + std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); + content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); + content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); + content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); + + if (status.isNewSeries_) + { + if (hasExpectedInstances_) { - if (hasExpectedInstances_) - { - content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, - boost::lexical_cast(expectedInstances_)); - } - - // New in Orthanc 1.9.0 - content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, - origin_.GetRemoteAetC()); - } - // Attach the auto-computed metadata for the instance level, - // reflecting these additions into the input metadata map - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_ReceptionDate, now); - SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, - origin_.GetRemoteAetC()); - SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, - EnumerationToString(origin_.GetRequestOrigin())); - - std::string s; - - if (origin_.LookupRemoteIp(s)) - { - // New in Orthanc 1.4.0 - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_RemoteIp, s); + content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, + boost::lexical_cast(expectedInstances_)); } - if (origin_.LookupCalledAet(s)) - { - // New in Orthanc 1.4.0 - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_CalledAet, s); - } - - if (origin_.LookupHttpUsername(s)) - { - // New in Orthanc 1.4.0 - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_HttpUsername, s); - } + // New in Orthanc 1.9.0 + content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, + origin_.GetRemoteAetC()); + } + // Attach the auto-computed metadata for the instance level, + // reflecting these additions into the input metadata map + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_ReceptionDate, now); + SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, + origin_.GetRemoteAetC()); + SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, + EnumerationToString(origin_.GetRequestOrigin())); + + std::string s; + + if (origin_.LookupRemoteIp(s)) + { + // New in Orthanc 1.4.0 + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_RemoteIp, s); + } + + if (origin_.LookupCalledAet(s)) + { + // New in Orthanc 1.4.0 + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_CalledAet, s); + } + + if (origin_.LookupHttpUsername(s)) + { + // New in Orthanc 1.4.0 + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_HttpUsername, s); } - - // Following metadatas are also updated if reconstructing the instance. - // They might be missing since they have been introduced along Orthanc versions. - - if (hasTransferSyntax_) - { - // New in Orthanc 1.2.0 - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_TransferSyntax, - GetTransferSyntaxUid(transferSyntax_)); - } - - if (hasPixelDataOffset_) - { - // New in Orthanc 1.9.1 - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_PixelDataOffset, - boost::lexical_cast(pixelDataOffset_)); - } - - const DicomValue* value; - if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && - !value->IsNull() && + } + + // Following metadatas are also updated if reconstructing the instance. + // They might be missing since they have been introduced along Orthanc versions. + + if (hasTransferSyntax_) + { + // New in Orthanc 1.2.0 + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_TransferSyntax, + GetTransferSyntaxUid(transferSyntax_)); + } + + if (hasPixelDataOffset_) + { + // New in Orthanc 1.9.1 + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_PixelDataOffset, + boost::lexical_cast(pixelDataOffset_)); + } + + const DicomValue* value; + if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && + !value->IsNull() && + !value->IsBinary()) + { + SetInstanceMetadata(content, instanceMetadata_, instanceId, + MetadataType_Instance_SopClassUid, value->GetContent()); + } + + + if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || + (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) + { + if (!value->IsNull() && !value->IsBinary()) { SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_SopClassUid, value->GetContent()); - } - - - if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || - (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) - { - if (!value->IsNull() && - !value->IsBinary()) - { - SetInstanceMetadata(content, instanceMetadata_, instanceId, - MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); - } - } - - - transaction.SetResourcesContent(content); - } - - - // Check whether the series of this new instance is now completed - int64_t expectedNumberOfInstances; - if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) - { - SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); - if (seriesStatus == SeriesStatus_Complete) - { - transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); + MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); } } - - transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); - transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); - transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); - - // Mark the parent resources of this instance as unstable - transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); - transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); - transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); - transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); - - storeStatus_ = StoreStatus_Success; + + + transaction.SetResourcesContent(content); } - catch (OrthancException& e) + + + // Check whether the series of this new instance is now completed + int64_t expectedNumberOfInstances; + if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) { - if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize) - { - throw; // the transaction has failed -> do not commit the current transaction (and retry) - } - else + SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); + if (seriesStatus == SeriesStatus_Complete) { - LOG(ERROR) << "EXCEPTION [" << e.What() << " - " << e.GetDetails() << "]"; - - if (e.GetErrorCode() == ErrorCode_FullStorage) - { - throw; // do not commit the current transaction - } - - // this is an expected failure, exit normaly and commit the current transaction - storeStatus_ = StoreStatus_Failure; + transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); } } + + transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); + transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); + transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); + + // Mark the parent resources of this instance as unstable + transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); + transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); + transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); + transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); + + storeStatus_ = StoreStatus_Success; } }; @@ -3417,8 +3413,24 @@ Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); - Apply(operations); - return operations.GetStoreStatus(); + + try + { + Apply(operations); + return operations.GetStoreStatus(); + } + catch (OrthancException& e) + { + if (e.GetErrorCode() == ErrorCode_FullStorage) + { + return StoreStatus_StorageFull; + } + else + { + // the transaction has failed -> do not commit the current transaction (and retry) + throw; + } + } } @@ -3476,7 +3488,7 @@ int64_t resourceId; if (!transaction.LookupResource(resourceId, resourceType, publicId_)) { - status_ = StoreStatus_Failure; // Inexistent resource + throw OrthancException(ErrorCode_InexistentItem, HttpStatus_404_NotFound); } else { diff -r 7d913ee2f665 -r f26ed26a7793 OrthancServer/Sources/Search/HierarchicalMatcher.cpp --- a/OrthancServer/Sources/Search/HierarchicalMatcher.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancServer/Sources/Search/HierarchicalMatcher.cpp Wed May 24 08:56:41 2023 +0200 @@ -248,6 +248,8 @@ { std::unique_ptr target(new DcmDataset); + std::string currentPrivateCreator = ""; + for (std::set::const_iterator it = flatTags_.begin(); it != flatTags_.end(); ++it) { @@ -257,13 +259,19 @@ if (source.findAndGetElement(tag, element).good() && element != NULL) { - if (it->IsPrivate()) + if (tag.isPrivateReservation()) { - throw OrthancException(ErrorCode_NotImplemented, - "Not applicable to private tags: " + it->Format()); + OFString privateCreator; + element->getOFString(privateCreator, 0, false); + currentPrivateCreator = privateCreator.c_str(); } - - std::unique_ptr cloned(FromDcmtkBridge::CreateElementForTag(*it, "" /* no private creator */)); + else if (!it->IsPrivate()) + { + // reset the private creator as soon as we reach the end of the current private block + currentPrivateCreator = ""; + } + + std::unique_ptr cloned(FromDcmtkBridge::CreateElementForTag(*it, currentPrivateCreator.c_str())); cloned->copyFrom(*element); target->insert(cloned.release()); } diff -r 7d913ee2f665 -r f26ed26a7793 OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Wed May 24 08:54:54 2023 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Wed May 24 08:56:41 2023 +0200 @@ -688,8 +688,12 @@ break; case StoreStatus_Failure: - LOG(ERROR) << "Store failure"; - break; + LOG(ERROR) << "Unknown store failure"; + throw OrthancException(ErrorCode_InternalError, HttpStatus_500_InternalServerError); + + case StoreStatus_StorageFull: + LOG(ERROR) << "Storage full"; + throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage); default: // This should never happen