changeset 5301:f26ed26a7793 am-experimental

merge
author Alain Mazy <am@osimis.io>
date Wed, 24 May 2023 08:56:41 +0200
parents 7d913ee2f665 (current diff) 76dc541c5dda (diff)
children 9504de20d43d
files NEWS OrthancFramework/Sources/Enumerations.cpp OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/ServerContext.cpp
diffstat 19 files changed, 375 insertions(+), 647 deletions(-) [+]
line wrap: on
line diff
--- 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 \
--- 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
--- 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
--- 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}
--- 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 <algorithm>, as it's quite big
- #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB)
-    #include <utility>   //Dinkum libraries define std::swap in utility which is lighter than algorithm
-+#elif defined(__LSB_VERSION__)
-+#  include <utility>
- #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
--- 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 <algorithm>, as it's quite big
- #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB)
-    #include <utility>   //Dinkum libraries define std::swap in utility which is lighter than algorithm
-+#elif defined(__LSB_VERSION__)
-+#  include <utility>
- #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~
--- 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 <algorithm>, as it's quite big
- #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB)
-    #include <utility>   //Dinkum libraries define std::swap in utility which is lighter than algorithm
-+#elif defined(__LSB_VERSION__)
-+#  include <utility>
- #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 <time.h> // 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
--- 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 <algorithm>, as it's quite big
- #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB)
-    #include <utility>   //Dinkum libraries define std::swap in utility which is lighter than algorithm
-+#elif defined(__LSB_VERSION__)
-+#  include <utility>
- #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~
--- 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 */
--- 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 */
--- 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 <boost/thread/mutex.hpp>
 #include <string.h>
 #include <cassert>
+#include <boost/algorithm/string/replace.hpp>
 
 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" ||
--- 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
--- 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());
--- 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" 
--- 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);
 }
 
 
--- 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"
--- 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<std::string>(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<std::string>(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<std::string>(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<std::string>(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
         {
--- 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<DcmDataset> target(new DcmDataset);
 
+    std::string currentPrivateCreator = "";
+
     for (std::set<DicomTag>::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<DcmElement> 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<DcmElement> cloned(FromDcmtkBridge::CreateElementForTag(*it, currentPrivateCreator.c_str()));
         cloned->copyFrom(*element);
         target->insert(cloned.release());
       }
--- 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