changeset 36:fd58eb5749ed

CMake simplification using DownloadOrthancFramework.cmake
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 06 Jul 2020 17:37:30 +0200
parents 77f4e165e535
children 499d090f3d3f
files CMakeLists.txt CodeAnalysis/sdk.cpp.mustache NEWS Resources/Builders/DebianStable/Dockerfile-BuildEnvironment Resources/Builders/Dockerfile-MinGW-BuildEnvironment Resources/Builders/MinGW32-Python2.7/docker-internal.sh Resources/Builders/MinGW32-Python3.7/docker-internal.sh Resources/Builders/MinGW32-Python3.8/docker-internal.sh Resources/Builders/MinGW64-Python2.7/docker-internal.sh Resources/Builders/MinGW64-Python3.7/docker-internal.sh Resources/Builders/MinGW64-Python3.8/docker-internal.sh Resources/Orthanc/CMake/AutoGeneratedCode.cmake Resources/Orthanc/CMake/Compiler.cmake Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Resources/Orthanc/CMake/DownloadPackage.cmake Resources/Orthanc/Core/Compatibility.h Resources/Orthanc/Plugins/ExportedSymbolsPlugins.list Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h Resources/Orthanc/Plugins/OrthancPluginException.h Resources/Orthanc/Plugins/OrthancPluginsExports.cmake Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h Resources/Orthanc/Plugins/Samples/Common/OrthancPluginException.h Resources/Orthanc/Plugins/Samples/Common/VersionScript.map Resources/Orthanc/Plugins/VersionScriptPlugins.map Resources/Orthanc/Resources/CMake/BoostConfiguration.cmake Resources/Orthanc/Resources/CMake/Compiler.cmake Resources/Orthanc/Resources/CMake/DownloadPackage.cmake Resources/Orthanc/Resources/CMake/JsonCppConfiguration.cmake Resources/Orthanc/Resources/LinuxStandardBaseToolchain.cmake Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake Resources/Orthanc/Resources/MinGWToolchain.cmake Resources/Orthanc/Resources/Patches/boost-1.69.0-linux-standard-base.patch Resources/Orthanc/Resources/ThirdParty/patch/NOTES.txt Resources/Orthanc/Resources/ThirdParty/patch/msys-1.0.dll Resources/Orthanc/Resources/ThirdParty/patch/patch.exe Resources/Orthanc/Resources/ThirdParty/patch/patch.exe.manifest Resources/Orthanc/Resources/WindowsResources.py Resources/Orthanc/Resources/WindowsResources.rc Resources/Orthanc/Toolchains/LinuxStandardBaseToolchain.cmake Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake Resources/Orthanc/Toolchains/MinGWToolchain.cmake Resources/SyncOrthancFolder.py Sources/Autogenerated/sdk.cpp Sources/OnChangeCallback.cpp Sources/OnStoredInstanceCallback.cpp Sources/Plugin.cpp Sources/PythonFunction.cpp Sources/PythonFunction.h Sources/PythonLock.cpp Sources/PythonModule.cpp Sources/PythonModule.h Sources/PythonObject.cpp Sources/PythonString.cpp Sources/RestCallbacks.cpp
diffstat 58 files changed, 6088 insertions(+), 6048 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu May 28 21:02:23 2020 +0200
+++ b/CMakeLists.txt	Mon Jul 06 17:37:30 2020 +0200
@@ -3,14 +3,55 @@
 
 set(PLUGIN_VERSION "mainline")
 
+if (PLUGIN_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.7.0")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
 
 set(PYTHON_VERSION "3.6" CACHE STRING "Version of Python to be used")
 set(PYTHON_WINDOWS_ROOT "" CACHE STRING "")
 set(PYTHON_LIBRARY_NAME "" CACHE STRING "")
 set(PYTHON_WINDOWS_USE_RELEASE_LIBS ON CACHE BOOL "Use the release Python libraries when building with Microsoft Visual Studio, even when compiling in _DEBUG mode (set it to OFF if you require linking to a Python debug build)")
-set(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost")
-set(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
-set(USE_LEGACY_JSONCPP OFF CACHE BOOL "Use the old branch 0.x.y of JsonCpp, that does not require a C++11 compiler (for LSB and old versions of Visual Studio)")
+
+
+# Parameters of the build
+set(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
+set(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc framework (can be \"system\", \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_VERSION "${ORTHANC_FRAMEWORK_DEFAULT_VERSION}" CACHE STRING "Version of the Orthanc framework")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+# Advanced parameters to fine-tune linking against system libraries
+set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
+set(ORTHANC_FRAMEWORK_STATIC OFF CACHE BOOL "If linking against the Orthanc framework system library, indicates whether this library was statically linked")
+mark_as_advanced(ORTHANC_FRAMEWORK_STATIC)
+
+
+# Download and setup the Orthanc framework
+include(${CMAKE_SOURCE_DIR}/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake)
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "system")
+  #link_libraries(${ORTHANC_FRAMEWORK_LIBRARIES})
+
+else()
+  include(${ORTHANC_FRAMEWORK_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+  
+  #set(ENABLE_MODULE_IMAGES OFF CACHE INTERNAL "")
+  #set(ENABLE_MODULE_JOBS OFF CACHE INTERNAL "")
+  #set(ENABLE_MODULE_DICOM OFF CACHE INTERNAL "")
+  
+  include(${ORTHANC_FRAMEWORK_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
+  include_directories(${ORTHANC_FRAMEWORK_ROOT}/Sources)
+endif()
+
+
+include(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake)
+
 
 include(CheckIncludeFile)
 include(CheckIncludeFileCXX)
@@ -66,17 +107,11 @@
   endif()
 endif()
 
-set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/Resources/Orthanc)
-include(${ORTHANC_ROOT}/Resources/CMake/Compiler.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
-
 
 if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
   execute_process(
     COMMAND 
-    ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+    ${PYTHON_EXECUTABLE} ${ORTHANC_FRAMEWORK_ROOT}/Resources/WindowsResources.py
     ${PLUGIN_VERSION} "Python plugin" OrthancPython.dll
     "Plugin to create Orthanc plugins using Python"
     ERROR_VARIABLE Failure
@@ -92,7 +127,7 @@
 
 
 include_directories(
-  ${ORTHANC_ROOT}/Sdk-1.5.7
+  ${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.5.7
   )
 
 add_definitions(
@@ -100,7 +135,6 @@
   )
 
 include_directories(
-  ${ORTHANC_ROOT}/Plugins/Samples/Common
   ${PYTHON_INCLUDE_DIRS}
   )
 
@@ -117,7 +151,7 @@
   Sources/RestCallbacks.cpp
 
   # Third-party sources
-  ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
+  ${CMAKE_SOURCE_DIR}/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp
   ${BOOST_SOURCES}
   ${JSONCPP_SOURCES}
   ${WINDOWS_RESOURCES}
--- a/CodeAnalysis/sdk.cpp.mustache	Thu May 28 21:02:23 2020 +0200
+++ b/CodeAnalysis/sdk.cpp.mustache	Mon Jul 06 17:37:30 2020 +0200
@@ -21,7 +21,7 @@
 
 #include "../PythonLock.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 {{#enumerations}}
 #include "./sdk_{{name}}.impl.h"
--- a/NEWS	Thu May 28 21:02:23 2020 +0200
+++ b/NEWS	Mon Jul 06 17:37:30 2020 +0200
@@ -4,6 +4,7 @@
 * Fix compilation using Visual Studio
 * The "Calling Python..." info logs are disabled if "PythonVerbose" is "false"
 * New builders for Windows: Supporting 32 / 64bit with Python 2.7 / 3.7 / 3.8
+* Support of dynamic linking against the system-wide Orthanc framework library
 
 
 Version 1.0 (2020-04-01)
--- a/Resources/Builders/DebianStable/Dockerfile-BuildEnvironment	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/DebianStable/Dockerfile-BuildEnvironment	Mon Jul 06 17:37:30 2020 +0200
@@ -5,5 +5,5 @@
 
 RUN apt-get -y clean && apt-get -y update
 RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \
-    nano build-essential unzip cmake pkg-config libpython3.7-dev && \
+    nano build-essential unzip cmake pkg-config libpython3.7-dev mercurial && \
     apt-get clean && rm -rf /var/lib/apt/lists/*
--- a/Resources/Builders/Dockerfile-MinGW-BuildEnvironment	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/Dockerfile-MinGW-BuildEnvironment	Mon Jul 06 17:37:30 2020 +0200
@@ -5,5 +5,5 @@
 
 RUN apt-get -y clean && apt-get -y update
 RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \
-    nano unzip cmake mingw-w64 patch wget bzip2 python && \
+    nano unzip cmake mingw-w64 patch wget bzip2 python mercurial && \
     apt-get clean && rm -rf /var/lib/apt/lists/*
--- a/Resources/Builders/MinGW32-Python2.7/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW32-Python2.7/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -18,7 +18,7 @@
       -DSTATIC_BUILD=ON \
       -DPYTHON_VERSION=2.7 \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/python-2.7.17-win32/ \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- a/Resources/Builders/MinGW32-Python3.7/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW32-Python3.7/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -18,7 +18,7 @@
       -DSTATIC_BUILD=ON \
       -DPYTHON_VERSION=3.7 \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/python-3.7.7-win32/ \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- a/Resources/Builders/MinGW32-Python3.8/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW32-Python3.8/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -25,7 +25,7 @@
       -DPYTHON_VERSION=3.8 \
       -DPYTHON_LIBRARY_NAME=${LIBRARY_NAME} \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/Python-3.8.3-win32/ \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- a/Resources/Builders/MinGW64-Python2.7/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW64-Python2.7/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -18,7 +18,7 @@
       -DSTATIC_BUILD=ON \
       -DPYTHON_VERSION=2.7 \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/Python27/ \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- a/Resources/Builders/MinGW64-Python3.7/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW64-Python3.7/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -18,7 +18,7 @@
       -DSTATIC_BUILD=ON \
       -DPYTHON_VERSION=3.7 \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/python-3.7.7-win64/ \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- a/Resources/Builders/MinGW64-Python3.8/docker-internal.sh	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/Builders/MinGW64-Python3.8/docker-internal.sh	Mon Jul 06 17:37:30 2020 +0200
@@ -25,7 +25,7 @@
       -DPYTHON_VERSION=3.8 \
       -DPYTHON_LIBRARY_NAME=${LIBRARY_NAME} \
       -DPYTHON_WINDOWS_ROOT=/tmp/build/python-3.8.3-win64 \
-      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake \
+      -DCMAKE_TOOLCHAIN_FILE=/source/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake \
       -DCMAKE_INSTALL_PREFIX=/target 
 
 make -j`nproc`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/CMake/AutoGeneratedCode.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,58 @@
+set(EMBED_RESOURCES_PYTHON "${CMAKE_CURRENT_LIST_DIR}/../EmbedResources.py"
+  CACHE INTERNAL "Path to the EmbedResources.py script from Orthanc")
+set(AUTOGENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/AUTOGENERATED")
+set(AUTOGENERATED_SOURCES)
+
+file(MAKE_DIRECTORY ${AUTOGENERATED_DIR})
+include_directories(${AUTOGENERATED_DIR})
+
+macro(EmbedResources)
+  # Convert a semicolon separated list to a whitespace separated string
+  set(SCRIPT_OPTIONS)
+  set(SCRIPT_ARGUMENTS)
+  set(DEPENDENCIES)
+  set(IS_PATH_NAME false)
+
+  set(TARGET_BASE "${AUTOGENERATED_DIR}/EmbeddedResources")
+
+  # Loop over the arguments of the function
+  foreach(arg ${ARGN})
+    # Extract the first character of the argument
+    string(SUBSTRING "${arg}" 0 1 FIRST_CHAR)
+    if (${FIRST_CHAR} STREQUAL "-")
+      # If the argument starts with a dash "-", this is an option to
+      # EmbedResources.py
+      if (${arg} MATCHES "--target=.*")
+        # Does the argument starts with "--target="?
+        string(SUBSTRING "${arg}" 9 -1 TARGET)  # 9 is the length of "--target="
+        set(TARGET_BASE "${AUTOGENERATED_DIR}/${TARGET}")
+      else()
+        list(APPEND SCRIPT_OPTIONS ${arg})
+      endif()
+    else()
+      if (${IS_PATH_NAME})
+        list(APPEND SCRIPT_ARGUMENTS "${arg}")
+        list(APPEND DEPENDENCIES "${arg}")
+        set(IS_PATH_NAME false)
+      else()
+        list(APPEND SCRIPT_ARGUMENTS "${arg}")
+        set(IS_PATH_NAME true)
+      endif()
+    endif()
+  endforeach()
+
+  add_custom_command(
+    OUTPUT
+    "${TARGET_BASE}.h"
+    "${TARGET_BASE}.cpp"
+    COMMAND ${PYTHON_EXECUTABLE} ${EMBED_RESOURCES_PYTHON}
+            ${SCRIPT_OPTIONS} "${TARGET_BASE}" ${SCRIPT_ARGUMENTS}
+    DEPENDS
+    ${EMBED_RESOURCES_PYTHON}
+    ${DEPENDENCIES}
+    )
+
+  list(APPEND AUTOGENERATED_SOURCES
+    "${TARGET_BASE}.cpp"
+    ) 
+endmacro()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/CMake/Compiler.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,243 @@
+# This file sets all the compiler-related flags
+
+
+# Save the current compiler flags to the cache every time cmake configures the project
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "compiler flags" FORCE)
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "compiler flags" FORCE)
+
+
+include(CheckLibraryExists)
+
+if ((CMAKE_CROSSCOMPILING AND NOT
+      "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") OR    
+    "${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+  # Cross-compilation necessarily implies standalone and static build
+  SET(STATIC_BUILD ON)
+  SET(STANDALONE_BUILD ON)
+endif()
+
+
+if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+  # Cache the environment variables "LSB_CC" and "LSB_CXX" for further
+  # use by "ExternalProject" in CMake
+  SET(CMAKE_LSB_CC $ENV{LSB_CC} CACHE STRING "")
+  SET(CMAKE_LSB_CXX $ENV{LSB_CXX} CACHE STRING "")
+endif()
+
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long")
+
+  # --std=c99 makes libcurl not to compile
+  # -pedantic gives a lot of warnings on OpenSSL 
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros")
+
+  if (CMAKE_CROSSCOMPILING)
+    # http://stackoverflow.com/a/3543845/881731
+    set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -I<CMAKE_CURRENT_SOURCE_DIR> <SOURCE> <OBJECT>")
+  endif()
+
+elseif (MSVC)
+  # Use static runtime under Visual Studio
+  # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace
+  # http://stackoverflow.com/a/6510446
+  foreach(flag_var
+    CMAKE_C_FLAGS_DEBUG
+    CMAKE_CXX_FLAGS_DEBUG
+    CMAKE_C_FLAGS_RELEASE 
+    CMAKE_CXX_FLAGS_RELEASE
+    CMAKE_C_FLAGS_MINSIZEREL 
+    CMAKE_CXX_FLAGS_MINSIZEREL 
+    CMAKE_C_FLAGS_RELWITHDEBINFO 
+    CMAKE_CXX_FLAGS_RELWITHDEBINFO) 
+    string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+    string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}")
+  endforeach(flag_var)
+
+  # Add /Zm256 compiler option to Visual Studio to fix PCH errors
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm256")
+
+  # New in Orthanc 1.5.5
+  if (MSVC_MULTIPLE_PROCESSES)
+    # "If you omit the processMax argument in the /MP option, the
+    # compiler obtains the number of effective processors from the
+    # operating system, and then creates one process per effective
+    # processor"
+    # https://blog.kitware.com/cmake-building-with-all-your-cores/
+    # https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+  endif()
+    
+  add_definitions(
+    -D_CRT_SECURE_NO_WARNINGS=1
+    -D_CRT_SECURE_NO_DEPRECATE=1
+    )
+
+  if (MSVC_VERSION LESS 1600)
+    # Starting with Visual Studio >= 2010 (i.e. macro _MSC_VER >=
+    # 1600), Microsoft ships a standard-compliant <stdint.h>
+    # header. For earlier versions of Visual Studio, give access to a
+    # compatibility header.
+    # http://stackoverflow.com/a/70630/881731
+    # https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h#External_links
+    include_directories(${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/VisualStudio)
+  endif()
+
+  link_libraries(netapi32)
+endif()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+  # In FreeBSD/OpenBSD, the "/usr/local/" folder contains the ports and need to be imported
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/local/include")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/local/include")
+  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/lib")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L/usr/local/lib")
+endif()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND
+      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+    # The "--no-undefined" linker flag makes the shared libraries
+    # (plugins ModalityWorklists and ServeFolders) fail to compile on
+    # OpenBSD, and make the PostgreSQL plugin complain about missing
+    # "environ" global variable in FreeBSD
+    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
+  endif()
+
+  # Remove the "-rdynamic" option
+  # http://www.mail-archive.com/cmake@cmake.org/msg08837.html
+  set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
+  link_libraries(pthread)
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+    link_libraries(rt)
+  endif()
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
+      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+    link_libraries(dl)
+  endif()
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
+      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+    # The "--as-needed" linker flag is not available on FreeBSD and OpenBSD
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed")
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
+  endif()
+
+  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
+      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+    # FreeBSD/OpenBSD have just one single interface for file
+    # handling, which is 64bit clean, so there is no need to define macro
+    # for LFS (Large File Support).
+    # https://ohse.de/uwe/articles/lfs.html
+    add_definitions(
+      -D_LARGEFILE64_SOURCE=1 
+      -D_FILE_OFFSET_BITS=64
+      )
+  endif()
+
+elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+  if (MSVC)
+    message("MSVC compiler version = " ${MSVC_VERSION} "\n")
+    # Starting Visual Studio 2013 (version 1800), it is not possible
+    # to target Windows XP anymore
+    if (MSVC_VERSION LESS 1800)
+      add_definitions(
+        -DWINVER=0x0501
+        -D_WIN32_WINNT=0x0501
+        )
+    endif()
+  else()
+    add_definitions(
+      -DWINVER=0x0501
+      -D_WIN32_WINNT=0x0501
+      )
+  endif()
+
+  add_definitions(
+    -D_CRT_SECURE_NO_WARNINGS=1
+    )
+  link_libraries(rpcrt4 ws2_32)
+
+  if (CMAKE_COMPILER_IS_GNUCXX)
+    # Some additional C/C++ compiler flags for MinGW
+    SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}")
+
+    if (DYNAMIC_MINGW_STDLIB)
+    else()
+      # This is a patch for MinGW64
+      SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+      SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+    endif()
+
+    CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD)
+    if (HAVE_WIN_PTHREAD)
+      if (DYNAMIC_MINGW_STDLIB)
+      else()
+        # This line is necessary to compile with recent versions of MinGW,
+        # otherwise "libwinpthread-1.dll" is not statically linked.
+        SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic")
+      endif()
+      add_definitions(-DHAVE_WIN_PTHREAD=1)
+    else()
+      add_definitions(-DHAVE_WIN_PTHREAD=0)
+    endif()
+  endif()
+
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+  add_definitions(
+    -D_XOPEN_SOURCE=1
+    )
+  link_libraries(iconv)
+
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
+  message("Building using Emscripten (for WebAssembly or asm.js targets)")
+  include(${CMAKE_CURRENT_LIST_DIR}/EmscriptenParameters.cmake)
+  
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Android")
+
+else()
+  message("Unknown target platform: ${CMAKE_SYSTEM_NAME}")
+  message(FATAL_ERROR "Support your platform here")
+endif()
+
+
+if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING)
+  if (CMAKE_COMPILER_IS_GNUCXX)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
+    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pg")
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg")
+  else()
+    message(FATAL_ERROR "Don't know how to enable profiling on your configuration")
+  endif()
+endif()
+
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+  # "When creating a static library using binutils (ar) and there
+  # exist a duplicate object name (e.g. a/Foo.cpp.o, b/Foo.cpp.o), the
+  # resulting static library can end up having only one of the
+  # duplicate objects. [...] This bug only happens if there are many
+  # objects." The trick consists in replacing the "r" argument
+  # ("replace") provided to "ar" (as used in CMake < 3.1) by the "q"
+  # argument ("quick append"). This is because of the fact that CMake
+  # will invoke "ar" several times with several batches of ".o"
+  # objects, and using "r" would overwrite symbols defined in
+  # preceding batches. https://cmake.org/Bug/view.php?id=14874
+  set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> <LINK_FLAGS> q <TARGET> <OBJECTS>")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,532 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2020 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+##
+## Check whether the parent script sets the mandatory variables
+##
+
+if (NOT DEFINED ORTHANC_FRAMEWORK_SOURCE OR
+    (NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "system" AND
+     NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg" AND
+     NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "web" AND
+     NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "archive" AND
+     NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "path"))
+  message(FATAL_ERROR "The variable ORTHANC_FRAMEWORK_SOURCE must be set to \"system\", \"hg\", \"web\", \"archive\" or \"path\"")
+endif()
+
+
+##
+## Detection of the requested version
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg" OR
+    ORTHANC_FRAMEWORK_SOURCE STREQUAL "archive" OR
+    ORTHANC_FRAMEWORK_SOURCE STREQUAL "web")
+  if (NOT DEFINED ORTHANC_FRAMEWORK_VERSION)
+    message(FATAL_ERROR "The variable ORTHANC_FRAMEWORK_VERSION must be set")
+  endif()
+
+  if (DEFINED ORTHANC_FRAMEWORK_MAJOR OR
+      DEFINED ORTHANC_FRAMEWORK_MINOR OR
+      DEFINED ORTHANC_FRAMEWORK_REVISION OR
+      DEFINED ORTHANC_FRAMEWORK_MD5)
+    message(FATAL_ERROR "Some internal variable has been set")
+  endif()
+
+  set(ORTHANC_FRAMEWORK_MD5 "")
+
+  if (NOT DEFINED ORTHANC_FRAMEWORK_BRANCH)
+    if (ORTHANC_FRAMEWORK_VERSION STREQUAL "mainline")
+      set(ORTHANC_FRAMEWORK_BRANCH "default")
+      set(ORTHANC_FRAMEWORK_MAJOR 999)
+      set(ORTHANC_FRAMEWORK_MINOR 999)
+      set(ORTHANC_FRAMEWORK_REVISION 999)
+
+    else()
+      set(ORTHANC_FRAMEWORK_BRANCH "Orthanc-${ORTHANC_FRAMEWORK_VERSION}")
+
+      set(RE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
+      string(REGEX REPLACE ${RE} "\\1" ORTHANC_FRAMEWORK_MAJOR ${ORTHANC_FRAMEWORK_VERSION})
+      string(REGEX REPLACE ${RE} "\\2" ORTHANC_FRAMEWORK_MINOR ${ORTHANC_FRAMEWORK_VERSION})
+      string(REGEX REPLACE ${RE} "\\3" ORTHANC_FRAMEWORK_REVISION ${ORTHANC_FRAMEWORK_VERSION})
+
+      if (NOT ORTHANC_FRAMEWORK_MAJOR MATCHES "^[0-9]+$" OR
+          NOT ORTHANC_FRAMEWORK_MINOR MATCHES "^[0-9]+$" OR
+          NOT ORTHANC_FRAMEWORK_REVISION MATCHES "^[0-9]+$")
+        message("Bad version of the Orthanc framework: ${ORTHANC_FRAMEWORK_VERSION}")
+      endif()
+
+      if (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.3.1")
+        set(ORTHANC_FRAMEWORK_MD5 "dac95bd6cf86fb19deaf4e612961f378")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.3.2")
+        set(ORTHANC_FRAMEWORK_MD5 "d0ccdf68e855d8224331f13774992750")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.4.0")
+        set(ORTHANC_FRAMEWORK_MD5 "81e15f34d97ac32bbd7d26e85698835a")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.4.1")
+        set(ORTHANC_FRAMEWORK_MD5 "9b6f6114264b17ed421b574cd6476127")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.4.2")
+        set(ORTHANC_FRAMEWORK_MD5 "d1ee84927dcf668e60eb5868d24b9394")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.0")
+        set(ORTHANC_FRAMEWORK_MD5 "4429d8d9dea4ff6648df80ec3c64d79e")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.1")
+        set(ORTHANC_FRAMEWORK_MD5 "099671538865e5da96208b37494d6718")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.2")
+        set(ORTHANC_FRAMEWORK_MD5 "8867050f3e9a1ce6157c1ea7a9433b1b")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.3")
+        set(ORTHANC_FRAMEWORK_MD5 "bf2f5ed1adb8b0fc5f10d278e68e1dfe")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.4")
+        set(ORTHANC_FRAMEWORK_MD5 "404baef5d4c43e7c5d9410edda8ef5a5")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.5")
+        set(ORTHANC_FRAMEWORK_MD5 "cfc437e0687ae4bd725fd93dc1f08bc4")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.6")
+        set(ORTHANC_FRAMEWORK_MD5 "3c29de1e289b5472342947168f0105c0")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.7")
+        set(ORTHANC_FRAMEWORK_MD5 "e1b76f01116d9b5d4ac8cc39980560e3")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.8")
+        set(ORTHANC_FRAMEWORK_MD5 "82323e8c49a667f658a3639ea4dbc336")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.6.0")
+        set(ORTHANC_FRAMEWORK_MD5 "eab428d6e53f61e847fa360bb17ebe25")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.6.1")
+        set(ORTHANC_FRAMEWORK_MD5 "3971f5de96ba71dc9d3f3690afeaa7c0")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.7.0")
+        set(ORTHANC_FRAMEWORK_MD5 "ce5f689e852b01d3672bd3d2f952a5ef")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.7.1")
+        set(ORTHANC_FRAMEWORK_MD5 "3c171217f930abe80246997bdbcaf7cc")
+
+      # Below this point are development snapshots that were used to
+      # release some plugin, before an official release of the Orthanc
+      # framework was available. Here is the command to be used to
+      # generate a proper archive:
+      #
+      #   $ hg archive /tmp/Orthanc-`hg id -i | sed 's/\+//'`.tar.gz
+      #
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "ae0e3fd609df")
+        # DICOMweb 1.1 (framework pre-1.6.0)
+        set(ORTHANC_FRAMEWORK_MD5 "7e09e9b530a2f527854f0b782d7e0645")
+      endif()
+    endif()
+  endif()
+
+elseif (NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "system")
+  message("Using the Orthanc framework from a path of the filesystem. Assuming mainline version.")
+  set(ORTHANC_FRAMEWORK_MAJOR 999)
+  set(ORTHANC_FRAMEWORK_MINOR 999)
+  set(ORTHANC_FRAMEWORK_REVISION 999)
+endif()
+
+
+
+##
+## Detection of the third-party software
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg")
+  find_program(ORTHANC_FRAMEWORK_HG hg)
+  
+  if (${ORTHANC_FRAMEWORK_HG} MATCHES "ORTHANC_FRAMEWORK_HG-NOTFOUND")
+    message(FATAL_ERROR "Please install Mercurial")
+  endif()
+endif()
+
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "archive" OR
+    ORTHANC_FRAMEWORK_SOURCE STREQUAL "web")
+  if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+    find_program(ORTHANC_FRAMEWORK_7ZIP 7z 
+      PATHS 
+      "$ENV{ProgramFiles}/7-Zip"
+      "$ENV{ProgramW6432}/7-Zip"
+      )
+
+    if (${ORTHANC_FRAMEWORK_7ZIP} MATCHES "ORTHANC_FRAMEWORK_7ZIP-NOTFOUND")
+      message(FATAL_ERROR "Please install the '7-zip' software (http://www.7-zip.org/)")
+    endif()
+
+  else()
+    find_program(ORTHANC_FRAMEWORK_TAR tar)
+    if (${ORTHANC_FRAMEWORK_TAR} MATCHES "ORTHANC_FRAMEWORK_TAR-NOTFOUND")
+      message(FATAL_ERROR "Please install the 'tar' package")
+    endif()
+  endif()
+endif()
+
+
+
+##
+## Case of the Orthanc framework specified as a path on the filesystem
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "path")
+  if (NOT DEFINED ORTHANC_FRAMEWORK_ROOT)
+    message(FATAL_ERROR "The variable ORTHANC_FRAMEWORK_ROOT must provide the path to the sources of Orthanc")
+  endif()
+  
+  if (NOT EXISTS ${ORTHANC_FRAMEWORK_ROOT})
+    message(FATAL_ERROR "Non-existing directory: ${ORTHANC_FRAMEWORK_ROOT}")
+  endif()
+  
+  if (NOT EXISTS ${ORTHANC_FRAMEWORK_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+    message(FATAL_ERROR "Directory not containing the source code of the Orthanc framework: ${ORTHANC_FRAMEWORK_ROOT}")
+  endif()
+endif()
+
+
+
+##
+## Case of the Orthanc framework cloned using Mercurial
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg")
+  if (NOT STATIC_BUILD AND NOT ALLOW_DOWNLOADS)
+    message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON")
+  endif()
+
+  set(ORTHANC_ROOT ${CMAKE_BINARY_DIR}/orthanc)
+
+  if (EXISTS ${ORTHANC_ROOT})
+    message("Updating the Orthanc source repository using Mercurial")
+    execute_process(
+      COMMAND ${ORTHANC_FRAMEWORK_HG} pull
+      WORKING_DIRECTORY ${ORTHANC_ROOT}
+      RESULT_VARIABLE Failure
+      )    
+  else()
+    message("Forking the Orthanc source repository using Mercurial")
+    execute_process(
+      COMMAND ${ORTHANC_FRAMEWORK_HG} clone "https://hg.orthanc-server.com/orthanc/"
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+      RESULT_VARIABLE Failure
+      )    
+  endif()
+
+  if (Failure OR NOT EXISTS ${ORTHANC_ROOT})
+    message(FATAL_ERROR "Cannot fork the Orthanc repository")
+  endif()
+
+  message("Setting branch of the Orthanc repository to: ${ORTHANC_FRAMEWORK_BRANCH}")
+
+  execute_process(
+    COMMAND ${ORTHANC_FRAMEWORK_HG} update -c ${ORTHANC_FRAMEWORK_BRANCH}
+    WORKING_DIRECTORY ${ORTHANC_ROOT}
+    RESULT_VARIABLE Failure
+    )
+
+  if (Failure)
+    message(FATAL_ERROR "Error while running Mercurial")
+  endif()
+
+  unset(ORTHANC_FRAMEWORK_ROOT CACHE)
+  set(ORTHANC_FRAMEWORK_ROOT "${ORTHANC_ROOT}/OrthancFramework" CACHE
+    STRING "Path to the Orthanc framework source directory")
+
+  if (NOT EXISTS ${ORTHANC_FRAMEWORK_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+    message(FATAL_ERROR "Directory not containing the source code of the Orthanc framework: ${ORTHANC_ROOT}")
+  endif()
+
+  unset(ORTHANC_ROOT)
+endif()
+
+
+
+##
+## Case of the Orthanc framework provided as a source archive on the
+## filesystem
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "archive")
+  if (NOT DEFINED ORTHANC_FRAMEWORK_ARCHIVE)
+    message(FATAL_ERROR "The variable ORTHANC_FRAMEWORK_ARCHIVE must provide the path to the sources of Orthanc")
+  endif()
+endif()
+
+
+
+##
+## Case of the Orthanc framework downloaded from the Web
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "web")
+  if (DEFINED ORTHANC_FRAMEWORK_URL)
+    string(REGEX REPLACE "^.*/" "" ORTHANC_FRAMEMORK_FILENAME "${ORTHANC_FRAMEWORK_URL}")
+  else()
+    # Default case: Download from the official Web site
+    set(ORTHANC_FRAMEMORK_FILENAME Orthanc-${ORTHANC_FRAMEWORK_VERSION}.tar.gz)
+    set(ORTHANC_FRAMEWORK_URL "http://orthanc.osimis.io/ThirdPartyDownloads/orthanc-framework/${ORTHANC_FRAMEMORK_FILENAME}")
+  endif()
+
+  set(ORTHANC_FRAMEWORK_ARCHIVE "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${ORTHANC_FRAMEMORK_FILENAME}")
+
+  if (NOT EXISTS "${ORTHANC_FRAMEWORK_ARCHIVE}")
+    if (NOT STATIC_BUILD AND NOT ALLOW_DOWNLOADS)
+      message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON")
+    endif()
+
+    message("Downloading: ${ORTHANC_FRAMEWORK_URL}")
+
+    file(DOWNLOAD
+      "${ORTHANC_FRAMEWORK_URL}" "${ORTHANC_FRAMEWORK_ARCHIVE}" 
+      SHOW_PROGRESS EXPECTED_MD5 "${ORTHANC_FRAMEWORK_MD5}"
+      TIMEOUT 60
+      INACTIVITY_TIMEOUT 60
+      )
+  else()
+    message("Using local copy of: ${ORTHANC_FRAMEWORK_URL}")
+  endif()  
+endif()
+
+
+
+
+##
+## Uncompressing the Orthanc framework, if it was retrieved from a
+## source archive on the filesystem, or from the official Web site
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "archive" OR
+    ORTHANC_FRAMEWORK_SOURCE STREQUAL "web")
+
+  if (NOT DEFINED ORTHANC_FRAMEWORK_ARCHIVE OR
+      NOT DEFINED ORTHANC_FRAMEWORK_VERSION OR
+      NOT DEFINED ORTHANC_FRAMEWORK_MD5)
+    message(FATAL_ERROR "Internal error")
+  endif()
+
+  if (ORTHANC_FRAMEWORK_MD5 STREQUAL "")
+    message(FATAL_ERROR "Unknown release of Orthanc: ${ORTHANC_FRAMEWORK_VERSION}")
+  endif()
+
+  file(MD5 ${ORTHANC_FRAMEWORK_ARCHIVE} ActualMD5)
+
+  if (NOT "${ActualMD5}" STREQUAL "${ORTHANC_FRAMEWORK_MD5}")
+    message(FATAL_ERROR "The MD5 hash of the Orthanc archive is invalid: ${ORTHANC_FRAMEWORK_ARCHIVE}")
+  endif()
+
+  set(ORTHANC_ROOT "${CMAKE_BINARY_DIR}/Orthanc-${ORTHANC_FRAMEWORK_VERSION}")
+
+  if (NOT IS_DIRECTORY "${ORTHANC_ROOT}")
+    if (NOT ORTHANC_FRAMEWORK_ARCHIVE MATCHES ".tar.gz$")
+      message(FATAL_ERROR "Archive should have the \".tar.gz\" extension: ${ORTHANC_FRAMEWORK_ARCHIVE}")
+    endif()
+    
+    message("Uncompressing: ${ORTHANC_FRAMEWORK_ARCHIVE}")
+
+    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+      # How to silently extract files using 7-zip
+      # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
+
+      execute_process(
+        COMMAND ${ORTHANC_FRAMEWORK_7ZIP} e -y ${ORTHANC_FRAMEWORK_ARCHIVE}
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+        RESULT_VARIABLE Failure
+        OUTPUT_QUIET
+        )
+      
+      if (Failure)
+        message(FATAL_ERROR "Error while running the uncompression tool")
+      endif()
+
+      get_filename_component(TMP_FILENAME "${ORTHANC_FRAMEWORK_ARCHIVE}" NAME)
+      string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}")
+
+      execute_process(
+        COMMAND ${ORTHANC_FRAMEWORK_7ZIP} x -y ${TMP_FILENAME2}
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+        RESULT_VARIABLE Failure
+        OUTPUT_QUIET
+        )
+
+    else()
+      execute_process(
+        COMMAND sh -c "${ORTHANC_FRAMEWORK_TAR} xfz ${ORTHANC_FRAMEWORK_ARCHIVE}"
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+        RESULT_VARIABLE Failure
+        )
+    endif()
+   
+    if (Failure)
+      message(FATAL_ERROR "Error while running the uncompression tool")
+    endif()
+
+    if (NOT IS_DIRECTORY "${ORTHANC_ROOT}")
+      message(FATAL_ERROR "The Orthanc framework was not uncompressed at the proper location. Check the CMake instructions.")
+    endif()
+  endif()
+
+  unset(ORTHANC_FRAMEWORK_ROOT CACHE)
+  set(ORTHANC_FRAMEWORK_ROOT "${ORTHANC_ROOT}/OrthancFramework" CACHE
+    STRING "Path to the Orthanc framework source directory")
+
+  if (NOT EXISTS ${ORTHANC_FRAMEWORK_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+    message(FATAL_ERROR "Directory not containing the source code of the Orthanc framework: ${ORTHANC_ROOT}")
+  endif()
+
+  unset(ORTHANC_ROOT)
+endif()
+
+
+
+##
+## Case of the Orthanc framework installed as a shared library in a
+## GNU/Linux distribution (typically Debian). New in Orthanc 1.7.2.
+##
+
+if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "system")
+  set(ORTHANC_FRAMEWORK_LIBDIR "" CACHE PATH "")
+
+  if (CMAKE_SYSTEM_NAME STREQUAL "Windows" AND
+      CMAKE_COMPILER_IS_GNUCXX) # MinGW
+    set(DYNAMIC_MINGW_STDLIB ON)   # Disable static linking against libc (to throw exceptions)
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++")
+  endif()
+  
+  include(CheckIncludeFile)
+  include(CheckIncludeFileCXX)
+  include(FindPythonInterp)
+  include(${CMAKE_CURRENT_LIST_DIR}/Compiler.cmake)
+  include(${CMAKE_CURRENT_LIST_DIR}/DownloadPackage.cmake)
+  include(${CMAKE_CURRENT_LIST_DIR}/AutoGeneratedCode.cmake)
+  set(EMBED_RESOURCES_PYTHON ${CMAKE_CURRENT_LIST_DIR}/EmbedResources.py)
+
+  if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" OR
+      ORTHANC_FRAMEWORK_STATIC)
+    include_directories(${ORTHANC_FRAMEWORK_ROOT}/..)
+  else()
+    # Look for mandatory dependency JsonCpp (cf. JsonCppConfiguration.cmake)
+    find_path(JSONCPP_INCLUDE_DIR json/reader.h
+      /usr/include/jsoncpp
+      /usr/local/include/jsoncpp
+      )
+
+    message("JsonCpp include dir: ${JSONCPP_INCLUDE_DIR}")
+    include_directories(${JSONCPP_INCLUDE_DIR})
+    link_libraries(jsoncpp)
+
+    CHECK_INCLUDE_FILE_CXX(${JSONCPP_INCLUDE_DIR}/json/reader.h HAVE_JSONCPP_H)
+    if (NOT HAVE_JSONCPP_H)
+      message(FATAL_ERROR "Please install the libjsoncpp-dev package")
+    endif()
+
+    # Look for mandatory dependency Boost (cf. BoostConfiguration.cmake)
+    include(FindBoost)
+    find_package(Boost COMPONENTS filesystem thread system date_time regex ${ORTHANC_BOOST_COMPONENTS})
+
+    if (NOT Boost_FOUND)
+      message(FATAL_ERROR "Unable to locate Boost on this system")
+    endif()
+    
+    include_directories(${Boost_INCLUDE_DIRS})
+    link_libraries(${Boost_LIBRARIES})
+
+    # Optional component - Lua
+    if (ENABLE_LUA)
+      include(FindLua)
+
+      if (NOT LUA_FOUND)
+        message(FATAL_ERROR "Please install the liblua-dev package")
+      endif()
+
+      include_directories(${LUA_INCLUDE_DIR})
+      link_libraries(${LUA_LIBRARIES})
+    endif()
+
+    # Optional component - SQLite
+    if (ENABLE_SQLITE)    
+      CHECK_INCLUDE_FILE(sqlite3.h HAVE_SQLITE_H)
+      if (NOT HAVE_SQLITE_H)
+        message(FATAL_ERROR "Please install the libsqlite3-dev package")
+      endif()
+      link_libraries(sqlite3)
+    endif()
+
+    # Optional component - Pugixml
+    if (ENABLE_PUGIXML)
+      CHECK_INCLUDE_FILE_CXX(pugixml.hpp HAVE_PUGIXML_H)
+      if (NOT HAVE_PUGIXML_H)
+        message(FATAL_ERROR "Please install the libpugixml-dev package")
+      endif()      
+      link_libraries(pugixml)
+    endif()
+
+    # Optional component - DCMTK
+    if (ENABLE_DCMTK)
+      include(FindDCMTK)
+      include_directories(${DCMTK_INCLUDE_DIRS})
+      link_libraries(${DCMTK_LIBRARIES})
+    endif()
+  endif()
+
+  # Look for Orthanc framework shared library
+  include(CheckCXXSymbolExists)
+
+  if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+    set(ORTHANC_FRAMEWORK_INCLUDE_DIR ${ORTHANC_FRAMEWORK_ROOT})
+  else()
+    find_path(ORTHANC_FRAMEWORK_INCLUDE_DIR OrthancFramework.h
+      /usr/include/orthanc-framework
+      /usr/local/include/orthanc-framework
+      ${ORTHANC_FRAMEWORK_ROOT}
+      )
+  endif()
+
+  if (${ORTHANC_FRAMEWORK_INCLUDE_DIR} STREQUAL "ORTHANC_FRAMEWORK_INCLUDE_DIR-NOTFOUND")
+    message(FATAL_ERROR "Cannot locate the OrthancFramework.h header")
+  endif()
+  
+  message("Orthanc framework include dir: ${ORTHANC_FRAMEWORK_INCLUDE_DIR}")
+  include_directories(${ORTHANC_FRAMEWORK_INCLUDE_DIR})
+  
+  if ("${ORTHANC_FRAMEWORK_LIBDIR}" STREQUAL "")
+    set(ORTHANC_FRAMEWORK_LIBRARIES OrthancFramework)
+  else()
+    if (MSVC)
+      set(Suffix ".lib")
+      set(Prefix "")
+    else()
+      list(GET CMAKE_FIND_LIBRARY_PREFIXES 0 Prefix)
+      list(GET CMAKE_FIND_LIBRARY_SUFFIXES 0 Suffix)
+    endif()
+    set(ORTHANC_FRAMEWORK_LIBRARIES ${ORTHANC_FRAMEWORK_LIBDIR}/${Prefix}OrthancFramework${Suffix})
+  endif()
+
+  set(CMAKE_REQUIRED_INCLUDES "${ORTHANC_FRAMEWORK_INCLUDE_DIR}")
+  set(CMAKE_REQUIRED_LIBRARIES "${ORTHANC_FRAMEWORK_LIBRARIES}")
+  
+  check_cxx_symbol_exists("Orthanc::InitializeFramework" "OrthancFramework.h" HAVE_ORTHANC_FRAMEWORK)
+  if (NOT HAVE_ORTHANC_FRAMEWORK)
+    message(FATAL_ERROR "Cannot find the Orthanc framework")
+  endif()
+
+  if (NOT "${ORTHANC_FRAMEWORK_ROOT}" STREQUAL "")
+    include_directories(${ORTHANC_FRAMEWORK_ROOT})
+  endif()
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/CMake/DownloadPackage.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,258 @@
+macro(GetUrlFilename TargetVariable Url)
+  string(REGEX REPLACE "^.*/" "" ${TargetVariable} "${Url}")
+endmacro()
+
+
+macro(GetUrlExtension TargetVariable Url)
+  #string(REGEX REPLACE "^.*/[^.]*\\." "" TMP "${Url}")
+  string(REGEX REPLACE "^.*\\." "" TMP "${Url}")
+  string(TOLOWER "${TMP}" "${TargetVariable}")
+endmacro()
+
+
+
+##
+## Setup the patch command-line tool
+##
+
+if (NOT ORTHANC_DISABLE_PATCH)
+  if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+    set(PATCH_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/../ThirdParty/patch/patch.exe)
+    if (NOT EXISTS ${PATCH_EXECUTABLE})
+      message(FATAL_ERROR "Unable to find the patch.exe tool that is shipped with Orthanc")
+    endif()
+
+  else ()
+    find_program(PATCH_EXECUTABLE patch)
+    if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND")
+      message(FATAL_ERROR "Please install the 'patch' standard command-line tool")
+    endif()
+  endif()
+endif()
+
+
+
+##
+## Check the existence of the required decompression tools
+##
+
+if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+  find_program(ZIP_EXECUTABLE 7z 
+    PATHS 
+    "$ENV{ProgramFiles}/7-Zip"
+    "$ENV{ProgramW6432}/7-Zip"
+    )
+
+  if (${ZIP_EXECUTABLE} MATCHES "ZIP_EXECUTABLE-NOTFOUND")
+    message(FATAL_ERROR "Please install the '7-zip' software (http://www.7-zip.org/)")
+  endif()
+
+else()
+  find_program(UNZIP_EXECUTABLE unzip)
+  if (${UNZIP_EXECUTABLE} MATCHES "UNZIP_EXECUTABLE-NOTFOUND")
+    message(FATAL_ERROR "Please install the 'unzip' package")
+  endif()
+
+  find_program(TAR_EXECUTABLE tar)
+  if (${TAR_EXECUTABLE} MATCHES "TAR_EXECUTABLE-NOTFOUND")
+    message(FATAL_ERROR "Please install the 'tar' package")
+  endif()
+
+  find_program(GUNZIP_EXECUTABLE gunzip)
+  if (${GUNZIP_EXECUTABLE} MATCHES "GUNZIP_EXECUTABLE-NOTFOUND")
+    message(FATAL_ERROR "Please install the 'gzip' package")
+  endif()
+endif()
+
+
+macro(DownloadFile MD5 Url)
+  GetUrlFilename(TMP_FILENAME "${Url}")
+
+  set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}")
+  if (NOT EXISTS "${TMP_PATH}")
+    message("Downloading ${Url}")
+
+    # This fixes issue 6: "I think cmake shouldn't download the
+    # packages which are not in the system, it should stop and let
+    # user know."
+    # https://code.google.com/p/orthanc/issues/detail?id=6
+    if (NOT STATIC_BUILD AND NOT ALLOW_DOWNLOADS)
+      message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON")
+    endif()
+
+    if ("${MD5}" STREQUAL "no-check")
+      message(WARNING "Not checking the MD5 of: ${Url}")
+      file(DOWNLOAD "${Url}" "${TMP_PATH}"
+        SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60
+        STATUS Failure)
+    else()
+      file(DOWNLOAD "${Url}" "${TMP_PATH}"
+        SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60
+        EXPECTED_MD5 "${MD5}" STATUS Failure)
+    endif()
+
+    list(GET Failure 0 Status)
+    if (NOT Status EQUAL 0)
+      message(FATAL_ERROR "Cannot download file: ${Url}")
+    endif()
+    
+  else()
+    message("Using local copy of ${Url}")
+
+    if ("${MD5}" STREQUAL "no-check")
+      message(WARNING "Not checking the MD5 of: ${Url}")
+    else()
+      file(MD5 ${TMP_PATH} ActualMD5)
+      if (NOT "${ActualMD5}" STREQUAL "${MD5}")
+        message(FATAL_ERROR "The MD5 hash of a previously download file is invalid: ${TMP_PATH}")
+      endif()
+    endif()
+  endif()
+endmacro()
+
+
+macro(DownloadPackage MD5 Url TargetDirectory)
+  if (NOT IS_DIRECTORY "${TargetDirectory}")
+    DownloadFile("${MD5}" "${Url}")
+    
+    GetUrlExtension(TMP_EXTENSION "${Url}")
+    #message(${TMP_EXTENSION})
+    message("Uncompressing ${TMP_FILENAME}")
+
+    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+      # How to silently extract files using 7-zip
+      # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
+
+      if (("${TMP_EXTENSION}" STREQUAL "gz") OR 
+          ("${TMP_EXTENSION}" STREQUAL "tgz") OR
+          ("${TMP_EXTENSION}" STREQUAL "xz"))
+        execute_process(
+          COMMAND ${ZIP_EXECUTABLE} e -y ${TMP_PATH}
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          OUTPUT_QUIET
+          )
+
+        if (Failure)
+          message(FATAL_ERROR "Error while running the uncompression tool")
+        endif()
+
+        if ("${TMP_EXTENSION}" STREQUAL "tgz")
+          string(REGEX REPLACE ".tgz$" ".tar" TMP_FILENAME2 "${TMP_FILENAME}")
+        elseif ("${TMP_EXTENSION}" STREQUAL "gz")
+          string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}")
+        elseif ("${TMP_EXTENSION}" STREQUAL "xz")
+          string(REGEX REPLACE ".xz" "" TMP_FILENAME2 "${TMP_FILENAME}")
+        endif()
+
+        execute_process(
+          COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_FILENAME2}
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          OUTPUT_QUIET
+          )
+      elseif ("${TMP_EXTENSION}" STREQUAL "zip")
+        execute_process(
+          COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_PATH}
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          OUTPUT_QUIET
+          )
+      else()
+        message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}")
+      endif()
+
+    else()
+      if ("${TMP_EXTENSION}" STREQUAL "zip")
+        execute_process(
+          COMMAND sh -c "${UNZIP_EXECUTABLE} -q ${TMP_PATH}"
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+        )
+      elseif (("${TMP_EXTENSION}" STREQUAL "gz") OR ("${TMP_EXTENSION}" STREQUAL "tgz"))
+        #message("tar xvfz ${TMP_PATH}")
+        execute_process(
+          COMMAND sh -c "${TAR_EXECUTABLE} xfz ${TMP_PATH}"
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          )
+      elseif ("${TMP_EXTENSION}" STREQUAL "bz2")
+        execute_process(
+          COMMAND sh -c "${TAR_EXECUTABLE} xfj ${TMP_PATH}"
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          )
+      elseif ("${TMP_EXTENSION}" STREQUAL "xz")
+        execute_process(
+          COMMAND sh -c "${TAR_EXECUTABLE} xf ${TMP_PATH}"
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          )
+      else()
+        message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}")
+      endif()
+    endif()
+   
+    if (Failure)
+      message(FATAL_ERROR "Error while running the uncompression tool")
+    endif()
+
+    if (NOT IS_DIRECTORY "${TargetDirectory}")
+      message(FATAL_ERROR "The package was not uncompressed at the proper location. Check the CMake instructions.")
+    endif()
+  endif()
+endmacro()
+
+
+
+macro(DownloadCompressedFile MD5 Url TargetFile)
+  if (NOT EXISTS "${TargetFile}")
+    DownloadFile("${MD5}" "${Url}")
+    
+    GetUrlExtension(TMP_EXTENSION "${Url}")
+    #message(${TMP_EXTENSION})
+    message("Uncompressing ${TMP_FILENAME}")
+
+    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+      # How to silently extract files using 7-zip
+      # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
+
+      if ("${TMP_EXTENSION}" STREQUAL "gz")
+        execute_process(
+          # "-so" writes uncompressed file to stdout
+          COMMAND ${ZIP_EXECUTABLE} e -so -y ${TMP_PATH}
+          OUTPUT_FILE "${TargetFile}"
+          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+          RESULT_VARIABLE Failure
+          OUTPUT_QUIET
+          )
+
+        if (Failure)
+          message(FATAL_ERROR "Error while running the uncompression tool")
+        endif()
+
+      else()
+        message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}")
+      endif()
+
+    else()
+      if ("${TMP_EXTENSION}" STREQUAL "gz")
+        execute_process(
+          COMMAND sh -c "${GUNZIP_EXECUTABLE} -c ${TMP_PATH}"
+          OUTPUT_FILE "${TargetFile}"
+          RESULT_VARIABLE Failure
+          )
+      else()
+        message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}")
+      endif()
+    endif()
+   
+    if (Failure)
+      message(FATAL_ERROR "Error while running the uncompression tool")
+    endif()
+
+    if (NOT EXISTS "${TargetFile}")
+      message(FATAL_ERROR "The file was not uncompressed at the proper location. Check the CMake instructions.")
+    endif()
+  endif()
+endmacro()
--- a/Resources/Orthanc/Core/Compatibility.h	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-//#define Orthanc_Compatibility_h_STR2(x) #x
-//#define Orthanc_Compatibility_h_STR1(x) Orthanc_Compatibility_h_STR2(x)
-
-//#pragma message("__cplusplus = " Orthanc_Compatibility_h_STR1(__cplusplus))
-
-#if (defined _MSC_VER)
-//#  pragma message("_MSC_VER = " Orthanc_Compatibility_h_STR1(_MSC_VER))
-//#  pragma message("_MSVC_LANG = " Orthanc_Compatibility_h_STR1(_MSVC_LANG))
-// The __cplusplus macro cannot be used in Visual C++ < 1914 (VC++ 15.7)
-// However, even in recent versions, __cplusplus will only be correct (that is,
-// correctly defines the supported C++ version) if a special flag is passed to
-// the compiler ("/Zc:__cplusplus")
-// To make this header more robust, we use the _MSVC_LANG equivalent macro.
-
-// please note that not all C++11 features are supported when _MSC_VER == 1600
-// (or higher). This header file can be made for fine-grained, if required, 
-// based on specific _MSC_VER values
-
-#  if _MSC_VER >= 1600
-#    define ORTHANC_Cxx03_DETECTED 0
-#  else
-#    define ORTHANC_Cxx03_DETECTED 1
-#  endif
-
-#else
-// of _MSC_VER is not defined, we assume __cplusplus is correctly defined
-// if __cplusplus is not defined (very old compilers??), then the following
-// test will compare 0 < 201103L and will be true --> safe.
-#  if __cplusplus < 201103L
-#    define ORTHANC_Cxx03_DETECTED 1
-#  else
-#    define ORTHANC_Cxx03_DETECTED 0
-#  endif
-#endif
-
-#if ORTHANC_Cxx03_DETECTED == 1
-//#pragma message("C++ 11 support is not present.")
-
-/**
- * "std::unique_ptr" was introduced in C++11, and "std::auto_ptr" was
- * removed in C++17. We emulate "std::auto_ptr" using boost: "The
- * smart pointer unique_ptr [is] a drop-in replacement for
- * std::unique_ptr, usable also from C++03 compilers." This is only
- * available if Boost >= 1.57.0 (from November 2014).
- * https://www.boost.org/doc/libs/1_57_0/doc/html/move/reference.html#header.boost.move.unique_ptr_hpp
- **/
-
-#include <boost/move/unique_ptr.hpp>
-
-namespace std
-{
-  template <typename T>
-  class unique_ptr : public boost::movelib::unique_ptr<T>
-  {
-  public:
-    explicit unique_ptr() :
-      boost::movelib::unique_ptr<T>()
-    {
-    }      
-
-    explicit unique_ptr(T* p) :
-      boost::movelib::unique_ptr<T>(p)
-    {
-    }      
-  };
-}
-#else
-//# pragma message("C++ 11 support is present.")
-# include <memory>
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/ExportedSymbolsPlugins.list	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,7 @@
+# This is the list of the symbols that must be exported by Orthanc
+# plugins, if targeting OS X
+
+_OrthancPluginInitialize
+_OrthancPluginFinalize
+_OrthancPluginGetName
+_OrthancPluginGetVersion
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,3395 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancPluginCppWrapper.h"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/move/unique_ptr.hpp>
+#include <boost/thread.hpp>
+#include <json/reader.h>
+#include <json/writer.h>
+
+
+#if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
+static const OrthancPluginErrorCode OrthancPluginErrorCode_NullPointer = OrthancPluginErrorCode_Plugin;
+#endif
+
+
+namespace OrthancPlugins
+{
+  static OrthancPluginContext* globalContext_ = NULL;
+
+
+  void SetGlobalContext(OrthancPluginContext* context)
+  {
+    if (context == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
+    }
+    else if (globalContext_ == NULL)
+    {
+      globalContext_ = context;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
+    }
+  }
+
+
+  bool HasGlobalContext()
+  {
+    return globalContext_ != NULL;
+  }
+
+
+  OrthancPluginContext* GetGlobalContext()
+  {
+    if (globalContext_ == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
+    }
+    else
+    {
+      return globalContext_;
+    }
+  }
+
+
+  void MemoryBuffer::Check(OrthancPluginErrorCode code)
+  {
+    if (code != OrthancPluginErrorCode_Success)
+    {
+      // Prevent using garbage information
+      buffer_.data = NULL;
+      buffer_.size = 0;
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+    }
+  }
+
+
+  bool MemoryBuffer::CheckHttp(OrthancPluginErrorCode code)
+  {
+    if (code != OrthancPluginErrorCode_Success)
+    {
+      // Prevent using garbage information
+      buffer_.data = NULL;
+      buffer_.size = 0;
+    }
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (code == OrthancPluginErrorCode_UnknownResource ||
+             code == OrthancPluginErrorCode_InexistentItem)
+    {
+      return false;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+    }
+  }
+
+
+  MemoryBuffer::MemoryBuffer()
+  {
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+  MemoryBuffer::MemoryBuffer(const void* buffer,
+                             size_t size)
+  {
+    uint32_t s = static_cast<uint32_t>(size);
+    if (static_cast<size_t>(s) != size)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
+    }
+    else if (OrthancPluginCreateMemoryBuffer(GetGlobalContext(), &buffer_, s) !=
+             OrthancPluginErrorCode_Success)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
+    }
+    else
+    {
+      memcpy(buffer_.data, buffer, size);
+    }
+  }
+#endif
+
+
+  void MemoryBuffer::Clear()
+  {
+    if (buffer_.data != NULL)
+    {
+      OrthancPluginFreeMemoryBuffer(GetGlobalContext(), &buffer_);
+      buffer_.data = NULL;
+      buffer_.size = 0;
+    }
+  }
+
+
+  void MemoryBuffer::Assign(OrthancPluginMemoryBuffer& other)
+  {
+    Clear();
+
+    buffer_.data = other.data;
+    buffer_.size = other.size;
+
+    other.data = NULL;
+    other.size = 0;
+  }
+
+
+  void MemoryBuffer::Swap(MemoryBuffer& other)
+  {
+    std::swap(buffer_.data, other.buffer_.data);
+    std::swap(buffer_.size, other.buffer_.size);
+  }
+
+
+  OrthancPluginMemoryBuffer MemoryBuffer::Release()
+  {
+    OrthancPluginMemoryBuffer result = buffer_;
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+
+    return result;
+  }
+
+
+  void MemoryBuffer::ToString(std::string& target) const
+  {
+    if (buffer_.size == 0)
+    {
+      target.clear();
+    }
+    else
+    {
+      target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
+    }
+  }
+
+
+  void MemoryBuffer::ToJson(Json::Value& target) const
+  {
+    if (buffer_.data == NULL ||
+        buffer_.size == 0)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+
+    const char* tmp = reinterpret_cast<const char*>(buffer_.data);
+
+    Json::Reader reader;
+    if (!reader.parse(tmp, tmp + buffer_.size, target))
+    {
+      LogError("Cannot convert some memory buffer to JSON");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+  }
+
+
+  bool MemoryBuffer::RestApiGet(const std::string& uri,
+                                bool applyPlugins)
+  {
+    Clear();
+
+    if (applyPlugins)
+    {
+      return CheckHttp(OrthancPluginRestApiGetAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str()));
+    }
+    else
+    {
+      return CheckHttp(OrthancPluginRestApiGet(GetGlobalContext(), &buffer_, uri.c_str()));
+    }
+  }
+
+  bool MemoryBuffer::RestApiGet(const std::string& uri,
+                                const std::map<std::string, std::string>& httpHeaders,
+                                bool applyPlugins)
+  {
+    Clear();
+
+    std::vector<const char*> headersKeys;
+    std::vector<const char*> headersValues;
+    
+    for (std::map<std::string, std::string>::const_iterator
+           it = httpHeaders.begin(); it != httpHeaders.end(); it++)
+    {
+      headersKeys.push_back(it->first.c_str());
+      headersValues.push_back(it->second.c_str());
+    }
+
+    return CheckHttp(OrthancPluginRestApiGet2(
+                       GetGlobalContext(), &buffer_, uri.c_str(), httpHeaders.size(),
+                       (headersKeys.empty() ? NULL : &headersKeys[0]),
+                       (headersValues.empty() ? NULL : &headersValues[0]), applyPlugins));
+  }
+
+  bool MemoryBuffer::RestApiPost(const std::string& uri,
+                                 const void* body,
+                                 size_t bodySize,
+                                 bool applyPlugins)
+  {
+    Clear();
+    
+    // Cast for compatibility with Orthanc SDK <= 1.5.6
+    const char* b = reinterpret_cast<const char*>(body);
+
+    if (applyPlugins)
+    {
+      return CheckHttp(OrthancPluginRestApiPostAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
+    }
+    else
+    {
+      return CheckHttp(OrthancPluginRestApiPost(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
+    }
+  }
+
+
+  bool MemoryBuffer::RestApiPut(const std::string& uri,
+                                const void* body,
+                                size_t bodySize,
+                                bool applyPlugins)
+  {
+    Clear();
+
+    // Cast for compatibility with Orthanc SDK <= 1.5.6
+    const char* b = reinterpret_cast<const char*>(body);
+
+    if (applyPlugins)
+    {
+      return CheckHttp(OrthancPluginRestApiPutAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
+    }
+    else
+    {
+      return CheckHttp(OrthancPluginRestApiPut(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
+    }
+  }
+
+
+  bool MemoryBuffer::RestApiPost(const std::string& uri,
+                                 const Json::Value& body,
+                                 bool applyPlugins)
+  {
+    Json::FastWriter writer;
+    return RestApiPost(uri, writer.write(body), applyPlugins);
+  }
+
+
+  bool MemoryBuffer::RestApiPut(const std::string& uri,
+                                const Json::Value& body,
+                                bool applyPlugins)
+  {
+    Json::FastWriter writer;
+    return RestApiPut(uri, writer.write(body), applyPlugins);
+  }
+
+
+  void MemoryBuffer::CreateDicom(const Json::Value& tags,
+                                 OrthancPluginCreateDicomFlags flags)
+  {
+    Clear();
+
+    Json::FastWriter writer;
+    std::string s = writer.write(tags);
+
+    Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), NULL, flags));
+  }
+
+  void MemoryBuffer::CreateDicom(const Json::Value& tags,
+                                 const OrthancImage& pixelData,
+                                 OrthancPluginCreateDicomFlags flags)
+  {
+    Clear();
+
+    Json::FastWriter writer;
+    std::string s = writer.write(tags);
+
+    Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), pixelData.GetObject(), flags));
+  }
+
+
+  void MemoryBuffer::ReadFile(const std::string& path)
+  {
+    Clear();
+    Check(OrthancPluginReadFile(GetGlobalContext(), &buffer_, path.c_str()));
+  }
+
+
+  void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
+  {
+    Clear();
+    Check(OrthancPluginWorklistGetDicomQuery(GetGlobalContext(), &buffer_, query));
+  }
+
+
+  void OrthancString::Assign(char* str)
+  {
+    Clear();
+
+    if (str != NULL)
+    {
+      str_ = str;
+    }
+  }
+
+
+  void OrthancString::Clear()
+  {
+    if (str_ != NULL)
+    {
+      OrthancPluginFreeString(GetGlobalContext(), str_);
+      str_ = NULL;
+    }
+  }
+
+
+  void OrthancString::ToString(std::string& target) const
+  {
+    if (str_ == NULL)
+    {
+      target.clear();
+    }
+    else
+    {
+      target.assign(str_);
+    }
+  }
+
+
+  void OrthancString::ToJson(Json::Value& target) const
+  {
+    if (str_ == NULL)
+    {
+      LogError("Cannot convert an empty memory buffer to JSON");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+
+    Json::Reader reader;
+    if (!reader.parse(str_, target))
+    {
+      LogError("Cannot convert some memory buffer to JSON");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+  }
+
+
+  void MemoryBuffer::DicomToJson(Json::Value& target,
+                                 OrthancPluginDicomToJsonFormat format,
+                                 OrthancPluginDicomToJsonFlags flags,
+                                 uint32_t maxStringLength)
+  {
+    OrthancString str;
+    str.Assign(OrthancPluginDicomBufferToJson
+               (GetGlobalContext(), GetData(), GetSize(), format, flags, maxStringLength));
+    str.ToJson(target);
+  }
+
+
+  bool MemoryBuffer::HttpGet(const std::string& url,
+                             const std::string& username,
+                             const std::string& password)
+  {
+    Clear();
+    return CheckHttp(OrthancPluginHttpGet(GetGlobalContext(), &buffer_, url.c_str(),
+                                          username.empty() ? NULL : username.c_str(),
+                                          password.empty() ? NULL : password.c_str()));
+  }
+
+
+  bool MemoryBuffer::HttpPost(const std::string& url,
+                              const std::string& body,
+                              const std::string& username,
+                              const std::string& password)
+  {
+    Clear();
+    return CheckHttp(OrthancPluginHttpPost(GetGlobalContext(), &buffer_, url.c_str(),
+                                           body.c_str(), body.size(),
+                                           username.empty() ? NULL : username.c_str(),
+                                           password.empty() ? NULL : password.c_str()));
+  }
+
+
+  bool MemoryBuffer::HttpPut(const std::string& url,
+                             const std::string& body,
+                             const std::string& username,
+                             const std::string& password)
+  {
+    Clear();
+    return CheckHttp(OrthancPluginHttpPut(GetGlobalContext(), &buffer_, url.c_str(),
+                                          body.empty() ? NULL : body.c_str(),
+                                          body.size(),
+                                          username.empty() ? NULL : username.c_str(),
+                                          password.empty() ? NULL : password.c_str()));
+  }
+
+
+  void MemoryBuffer::GetDicomInstance(const std::string& instanceId)
+  {
+    Clear();
+    Check(OrthancPluginGetDicomForInstance(GetGlobalContext(), &buffer_, instanceId.c_str()));
+  }
+
+
+  bool HttpDelete(const std::string& url,
+                  const std::string& username,
+                  const std::string& password)
+  {
+    OrthancPluginErrorCode error = OrthancPluginHttpDelete
+      (GetGlobalContext(), url.c_str(),
+       username.empty() ? NULL : username.c_str(),
+       password.empty() ? NULL : password.c_str());
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource ||
+             error == OrthancPluginErrorCode_InexistentItem)
+    {
+      return false;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+    }
+  }
+
+
+  void LogError(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogError(GetGlobalContext(), message.c_str());
+    }
+  }
+
+
+  void LogWarning(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
+    }
+  }
+
+
+  void LogInfo(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
+    }
+  }
+
+
+  void OrthancConfiguration::LoadConfiguration()
+  {
+    OrthancString str;
+    str.Assign(OrthancPluginGetConfiguration(GetGlobalContext()));
+
+    if (str.GetContent() == NULL)
+    {
+      LogError("Cannot access the Orthanc configuration");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+
+    str.ToJson(configuration_);
+
+    if (configuration_.type() != Json::objectValue)
+    {
+      LogError("Unable to read the Orthanc configuration");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+  }
+    
+
+  OrthancConfiguration::OrthancConfiguration()
+  {
+    LoadConfiguration();
+  }
+
+
+  OrthancConfiguration::OrthancConfiguration(bool loadConfiguration)
+  {
+    if (loadConfiguration)
+    {
+      LoadConfiguration();
+    }
+    else
+    {
+      configuration_ = Json::objectValue;
+    }
+  }
+
+
+  std::string OrthancConfiguration::GetPath(const std::string& key) const
+  {
+    if (path_.empty())
+    {
+      return key;
+    }
+    else
+    {
+      return path_ + "." + key;
+    }
+  }
+
+
+  bool OrthancConfiguration::IsSection(const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    return (configuration_.isMember(key) &&
+            configuration_[key].type() == Json::objectValue);
+  }
+
+
+  void OrthancConfiguration::GetSection(OrthancConfiguration& target,
+                                        const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    target.path_ = GetPath(key);
+
+    if (!configuration_.isMember(key))
+    {
+      target.configuration_ = Json::objectValue;
+    }
+    else
+    {
+      if (configuration_[key].type() != Json::objectValue)
+      {
+        LogError("The configuration section \"" + target.path_ +
+                 "\" is not an associative array as expected");
+
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+      }
+
+      target.configuration_ = configuration_[key];
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupStringValue(std::string& target,
+                                               const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    if (configuration_[key].type() != Json::stringValue)
+    {
+      LogError("The configuration option \"" + GetPath(key) +
+               "\" is not a string as expected");
+
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+
+    target = configuration_[key].asString();
+    return true;
+  }
+
+
+  bool OrthancConfiguration::LookupIntegerValue(int& target,
+                                                const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    switch (configuration_[key].type())
+    {
+      case Json::intValue:
+        target = configuration_[key].asInt();
+        return true;
+
+      case Json::uintValue:
+        target = configuration_[key].asUInt();
+        return true;
+
+      default:
+        LogError("The configuration option \"" + GetPath(key) +
+                 "\" is not an integer as expected");
+
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
+                                                        const std::string& key) const
+  {
+    int tmp;
+    if (!LookupIntegerValue(tmp, key))
+    {
+      return false;
+    }
+
+    if (tmp < 0)
+    {
+      LogError("The configuration option \"" + GetPath(key) +
+               "\" is not a positive integer as expected");
+
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+    else
+    {
+      target = static_cast<unsigned int>(tmp);
+      return true;
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupBooleanValue(bool& target,
+                                                const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    if (configuration_[key].type() != Json::booleanValue)
+    {
+      LogError("The configuration option \"" + GetPath(key) +
+               "\" is not a Boolean as expected");
+
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+
+    target = configuration_[key].asBool();
+    return true;
+  }
+
+
+  bool OrthancConfiguration::LookupFloatValue(float& target,
+                                              const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    switch (configuration_[key].type())
+    {
+      case Json::realValue:
+        target = configuration_[key].asFloat();
+        return true;
+
+      case Json::intValue:
+        target = static_cast<float>(configuration_[key].asInt());
+        return true;
+
+      case Json::uintValue:
+        target = static_cast<float>(configuration_[key].asUInt());
+        return true;
+
+      default:
+        LogError("The configuration option \"" + GetPath(key) +
+                 "\" is not an integer as expected");
+
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupListOfStrings(std::list<std::string>& target,
+                                                 const std::string& key,
+                                                 bool allowSingleString) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    target.clear();
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    switch (configuration_[key].type())
+    {
+      case Json::arrayValue:
+      {
+        bool ok = true;
+
+        for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++)
+        {
+          if (configuration_[key][i].type() == Json::stringValue)
+          {
+            target.push_back(configuration_[key][i].asString());
+          }
+          else
+          {
+            ok = false;
+          }
+        }
+
+        if (ok)
+        {
+          return true;
+        }
+
+        break;
+      }
+
+      case Json::stringValue:
+        if (allowSingleString)
+        {
+          target.push_back(configuration_[key].asString());
+          return true;
+        }
+
+        break;
+
+      default:
+        break;
+    }
+
+    LogError("The configuration option \"" + GetPath(key) +
+             "\" is not a list of strings as expected");
+
+    ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+  }
+
+
+  bool OrthancConfiguration::LookupSetOfStrings(std::set<std::string>& target,
+                                                const std::string& key,
+                                                bool allowSingleString) const
+  {
+    std::list<std::string> lst;
+
+    if (LookupListOfStrings(lst, key, allowSingleString))
+    {
+      target.clear();
+
+      for (std::list<std::string>::const_iterator
+             it = lst.begin(); it != lst.end(); ++it)
+      {
+        target.insert(*it);
+      }
+
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  std::string OrthancConfiguration::GetStringValue(const std::string& key,
+                                                   const std::string& defaultValue) const
+  {
+    std::string tmp;
+    if (LookupStringValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  int OrthancConfiguration::GetIntegerValue(const std::string& key,
+                                            int defaultValue) const
+  {
+    int tmp;
+    if (LookupIntegerValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
+                                                             unsigned int defaultValue) const
+  {
+    unsigned int tmp;
+    if (LookupUnsignedIntegerValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  bool OrthancConfiguration::GetBooleanValue(const std::string& key,
+                                             bool defaultValue) const
+  {
+    bool tmp;
+    if (LookupBooleanValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  float OrthancConfiguration::GetFloatValue(const std::string& key,
+                                            float defaultValue) const
+  {
+    float tmp;
+    if (LookupFloatValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  void OrthancConfiguration::GetDictionary(std::map<std::string, std::string>& target,
+                                           const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    target.clear();
+
+    if (!configuration_.isMember(key))
+    {
+      return;
+    }
+
+    if (configuration_[key].type() != Json::objectValue)
+    {
+      LogError("The configuration option \"" + GetPath(key) +
+               "\" is not a string as expected");
+
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+
+    Json::Value::Members members = configuration_[key].getMemberNames();
+
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      const Json::Value& value = configuration_[key][members[i]];
+
+      if (value.type() == Json::stringValue)
+      {
+        target[members[i]] = value.asString();
+      }
+      else
+      {
+        LogError("The configuration option \"" + GetPath(key) +
+                 "\" is not a dictionary mapping strings to strings");
+
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+      }
+    }
+  }
+
+
+  void OrthancImage::Clear()
+  {
+    if (image_ != NULL)
+    {
+      OrthancPluginFreeImage(GetGlobalContext(), image_);
+      image_ = NULL;
+    }
+  }
+
+
+  void OrthancImage::CheckImageAvailable() const
+  {
+    if (image_ == NULL)
+    {
+      LogError("Trying to access a NULL image");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+    }
+  }
+
+
+  OrthancImage::OrthancImage() :
+    image_(NULL)
+  {
+  }
+
+
+  OrthancImage::OrthancImage(OrthancPluginImage*  image) :
+    image_(image)
+  {
+  }
+
+
+  OrthancImage::OrthancImage(OrthancPluginPixelFormat  format,
+                             uint32_t                  width,
+                             uint32_t                  height)
+  {
+    image_ = OrthancPluginCreateImage(GetGlobalContext(), format, width, height);
+
+    if (image_ == NULL)
+    {
+      LogError("Cannot create an image");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+  }
+
+
+  OrthancImage::OrthancImage(OrthancPluginPixelFormat  format,
+                             uint32_t                  width,
+                             uint32_t                  height,
+                             uint32_t                  pitch,
+                             void*                     buffer)
+  {
+    image_ = OrthancPluginCreateImageAccessor
+      (GetGlobalContext(), format, width, height, pitch, buffer);
+
+    if (image_ == NULL)
+    {
+      LogError("Cannot create an image accessor");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+  }
+
+  void OrthancImage::UncompressPngImage(const void* data,
+                                        size_t size)
+  {
+    Clear();
+
+    image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Png);
+
+    if (image_ == NULL)
+    {
+      LogError("Cannot uncompress a PNG image");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+    }
+  }
+
+
+  void OrthancImage::UncompressJpegImage(const void* data,
+                                         size_t size)
+  {
+    Clear();
+    image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Jpeg);
+    if (image_ == NULL)
+    {
+      LogError("Cannot uncompress a JPEG image");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+    }
+  }
+
+
+  void OrthancImage::DecodeDicomImage(const void* data,
+                                      size_t size,
+                                      unsigned int frame)
+  {
+    Clear();
+    image_ = OrthancPluginDecodeDicomImage(GetGlobalContext(), data, size, frame);
+    if (image_ == NULL)
+    {
+      LogError("Cannot uncompress a DICOM image");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+    }
+  }
+
+
+  OrthancPluginPixelFormat OrthancImage::GetPixelFormat() const
+  {
+    CheckImageAvailable();
+    return OrthancPluginGetImagePixelFormat(GetGlobalContext(), image_);
+  }
+
+
+  unsigned int OrthancImage::GetWidth() const
+  {
+    CheckImageAvailable();
+    return OrthancPluginGetImageWidth(GetGlobalContext(), image_);
+  }
+
+
+  unsigned int OrthancImage::GetHeight() const
+  {
+    CheckImageAvailable();
+    return OrthancPluginGetImageHeight(GetGlobalContext(), image_);
+  }
+
+
+  unsigned int OrthancImage::GetPitch() const
+  {
+    CheckImageAvailable();
+    return OrthancPluginGetImagePitch(GetGlobalContext(), image_);
+  }
+
+
+  void* OrthancImage::GetBuffer() const
+  {
+    CheckImageAvailable();
+    return OrthancPluginGetImageBuffer(GetGlobalContext(), image_);
+  }
+
+
+  void OrthancImage::CompressPngImage(MemoryBuffer& target) const
+  {
+    CheckImageAvailable();
+
+    OrthancPlugins::MemoryBuffer answer;
+    OrthancPluginCompressPngImage(GetGlobalContext(), *answer, GetPixelFormat(),
+                                  GetWidth(), GetHeight(), GetPitch(), GetBuffer());
+
+    target.Swap(answer);
+  }
+
+
+  void OrthancImage::CompressJpegImage(MemoryBuffer& target,
+                                       uint8_t quality) const
+  {
+    CheckImageAvailable();
+
+    OrthancPlugins::MemoryBuffer answer;
+    OrthancPluginCompressJpegImage(GetGlobalContext(), *answer, GetPixelFormat(),
+                                   GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
+
+    target.Swap(answer);
+  }
+
+
+  void OrthancImage::AnswerPngImage(OrthancPluginRestOutput* output) const
+  {
+    CheckImageAvailable();
+    OrthancPluginCompressAndAnswerPngImage(GetGlobalContext(), output, GetPixelFormat(),
+                                           GetWidth(), GetHeight(), GetPitch(), GetBuffer());
+  }
+
+
+  void OrthancImage::AnswerJpegImage(OrthancPluginRestOutput* output,
+                                     uint8_t quality) const
+  {
+    CheckImageAvailable();
+    OrthancPluginCompressAndAnswerJpegImage(GetGlobalContext(), output, GetPixelFormat(),
+                                            GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
+  }
+
+
+  OrthancPluginImage* OrthancImage::Release()
+  {
+    CheckImageAvailable();
+    OrthancPluginImage* tmp = image_;
+    image_ = NULL;
+    return tmp;
+  }
+
+
+#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+  FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
+    matcher_(NULL),
+    worklist_(worklist)
+  {
+    if (worklist_ == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+    }
+  }
+
+
+  void FindMatcher::SetupDicom(const void*  query,
+                               uint32_t     size)
+  {
+    worklist_ = NULL;
+
+    matcher_ = OrthancPluginCreateFindMatcher(GetGlobalContext(), query, size);
+    if (matcher_ == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+  }
+
+
+  FindMatcher::~FindMatcher()
+  {
+    // The "worklist_" field
+
+    if (matcher_ != NULL)
+    {
+      OrthancPluginFreeFindMatcher(GetGlobalContext(), matcher_);
+    }
+  }
+
+
+
+  bool FindMatcher::IsMatch(const void*  dicom,
+                            uint32_t     size) const
+  {
+    int32_t result;
+
+    if (matcher_ != NULL)
+    {
+      result = OrthancPluginFindMatcherIsMatch(GetGlobalContext(), matcher_, dicom, size);
+    }
+    else if (worklist_ != NULL)
+    {
+      result = OrthancPluginWorklistIsMatch(GetGlobalContext(), worklist_, dicom, size);
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+
+    if (result == 0)
+    {
+      return false;
+    }
+    else if (result == 1)
+    {
+      return true;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+  }
+
+#endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
+
+  void AnswerJson(const Json::Value& value,
+                  OrthancPluginRestOutput* output
+    )
+  {
+    Json::StyledWriter writer;
+    std::string bodyString = writer.write(value);
+
+    OrthancPluginAnswerBuffer(GetGlobalContext(), output, bodyString.c_str(), bodyString.size(), "application/json");
+  }
+
+  void AnswerString(const std::string& answer,
+                    const char* mimeType,
+                    OrthancPluginRestOutput* output
+    )
+  {
+    OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType);
+  }
+
+  void AnswerHttpError(uint16_t httpError, OrthancPluginRestOutput *output)
+  {
+    OrthancPluginSendHttpStatusCode(GetGlobalContext(), output, httpError);
+  }
+
+  void AnswerMethodNotAllowed(OrthancPluginRestOutput *output, const char* allowedMethods)
+  {
+    OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowedMethods);
+  }
+
+  bool RestApiGetString(std::string& result,
+                        const std::string& uri,
+                        bool applyPlugins)
+  {
+    MemoryBuffer answer;
+    if (!answer.RestApiGet(uri, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      answer.ToString(result);
+      return true;
+    }
+  }
+
+  bool RestApiGetString(std::string& result,
+                        const std::string& uri,
+                        const std::map<std::string, std::string>& httpHeaders,
+                        bool applyPlugins)
+  {
+    MemoryBuffer answer;
+    if (!answer.RestApiGet(uri, httpHeaders, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      answer.ToString(result);
+      return true;
+    }
+  }
+
+
+
+  bool RestApiGet(Json::Value& result,
+                  const std::string& uri,
+                  bool applyPlugins)
+  {
+    MemoryBuffer answer;
+
+    if (!answer.RestApiGet(uri, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      if (!answer.IsEmpty())
+      {
+        answer.ToJson(result);
+      }
+      return true;
+    }
+  }
+
+
+  bool RestApiPost(std::string& result,
+                   const std::string& uri,
+                   const void* body,
+                   size_t bodySize,
+                   bool applyPlugins)
+  {
+    MemoryBuffer answer;
+
+    if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      if (!answer.IsEmpty())
+      {
+        result.assign(answer.GetData(), answer.GetSize());
+      }
+      return true;
+    }
+  }
+
+
+  bool RestApiPost(Json::Value& result,
+                   const std::string& uri,
+                   const void* body,
+                   size_t bodySize,
+                   bool applyPlugins)
+  {
+    MemoryBuffer answer;
+
+    if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      if (!answer.IsEmpty())
+      {
+        answer.ToJson(result);
+      }
+      return true;
+    }
+  }
+
+
+  bool RestApiPost(Json::Value& result,
+                   const std::string& uri,
+                   const Json::Value& body,
+                   bool applyPlugins)
+  {
+    Json::FastWriter writer;
+    return RestApiPost(result, uri, writer.write(body), applyPlugins);
+  }
+
+
+  bool RestApiPut(Json::Value& result,
+                  const std::string& uri,
+                  const void* body,
+                  size_t bodySize,
+                  bool applyPlugins)
+  {
+    MemoryBuffer answer;
+
+    if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
+    {
+      return false;
+    }
+    else
+    {
+      if (!answer.IsEmpty()) // i.e, on a PUT to metadata/..., orthanc returns an empty response
+      {
+        answer.ToJson(result);
+      }
+      return true;
+    }
+  }
+
+
+  bool RestApiPut(Json::Value& result,
+                  const std::string& uri,
+                  const Json::Value& body,
+                  bool applyPlugins)
+  {
+    Json::FastWriter writer;
+    return RestApiPut(result, uri, writer.write(body), applyPlugins);
+  }
+
+
+  bool RestApiDelete(const std::string& uri,
+                     bool applyPlugins)
+  {
+    OrthancPluginErrorCode error;
+
+    if (applyPlugins)
+    {
+      error = OrthancPluginRestApiDeleteAfterPlugins(GetGlobalContext(), uri.c_str());
+    }
+    else
+    {
+      error = OrthancPluginRestApiDelete(GetGlobalContext(), uri.c_str());
+    }
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource ||
+             error == OrthancPluginErrorCode_InexistentItem)
+    {
+      return false;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+    }
+  }
+
+
+  void ReportMinimalOrthancVersion(unsigned int major,
+                                   unsigned int minor,
+                                   unsigned int revision)
+  {
+    LogError("Your version of the Orthanc core (" +
+             std::string(GetGlobalContext()->orthancVersion) +
+             ") is too old to run this plugin (version " +
+             boost::lexical_cast<std::string>(major) + "." +
+             boost::lexical_cast<std::string>(minor) + "." +
+             boost::lexical_cast<std::string>(revision) +
+             " is required)");
+  }
+
+
+  bool CheckMinimalOrthancVersion(unsigned int major,
+                                  unsigned int minor,
+                                  unsigned int revision)
+  {
+    if (!HasGlobalContext())
+    {
+      LogError("Bad Orthanc context in the plugin");
+      return false;
+    }
+
+    if (!strcmp(GetGlobalContext()->orthancVersion, "mainline"))
+    {
+      // Assume compatibility with the mainline
+      return true;
+    }
+
+    // Parse the version of the Orthanc core
+    int aa, bb, cc;
+    if (
+#ifdef _MSC_VER
+      sscanf_s
+#else
+      sscanf
+#endif
+      (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 ||
+      aa < 0 ||
+      bb < 0 ||
+      cc < 0)
+    {
+      return false;
+    }
+
+    unsigned int a = static_cast<unsigned int>(aa);
+    unsigned int b = static_cast<unsigned int>(bb);
+    unsigned int c = static_cast<unsigned int>(cc);
+
+    // Check the major version number
+
+    if (a > major)
+    {
+      return true;
+    }
+
+    if (a < major)
+    {
+      return false;
+    }
+
+
+    // Check the minor version number
+    assert(a == major);
+
+    if (b > minor)
+    {
+      return true;
+    }
+
+    if (b < minor)
+    {
+      return false;
+    }
+
+    // Check the patch level version number
+    assert(a == major && b == minor);
+
+    if (c >= revision)
+    {
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
+  const char* AutodetectMimeType(const std::string& path)
+  {
+    const char* mime = OrthancPluginAutodetectMimeType(GetGlobalContext(), path.c_str());
+
+    if (mime == NULL)
+    {
+      // Should never happen, just for safety
+      return "application/octet-stream";
+    }
+    else
+    {
+      return mime;
+    }
+  }
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_PEERS == 1
+  size_t OrthancPeers::GetPeerIndex(const std::string& name) const
+  {
+    size_t index;
+    if (LookupName(index, name))
+    {
+      return index;
+    }
+    else
+    {
+      LogError("Inexistent peer: " + name);
+      ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
+    }
+  }
+
+
+  OrthancPeers::OrthancPeers() :
+    peers_(NULL),
+    timeout_(0)
+  {
+    peers_ = OrthancPluginGetPeers(GetGlobalContext());
+
+    if (peers_ == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+    }
+
+    uint32_t count = OrthancPluginGetPeersCount(GetGlobalContext(), peers_);
+
+    for (uint32_t i = 0; i < count; i++)
+    {
+      const char* name = OrthancPluginGetPeerName(GetGlobalContext(), peers_, i);
+      if (name == NULL)
+      {
+        OrthancPluginFreePeers(GetGlobalContext(), peers_);
+        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      }
+
+      index_[name] = i;
+    }
+  }
+
+
+  OrthancPeers::~OrthancPeers()
+  {
+    if (peers_ != NULL)
+    {
+      OrthancPluginFreePeers(GetGlobalContext(), peers_);
+    }
+  }
+
+
+  bool OrthancPeers::LookupName(size_t& target,
+                                const std::string& name) const
+  {
+    Index::const_iterator found = index_.find(name);
+
+    if (found == index_.end())
+    {
+      return false;
+    }
+    else
+    {
+      target = found->second;
+      return true;
+    }
+  }
+
+
+  std::string OrthancPeers::GetPeerName(size_t index) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      const char* s = OrthancPluginGetPeerName(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
+      if (s == NULL)
+      {
+        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      }
+      else
+      {
+        return s;
+      }
+    }
+  }
+
+
+  std::string OrthancPeers::GetPeerUrl(size_t index) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      const char* s = OrthancPluginGetPeerUrl(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
+      if (s == NULL)
+      {
+        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      }
+      else
+      {
+        return s;
+      }
+    }
+  }
+
+
+  std::string OrthancPeers::GetPeerUrl(const std::string& name) const
+  {
+    return GetPeerUrl(GetPeerIndex(name));
+  }
+
+
+  bool OrthancPeers::LookupUserProperty(std::string& value,
+                                        size_t index,
+                                        const std::string& key) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      const char* s = OrthancPluginGetPeerUserProperty(GetGlobalContext(), peers_, static_cast<uint32_t>(index), key.c_str());
+      if (s == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        value.assign(s);
+        return true;
+      }
+    }
+  }
+
+
+  bool OrthancPeers::LookupUserProperty(std::string& value,
+                                        const std::string& peer,
+                                        const std::string& key) const
+  {
+    return LookupUserProperty(value, GetPeerIndex(peer), key);
+  }
+
+
+  bool OrthancPeers::DoGet(MemoryBuffer& target,
+                           size_t index,
+                           const std::string& uri) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+
+    OrthancPlugins::MemoryBuffer answer;
+    uint16_t status;
+    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
+      (GetGlobalContext(), *answer, NULL, &status, peers_,
+       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Get, uri.c_str(),
+       0, NULL, NULL, NULL, 0, timeout_);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      target.Swap(answer);
+      return (status == 200);
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoGet(MemoryBuffer& target,
+                           const std::string& name,
+                           const std::string& uri) const
+  {
+    size_t index;
+    return (LookupName(index, name) &&
+            DoGet(target, index, uri));
+  }
+
+
+  bool OrthancPeers::DoGet(Json::Value& target,
+                           size_t index,
+                           const std::string& uri) const
+  {
+    MemoryBuffer buffer;
+
+    if (DoGet(buffer, index, uri))
+    {
+      buffer.ToJson(target);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoGet(Json::Value& target,
+                           const std::string& name,
+                           const std::string& uri) const
+  {
+    MemoryBuffer buffer;
+
+    if (DoGet(buffer, name, uri))
+    {
+      buffer.ToJson(target);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoPost(MemoryBuffer& target,
+                            const std::string& name,
+                            const std::string& uri,
+                            const std::string& body) const
+  {
+    size_t index;
+    return (LookupName(index, name) &&
+            DoPost(target, index, uri, body));
+  }
+
+
+  bool OrthancPeers::DoPost(Json::Value& target,
+                            size_t index,
+                            const std::string& uri,
+                            const std::string& body) const
+  {
+    MemoryBuffer buffer;
+
+    if (DoPost(buffer, index, uri, body))
+    {
+      buffer.ToJson(target);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoPost(Json::Value& target,
+                            const std::string& name,
+                            const std::string& uri,
+                            const std::string& body) const
+  {
+    MemoryBuffer buffer;
+
+    if (DoPost(buffer, name, uri, body))
+    {
+      buffer.ToJson(target);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoPost(MemoryBuffer& target,
+                            size_t index,
+                            const std::string& uri,
+                            const std::string& body) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+
+    OrthancPlugins::MemoryBuffer answer;
+    uint16_t status;
+    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
+      (GetGlobalContext(), *answer, NULL, &status, peers_,
+       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Post, uri.c_str(),
+       0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      target.Swap(answer);
+      return (status == 200);
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoPut(size_t index,
+                           const std::string& uri,
+                           const std::string& body) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+
+    OrthancPlugins::MemoryBuffer answer;
+    uint16_t status;
+    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
+      (GetGlobalContext(), *answer, NULL, &status, peers_,
+       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Put, uri.c_str(),
+       0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      return (status == 200);
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoPut(const std::string& name,
+                           const std::string& uri,
+                           const std::string& body) const
+  {
+    size_t index;
+    return (LookupName(index, name) &&
+            DoPut(index, uri, body));
+  }
+
+
+  bool OrthancPeers::DoDelete(size_t index,
+                              const std::string& uri) const
+  {
+    if (index >= index_.size())
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+
+    OrthancPlugins::MemoryBuffer answer;
+    uint16_t status;
+    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
+      (GetGlobalContext(), *answer, NULL, &status, peers_,
+       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Delete, uri.c_str(),
+       0, NULL, NULL, NULL, 0, timeout_);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      return (status == 200);
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool OrthancPeers::DoDelete(const std::string& name,
+                              const std::string& uri) const
+  {
+    size_t index;
+    return (LookupName(index, name) &&
+            DoDelete(index, uri));
+  }
+#endif
+
+
+
+
+
+  /******************************************************************
+   ** JOBS
+   ******************************************************************/
+
+#if HAS_ORTHANC_PLUGIN_JOB == 1
+  void OrthancJob::CallbackFinalize(void* job)
+  {
+    if (job != NULL)
+    {
+      delete reinterpret_cast<OrthancJob*>(job);
+    }
+  }
+
+
+  float OrthancJob::CallbackGetProgress(void* job)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      return reinterpret_cast<OrthancJob*>(job)->progress_;
+    }
+    catch (...)
+    {
+      return 0;
+    }
+  }
+
+
+  const char* OrthancJob::CallbackGetContent(void* job)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      return reinterpret_cast<OrthancJob*>(job)->content_.c_str();
+    }
+    catch (...)
+    {
+      return 0;
+    }
+  }
+
+
+  const char* OrthancJob::CallbackGetSerialized(void* job)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      const OrthancJob& tmp = *reinterpret_cast<OrthancJob*>(job);
+
+      if (tmp.hasSerialized_)
+      {
+        return tmp.serialized_.c_str();
+      }
+      else
+      {
+        return NULL;
+      }
+    }
+    catch (...)
+    {
+      return 0;
+    }
+  }
+
+
+  OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      return reinterpret_cast<OrthancJob*>(job)->Step();
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
+    {
+      return OrthancPluginJobStepStatus_Failure;
+    }
+    catch (...)
+    {
+      return OrthancPluginJobStepStatus_Failure;
+    }
+  }
+
+
+  OrthancPluginErrorCode OrthancJob::CallbackStop(void* job,
+                                                  OrthancPluginJobStopReason reason)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      reinterpret_cast<OrthancJob*>(job)->Stop(reason);
+      return OrthancPluginErrorCode_Success;
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+    }
+    catch (...)
+    {
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+
+
+  OrthancPluginErrorCode OrthancJob::CallbackReset(void* job)
+  {
+    assert(job != NULL);
+
+    try
+    {
+      reinterpret_cast<OrthancJob*>(job)->Reset();
+      return OrthancPluginErrorCode_Success;
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+    }
+    catch (...)
+    {
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+
+
+  void OrthancJob::ClearContent()
+  {
+    Json::Value empty = Json::objectValue;
+    UpdateContent(empty);
+  }
+
+
+  void OrthancJob::UpdateContent(const Json::Value& content)
+  {
+    if (content.type() != Json::objectValue)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
+    }
+    else
+    {
+      Json::FastWriter writer;
+      content_ = writer.write(content);
+    }
+  }
+
+
+  void OrthancJob::ClearSerialized()
+  {
+    hasSerialized_ = false;
+    serialized_.clear();
+  }
+
+
+  void OrthancJob::UpdateSerialized(const Json::Value& serialized)
+  {
+    if (serialized.type() != Json::objectValue)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
+    }
+    else
+    {
+      Json::FastWriter writer;
+      serialized_ = writer.write(serialized);
+      hasSerialized_ = true;
+    }
+  }
+
+
+  void OrthancJob::UpdateProgress(float progress)
+  {
+    if (progress < 0 ||
+        progress > 1)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+    }
+
+    progress_ = progress;
+  }
+
+
+  OrthancJob::OrthancJob(const std::string& jobType) :
+    jobType_(jobType),
+    progress_(0)
+  {
+    ClearContent();
+    ClearSerialized();
+  }
+
+
+  OrthancPluginJob* OrthancJob::Create(OrthancJob* job)
+  {
+    if (job == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
+    }
+
+    OrthancPluginJob* orthanc = OrthancPluginCreateJob(
+      GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(),
+      CallbackGetProgress, CallbackGetContent, CallbackGetSerialized,
+      CallbackStep, CallbackStop, CallbackReset);
+
+    if (orthanc == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+    }
+    else
+    {
+      return orthanc;
+    }
+  }
+
+
+  std::string OrthancJob::Submit(OrthancJob* job,
+                                 int priority)
+  {
+    if (job == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
+    }
+
+    OrthancPluginJob* orthanc = Create(job);
+
+    char* id = OrthancPluginSubmitJob(GetGlobalContext(), orthanc, priority);
+
+    if (id == NULL)
+    {
+      LogError("Plugin cannot submit job");
+      OrthancPluginFreeJob(GetGlobalContext(), orthanc);
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+    }
+    else
+    {
+      std::string tmp(id);
+      tmp.assign(id);
+      OrthancPluginFreeString(GetGlobalContext(), id);
+
+      return tmp;
+    }
+  }
+
+
+  void OrthancJob::SubmitAndWait(Json::Value& result,
+                                 OrthancJob* job /* takes ownership */,
+                                 int priority)
+  {
+    std::string id = Submit(job, priority);
+
+    for (;;)
+    {
+      boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+
+      Json::Value status;
+      if (!RestApiGet(status, "/jobs/" + id, false) ||
+          !status.isMember("State") ||
+          status["State"].type() != Json::stringValue)
+      {
+        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);        
+      }
+
+      const std::string state = status["State"].asString();
+      if (state == "Success")
+      {
+        if (status.isMember("Content"))
+        {
+          result = status["Content"];
+        }
+        else
+        {
+          result = Json::objectValue;
+        }
+
+        return;
+      }
+      else if (state == "Running")
+      {
+        continue;
+      }
+      else if (!status.isMember("ErrorCode") ||
+               status["ErrorCode"].type() != Json::intValue)
+      {
+        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
+      }
+      else
+      {
+        if (!status.isMember("ErrorDescription") ||
+            status["ErrorDescription"].type() != Json::stringValue)
+        {
+          ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
+        }
+        else
+        {
+#if HAS_ORTHANC_EXCEPTION == 1
+          throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
+                                          status["ErrorDescription"].asString());
+#else
+          LogError("Exception while executing the job: " + status["ErrorDescription"].asString());
+          ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());          
+#endif
+        }
+      }
+    }
+  }
+
+
+  void OrthancJob::SubmitFromRestApiPost(OrthancPluginRestOutput* output,
+                                         const Json::Value& body,
+                                         OrthancJob* job)
+  {
+    static const char* KEY_SYNCHRONOUS = "Synchronous";
+    static const char* KEY_ASYNCHRONOUS = "Asynchronous";
+    static const char* KEY_PRIORITY = "Priority";
+
+    boost::movelib::unique_ptr<OrthancJob> protection(job);
+  
+    if (body.type() != Json::objectValue)
+    {
+#if HAS_ORTHANC_EXCEPTION == 1
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                      "Expected a JSON object in the body");
+#else
+      LogError("Expected a JSON object in the body");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+#endif
+    }
+
+    bool synchronous = true;
+  
+    if (body.isMember(KEY_SYNCHRONOUS))
+    {
+      if (body[KEY_SYNCHRONOUS].type() != Json::booleanValue)
+      {
+#if HAS_ORTHANC_EXCEPTION == 1
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                        "Option \"" + std::string(KEY_SYNCHRONOUS) +
+                                        "\" must be Boolean");
+#else
+        LogError("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+#endif
+      }
+      else
+      {
+        synchronous = body[KEY_SYNCHRONOUS].asBool();
+      }
+    }
+
+    if (body.isMember(KEY_ASYNCHRONOUS))
+    {
+      if (body[KEY_ASYNCHRONOUS].type() != Json::booleanValue)
+      {
+#if HAS_ORTHANC_EXCEPTION == 1
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                        "Option \"" + std::string(KEY_ASYNCHRONOUS) +
+                                        "\" must be Boolean");
+#else
+        LogError("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+#endif
+      }
+      else
+      {
+        synchronous = !body[KEY_ASYNCHRONOUS].asBool();
+      }
+    }
+
+    int priority = 0;
+
+    if (body.isMember(KEY_PRIORITY))
+    {
+      if (body[KEY_PRIORITY].type() != Json::booleanValue)
+      {
+#if HAS_ORTHANC_EXCEPTION == 1
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                        "Option \"" + std::string(KEY_PRIORITY) +
+                                        "\" must be an integer");
+#else
+        LogError("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
+        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+#endif
+      }
+      else
+      {
+        priority = !body[KEY_PRIORITY].asInt();
+      }
+    }
+  
+    Json::Value result;
+
+    if (synchronous)
+    {
+      OrthancPlugins::OrthancJob::SubmitAndWait(result, protection.release(), priority);
+    }
+    else
+    {
+      std::string id = OrthancPlugins::OrthancJob::Submit(protection.release(), priority);
+
+      result = Json::objectValue;
+      result["ID"] = id;
+      result["Path"] = "/jobs/" + id;
+    }
+
+    std::string s = result.toStyledString();
+    OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(),
+                              s.size(), "application/json");
+  }
+
+#endif
+
+
+
+
+  /******************************************************************
+   ** METRICS
+   ******************************************************************/
+
+#if HAS_ORTHANC_PLUGIN_METRICS == 1
+  MetricsTimer::MetricsTimer(const char* name) :
+    name_(name)
+  {
+    start_ = boost::posix_time::microsec_clock::universal_time();
+  }
+  
+  MetricsTimer::~MetricsTimer()
+  {
+    const boost::posix_time::ptime stop = boost::posix_time::microsec_clock::universal_time();
+    const boost::posix_time::time_duration diff = stop - start_;
+    OrthancPluginSetMetricsValue(GetGlobalContext(), name_.c_str(), static_cast<float>(diff.total_milliseconds()),
+                                 OrthancPluginMetricsType_Timer);
+  }
+#endif
+
+
+
+
+  /******************************************************************
+   ** HTTP CLIENT
+   ******************************************************************/
+
+#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
+  class HttpClient::RequestBodyWrapper : public boost::noncopyable
+  {
+  private:
+    static RequestBodyWrapper& GetObject(void* body)
+    {
+      assert(body != NULL);
+      return *reinterpret_cast<RequestBodyWrapper*>(body);
+    }
+
+    IRequestBody&  body_;
+    bool           done_;
+    std::string    chunk_;
+
+  public:
+    RequestBodyWrapper(IRequestBody& body) :
+      body_(body),
+      done_(false)
+    {
+    }      
+
+    static uint8_t IsDone(void* body)
+    {
+      return GetObject(body).done_;
+    }
+    
+    static const void* GetChunkData(void* body)
+    {
+      return GetObject(body).chunk_.c_str();
+    }
+    
+    static uint32_t GetChunkSize(void* body)
+    {
+      return static_cast<uint32_t>(GetObject(body).chunk_.size());
+    }
+
+    static OrthancPluginErrorCode Next(void* body)
+    {
+      RequestBodyWrapper& that = GetObject(body);
+        
+      if (that.done_)
+      {
+        return OrthancPluginErrorCode_BadSequenceOfCalls;
+      }
+      else
+      {
+        try
+        {
+          that.done_ = !that.body_.ReadNextChunk(that.chunk_);
+          return OrthancPluginErrorCode_Success;
+        }
+        catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+        {
+          return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+        }
+        catch (...)
+        {
+          return OrthancPluginErrorCode_InternalError;
+        }
+      }
+    }    
+  };
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+  static OrthancPluginErrorCode AnswerAddHeaderCallback(void* answer,
+                                                        const char* key,
+                                                        const char* value)
+  {
+    assert(answer != NULL && key != NULL && value != NULL);
+
+    try
+    {
+      reinterpret_cast<HttpClient::IAnswer*>(answer)->AddHeader(key, value);
+      return OrthancPluginErrorCode_Success;
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+    }
+    catch (...)
+    {
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+  static OrthancPluginErrorCode AnswerAddChunkCallback(void* answer,
+                                                       const void* data,
+                                                       uint32_t size)
+  {
+    assert(answer != NULL);
+
+    try
+    {
+      reinterpret_cast<HttpClient::IAnswer*>(answer)->AddChunk(data, size);
+      return OrthancPluginErrorCode_Success;
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+    }
+    catch (...)
+    {
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+#endif
+
+
+  HttpClient::HttpClient() :
+    httpStatus_(0),
+    method_(OrthancPluginHttpMethod_Get),
+    timeout_(0),
+    pkcs11_(false),
+    chunkedBody_(NULL),
+    allowChunkedTransfers_(true)
+  {
+  }
+
+
+  void HttpClient::AddHeaders(const HttpHeaders& headers)
+  {
+    for (HttpHeaders::const_iterator it = headers.begin();
+         it != headers.end(); ++it)
+    {
+      headers_[it->first] = it->second;
+    }
+  }
+
+  
+  void HttpClient::SetCredentials(const std::string& username,
+                                  const std::string& password)
+  {
+    username_ = username;
+    password_ = password;
+  }
+
+  
+  void HttpClient::ClearCredentials()
+  {
+    username_.empty();
+    password_.empty();
+  }
+
+
+  void HttpClient::SetCertificate(const std::string& certificateFile,
+                                  const std::string& keyFile,
+                                  const std::string& keyPassword)
+  {
+    certificateFile_ = certificateFile;
+    certificateKeyFile_ = keyFile;
+    certificateKeyPassword_ = keyPassword;
+  }
+
+  
+  void HttpClient::ClearCertificate()
+  {
+    certificateFile_.clear();
+    certificateKeyFile_.clear();
+    certificateKeyPassword_.clear();
+  }
+
+
+  void HttpClient::ClearBody()
+  {
+    fullBody_.clear();
+    chunkedBody_ = NULL;
+  }
+
+  
+  void HttpClient::SwapBody(std::string& body)
+  {
+    fullBody_.swap(body);
+    chunkedBody_ = NULL;
+  }
+
+  
+  void HttpClient::SetBody(const std::string& body)
+  {
+    fullBody_ = body;
+    chunkedBody_ = NULL;
+  }
+
+  
+  void HttpClient::SetBody(IRequestBody& body)
+  {
+    fullBody_.clear();
+    chunkedBody_ = &body;
+  }
+
+
+  namespace
+  {
+    class HeadersWrapper : public boost::noncopyable
+    {
+    private:
+      std::vector<const char*>  headersKeys_;
+      std::vector<const char*>  headersValues_;
+
+    public:
+      HeadersWrapper(const HttpClient::HttpHeaders& headers)
+      {
+        headersKeys_.reserve(headers.size());
+        headersValues_.reserve(headers.size());
+
+        for (HttpClient::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it)
+        {
+          headersKeys_.push_back(it->first.c_str());
+          headersValues_.push_back(it->second.c_str());
+        }
+      }
+
+      void AddStaticString(const char* key,
+                           const char* value)
+      {
+        headersKeys_.push_back(key);
+        headersValues_.push_back(value);
+      }
+
+      uint32_t GetCount() const
+      {
+        return headersKeys_.size();
+      }
+
+      const char* const* GetKeys() const
+      {
+        return headersKeys_.empty() ? NULL : &headersKeys_[0];
+      }
+
+      const char* const* GetValues() const
+      {
+        return headersValues_.empty() ? NULL : &headersValues_[0];
+      }
+    };
+
+
+    class MemoryRequestBody : public HttpClient::IRequestBody
+    {
+    private:
+      std::string  body_;
+      bool         done_;
+
+    public:
+      MemoryRequestBody(const std::string& body) :
+        body_(body),
+        done_(false)
+      {
+        if (body_.empty())
+        {
+          done_ = true;
+        }
+      }
+
+      virtual bool ReadNextChunk(std::string& chunk)
+      {
+        if (done_)
+        {
+          return false;
+        }
+        else
+        {
+          chunk.swap(body_);
+          done_ = true;
+          return true;
+        }
+      }
+    };
+
+
+    // This class mimics Orthanc::ChunkedBuffer
+    class ChunkedBuffer : public boost::noncopyable
+    {
+    private:
+      typedef std::list<std::string*>  Content;
+
+      Content  content_;
+      size_t   size_;
+
+    public:
+      ChunkedBuffer() :
+        size_(0)
+      {
+      }
+
+      ~ChunkedBuffer()
+      {
+        Clear();
+      }
+
+      void Clear()
+      {
+        for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
+        {
+          assert(*it != NULL);
+          delete *it;
+        }
+
+        content_.clear();
+      }
+
+      void Flatten(std::string& target) const
+      {
+        target.resize(size_);
+
+        size_t pos = 0;
+
+        for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
+        {
+          assert(*it != NULL);
+          size_t s = (*it)->size();
+
+          if (s != 0)
+          {
+            memcpy(&target[pos], (*it)->c_str(), s);
+            pos += s;
+          }
+        }
+
+        assert(size_ == 0 ||
+               pos == target.size());
+      }
+
+      void AddChunk(const void* data,
+                    size_t size)
+      {
+        content_.push_back(new std::string(reinterpret_cast<const char*>(data), size));
+        size_ += size;
+      }
+
+      void AddChunk(const std::string& chunk)
+      {
+        content_.push_back(new std::string(chunk));
+        size_ += chunk.size();
+      }
+    };
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+    class MemoryAnswer : public HttpClient::IAnswer
+    {
+    private:
+      HttpClient::HttpHeaders  headers_;
+      ChunkedBuffer            body_;
+
+    public:
+      const HttpClient::HttpHeaders& GetHeaders() const
+      {
+        return headers_;
+      }
+
+      const ChunkedBuffer& GetBody() const
+      {
+        return body_;
+      }
+
+      virtual void AddHeader(const std::string& key,
+                             const std::string& value)
+      {
+        headers_[key] = value;
+      }
+
+      virtual void AddChunk(const void* data,
+                            size_t size)
+      {
+        body_.AddChunk(data, size);
+      }
+    };
+#endif
+  }
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+  void HttpClient::ExecuteWithStream(uint16_t& httpStatus,
+                                     IAnswer& answer,
+                                     IRequestBody& body) const
+  {
+    HeadersWrapper h(headers_);
+
+    if (method_ == OrthancPluginHttpMethod_Post ||
+        method_ == OrthancPluginHttpMethod_Put)
+    {
+      // Automatically set the "Transfer-Encoding" header if absent
+      bool found = false;
+
+      for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it)
+      {
+        if (boost::iequals(it->first, "Transfer-Encoding"))
+        {
+          found = true;
+          break;
+        }
+      }
+
+      if (!found)
+      {
+        h.AddStaticString("Transfer-Encoding", "chunked");
+      }
+    }
+
+    RequestBodyWrapper request(body);
+        
+    OrthancPluginErrorCode error = OrthancPluginChunkedHttpClient(
+      GetGlobalContext(),
+      &answer,
+      AnswerAddChunkCallback,
+      AnswerAddHeaderCallback,
+      &httpStatus,
+      method_,
+      url_.c_str(),
+      h.GetCount(),
+      h.GetKeys(),
+      h.GetValues(),
+      &request,
+      RequestBodyWrapper::IsDone,
+      RequestBodyWrapper::GetChunkData,
+      RequestBodyWrapper::GetChunkSize,
+      RequestBodyWrapper::Next,
+      username_.empty() ? NULL : username_.c_str(),
+      password_.empty() ? NULL : password_.c_str(),
+      timeout_,
+      certificateFile_.empty() ? NULL : certificateFile_.c_str(),
+      certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
+      certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
+      pkcs11_ ? 1 : 0);
+
+    if (error != OrthancPluginErrorCode_Success)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+    }
+  }
+#endif    
+
+
+  void HttpClient::ExecuteWithoutStream(uint16_t& httpStatus,
+                                        HttpHeaders& answerHeaders,
+                                        std::string& answerBody,
+                                        const std::string& body) const
+  {
+    HeadersWrapper headers(headers_);
+
+    MemoryBuffer answerBodyBuffer, answerHeadersBuffer;
+
+    OrthancPluginErrorCode error = OrthancPluginHttpClient(
+      GetGlobalContext(),
+      *answerBodyBuffer,
+      *answerHeadersBuffer,
+      &httpStatus,
+      method_,
+      url_.c_str(),
+      headers.GetCount(),
+      headers.GetKeys(),
+      headers.GetValues(),
+      body.empty() ? NULL : body.c_str(),
+      body.size(),
+      username_.empty() ? NULL : username_.c_str(),
+      password_.empty() ? NULL : password_.c_str(),
+      timeout_,
+      certificateFile_.empty() ? NULL : certificateFile_.c_str(),
+      certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
+      certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
+      pkcs11_ ? 1 : 0);
+
+    if (error != OrthancPluginErrorCode_Success)
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+    }
+
+    Json::Value v;
+    answerHeadersBuffer.ToJson(v);
+
+    if (v.type() != Json::objectValue)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+    }
+
+    Json::Value::Members members = v.getMemberNames();
+    answerHeaders.clear();
+
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      const Json::Value& h = v[members[i]];
+      if (h.type() != Json::stringValue)
+      {
+        ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+      }
+      else
+      {
+        answerHeaders[members[i]] = h.asString();
+      }
+    }
+
+    answerBodyBuffer.ToString(answerBody);
+  }
+
+
+  void HttpClient::Execute(IAnswer& answer)
+  {
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+    if (allowChunkedTransfers_)
+    {
+      if (chunkedBody_ != NULL)
+      {
+        ExecuteWithStream(httpStatus_, answer, *chunkedBody_);
+      }
+      else
+      {
+        MemoryRequestBody wrapper(fullBody_);
+        ExecuteWithStream(httpStatus_, answer, wrapper);
+      }
+
+      return;
+    }
+#endif
+    
+    // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
+    // transfers are disabled. This results in higher memory usage
+    // (all chunks from the answer body are sent at once)
+
+    HttpHeaders answerHeaders;
+    std::string answerBody;
+    Execute(answerHeaders, answerBody);
+
+    for (HttpHeaders::const_iterator it = answerHeaders.begin(); 
+         it != answerHeaders.end(); ++it)
+    {
+      answer.AddHeader(it->first, it->second);      
+    }
+
+    if (!answerBody.empty())
+    {
+      answer.AddChunk(answerBody.c_str(), answerBody.size());
+    }
+  }
+
+
+  void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
+                           std::string& answerBody /* out */)
+  {
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+    if (allowChunkedTransfers_)
+    {
+      MemoryAnswer answer;
+      Execute(answer);
+      answerHeaders = answer.GetHeaders();
+      answer.GetBody().Flatten(answerBody);
+      return;
+    }
+#endif
+    
+    // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
+    // transfers are disabled. This results in higher memory usage
+    // (all chunks from the request body are sent at once)
+
+    if (chunkedBody_ != NULL)
+    {
+      ChunkedBuffer buffer;
+      
+      std::string chunk;
+      while (chunkedBody_->ReadNextChunk(chunk))
+      {
+        buffer.AddChunk(chunk);
+      }
+
+      std::string body;
+      buffer.Flatten(body);
+
+      ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, body);
+    }
+    else
+    {
+      ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, fullBody_);
+    }
+  }
+
+
+  void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
+                           Json::Value& answerBody /* out */)
+  {
+    std::string body;
+    Execute(answerHeaders, body);
+    
+    Json::Reader reader;
+    if (!reader.parse(body, answerBody))
+    {
+      LogError("Cannot convert HTTP answer body to JSON");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+    }
+  }
+
+
+  void HttpClient::Execute()
+  {
+    HttpHeaders answerHeaders;
+    std::string body;
+    Execute(answerHeaders, body);
+  }
+
+#endif  /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */
+
+
+
+
+
+  /******************************************************************
+   ** CHUNKED HTTP SERVER
+   ******************************************************************/
+
+  namespace Internals
+  {
+    void NullRestCallback(OrthancPluginRestOutput* output,
+                          const char* url,
+                          const OrthancPluginHttpRequest* request)
+    {
+    }
+  
+    IChunkedRequestReader *NullChunkedRestCallback(const char* url,
+                                                   const OrthancPluginHttpRequest* request)
+    {
+      return NULL;
+    }
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+
+    OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
+      OrthancPluginServerChunkedRequestReader* reader,
+      const void*                              data,
+      uint32_t                                 size)
+    {
+      try
+      {
+        if (reader == NULL)
+        {
+          return OrthancPluginErrorCode_InternalError;
+        }
+
+        reinterpret_cast<IChunkedRequestReader*>(reader)->AddChunk(data, size);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+      {
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+
+    
+    OrthancPluginErrorCode ChunkedRequestReaderExecute(
+      OrthancPluginServerChunkedRequestReader* reader,
+      OrthancPluginRestOutput*                 output)
+    {
+      try
+      {
+        if (reader == NULL)
+        {
+          return OrthancPluginErrorCode_InternalError;
+        }
+
+        reinterpret_cast<IChunkedRequestReader*>(reader)->Execute(output);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+      {
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+
+    
+    void ChunkedRequestReaderFinalize(
+      OrthancPluginServerChunkedRequestReader* reader)
+    {
+      if (reader != NULL)
+      {
+        delete reinterpret_cast<IChunkedRequestReader*>(reader);
+      }
+    }
+
+#else
+    
+    OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
+                                                    const char* url,
+                                                    const OrthancPluginHttpRequest* request,
+                                                    RestCallback         GetHandler,
+                                                    ChunkedRestCallback  PostHandler,
+                                                    RestCallback         DeleteHandler,
+                                                    ChunkedRestCallback  PutHandler)
+    {
+      try
+      {
+        std::string allowed;
+
+        if (GetHandler != Internals::NullRestCallback)
+        {
+          allowed += "GET";
+        }
+
+        if (PostHandler != Internals::NullChunkedRestCallback)
+        {
+          if (!allowed.empty())
+          {
+            allowed += ",";
+          }
+        
+          allowed += "POST";
+        }
+
+        if (DeleteHandler != Internals::NullRestCallback)
+        {
+          if (!allowed.empty())
+          {
+            allowed += ",";
+          }
+        
+          allowed += "DELETE";
+        }
+
+        if (PutHandler != Internals::NullChunkedRestCallback)
+        {
+          if (!allowed.empty())
+          {
+            allowed += ",";
+          }
+        
+          allowed += "PUT";
+        }
+      
+        switch (request->method)
+        {
+          case OrthancPluginHttpMethod_Get:
+            if (GetHandler == Internals::NullRestCallback)
+            {
+              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
+            }
+            else
+            {
+              GetHandler(output, url, request);
+            }
+
+            break;
+
+          case OrthancPluginHttpMethod_Post:
+            if (PostHandler == Internals::NullChunkedRestCallback)
+            {
+              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
+            }
+            else
+            {
+              boost::movelib::unique_ptr<IChunkedRequestReader> reader(PostHandler(url, request));
+              if (reader.get() == NULL)
+              {
+                ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+              }
+              else
+              {
+                reader->AddChunk(request->body, request->bodySize);
+                reader->Execute(output);
+              }
+            }
+
+            break;
+
+          case OrthancPluginHttpMethod_Delete:
+            if (DeleteHandler == Internals::NullRestCallback)
+            {
+              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
+            }
+            else
+            {
+              DeleteHandler(output, url, request);
+            }
+
+            break;
+
+          case OrthancPluginHttpMethod_Put:
+            if (PutHandler == Internals::NullChunkedRestCallback)
+            {
+              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
+            }
+            else
+            {
+              boost::movelib::unique_ptr<IChunkedRequestReader> reader(PutHandler(url, request));
+              if (reader.get() == NULL)
+              {
+                ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+              }
+              else
+              {
+                reader->AddChunk(request->body, request->bodySize);
+                reader->Execute(output);
+              }
+            }
+
+            break;
+
+          default:
+            ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+        }
+
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+      {
+#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
+        if (HasGlobalContext() &&
+            e.HasDetails())
+        {
+          // The "false" instructs Orthanc not to log the detailed
+          // error message. This is to avoid duplicating the details,
+          // because "OrthancException" already does it on construction.
+          OrthancPluginSetHttpErrorDetails
+            (GetGlobalContext(), output, e.GetDetails(), false);
+        }
+#endif
+
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+#endif
+  }
+
+
+#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+  OrthancPluginErrorCode IStorageCommitmentScpHandler::Lookup(
+    OrthancPluginStorageCommitmentFailureReason* target,
+    void* rawHandler,
+    const char* sopClassUid,
+    const char* sopInstanceUid)
+  {
+    assert(target != NULL &&
+           rawHandler != NULL);
+      
+    try
+    {
+      IStorageCommitmentScpHandler& handler = *reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
+      *target = handler.Lookup(sopClassUid, sopInstanceUid);
+      return OrthancPluginErrorCode_Success;
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+    }
+    catch (...)
+    {
+      return OrthancPluginErrorCode_Plugin;
+    }
+  }
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+  void IStorageCommitmentScpHandler::Destructor(void* rawHandler)
+  {
+    assert(rawHandler != NULL);
+    delete reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
+  }
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)    
+  DicomInstance::DicomInstance(const OrthancPluginDicomInstance* instance) :
+    toFree_(false),
+    instance_(instance)
+  {
+  }
+#else
+  DicomInstance::DicomInstance(OrthancPluginDicomInstance* instance) :
+    toFree_(false),
+    instance_(instance)
+  {
+  }
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+  DicomInstance::DicomInstance(const void* buffer,
+                               size_t size) :
+    toFree_(true),
+    instance_(OrthancPluginCreateDicomInstance(GetGlobalContext(), buffer, size))
+  {
+    if (instance_ == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
+    }
+  }
+#endif
+
+
+  DicomInstance::~DicomInstance()
+  {
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    if (toFree_ &&
+        instance_ != NULL)
+    {
+      OrthancPluginFreeDicomInstance(
+        GetGlobalContext(), const_cast<OrthancPluginDicomInstance*>(instance_));
+    }
+#endif
+  }
+
+  
+  std::string DicomInstance::GetRemoteAet() const
+  {
+    const char* s = OrthancPluginGetInstanceRemoteAet(GetGlobalContext(), instance_);
+    if (s == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+    }
+    else
+    {
+      return std::string(s);
+    }
+  }
+
+
+  void DicomInstance::GetJson(Json::Value& target) const
+  {
+    OrthancString s;
+    s.Assign(OrthancPluginGetInstanceJson(GetGlobalContext(), instance_));
+    s.ToJson(target);
+  }
+  
+
+  void DicomInstance::GetSimplifiedJson(Json::Value& target) const
+  {
+    OrthancString s;
+    s.Assign(OrthancPluginGetInstanceSimplifiedJson(GetGlobalContext(), instance_));
+    s.ToJson(target);
+  }
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
+  std::string DicomInstance::GetTransferSyntaxUid() const
+  {
+    OrthancString s;
+    s.Assign(OrthancPluginGetInstanceTransferSyntaxUid(GetGlobalContext(), instance_));
+
+    std::string result;
+    s.ToString(result);
+    return result;
+  }
+#endif
+
+  
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
+  bool DicomInstance::HasPixelData() const
+  {
+    int32_t result = OrthancPluginHasInstancePixelData(GetGlobalContext(), instance_);
+    if (result < 0)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+    }
+    else
+    {
+      return (result != 0);
+    }
+  }
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)  
+  void DicomInstance::GetRawFrame(std::string& target,
+                                  unsigned int frameIndex) const
+  {
+    MemoryBuffer buffer;
+    OrthancPluginErrorCode code = OrthancPluginGetInstanceRawFrame(
+      GetGlobalContext(), *buffer, instance_, frameIndex);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      buffer.ToString(target);
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+    }
+  }
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)  
+  OrthancImage* DicomInstance::GetDecodedFrame(unsigned int frameIndex) const
+  {
+    OrthancPluginImage* image = OrthancPluginGetInstanceDecodedFrame(
+      GetGlobalContext(), instance_, frameIndex);
+
+    if (image == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+    }
+    else
+    {
+      return new OrthancImage(image);
+    }
+  }
+#endif  
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+  void DicomInstance::Serialize(std::string& target) const
+  {
+    MemoryBuffer buffer;
+    OrthancPluginErrorCode code = OrthancPluginSerializeDicomInstance(
+      GetGlobalContext(), *buffer, instance_);
+
+    if (code == OrthancPluginErrorCode_Success)
+    {
+      buffer.ToString(target);
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+    }
+  }
+#endif
+  
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+  DicomInstance* DicomInstance::Transcode(const void* buffer,
+                                          size_t size,
+                                          const std::string& transferSyntax)
+  {
+    OrthancPluginDicomInstance* instance = OrthancPluginTranscodeDicomInstance(
+      GetGlobalContext(), buffer, size, transferSyntax.c_str());
+
+    if (instance == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
+    }
+    else
+    {
+      boost::movelib::unique_ptr<DicomInstance> result(new DicomInstance(instance));
+      result->toFree_ = true;
+      return result.release();
+    }
+  }
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,1240 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "OrthancPluginException.h"
+
+#include <orthanc/OrthancCPlugin.h>
+#include <boost/noncopyable.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <json/value.h>
+#include <vector>
+#include <list>
+#include <set>
+#include <map>
+
+
+
+/**
+ * The definition of ORTHANC_PLUGINS_VERSION_IS_ABOVE below is for
+ * backward compatibility with Orthanc SDK <= 1.3.0.
+ * 
+ *   $ hg diff -r Orthanc-1.3.0:Orthanc-1.3.1 ../../../Plugins/Include/orthanc/OrthancCPlugin.h
+ *
+ **/
+#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)
+#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision)        \
+  (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major ||                      \
+   (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major &&                    \
+    (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor ||                    \
+     (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor &&                  \
+      ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
+#endif
+
+
+#if !defined(ORTHANC_FRAMEWORK_VERSION_IS_ABOVE)
+#define ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(major, minor, revision)      \
+  (ORTHANC_VERSION_MAJOR > major ||                                     \
+   (ORTHANC_VERSION_MAJOR == major &&                                   \
+    (ORTHANC_VERSION_MINOR > minor ||                                   \
+     (ORTHANC_VERSION_MINOR == minor &&                                 \
+      ORTHANC_VERSION_REVISION >= revision))))
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
+// The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0
+#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  1
+#else
+#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  0
+#endif
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 2)
+#  define HAS_ORTHANC_PLUGIN_PEERS  1
+#  define HAS_ORTHANC_PLUGIN_JOB    1
+#else
+#  define HAS_ORTHANC_PLUGIN_PEERS  0
+#  define HAS_ORTHANC_PLUGIN_JOB    0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
+#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  1
+#else
+#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4)
+#  define HAS_ORTHANC_PLUGIN_METRICS  1
+#else
+#  define HAS_ORTHANC_PLUGIN_METRICS  0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 1, 0)
+#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  1
+#else
+#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
+#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  1
+#else
+#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
+#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  1
+#else
+#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  0
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 0)
+#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  1
+#else
+#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  0
+#endif
+
+
+
+namespace OrthancPlugins
+{
+  typedef void (*RestCallback) (OrthancPluginRestOutput* output,
+                                const char* url,
+                                const OrthancPluginHttpRequest* request);
+
+  void SetGlobalContext(OrthancPluginContext* context);
+
+  bool HasGlobalContext();
+
+  OrthancPluginContext* GetGlobalContext();
+
+  
+  class OrthancImage;
+
+
+  class MemoryBuffer : public boost::noncopyable
+  {
+  private:
+    OrthancPluginMemoryBuffer  buffer_;
+
+    void Check(OrthancPluginErrorCode code);
+
+    bool CheckHttp(OrthancPluginErrorCode code);
+
+  public:
+    MemoryBuffer();
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    // This constructor makes a copy of the given buffer in the memory
+    // handled by the Orthanc core
+    MemoryBuffer(const void* buffer,
+                 size_t size);
+#endif
+
+    ~MemoryBuffer()
+    {
+      Clear();
+    }
+
+    OrthancPluginMemoryBuffer* operator*()
+    {
+      return &buffer_;
+    }
+
+    // This transfers ownership from "other" to "this"
+    void Assign(OrthancPluginMemoryBuffer& other);
+
+    void Swap(MemoryBuffer& other);
+
+    OrthancPluginMemoryBuffer Release();
+
+    const char* GetData() const
+    {
+      if (buffer_.size > 0)
+      {
+        return reinterpret_cast<const char*>(buffer_.data);
+      }
+      else
+      {
+        return NULL;
+      }
+    }
+
+    size_t GetSize() const
+    {
+      return buffer_.size;
+    }
+
+    bool IsEmpty() const
+    {
+      return GetSize() == 0 || GetData() == NULL;
+    }
+
+    void Clear();
+
+    void ToString(std::string& target) const;
+
+    void ToJson(Json::Value& target) const;
+
+    bool RestApiGet(const std::string& uri,
+                    bool applyPlugins);
+
+    bool RestApiGet(const std::string& uri,
+                    const std::map<std::string, std::string>& httpHeaders,
+                    bool applyPlugins);
+
+    bool RestApiPost(const std::string& uri,
+                     const void* body,
+                     size_t bodySize,
+                     bool applyPlugins);
+
+    bool RestApiPut(const std::string& uri,
+                    const void* body,
+                    size_t bodySize,
+                    bool applyPlugins);
+
+    bool RestApiPost(const std::string& uri,
+                     const Json::Value& body,
+                     bool applyPlugins);
+
+    bool RestApiPut(const std::string& uri,
+                    const Json::Value& body,
+                    bool applyPlugins);
+
+    bool RestApiPost(const std::string& uri,
+                     const std::string& body,
+                     bool applyPlugins)
+    {
+      return RestApiPost(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
+    }
+
+    bool RestApiPut(const std::string& uri,
+                    const std::string& body,
+                    bool applyPlugins)
+    {
+      return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
+    }
+
+    void CreateDicom(const Json::Value& tags,
+                     OrthancPluginCreateDicomFlags flags);
+
+    void CreateDicom(const Json::Value& tags,
+                     const OrthancImage& pixelData,
+                     OrthancPluginCreateDicomFlags flags);
+
+    void ReadFile(const std::string& path);
+
+    void GetDicomQuery(const OrthancPluginWorklistQuery* query);
+
+    void DicomToJson(Json::Value& target,
+                     OrthancPluginDicomToJsonFormat format,
+                     OrthancPluginDicomToJsonFlags flags,
+                     uint32_t maxStringLength);
+
+    bool HttpGet(const std::string& url,
+                 const std::string& username,
+                 const std::string& password);
+
+    bool HttpPost(const std::string& url,
+                  const std::string& body,
+                  const std::string& username,
+                  const std::string& password);
+
+    bool HttpPut(const std::string& url,
+                 const std::string& body,
+                 const std::string& username,
+                 const std::string& password);
+
+    void GetDicomInstance(const std::string& instanceId);
+  };
+
+
+  class OrthancString : public boost::noncopyable
+  {
+  private:
+    char*   str_;
+
+    void Clear();
+
+  public:
+    OrthancString() :
+      str_(NULL)
+    {
+    }
+
+    ~OrthancString()
+    {
+      Clear();
+    }
+
+    // This transfers ownership, warning: The string must have been
+    // allocated by the Orthanc core
+    void Assign(char* str);
+
+    const char* GetContent() const
+    {
+      return str_;
+    }
+
+    void ToString(std::string& target) const;
+
+    void ToJson(Json::Value& target) const;
+  };
+
+
+  class OrthancConfiguration : public boost::noncopyable
+  {
+  private:
+    Json::Value  configuration_;  // Necessarily a Json::objectValue
+    std::string  path_;
+
+    std::string GetPath(const std::string& key) const;
+
+    void LoadConfiguration();
+    
+  public:
+    OrthancConfiguration();
+
+    explicit OrthancConfiguration(bool load);
+
+    const Json::Value& GetJson() const
+    {
+      return configuration_;
+    }
+
+    bool IsSection(const std::string& key) const;
+
+    void GetSection(OrthancConfiguration& target,
+                    const std::string& key) const;
+
+    bool LookupStringValue(std::string& target,
+                           const std::string& key) const;
+    
+    bool LookupIntegerValue(int& target,
+                            const std::string& key) const;
+
+    bool LookupUnsignedIntegerValue(unsigned int& target,
+                                    const std::string& key) const;
+
+    bool LookupBooleanValue(bool& target,
+                            const std::string& key) const;
+
+    bool LookupFloatValue(float& target,
+                          const std::string& key) const;
+
+    bool LookupListOfStrings(std::list<std::string>& target,
+                             const std::string& key,
+                             bool allowSingleString) const;
+
+    bool LookupSetOfStrings(std::set<std::string>& target,
+                            const std::string& key,
+                            bool allowSingleString) const;
+
+    std::string GetStringValue(const std::string& key,
+                               const std::string& defaultValue) const;
+
+    int GetIntegerValue(const std::string& key,
+                        int defaultValue) const;
+
+    unsigned int GetUnsignedIntegerValue(const std::string& key,
+                                         unsigned int defaultValue) const;
+
+    bool GetBooleanValue(const std::string& key,
+                         bool defaultValue) const;
+
+    float GetFloatValue(const std::string& key,
+                        float defaultValue) const;
+
+    void GetDictionary(std::map<std::string, std::string>& target,
+                       const std::string& key) const;
+  };
+
+  class OrthancImage : public boost::noncopyable
+  {
+  private:
+    OrthancPluginImage*    image_;
+
+    void Clear();
+
+    void CheckImageAvailable() const;
+
+  public:
+    OrthancImage();
+
+    explicit OrthancImage(OrthancPluginImage* image);
+
+    OrthancImage(OrthancPluginPixelFormat  format,
+                 uint32_t                  width,
+                 uint32_t                  height);
+
+    OrthancImage(OrthancPluginPixelFormat  format,
+                 uint32_t                  width,
+                 uint32_t                  height,
+                 uint32_t                  pitch,
+                 void*                     buffer);
+
+    ~OrthancImage()
+    {
+      Clear();
+    }
+
+    void UncompressPngImage(const void* data,
+                            size_t size);
+
+    void UncompressJpegImage(const void* data,
+                             size_t size);
+
+    void DecodeDicomImage(const void* data,
+                          size_t size,
+                          unsigned int frame);
+
+    OrthancPluginPixelFormat GetPixelFormat() const;
+
+    unsigned int GetWidth() const;
+
+    unsigned int GetHeight() const;
+
+    unsigned int GetPitch() const;
+    
+    void* GetBuffer() const;
+
+    const OrthancPluginImage* GetObject() const
+    {
+      return image_;
+    }
+
+    void CompressPngImage(MemoryBuffer& target) const;
+
+    void CompressJpegImage(MemoryBuffer& target,
+                           uint8_t quality) const;
+
+    void AnswerPngImage(OrthancPluginRestOutput* output) const;
+
+    void AnswerJpegImage(OrthancPluginRestOutput* output,
+                         uint8_t quality) const;
+    
+    void* GetWriteableBuffer();
+
+    OrthancPluginImage* Release();
+  };
+
+
+#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+  class FindMatcher : public boost::noncopyable
+  {
+  private:
+    OrthancPluginFindMatcher*          matcher_;
+    const OrthancPluginWorklistQuery*  worklist_;
+
+    void SetupDicom(const void*            query,
+                    uint32_t               size);
+
+  public:
+    explicit FindMatcher(const OrthancPluginWorklistQuery*  worklist);
+
+    FindMatcher(const void*  query,
+                uint32_t     size)
+    {
+      SetupDicom(query, size);
+    }
+
+    explicit FindMatcher(const MemoryBuffer&  dicom)
+    {
+      SetupDicom(dicom.GetData(), dicom.GetSize());
+    }
+
+    ~FindMatcher();
+
+    bool IsMatch(const void*  dicom,
+                 uint32_t     size) const;
+
+    bool IsMatch(const MemoryBuffer& dicom) const
+    {
+      return IsMatch(dicom.GetData(), dicom.GetSize());
+    }
+  };
+#endif
+
+
+  bool RestApiGet(Json::Value& result,
+                  const std::string& uri,
+                  bool applyPlugins);
+
+  bool RestApiGetString(std::string& result,
+                        const std::string& uri,
+                        bool applyPlugins);
+
+  bool RestApiGetString(std::string& result,
+                        const std::string& uri,
+                        const std::map<std::string, std::string>& httpHeaders,
+                        bool applyPlugins);
+
+  bool RestApiPost(std::string& result,
+                   const std::string& uri,
+                   const void* body,
+                   size_t bodySize,
+                   bool applyPlugins);
+
+  bool RestApiPost(Json::Value& result,
+                   const std::string& uri,
+                   const void* body,
+                   size_t bodySize,
+                   bool applyPlugins);
+
+  bool RestApiPost(Json::Value& result,
+                   const std::string& uri,
+                   const Json::Value& body,
+                   bool applyPlugins);
+
+  inline bool RestApiPost(Json::Value& result,
+                          const std::string& uri,
+                          const std::string& body,
+                          bool applyPlugins)
+  {
+    return RestApiPost(result, uri, body.empty() ? NULL : body.c_str(),
+                       body.size(), applyPlugins);
+  }
+
+  inline bool RestApiPost(Json::Value& result,
+                          const std::string& uri,
+                          const MemoryBuffer& body,
+                          bool applyPlugins)
+  {
+    return RestApiPost(result, uri, body.GetData(),
+                       body.GetSize(), applyPlugins);
+  }
+
+  bool RestApiPut(Json::Value& result,
+                  const std::string& uri,
+                  const void* body,
+                  size_t bodySize,
+                  bool applyPlugins);
+
+  bool RestApiPut(Json::Value& result,
+                  const std::string& uri,
+                  const Json::Value& body,
+                  bool applyPlugins);
+
+  inline bool RestApiPut(Json::Value& result,
+                         const std::string& uri,
+                         const std::string& body,
+                         bool applyPlugins)
+  {
+    return RestApiPut(result, uri, body.empty() ? NULL : body.c_str(),
+                      body.size(), applyPlugins);
+  }
+
+  bool RestApiDelete(const std::string& uri,
+                     bool applyPlugins);
+
+  bool HttpDelete(const std::string& url,
+                  const std::string& username,
+                  const std::string& password);
+
+  void AnswerJson(const Json::Value& value,
+                  OrthancPluginRestOutput* output);
+
+  void AnswerString(const std::string& answer,
+                    const char* mimeType,
+                    OrthancPluginRestOutput* output);
+
+  void AnswerHttpError(uint16_t httpError,
+                       OrthancPluginRestOutput* output);
+
+  void AnswerMethodNotAllowed(OrthancPluginRestOutput* output, const char* allowedMethods);
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
+  const char* AutodetectMimeType(const std::string& path);
+#endif
+
+  void LogError(const std::string& message);
+
+  void LogWarning(const std::string& message);
+
+  void LogInfo(const std::string& message);
+
+  void ReportMinimalOrthancVersion(unsigned int major,
+                                   unsigned int minor,
+                                   unsigned int revision);
+  
+  bool CheckMinimalOrthancVersion(unsigned int major,
+                                  unsigned int minor,
+                                  unsigned int revision);
+
+
+  namespace Internals
+  {
+    template <RestCallback Callback>
+    static OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
+                                          const char* url,
+                                          const OrthancPluginHttpRequest* request)
+    {
+      try
+      {
+        Callback(output, url, request);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+      {
+#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
+        if (HasGlobalContext() &&
+            e.HasDetails())
+        {
+          // The "false" instructs Orthanc not to log the detailed
+          // error message. This is to avoid duplicating the details,
+          // because "OrthancException" already does it on construction.
+          OrthancPluginSetHttpErrorDetails
+            (GetGlobalContext(), output, e.GetDetails(), false);
+        }
+#endif
+
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+  }
+
+  
+  template <RestCallback Callback>
+  void RegisterRestCallback(const std::string& uri,
+                            bool isThreadSafe)
+  {
+    if (isThreadSafe)
+    {
+      OrthancPluginRegisterRestCallbackNoLock
+        (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
+    }
+    else
+    {
+      OrthancPluginRegisterRestCallback
+        (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
+    }
+  }
+
+
+#if HAS_ORTHANC_PLUGIN_PEERS == 1
+  class OrthancPeers : public boost::noncopyable
+  {
+  private:
+    typedef std::map<std::string, uint32_t>   Index;
+
+    OrthancPluginPeers   *peers_;
+    Index                 index_;
+    uint32_t              timeout_;
+
+    size_t GetPeerIndex(const std::string& name) const;
+
+  public:
+    OrthancPeers();
+
+    ~OrthancPeers();
+
+    uint32_t GetTimeout() const
+    {
+      return timeout_;
+    }
+
+    void SetTimeout(uint32_t timeout)
+    {
+      timeout_ = timeout;
+    }
+
+    bool LookupName(size_t& target,
+                    const std::string& name) const;
+
+    std::string GetPeerName(size_t index) const;
+
+    std::string GetPeerUrl(size_t index) const;
+
+    std::string GetPeerUrl(const std::string& name) const;
+
+    size_t GetPeersCount() const
+    {
+      return index_.size();
+    }
+
+    bool LookupUserProperty(std::string& value,
+                            size_t index,
+                            const std::string& key) const;
+
+    bool LookupUserProperty(std::string& value,
+                            const std::string& peer,
+                            const std::string& key) const;
+
+    bool DoGet(MemoryBuffer& target,
+               size_t index,
+               const std::string& uri) const;
+
+    bool DoGet(MemoryBuffer& target,
+               const std::string& name,
+               const std::string& uri) const;
+
+    bool DoGet(Json::Value& target,
+               size_t index,
+               const std::string& uri) const;
+
+    bool DoGet(Json::Value& target,
+               const std::string& name,
+               const std::string& uri) const;
+
+    bool DoPost(MemoryBuffer& target,
+                size_t index,
+                const std::string& uri,
+                const std::string& body) const;
+
+    bool DoPost(MemoryBuffer& target,
+                const std::string& name,
+                const std::string& uri,
+                const std::string& body) const;
+
+    bool DoPost(Json::Value& target,
+                size_t index,
+                const std::string& uri,
+                const std::string& body) const;
+
+    bool DoPost(Json::Value& target,
+                const std::string& name,
+                const std::string& uri,
+                const std::string& body) const;
+
+    bool DoPut(size_t index,
+               const std::string& uri,
+               const std::string& body) const;
+
+    bool DoPut(const std::string& name,
+               const std::string& uri,
+               const std::string& body) const;
+
+    bool DoDelete(size_t index,
+                  const std::string& uri) const;
+
+    bool DoDelete(const std::string& name,
+                  const std::string& uri) const;
+  };
+#endif
+
+
+
+#if HAS_ORTHANC_PLUGIN_JOB == 1
+  class OrthancJob : public boost::noncopyable
+  {
+  private:
+    std::string   jobType_;
+    std::string   content_;
+    bool          hasSerialized_;
+    std::string   serialized_;
+    float         progress_;
+
+    static void CallbackFinalize(void* job);
+
+    static float CallbackGetProgress(void* job);
+
+    static const char* CallbackGetContent(void* job);
+
+    static const char* CallbackGetSerialized(void* job);
+
+    static OrthancPluginJobStepStatus CallbackStep(void* job);
+
+    static OrthancPluginErrorCode CallbackStop(void* job,
+                                               OrthancPluginJobStopReason reason);
+
+    static OrthancPluginErrorCode CallbackReset(void* job);
+
+  protected:
+    void ClearContent();
+
+    void UpdateContent(const Json::Value& content);
+
+    void ClearSerialized();
+
+    void UpdateSerialized(const Json::Value& serialized);
+
+    void UpdateProgress(float progress);
+    
+  public:
+    OrthancJob(const std::string& jobType);
+    
+    virtual ~OrthancJob()
+    {
+    }
+
+    virtual OrthancPluginJobStepStatus Step() = 0;
+
+    virtual void Stop(OrthancPluginJobStopReason reason) = 0;
+    
+    virtual void Reset() = 0;
+
+    static OrthancPluginJob* Create(OrthancJob* job /* takes ownership */);
+
+    static std::string Submit(OrthancJob* job /* takes ownership */,
+                              int priority);
+
+    static void SubmitAndWait(Json::Value& result,
+                              OrthancJob* job /* takes ownership */,
+                              int priority);
+
+    // Submit a job from a POST on the REST API with the same
+    // conventions as in the Orthanc core (according to the
+    // "Synchronous" and "Priority" options)
+    static void SubmitFromRestApiPost(OrthancPluginRestOutput* output,
+                                      const Json::Value& body,
+                                      OrthancJob* job);
+  };
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_METRICS == 1
+  inline void SetMetricsValue(char* name,
+                              float value)
+  {
+    OrthancPluginSetMetricsValue(GetGlobalContext(), name,
+                                 value, OrthancPluginMetricsType_Default);
+  }
+
+  class MetricsTimer : public boost::noncopyable
+  {
+  private:
+    std::string               name_;
+    boost::posix_time::ptime  start_;
+
+  public:
+    explicit MetricsTimer(const char* name);
+
+    ~MetricsTimer();
+  };
+#endif
+
+
+#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
+  class HttpClient : public boost::noncopyable
+  {
+  public:
+    typedef std::map<std::string, std::string>  HttpHeaders;
+
+    class IRequestBody : public boost::noncopyable
+    {
+    public:
+      virtual ~IRequestBody()
+      {
+      }
+
+      virtual bool ReadNextChunk(std::string& chunk) = 0;
+    };
+
+
+    class IAnswer : public boost::noncopyable
+    {
+    public:
+      virtual ~IAnswer()
+      {
+      }
+
+      virtual void AddHeader(const std::string& key,
+                             const std::string& value) = 0;
+
+      virtual void AddChunk(const void* data,
+                            size_t size) = 0;
+    };
+
+
+  private:
+    class RequestBodyWrapper;
+
+    uint16_t                 httpStatus_;
+    OrthancPluginHttpMethod  method_;
+    std::string              url_;
+    HttpHeaders              headers_;
+    std::string              username_;
+    std::string              password_;
+    uint32_t                 timeout_;
+    std::string              certificateFile_;
+    std::string              certificateKeyFile_;
+    std::string              certificateKeyPassword_;
+    bool                     pkcs11_;
+    std::string              fullBody_;
+    IRequestBody*            chunkedBody_;
+    bool                     allowChunkedTransfers_;
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+    void ExecuteWithStream(uint16_t& httpStatus,  // out
+                           IAnswer& answer,       // out
+                           IRequestBody& body) const;
+#endif
+
+    void ExecuteWithoutStream(uint16_t& httpStatus,        // out
+                              HttpHeaders& answerHeaders,  // out
+                              std::string& answerBody,     // out
+                              const std::string& body) const;
+    
+  public:
+    HttpClient();
+
+    uint16_t GetHttpStatus() const
+    {
+      return httpStatus_;
+    }
+
+    void SetMethod(OrthancPluginHttpMethod method)
+    {
+      method_ = method;
+    }
+
+    const std::string& GetUrl() const
+    {
+      return url_;
+    }
+
+    void SetUrl(const std::string& url)
+    {
+      url_ = url;
+    }
+
+    void SetHeaders(const HttpHeaders& headers)
+    {
+      headers_ = headers;
+    }
+
+    void AddHeader(const std::string& key,
+                   const std::string& value)
+    {
+      headers_[key] = value;
+    }
+
+    void AddHeaders(const HttpHeaders& headers);
+
+    void SetCredentials(const std::string& username,
+                        const std::string& password);
+
+    void ClearCredentials();
+
+    void SetTimeout(unsigned int timeout)  // 0 for default timeout
+    {
+      timeout_ = timeout;
+    }
+
+    void SetCertificate(const std::string& certificateFile,
+                        const std::string& keyFile,
+                        const std::string& keyPassword);
+
+    void ClearCertificate();
+
+    void SetPkcs11(bool pkcs11)
+    {
+      pkcs11_ = pkcs11;
+    }
+
+    void ClearBody();
+
+    void SwapBody(std::string& body);
+
+    void SetBody(const std::string& body);
+
+    void SetBody(IRequestBody& body);
+
+    // This function can be used to disable chunked transfers if the
+    // remote server is Orthanc with a version <= 1.5.6.
+    void SetChunkedTransfersAllowed(bool allow)
+    {
+      allowChunkedTransfers_ = allow;
+    }
+
+    bool IsChunkedTransfersAllowed() const
+    {
+      return allowChunkedTransfers_;
+    }
+
+    void Execute(IAnswer& answer);
+
+    void Execute(HttpHeaders& answerHeaders /* out */,
+                 std::string& answerBody /* out */);
+
+    void Execute(HttpHeaders& answerHeaders /* out */,
+                 Json::Value& answerBody /* out */);
+
+    void Execute();
+  };
+#endif
+
+
+
+  class IChunkedRequestReader : public boost::noncopyable
+  {
+  public:
+    virtual ~IChunkedRequestReader()
+    {
+    }
+
+    virtual void AddChunk(const void* data,
+                          size_t size) = 0;
+
+    virtual void Execute(OrthancPluginRestOutput* output) = 0;
+  };
+
+
+  typedef IChunkedRequestReader* (*ChunkedRestCallback) (const char* url,
+                                                         const OrthancPluginHttpRequest* request);
+
+
+  namespace Internals
+  {
+    void NullRestCallback(OrthancPluginRestOutput* output,
+                          const char* url,
+                          const OrthancPluginHttpRequest* request);
+  
+    IChunkedRequestReader *NullChunkedRestCallback(const char* url,
+                                                   const OrthancPluginHttpRequest* request);
+
+
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+    template <ChunkedRestCallback Callback>
+    static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader,
+                                                const char* url,
+                                                const OrthancPluginHttpRequest* request)
+    {
+      try
+      {
+        if (reader == NULL)
+        {
+          return OrthancPluginErrorCode_InternalError;
+        }
+        else
+        {
+          *reader = reinterpret_cast<OrthancPluginServerChunkedRequestReader*>(Callback(url, request));
+          if (*reader == NULL)
+          {
+            return OrthancPluginErrorCode_Plugin;
+          }
+          else
+          {
+            return OrthancPluginErrorCode_Success;
+          }
+        }
+      }
+      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+      {
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+
+    OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
+      OrthancPluginServerChunkedRequestReader* reader,
+      const void*                              data,
+      uint32_t                                 size);
+
+    OrthancPluginErrorCode ChunkedRequestReaderExecute(
+      OrthancPluginServerChunkedRequestReader* reader,
+      OrthancPluginRestOutput*                 output);
+
+    void ChunkedRequestReaderFinalize(
+      OrthancPluginServerChunkedRequestReader* reader);
+
+#else  
+
+    OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
+                                                    const char* url,
+                                                    const OrthancPluginHttpRequest* request,
+                                                    RestCallback GetHandler,
+                                                    ChunkedRestCallback PostHandler,
+                                                    RestCallback DeleteHandler,
+                                                    ChunkedRestCallback PutHandler);
+
+    template<
+      RestCallback         GetHandler,
+      ChunkedRestCallback  PostHandler,
+      RestCallback         DeleteHandler,
+      ChunkedRestCallback  PutHandler
+      >
+    inline OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
+                                                           const char* url,
+                                                           const OrthancPluginHttpRequest* request)
+    {
+      return ChunkedRestCompatibility(output, url, request, GetHandler,
+                                      PostHandler, DeleteHandler, PutHandler);
+    }
+#endif
+  }
+
+
+
+  // NB: We use a templated class instead of a templated function, because
+  // default values are only available in functions since C++11
+  template<
+    RestCallback         GetHandler    = Internals::NullRestCallback,
+    ChunkedRestCallback  PostHandler   = Internals::NullChunkedRestCallback,
+    RestCallback         DeleteHandler = Internals::NullRestCallback,
+    ChunkedRestCallback  PutHandler    = Internals::NullChunkedRestCallback
+    >
+  class ChunkedRestRegistration : public boost::noncopyable
+  {
+  public:
+    static void Apply(const std::string& uri)
+    {
+#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+      OrthancPluginRegisterChunkedRestCallback(
+        GetGlobalContext(), uri.c_str(),
+        GetHandler == Internals::NullRestCallback         ? NULL : Internals::Protect<GetHandler>,
+        PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PostHandler>,
+        DeleteHandler == Internals::NullRestCallback      ? NULL : Internals::Protect<DeleteHandler>,
+        PutHandler == Internals::NullChunkedRestCallback  ? NULL : Internals::ChunkedProtect<PutHandler>,
+        Internals::ChunkedRequestReaderAddChunk,
+        Internals::ChunkedRequestReaderExecute,
+        Internals::ChunkedRequestReaderFinalize);
+#else
+      OrthancPluginRegisterRestCallbackNoLock(
+        GetGlobalContext(), uri.c_str(), 
+        Internals::ChunkedRestCompatibility<GetHandler, PostHandler, DeleteHandler, PutHandler>);
+#endif
+    }
+  };
+
+  
+
+#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+  class IStorageCommitmentScpHandler : public boost::noncopyable
+  {
+  public:
+    virtual ~IStorageCommitmentScpHandler()
+    {
+    }
+    
+    virtual OrthancPluginStorageCommitmentFailureReason Lookup(const std::string& sopClassUid,
+                                                               const std::string& sopInstanceUid) = 0;
+    
+    static OrthancPluginErrorCode Lookup(OrthancPluginStorageCommitmentFailureReason* target,
+                                         void* rawHandler,
+                                         const char* sopClassUid,
+                                         const char* sopInstanceUid);
+
+    static void Destructor(void* rawHandler);
+  };
+#endif
+
+
+  class DicomInstance : public boost::noncopyable
+  {
+  private:
+    bool toFree_;
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)    
+    const OrthancPluginDicomInstance*  instance_;
+#else
+    OrthancPluginDicomInstance*  instance_;
+#endif
+    
+  public:
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)    
+    DicomInstance(const OrthancPluginDicomInstance* instance);
+#else
+    DicomInstance(OrthancPluginDicomInstance* instance);
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    DicomInstance(const void* buffer,
+                  size_t size);
+#endif
+
+    ~DicomInstance();
+
+    std::string GetRemoteAet() const;
+
+    const void* GetBuffer() const
+    {
+      return OrthancPluginGetInstanceData(GetGlobalContext(), instance_);
+    }
+
+    size_t GetSize() const
+    {
+      return static_cast<size_t>(OrthancPluginGetInstanceSize(GetGlobalContext(), instance_));
+    }
+
+    void GetJson(Json::Value& target) const;
+
+    void GetSimplifiedJson(Json::Value& target) const;
+
+    OrthancPluginInstanceOrigin GetOrigin() const
+    {
+      return OrthancPluginGetInstanceOrigin(GetGlobalContext(), instance_);
+    }
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
+    std::string GetTransferSyntaxUid() const;
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
+    bool HasPixelData() const;
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    unsigned int GetFramesCount() const
+    {
+      return OrthancPluginGetInstanceFramesCount(GetGlobalContext(), instance_);
+    }
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    void GetRawFrame(std::string& target,
+                     unsigned int frameIndex) const;
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    OrthancImage* GetDecodedFrame(unsigned int frameIndex) const;
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    void Serialize(std::string& target) const;
+#endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
+    static DicomInstance* Transcode(const void* buffer,
+                                    size_t size,
+                                    const std::string& transferSyntax);
+#endif
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/OrthancPluginException.h	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,101 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if !defined(HAS_ORTHANC_EXCEPTION)
+#  error The macro HAS_ORTHANC_EXCEPTION must be defined
+#endif
+
+
+#if HAS_ORTHANC_EXCEPTION == 1
+#  include <OrthancException.h>
+#  define ORTHANC_PLUGINS_ERROR_ENUMERATION     ::Orthanc::ErrorCode
+#  define ORTHANC_PLUGINS_EXCEPTION_CLASS       ::Orthanc::OrthancException
+#  define ORTHANC_PLUGINS_GET_ERROR_CODE(code)  ::Orthanc::ErrorCode_ ## code
+#else
+#  include <orthanc/OrthancCPlugin.h>
+#  define ORTHANC_PLUGINS_ERROR_ENUMERATION     ::OrthancPluginErrorCode
+#  define ORTHANC_PLUGINS_EXCEPTION_CLASS       ::OrthancPlugins::PluginException
+#  define ORTHANC_PLUGINS_GET_ERROR_CODE(code)  ::OrthancPluginErrorCode_ ## code
+#endif
+
+
+#define ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code)                   \
+  throw ORTHANC_PLUGINS_EXCEPTION_CLASS(static_cast<ORTHANC_PLUGINS_ERROR_ENUMERATION>(code));
+
+
+#define ORTHANC_PLUGINS_THROW_EXCEPTION(code)                           \
+  throw ORTHANC_PLUGINS_EXCEPTION_CLASS(ORTHANC_PLUGINS_GET_ERROR_CODE(code));
+                                                  
+
+#define ORTHANC_PLUGINS_CHECK_ERROR(code)                           \
+  if (code != ORTHANC_PLUGINS_GET_ERROR_CODE(Success))              \
+  {                                                                 \
+    ORTHANC_PLUGINS_THROW_EXCEPTION(code);                          \
+  }
+
+
+namespace OrthancPlugins
+{
+#if HAS_ORTHANC_EXCEPTION == 0
+  class PluginException
+  {
+  private:
+    OrthancPluginErrorCode  code_;
+
+  public:
+    explicit PluginException(OrthancPluginErrorCode code) : code_(code)
+    {
+    }
+
+    OrthancPluginErrorCode GetErrorCode() const
+    {
+      return code_;
+    }
+
+    const char* What(OrthancPluginContext* context) const
+    {
+      const char* description = OrthancPluginGetErrorDescription(context, code_);
+      if (description)
+      {
+        return description;
+      }
+      else
+      {
+        return "No description available";
+      }
+    }
+  };
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,12 @@
+# In Orthanc <= 1.7.1, the instructions below were part of
+# "Compiler.cmake", and were protected by the (now unused) option
+# "ENABLE_PLUGINS_VERSION_SCRIPT" in CMake
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/VersionScriptPlugins.map")
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${CMAKE_CURRENT_LIST_DIR}/ExportedSymbolsPlugins.list")
+endif()
--- a/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3180 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "OrthancPluginCppWrapper.h"
-
-#include <boost/algorithm/string/predicate.hpp>
-#include <boost/move/unique_ptr.hpp>
-#include <boost/thread.hpp>
-#include <json/reader.h>
-#include <json/writer.h>
-
-
-namespace OrthancPlugins
-{
-  static OrthancPluginContext* globalContext_ = NULL;
-
-
-  void SetGlobalContext(OrthancPluginContext* context)
-  {
-    if (context == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
-    }
-    else if (globalContext_ == NULL)
-    {
-      globalContext_ = context;
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
-    }
-  }
-
-
-  bool HasGlobalContext()
-  {
-    return globalContext_ != NULL;
-  }
-
-
-  OrthancPluginContext* GetGlobalContext()
-  {
-    if (globalContext_ == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
-    }
-    else
-    {
-      return globalContext_;
-    }
-  }
-
-
-  void MemoryBuffer::Check(OrthancPluginErrorCode code)
-  {
-    if (code != OrthancPluginErrorCode_Success)
-    {
-      // Prevent using garbage information
-      buffer_.data = NULL;
-      buffer_.size = 0;
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
-    }
-  }
-
-
-  bool MemoryBuffer::CheckHttp(OrthancPluginErrorCode code)
-  {
-    if (code != OrthancPluginErrorCode_Success)
-    {
-      // Prevent using garbage information
-      buffer_.data = NULL;
-      buffer_.size = 0;
-    }
-
-    if (code == OrthancPluginErrorCode_Success)
-    {
-      return true;
-    }
-    else if (code == OrthancPluginErrorCode_UnknownResource ||
-             code == OrthancPluginErrorCode_InexistentItem)
-    {
-      return false;
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
-    }
-  }
-
-
-  MemoryBuffer::MemoryBuffer()
-  {
-    buffer_.data = NULL;
-    buffer_.size = 0;
-  }
-
-
-  void MemoryBuffer::Clear()
-  {
-    if (buffer_.data != NULL)
-    {
-      OrthancPluginFreeMemoryBuffer(GetGlobalContext(), &buffer_);
-      buffer_.data = NULL;
-      buffer_.size = 0;
-    }
-  }
-
-
-  void MemoryBuffer::Assign(OrthancPluginMemoryBuffer& other)
-  {
-    Clear();
-
-    buffer_.data = other.data;
-    buffer_.size = other.size;
-
-    other.data = NULL;
-    other.size = 0;
-  }
-
-
-  void MemoryBuffer::Swap(MemoryBuffer& other)
-  {
-    std::swap(buffer_.data, other.buffer_.data);
-    std::swap(buffer_.size, other.buffer_.size);
-  }
-
-
-  OrthancPluginMemoryBuffer MemoryBuffer::Release()
-  {
-    OrthancPluginMemoryBuffer result = buffer_;
-
-    buffer_.data = NULL;
-    buffer_.size = 0;
-
-    return result;
-  }
-
-
-  void MemoryBuffer::ToString(std::string& target) const
-  {
-    if (buffer_.size == 0)
-    {
-      target.clear();
-    }
-    else
-    {
-      target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
-    }
-  }
-
-
-  void MemoryBuffer::ToJson(Json::Value& target) const
-  {
-    if (buffer_.data == NULL ||
-        buffer_.size == 0)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-
-    const char* tmp = reinterpret_cast<const char*>(buffer_.data);
-
-    Json::Reader reader;
-    if (!reader.parse(tmp, tmp + buffer_.size, target))
-    {
-      LogError("Cannot convert some memory buffer to JSON");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-  }
-
-
-  bool MemoryBuffer::RestApiGet(const std::string& uri,
-                                bool applyPlugins)
-  {
-    Clear();
-
-    if (applyPlugins)
-    {
-      return CheckHttp(OrthancPluginRestApiGetAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str()));
-    }
-    else
-    {
-      return CheckHttp(OrthancPluginRestApiGet(GetGlobalContext(), &buffer_, uri.c_str()));
-    }
-  }
-
-  bool MemoryBuffer::RestApiGet(const std::string& uri,
-                                const std::map<std::string, std::string>& httpHeaders,
-                                bool applyPlugins)
-  {
-    Clear();
-
-    std::vector<const char*> headersKeys;
-    std::vector<const char*> headersValues;
-    
-    for (std::map<std::string, std::string>::const_iterator
-           it = httpHeaders.begin(); it != httpHeaders.end(); it++)
-    {
-      headersKeys.push_back(it->first.c_str());
-      headersValues.push_back(it->second.c_str());
-    }
-
-    return CheckHttp(OrthancPluginRestApiGet2(
-                       GetGlobalContext(), &buffer_, uri.c_str(), httpHeaders.size(),
-                       (headersKeys.empty() ? NULL : &headersKeys[0]),
-                       (headersValues.empty() ? NULL : &headersValues[0]), applyPlugins));
-  }
-
-  bool MemoryBuffer::RestApiPost(const std::string& uri,
-                                 const void* body,
-                                 size_t bodySize,
-                                 bool applyPlugins)
-  {
-    Clear();
-    
-    // Cast for compatibility with Orthanc SDK <= 1.5.6
-    const char* b = reinterpret_cast<const char*>(body);
-
-    if (applyPlugins)
-    {
-      return CheckHttp(OrthancPluginRestApiPostAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
-    }
-    else
-    {
-      return CheckHttp(OrthancPluginRestApiPost(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
-    }
-  }
-
-
-  bool MemoryBuffer::RestApiPut(const std::string& uri,
-                                const void* body,
-                                size_t bodySize,
-                                bool applyPlugins)
-  {
-    Clear();
-
-    // Cast for compatibility with Orthanc SDK <= 1.5.6
-    const char* b = reinterpret_cast<const char*>(body);
-
-    if (applyPlugins)
-    {
-      return CheckHttp(OrthancPluginRestApiPutAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
-    }
-    else
-    {
-      return CheckHttp(OrthancPluginRestApiPut(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
-    }
-  }
-
-
-  bool MemoryBuffer::RestApiPost(const std::string& uri,
-                                 const Json::Value& body,
-                                 bool applyPlugins)
-  {
-    Json::FastWriter writer;
-    return RestApiPost(uri, writer.write(body), applyPlugins);
-  }
-
-
-  bool MemoryBuffer::RestApiPut(const std::string& uri,
-                                const Json::Value& body,
-                                bool applyPlugins)
-  {
-    Json::FastWriter writer;
-    return RestApiPut(uri, writer.write(body), applyPlugins);
-  }
-
-
-  void MemoryBuffer::CreateDicom(const Json::Value& tags,
-                                 OrthancPluginCreateDicomFlags flags)
-  {
-    Clear();
-
-    Json::FastWriter writer;
-    std::string s = writer.write(tags);
-
-    Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), NULL, flags));
-  }
-
-  void MemoryBuffer::CreateDicom(const Json::Value& tags,
-                                 const OrthancImage& pixelData,
-                                 OrthancPluginCreateDicomFlags flags)
-  {
-    Clear();
-
-    Json::FastWriter writer;
-    std::string s = writer.write(tags);
-
-    Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), pixelData.GetObject(), flags));
-  }
-
-
-  void MemoryBuffer::ReadFile(const std::string& path)
-  {
-    Clear();
-    Check(OrthancPluginReadFile(GetGlobalContext(), &buffer_, path.c_str()));
-  }
-
-
-  void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
-  {
-    Clear();
-    Check(OrthancPluginWorklistGetDicomQuery(GetGlobalContext(), &buffer_, query));
-  }
-
-
-  void OrthancString::Assign(char* str)
-  {
-    Clear();
-
-    if (str != NULL)
-    {
-      str_ = str;
-    }
-  }
-
-
-  void OrthancString::Clear()
-  {
-    if (str_ != NULL)
-    {
-      OrthancPluginFreeString(GetGlobalContext(), str_);
-      str_ = NULL;
-    }
-  }
-
-
-  void OrthancString::ToString(std::string& target) const
-  {
-    if (str_ == NULL)
-    {
-      target.clear();
-    }
-    else
-    {
-      target.assign(str_);
-    }
-  }
-
-
-  void OrthancString::ToJson(Json::Value& target) const
-  {
-    if (str_ == NULL)
-    {
-      LogError("Cannot convert an empty memory buffer to JSON");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-
-    Json::Reader reader;
-    if (!reader.parse(str_, target))
-    {
-      LogError("Cannot convert some memory buffer to JSON");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-  }
-
-
-  void MemoryBuffer::DicomToJson(Json::Value& target,
-                                 OrthancPluginDicomToJsonFormat format,
-                                 OrthancPluginDicomToJsonFlags flags,
-                                 uint32_t maxStringLength)
-  {
-    OrthancString str;
-    str.Assign(OrthancPluginDicomBufferToJson
-               (GetGlobalContext(), GetData(), GetSize(), format, flags, maxStringLength));
-    str.ToJson(target);
-  }
-
-
-  bool MemoryBuffer::HttpGet(const std::string& url,
-                             const std::string& username,
-                             const std::string& password)
-  {
-    Clear();
-    return CheckHttp(OrthancPluginHttpGet(GetGlobalContext(), &buffer_, url.c_str(),
-                                          username.empty() ? NULL : username.c_str(),
-                                          password.empty() ? NULL : password.c_str()));
-  }
-
-
-  bool MemoryBuffer::HttpPost(const std::string& url,
-                              const std::string& body,
-                              const std::string& username,
-                              const std::string& password)
-  {
-    Clear();
-    return CheckHttp(OrthancPluginHttpPost(GetGlobalContext(), &buffer_, url.c_str(),
-                                           body.c_str(), body.size(),
-                                           username.empty() ? NULL : username.c_str(),
-                                           password.empty() ? NULL : password.c_str()));
-  }
-
-
-  bool MemoryBuffer::HttpPut(const std::string& url,
-                             const std::string& body,
-                             const std::string& username,
-                             const std::string& password)
-  {
-    Clear();
-    return CheckHttp(OrthancPluginHttpPut(GetGlobalContext(), &buffer_, url.c_str(),
-                                          body.empty() ? NULL : body.c_str(),
-                                          body.size(),
-                                          username.empty() ? NULL : username.c_str(),
-                                          password.empty() ? NULL : password.c_str()));
-  }
-
-
-  void MemoryBuffer::GetDicomInstance(const std::string& instanceId)
-  {
-    Clear();
-    Check(OrthancPluginGetDicomForInstance(GetGlobalContext(), &buffer_, instanceId.c_str()));
-  }
-
-
-  bool HttpDelete(const std::string& url,
-                  const std::string& username,
-                  const std::string& password)
-  {
-    OrthancPluginErrorCode error = OrthancPluginHttpDelete
-      (GetGlobalContext(), url.c_str(),
-       username.empty() ? NULL : username.c_str(),
-       password.empty() ? NULL : password.c_str());
-
-    if (error == OrthancPluginErrorCode_Success)
-    {
-      return true;
-    }
-    else if (error == OrthancPluginErrorCode_UnknownResource ||
-             error == OrthancPluginErrorCode_InexistentItem)
-    {
-      return false;
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
-    }
-  }
-
-
-  void LogError(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogError(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
-  void LogWarning(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
-  void LogInfo(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
-  void OrthancConfiguration::LoadConfiguration()
-  {
-    OrthancString str;
-    str.Assign(OrthancPluginGetConfiguration(GetGlobalContext()));
-
-    if (str.GetContent() == NULL)
-    {
-      LogError("Cannot access the Orthanc configuration");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-
-    str.ToJson(configuration_);
-
-    if (configuration_.type() != Json::objectValue)
-    {
-      LogError("Unable to read the Orthanc configuration");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-  }
-    
-
-  OrthancConfiguration::OrthancConfiguration()
-  {
-    LoadConfiguration();
-  }
-
-
-  OrthancConfiguration::OrthancConfiguration(bool loadConfiguration)
-  {
-    if (loadConfiguration)
-    {
-      LoadConfiguration();
-    }
-    else
-    {
-      configuration_ = Json::objectValue;
-    }
-  }
-
-
-  std::string OrthancConfiguration::GetPath(const std::string& key) const
-  {
-    if (path_.empty())
-    {
-      return key;
-    }
-    else
-    {
-      return path_ + "." + key;
-    }
-  }
-
-
-  bool OrthancConfiguration::IsSection(const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    return (configuration_.isMember(key) &&
-            configuration_[key].type() == Json::objectValue);
-  }
-
-
-  void OrthancConfiguration::GetSection(OrthancConfiguration& target,
-                                        const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    target.path_ = GetPath(key);
-
-    if (!configuration_.isMember(key))
-    {
-      target.configuration_ = Json::objectValue;
-    }
-    else
-    {
-      if (configuration_[key].type() != Json::objectValue)
-      {
-        LogError("The configuration section \"" + target.path_ +
-                 "\" is not an associative array as expected");
-
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-      }
-
-      target.configuration_ = configuration_[key];
-    }
-  }
-
-
-  bool OrthancConfiguration::LookupStringValue(std::string& target,
-                                               const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    if (!configuration_.isMember(key))
-    {
-      return false;
-    }
-
-    if (configuration_[key].type() != Json::stringValue)
-    {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a string as expected");
-
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-
-    target = configuration_[key].asString();
-    return true;
-  }
-
-
-  bool OrthancConfiguration::LookupIntegerValue(int& target,
-                                                const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    if (!configuration_.isMember(key))
-    {
-      return false;
-    }
-
-    switch (configuration_[key].type())
-    {
-      case Json::intValue:
-        target = configuration_[key].asInt();
-        return true;
-
-      case Json::uintValue:
-        target = configuration_[key].asUInt();
-        return true;
-
-      default:
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not an integer as expected");
-
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-  }
-
-
-  bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
-                                                        const std::string& key) const
-  {
-    int tmp;
-    if (!LookupIntegerValue(tmp, key))
-    {
-      return false;
-    }
-
-    if (tmp < 0)
-    {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a positive integer as expected");
-
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-    else
-    {
-      target = static_cast<unsigned int>(tmp);
-      return true;
-    }
-  }
-
-
-  bool OrthancConfiguration::LookupBooleanValue(bool& target,
-                                                const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    if (!configuration_.isMember(key))
-    {
-      return false;
-    }
-
-    if (configuration_[key].type() != Json::booleanValue)
-    {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a Boolean as expected");
-
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-
-    target = configuration_[key].asBool();
-    return true;
-  }
-
-
-  bool OrthancConfiguration::LookupFloatValue(float& target,
-                                              const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    if (!configuration_.isMember(key))
-    {
-      return false;
-    }
-
-    switch (configuration_[key].type())
-    {
-      case Json::realValue:
-        target = configuration_[key].asFloat();
-        return true;
-
-      case Json::intValue:
-        target = static_cast<float>(configuration_[key].asInt());
-        return true;
-
-      case Json::uintValue:
-        target = static_cast<float>(configuration_[key].asUInt());
-        return true;
-
-      default:
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not an integer as expected");
-
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-  }
-
-
-  bool OrthancConfiguration::LookupListOfStrings(std::list<std::string>& target,
-                                                 const std::string& key,
-                                                 bool allowSingleString) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    target.clear();
-
-    if (!configuration_.isMember(key))
-    {
-      return false;
-    }
-
-    switch (configuration_[key].type())
-    {
-      case Json::arrayValue:
-      {
-        bool ok = true;
-
-        for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++)
-        {
-          if (configuration_[key][i].type() == Json::stringValue)
-          {
-            target.push_back(configuration_[key][i].asString());
-          }
-          else
-          {
-            ok = false;
-          }
-        }
-
-        if (ok)
-        {
-          return true;
-        }
-
-        break;
-      }
-
-      case Json::stringValue:
-        if (allowSingleString)
-        {
-          target.push_back(configuration_[key].asString());
-          return true;
-        }
-
-        break;
-
-      default:
-        break;
-    }
-
-    LogError("The configuration option \"" + GetPath(key) +
-             "\" is not a list of strings as expected");
-
-    ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-  }
-
-
-  bool OrthancConfiguration::LookupSetOfStrings(std::set<std::string>& target,
-                                                const std::string& key,
-                                                bool allowSingleString) const
-  {
-    std::list<std::string> lst;
-
-    if (LookupListOfStrings(lst, key, allowSingleString))
-    {
-      target.clear();
-
-      for (std::list<std::string>::const_iterator
-             it = lst.begin(); it != lst.end(); ++it)
-      {
-        target.insert(*it);
-      }
-
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  std::string OrthancConfiguration::GetStringValue(const std::string& key,
-                                                   const std::string& defaultValue) const
-  {
-    std::string tmp;
-    if (LookupStringValue(tmp, key))
-    {
-      return tmp;
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  int OrthancConfiguration::GetIntegerValue(const std::string& key,
-                                            int defaultValue) const
-  {
-    int tmp;
-    if (LookupIntegerValue(tmp, key))
-    {
-      return tmp;
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
-                                                             unsigned int defaultValue) const
-  {
-    unsigned int tmp;
-    if (LookupUnsignedIntegerValue(tmp, key))
-    {
-      return tmp;
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  bool OrthancConfiguration::GetBooleanValue(const std::string& key,
-                                             bool defaultValue) const
-  {
-    bool tmp;
-    if (LookupBooleanValue(tmp, key))
-    {
-      return tmp;
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  float OrthancConfiguration::GetFloatValue(const std::string& key,
-                                            float defaultValue) const
-  {
-    float tmp;
-    if (LookupFloatValue(tmp, key))
-    {
-      return tmp;
-    }
-    else
-    {
-      return defaultValue;
-    }
-  }
-
-
-  void OrthancConfiguration::GetDictionary(std::map<std::string, std::string>& target,
-                                           const std::string& key) const
-  {
-    assert(configuration_.type() == Json::objectValue);
-
-    target.clear();
-
-    if (!configuration_.isMember(key))
-    {
-      return;
-    }
-
-    if (configuration_[key].type() != Json::objectValue)
-    {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a string as expected");
-
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-
-    Json::Value::Members members = configuration_[key].getMemberNames();
-
-    for (size_t i = 0; i < members.size(); i++)
-    {
-      const Json::Value& value = configuration_[key][members[i]];
-
-      if (value.type() == Json::stringValue)
-      {
-        target[members[i]] = value.asString();
-      }
-      else
-      {
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not a dictionary mapping strings to strings");
-
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-      }
-    }
-  }
-
-
-  void OrthancImage::Clear()
-  {
-    if (image_ != NULL)
-    {
-      OrthancPluginFreeImage(GetGlobalContext(), image_);
-      image_ = NULL;
-    }
-  }
-
-
-  void OrthancImage::CheckImageAvailable() const
-  {
-    if (image_ == NULL)
-    {
-      LogError("Trying to access a NULL image");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
-    }
-  }
-
-
-  OrthancImage::OrthancImage() :
-    image_(NULL)
-  {
-  }
-
-
-  OrthancImage::OrthancImage(OrthancPluginImage*  image) :
-    image_(image)
-  {
-  }
-
-
-  OrthancImage::OrthancImage(OrthancPluginPixelFormat  format,
-                             uint32_t                  width,
-                             uint32_t                  height)
-  {
-    image_ = OrthancPluginCreateImage(GetGlobalContext(), format, width, height);
-
-    if (image_ == NULL)
-    {
-      LogError("Cannot create an image");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-  }
-
-
-  OrthancImage::OrthancImage(OrthancPluginPixelFormat  format,
-                             uint32_t                  width,
-                             uint32_t                  height,
-                             uint32_t                  pitch,
-                             void*                     buffer)
-  {
-    image_ = OrthancPluginCreateImageAccessor
-      (GetGlobalContext(), format, width, height, pitch, buffer);
-
-    if (image_ == NULL)
-    {
-      LogError("Cannot create an image accessor");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-  }
-
-  void OrthancImage::UncompressPngImage(const void* data,
-                                        size_t size)
-  {
-    Clear();
-
-    image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Png);
-
-    if (image_ == NULL)
-    {
-      LogError("Cannot uncompress a PNG image");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
-    }
-  }
-
-
-  void OrthancImage::UncompressJpegImage(const void* data,
-                                         size_t size)
-  {
-    Clear();
-    image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Jpeg);
-    if (image_ == NULL)
-    {
-      LogError("Cannot uncompress a JPEG image");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
-    }
-  }
-
-
-  void OrthancImage::DecodeDicomImage(const void* data,
-                                      size_t size,
-                                      unsigned int frame)
-  {
-    Clear();
-    image_ = OrthancPluginDecodeDicomImage(GetGlobalContext(), data, size, frame);
-    if (image_ == NULL)
-    {
-      LogError("Cannot uncompress a DICOM image");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
-    }
-  }
-
-
-  OrthancPluginPixelFormat OrthancImage::GetPixelFormat() const
-  {
-    CheckImageAvailable();
-    return OrthancPluginGetImagePixelFormat(GetGlobalContext(), image_);
-  }
-
-
-  unsigned int OrthancImage::GetWidth() const
-  {
-    CheckImageAvailable();
-    return OrthancPluginGetImageWidth(GetGlobalContext(), image_);
-  }
-
-
-  unsigned int OrthancImage::GetHeight() const
-  {
-    CheckImageAvailable();
-    return OrthancPluginGetImageHeight(GetGlobalContext(), image_);
-  }
-
-
-  unsigned int OrthancImage::GetPitch() const
-  {
-    CheckImageAvailable();
-    return OrthancPluginGetImagePitch(GetGlobalContext(), image_);
-  }
-
-
-  const void* OrthancImage::GetBuffer() const
-  {
-    CheckImageAvailable();
-    return OrthancPluginGetImageBuffer(GetGlobalContext(), image_);
-  }
-
-
-  void OrthancImage::CompressPngImage(MemoryBuffer& target) const
-  {
-    CheckImageAvailable();
-
-    OrthancPlugins::MemoryBuffer answer;
-    OrthancPluginCompressPngImage(GetGlobalContext(), *answer, GetPixelFormat(),
-                                  GetWidth(), GetHeight(), GetPitch(), GetBuffer());
-
-    target.Swap(answer);
-  }
-
-
-  void OrthancImage::CompressJpegImage(MemoryBuffer& target,
-                                       uint8_t quality) const
-  {
-    CheckImageAvailable();
-
-    OrthancPlugins::MemoryBuffer answer;
-    OrthancPluginCompressJpegImage(GetGlobalContext(), *answer, GetPixelFormat(),
-                                   GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
-
-    target.Swap(answer);
-  }
-
-
-  void OrthancImage::AnswerPngImage(OrthancPluginRestOutput* output) const
-  {
-    CheckImageAvailable();
-    OrthancPluginCompressAndAnswerPngImage(GetGlobalContext(), output, GetPixelFormat(),
-                                           GetWidth(), GetHeight(), GetPitch(), GetBuffer());
-  }
-
-
-  void OrthancImage::AnswerJpegImage(OrthancPluginRestOutput* output,
-                                     uint8_t quality) const
-  {
-    CheckImageAvailable();
-    OrthancPluginCompressAndAnswerJpegImage(GetGlobalContext(), output, GetPixelFormat(),
-                                            GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
-  }
-
-
-
-#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
-  FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
-    matcher_(NULL),
-    worklist_(worklist)
-  {
-    if (worklist_ == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
-    }
-  }
-
-
-  void FindMatcher::SetupDicom(const void*  query,
-                               uint32_t     size)
-  {
-    worklist_ = NULL;
-
-    matcher_ = OrthancPluginCreateFindMatcher(GetGlobalContext(), query, size);
-    if (matcher_ == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-  }
-
-
-  FindMatcher::~FindMatcher()
-  {
-    // The "worklist_" field
-
-    if (matcher_ != NULL)
-    {
-      OrthancPluginFreeFindMatcher(GetGlobalContext(), matcher_);
-    }
-  }
-
-
-
-  bool FindMatcher::IsMatch(const void*  dicom,
-                            uint32_t     size) const
-  {
-    int32_t result;
-
-    if (matcher_ != NULL)
-    {
-      result = OrthancPluginFindMatcherIsMatch(GetGlobalContext(), matcher_, dicom, size);
-    }
-    else if (worklist_ != NULL)
-    {
-      result = OrthancPluginWorklistIsMatch(GetGlobalContext(), worklist_, dicom, size);
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-
-    if (result == 0)
-    {
-      return false;
-    }
-    else if (result == 1)
-    {
-      return true;
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-  }
-
-#endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
-
-  void AnswerJson(const Json::Value& value,
-                  OrthancPluginRestOutput* output
-    )
-  {
-    Json::StyledWriter writer;
-    std::string bodyString = writer.write(value);
-
-    OrthancPluginAnswerBuffer(GetGlobalContext(), output, bodyString.c_str(), bodyString.size(), "application/json");
-  }
-
-  void AnswerString(const std::string& answer,
-                    const char* mimeType,
-                    OrthancPluginRestOutput* output
-    )
-  {
-    OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType);
-  }
-
-  void AnswerHttpError(uint16_t httpError, OrthancPluginRestOutput *output)
-  {
-    OrthancPluginSendHttpStatusCode(GetGlobalContext(), output, httpError);
-  }
-
-  void AnswerMethodNotAllowed(OrthancPluginRestOutput *output, const char* allowedMethods)
-  {
-    OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowedMethods);
-  }
-
-  bool RestApiGetString(std::string& result,
-                        const std::string& uri,
-                        bool applyPlugins)
-  {
-    MemoryBuffer answer;
-    if (!answer.RestApiGet(uri, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      answer.ToString(result);
-      return true;
-    }
-  }
-
-  bool RestApiGetString(std::string& result,
-                        const std::string& uri,
-                        const std::map<std::string, std::string>& httpHeaders,
-                        bool applyPlugins)
-  {
-    MemoryBuffer answer;
-    if (!answer.RestApiGet(uri, httpHeaders, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      answer.ToString(result);
-      return true;
-    }
-  }
-
-
-
-  bool RestApiGet(Json::Value& result,
-                  const std::string& uri,
-                  bool applyPlugins)
-  {
-    MemoryBuffer answer;
-
-    if (!answer.RestApiGet(uri, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      if (!answer.IsEmpty())
-      {
-        answer.ToJson(result);
-      }
-      return true;
-    }
-  }
-
-
-  bool RestApiPost(std::string& result,
-                   const std::string& uri,
-                   const void* body,
-                   size_t bodySize,
-                   bool applyPlugins)
-  {
-    MemoryBuffer answer;
-
-    if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      if (!answer.IsEmpty())
-      {
-        result.assign(answer.GetData(), answer.GetSize());
-      }
-      return true;
-    }
-  }
-
-
-  bool RestApiPost(Json::Value& result,
-                   const std::string& uri,
-                   const void* body,
-                   size_t bodySize,
-                   bool applyPlugins)
-  {
-    MemoryBuffer answer;
-
-    if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      if (!answer.IsEmpty())
-      {
-        answer.ToJson(result);
-      }
-      return true;
-    }
-  }
-
-
-  bool RestApiPost(Json::Value& result,
-                   const std::string& uri,
-                   const Json::Value& body,
-                   bool applyPlugins)
-  {
-    Json::FastWriter writer;
-    return RestApiPost(result, uri, writer.write(body), applyPlugins);
-  }
-
-
-  bool RestApiPut(Json::Value& result,
-                  const std::string& uri,
-                  const void* body,
-                  size_t bodySize,
-                  bool applyPlugins)
-  {
-    MemoryBuffer answer;
-
-    if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
-    {
-      return false;
-    }
-    else
-    {
-      if (!answer.IsEmpty()) // i.e, on a PUT to metadata/..., orthanc returns an empty response
-      {
-        answer.ToJson(result);
-      }
-      return true;
-    }
-  }
-
-
-  bool RestApiPut(Json::Value& result,
-                  const std::string& uri,
-                  const Json::Value& body,
-                  bool applyPlugins)
-  {
-    Json::FastWriter writer;
-    return RestApiPut(result, uri, writer.write(body), applyPlugins);
-  }
-
-
-  bool RestApiDelete(const std::string& uri,
-                     bool applyPlugins)
-  {
-    OrthancPluginErrorCode error;
-
-    if (applyPlugins)
-    {
-      error = OrthancPluginRestApiDeleteAfterPlugins(GetGlobalContext(), uri.c_str());
-    }
-    else
-    {
-      error = OrthancPluginRestApiDelete(GetGlobalContext(), uri.c_str());
-    }
-
-    if (error == OrthancPluginErrorCode_Success)
-    {
-      return true;
-    }
-    else if (error == OrthancPluginErrorCode_UnknownResource ||
-             error == OrthancPluginErrorCode_InexistentItem)
-    {
-      return false;
-    }
-    else
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
-    }
-  }
-
-
-  void ReportMinimalOrthancVersion(unsigned int major,
-                                   unsigned int minor,
-                                   unsigned int revision)
-  {
-    LogError("Your version of the Orthanc core (" +
-             std::string(GetGlobalContext()->orthancVersion) +
-             ") is too old to run this plugin (version " +
-             boost::lexical_cast<std::string>(major) + "." +
-             boost::lexical_cast<std::string>(minor) + "." +
-             boost::lexical_cast<std::string>(revision) +
-             " is required)");
-  }
-
-
-  bool CheckMinimalOrthancVersion(unsigned int major,
-                                  unsigned int minor,
-                                  unsigned int revision)
-  {
-    if (!HasGlobalContext())
-    {
-      LogError("Bad Orthanc context in the plugin");
-      return false;
-    }
-
-    if (!strcmp(GetGlobalContext()->orthancVersion, "mainline"))
-    {
-      // Assume compatibility with the mainline
-      return true;
-    }
-
-    // Parse the version of the Orthanc core
-    int aa, bb, cc;
-    if (
-#ifdef _MSC_VER
-      sscanf_s
-#else
-      sscanf
-#endif
-      (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 ||
-      aa < 0 ||
-      bb < 0 ||
-      cc < 0)
-    {
-      return false;
-    }
-
-    unsigned int a = static_cast<unsigned int>(aa);
-    unsigned int b = static_cast<unsigned int>(bb);
-    unsigned int c = static_cast<unsigned int>(cc);
-
-    // Check the major version number
-
-    if (a > major)
-    {
-      return true;
-    }
-
-    if (a < major)
-    {
-      return false;
-    }
-
-
-    // Check the minor version number
-    assert(a == major);
-
-    if (b > minor)
-    {
-      return true;
-    }
-
-    if (b < minor)
-    {
-      return false;
-    }
-
-    // Check the patch level version number
-    assert(a == major && b == minor);
-
-    if (c >= revision)
-    {
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
-  const char* AutodetectMimeType(const std::string& path)
-  {
-    const char* mime = OrthancPluginAutodetectMimeType(GetGlobalContext(), path.c_str());
-
-    if (mime == NULL)
-    {
-      // Should never happen, just for safety
-      return "application/octet-stream";
-    }
-    else
-    {
-      return mime;
-    }
-  }
-#endif
-
-
-#if HAS_ORTHANC_PLUGIN_PEERS == 1
-  size_t OrthancPeers::GetPeerIndex(const std::string& name) const
-  {
-    size_t index;
-    if (LookupName(index, name))
-    {
-      return index;
-    }
-    else
-    {
-      LogError("Inexistent peer: " + name);
-      ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
-    }
-  }
-
-
-  OrthancPeers::OrthancPeers() :
-    peers_(NULL),
-    timeout_(0)
-  {
-    peers_ = OrthancPluginGetPeers(GetGlobalContext());
-
-    if (peers_ == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-    }
-
-    uint32_t count = OrthancPluginGetPeersCount(GetGlobalContext(), peers_);
-
-    for (uint32_t i = 0; i < count; i++)
-    {
-      const char* name = OrthancPluginGetPeerName(GetGlobalContext(), peers_, i);
-      if (name == NULL)
-      {
-        OrthancPluginFreePeers(GetGlobalContext(), peers_);
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-      }
-
-      index_[name] = i;
-    }
-  }
-
-
-  OrthancPeers::~OrthancPeers()
-  {
-    if (peers_ != NULL)
-    {
-      OrthancPluginFreePeers(GetGlobalContext(), peers_);
-    }
-  }
-
-
-  bool OrthancPeers::LookupName(size_t& target,
-                                const std::string& name) const
-  {
-    Index::const_iterator found = index_.find(name);
-
-    if (found == index_.end())
-    {
-      return false;
-    }
-    else
-    {
-      target = found->second;
-      return true;
-    }
-  }
-
-
-  std::string OrthancPeers::GetPeerName(size_t index) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      const char* s = OrthancPluginGetPeerName(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
-      if (s == NULL)
-      {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-      }
-      else
-      {
-        return s;
-      }
-    }
-  }
-
-
-  std::string OrthancPeers::GetPeerUrl(size_t index) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      const char* s = OrthancPluginGetPeerUrl(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
-      if (s == NULL)
-      {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-      }
-      else
-      {
-        return s;
-      }
-    }
-  }
-
-
-  std::string OrthancPeers::GetPeerUrl(const std::string& name) const
-  {
-    return GetPeerUrl(GetPeerIndex(name));
-  }
-
-
-  bool OrthancPeers::LookupUserProperty(std::string& value,
-                                        size_t index,
-                                        const std::string& key) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      const char* s = OrthancPluginGetPeerUserProperty(GetGlobalContext(), peers_, static_cast<uint32_t>(index), key.c_str());
-      if (s == NULL)
-      {
-        return false;
-      }
-      else
-      {
-        value.assign(s);
-        return true;
-      }
-    }
-  }
-
-
-  bool OrthancPeers::LookupUserProperty(std::string& value,
-                                        const std::string& peer,
-                                        const std::string& key) const
-  {
-    return LookupUserProperty(value, GetPeerIndex(peer), key);
-  }
-
-
-  bool OrthancPeers::DoGet(MemoryBuffer& target,
-                           size_t index,
-                           const std::string& uri) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-
-    OrthancPlugins::MemoryBuffer answer;
-    uint16_t status;
-    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
-      (GetGlobalContext(), *answer, NULL, &status, peers_,
-       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Get, uri.c_str(),
-       0, NULL, NULL, NULL, 0, timeout_);
-
-    if (code == OrthancPluginErrorCode_Success)
-    {
-      target.Swap(answer);
-      return (status == 200);
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoGet(MemoryBuffer& target,
-                           const std::string& name,
-                           const std::string& uri) const
-  {
-    size_t index;
-    return (LookupName(index, name) &&
-            DoGet(target, index, uri));
-  }
-
-
-  bool OrthancPeers::DoGet(Json::Value& target,
-                           size_t index,
-                           const std::string& uri) const
-  {
-    MemoryBuffer buffer;
-
-    if (DoGet(buffer, index, uri))
-    {
-      buffer.ToJson(target);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoGet(Json::Value& target,
-                           const std::string& name,
-                           const std::string& uri) const
-  {
-    MemoryBuffer buffer;
-
-    if (DoGet(buffer, name, uri))
-    {
-      buffer.ToJson(target);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoPost(MemoryBuffer& target,
-                            const std::string& name,
-                            const std::string& uri,
-                            const std::string& body) const
-  {
-    size_t index;
-    return (LookupName(index, name) &&
-            DoPost(target, index, uri, body));
-  }
-
-
-  bool OrthancPeers::DoPost(Json::Value& target,
-                            size_t index,
-                            const std::string& uri,
-                            const std::string& body) const
-  {
-    MemoryBuffer buffer;
-
-    if (DoPost(buffer, index, uri, body))
-    {
-      buffer.ToJson(target);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoPost(Json::Value& target,
-                            const std::string& name,
-                            const std::string& uri,
-                            const std::string& body) const
-  {
-    MemoryBuffer buffer;
-
-    if (DoPost(buffer, name, uri, body))
-    {
-      buffer.ToJson(target);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoPost(MemoryBuffer& target,
-                            size_t index,
-                            const std::string& uri,
-                            const std::string& body) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-
-    OrthancPlugins::MemoryBuffer answer;
-    uint16_t status;
-    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
-      (GetGlobalContext(), *answer, NULL, &status, peers_,
-       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Post, uri.c_str(),
-       0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
-
-    if (code == OrthancPluginErrorCode_Success)
-    {
-      target.Swap(answer);
-      return (status == 200);
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoPut(size_t index,
-                           const std::string& uri,
-                           const std::string& body) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-
-    OrthancPlugins::MemoryBuffer answer;
-    uint16_t status;
-    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
-      (GetGlobalContext(), *answer, NULL, &status, peers_,
-       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Put, uri.c_str(),
-       0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
-
-    if (code == OrthancPluginErrorCode_Success)
-    {
-      return (status == 200);
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoPut(const std::string& name,
-                           const std::string& uri,
-                           const std::string& body) const
-  {
-    size_t index;
-    return (LookupName(index, name) &&
-            DoPut(index, uri, body));
-  }
-
-
-  bool OrthancPeers::DoDelete(size_t index,
-                              const std::string& uri) const
-  {
-    if (index >= index_.size())
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-
-    OrthancPlugins::MemoryBuffer answer;
-    uint16_t status;
-    OrthancPluginErrorCode code = OrthancPluginCallPeerApi
-      (GetGlobalContext(), *answer, NULL, &status, peers_,
-       static_cast<uint32_t>(index), OrthancPluginHttpMethod_Delete, uri.c_str(),
-       0, NULL, NULL, NULL, 0, timeout_);
-
-    if (code == OrthancPluginErrorCode_Success)
-    {
-      return (status == 200);
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool OrthancPeers::DoDelete(const std::string& name,
-                              const std::string& uri) const
-  {
-    size_t index;
-    return (LookupName(index, name) &&
-            DoDelete(index, uri));
-  }
-#endif
-
-
-
-
-
-  /******************************************************************
-   ** JOBS
-   ******************************************************************/
-
-#if HAS_ORTHANC_PLUGIN_JOB == 1
-  void OrthancJob::CallbackFinalize(void* job)
-  {
-    if (job != NULL)
-    {
-      delete reinterpret_cast<OrthancJob*>(job);
-    }
-  }
-
-
-  float OrthancJob::CallbackGetProgress(void* job)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      return reinterpret_cast<OrthancJob*>(job)->progress_;
-    }
-    catch (...)
-    {
-      return 0;
-    }
-  }
-
-
-  const char* OrthancJob::CallbackGetContent(void* job)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      return reinterpret_cast<OrthancJob*>(job)->content_.c_str();
-    }
-    catch (...)
-    {
-      return 0;
-    }
-  }
-
-
-  const char* OrthancJob::CallbackGetSerialized(void* job)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      const OrthancJob& tmp = *reinterpret_cast<OrthancJob*>(job);
-
-      if (tmp.hasSerialized_)
-      {
-        return tmp.serialized_.c_str();
-      }
-      else
-      {
-        return NULL;
-      }
-    }
-    catch (...)
-    {
-      return 0;
-    }
-  }
-
-
-  OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      return reinterpret_cast<OrthancJob*>(job)->Step();
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
-    {
-      return OrthancPluginJobStepStatus_Failure;
-    }
-    catch (...)
-    {
-      return OrthancPluginJobStepStatus_Failure;
-    }
-  }
-
-
-  OrthancPluginErrorCode OrthancJob::CallbackStop(void* job,
-                                                  OrthancPluginJobStopReason reason)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      reinterpret_cast<OrthancJob*>(job)->Stop(reason);
-      return OrthancPluginErrorCode_Success;
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-    }
-    catch (...)
-    {
-      return OrthancPluginErrorCode_Plugin;
-    }
-  }
-
-
-  OrthancPluginErrorCode OrthancJob::CallbackReset(void* job)
-  {
-    assert(job != NULL);
-
-    try
-    {
-      reinterpret_cast<OrthancJob*>(job)->Reset();
-      return OrthancPluginErrorCode_Success;
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-    }
-    catch (...)
-    {
-      return OrthancPluginErrorCode_Plugin;
-    }
-  }
-
-
-  void OrthancJob::ClearContent()
-  {
-    Json::Value empty = Json::objectValue;
-    UpdateContent(empty);
-  }
-
-
-  void OrthancJob::UpdateContent(const Json::Value& content)
-  {
-    if (content.type() != Json::objectValue)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
-    }
-    else
-    {
-      Json::FastWriter writer;
-      content_ = writer.write(content);
-    }
-  }
-
-
-  void OrthancJob::ClearSerialized()
-  {
-    hasSerialized_ = false;
-    serialized_.clear();
-  }
-
-
-  void OrthancJob::UpdateSerialized(const Json::Value& serialized)
-  {
-    if (serialized.type() != Json::objectValue)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
-    }
-    else
-    {
-      Json::FastWriter writer;
-      serialized_ = writer.write(serialized);
-      hasSerialized_ = true;
-    }
-  }
-
-
-  void OrthancJob::UpdateProgress(float progress)
-  {
-    if (progress < 0 ||
-        progress > 1)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
-    }
-
-    progress_ = progress;
-  }
-
-
-  OrthancJob::OrthancJob(const std::string& jobType) :
-    jobType_(jobType),
-    progress_(0)
-  {
-    ClearContent();
-    ClearSerialized();
-  }
-
-
-  OrthancPluginJob* OrthancJob::Create(OrthancJob* job)
-  {
-    if (job == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
-    }
-
-    OrthancPluginJob* orthanc = OrthancPluginCreateJob(
-      GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(),
-      CallbackGetProgress, CallbackGetContent, CallbackGetSerialized,
-      CallbackStep, CallbackStop, CallbackReset);
-
-    if (orthanc == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-    }
-    else
-    {
-      return orthanc;
-    }
-  }
-
-
-  std::string OrthancJob::Submit(OrthancJob* job,
-                                 int priority)
-  {
-    if (job == NULL)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
-    }
-
-    OrthancPluginJob* orthanc = Create(job);
-
-    char* id = OrthancPluginSubmitJob(GetGlobalContext(), orthanc, priority);
-
-    if (id == NULL)
-    {
-      LogError("Plugin cannot submit job");
-      OrthancPluginFreeJob(GetGlobalContext(), orthanc);
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
-    }
-    else
-    {
-      std::string tmp(id);
-      tmp.assign(id);
-      OrthancPluginFreeString(GetGlobalContext(), id);
-
-      return tmp;
-    }
-  }
-
-
-  void OrthancJob::SubmitAndWait(Json::Value& result,
-                                 OrthancJob* job /* takes ownership */,
-                                 int priority)
-  {
-    std::string id = Submit(job, priority);
-
-    for (;;)
-    {
-      boost::this_thread::sleep(boost::posix_time::milliseconds(100));
-
-      Json::Value status;
-      if (!RestApiGet(status, "/jobs/" + id, false) ||
-          !status.isMember("State") ||
-          status["State"].type() != Json::stringValue)
-      {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);        
-      }
-
-      const std::string state = status["State"].asString();
-      if (state == "Success")
-      {
-        if (status.isMember("Content"))
-        {
-          result = status["Content"];
-        }
-        else
-        {
-          result = Json::objectValue;
-        }
-
-        return;
-      }
-      else if (state == "Running")
-      {
-        continue;
-      }
-      else if (!status.isMember("ErrorCode") ||
-               status["ErrorCode"].type() != Json::intValue)
-      {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
-      }
-      else
-      {
-        if (!status.isMember("ErrorDescription") ||
-            status["ErrorDescription"].type() != Json::stringValue)
-        {
-          ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
-        }
-        else
-        {
-#if HAS_ORTHANC_EXCEPTION == 1
-          throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
-                                          status["ErrorDescription"].asString());
-#else
-          LogError("Exception while executing the job: " + status["ErrorDescription"].asString());
-          ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());          
-#endif
-        }
-      }
-    }
-  }
-
-
-  void OrthancJob::SubmitFromRestApiPost(OrthancPluginRestOutput* output,
-                                         const Json::Value& body,
-                                         OrthancJob* job)
-  {
-    static const char* KEY_SYNCHRONOUS = "Synchronous";
-    static const char* KEY_ASYNCHRONOUS = "Asynchronous";
-    static const char* KEY_PRIORITY = "Priority";
-
-    boost::movelib::unique_ptr<OrthancJob> protection(job);
-  
-    if (body.type() != Json::objectValue)
-    {
-#if HAS_ORTHANC_EXCEPTION == 1
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                      "Expected a JSON object in the body");
-#else
-      LogError("Expected a JSON object in the body");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-#endif
-    }
-
-    bool synchronous = true;
-  
-    if (body.isMember(KEY_SYNCHRONOUS))
-    {
-      if (body[KEY_SYNCHRONOUS].type() != Json::booleanValue)
-      {
-#if HAS_ORTHANC_EXCEPTION == 1
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                        "Option \"" + std::string(KEY_SYNCHRONOUS) +
-                                        "\" must be Boolean");
-#else
-        LogError("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-#endif
-      }
-      else
-      {
-        synchronous = body[KEY_SYNCHRONOUS].asBool();
-      }
-    }
-
-    if (body.isMember(KEY_ASYNCHRONOUS))
-    {
-      if (body[KEY_ASYNCHRONOUS].type() != Json::booleanValue)
-      {
-#if HAS_ORTHANC_EXCEPTION == 1
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                        "Option \"" + std::string(KEY_ASYNCHRONOUS) +
-                                        "\" must be Boolean");
-#else
-        LogError("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-#endif
-      }
-      else
-      {
-        synchronous = !body[KEY_ASYNCHRONOUS].asBool();
-      }
-    }
-
-    int priority = 0;
-
-    if (body.isMember(KEY_PRIORITY))
-    {
-      if (body[KEY_PRIORITY].type() != Json::booleanValue)
-      {
-#if HAS_ORTHANC_EXCEPTION == 1
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                        "Option \"" + std::string(KEY_PRIORITY) +
-                                        "\" must be an integer");
-#else
-        LogError("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
-        ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-#endif
-      }
-      else
-      {
-        priority = !body[KEY_PRIORITY].asInt();
-      }
-    }
-  
-    Json::Value result;
-
-    if (synchronous)
-    {
-      OrthancPlugins::OrthancJob::SubmitAndWait(result, protection.release(), priority);
-    }
-    else
-    {
-      std::string id = OrthancPlugins::OrthancJob::Submit(protection.release(), priority);
-
-      result = Json::objectValue;
-      result["ID"] = id;
-      result["Path"] = "/jobs/" + id;
-    }
-
-    std::string s = result.toStyledString();
-    OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(),
-                              s.size(), "application/json");
-  }
-
-#endif
-
-
-
-
-  /******************************************************************
-   ** METRICS
-   ******************************************************************/
-
-#if HAS_ORTHANC_PLUGIN_METRICS == 1
-  MetricsTimer::MetricsTimer(const char* name) :
-    name_(name)
-  {
-    start_ = boost::posix_time::microsec_clock::universal_time();
-  }
-  
-  MetricsTimer::~MetricsTimer()
-  {
-    const boost::posix_time::ptime stop = boost::posix_time::microsec_clock::universal_time();
-    const boost::posix_time::time_duration diff = stop - start_;
-    OrthancPluginSetMetricsValue(GetGlobalContext(), name_.c_str(), static_cast<float>(diff.total_milliseconds()),
-                                 OrthancPluginMetricsType_Timer);
-  }
-#endif
-
-
-
-
-  /******************************************************************
-   ** HTTP CLIENT
-   ******************************************************************/
-
-#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
-  class HttpClient::RequestBodyWrapper : public boost::noncopyable
-  {
-  private:
-    static RequestBodyWrapper& GetObject(void* body)
-    {
-      assert(body != NULL);
-      return *reinterpret_cast<RequestBodyWrapper*>(body);
-    }
-
-    IRequestBody&  body_;
-    bool           done_;
-    std::string    chunk_;
-
-  public:
-    RequestBodyWrapper(IRequestBody& body) :
-      body_(body),
-      done_(false)
-    {
-    }      
-
-    static uint8_t IsDone(void* body)
-    {
-      return GetObject(body).done_;
-    }
-    
-    static const void* GetChunkData(void* body)
-    {
-      return GetObject(body).chunk_.c_str();
-    }
-    
-    static uint32_t GetChunkSize(void* body)
-    {
-      return static_cast<uint32_t>(GetObject(body).chunk_.size());
-    }
-
-    static OrthancPluginErrorCode Next(void* body)
-    {
-      RequestBodyWrapper& that = GetObject(body);
-        
-      if (that.done_)
-      {
-        return OrthancPluginErrorCode_BadSequenceOfCalls;
-      }
-      else
-      {
-        try
-        {
-          that.done_ = !that.body_.ReadNextChunk(that.chunk_);
-          return OrthancPluginErrorCode_Success;
-        }
-        catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-        {
-          return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-        }
-        catch (...)
-        {
-          return OrthancPluginErrorCode_InternalError;
-        }
-      }
-    }    
-  };
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-  static OrthancPluginErrorCode AnswerAddHeaderCallback(void* answer,
-                                                        const char* key,
-                                                        const char* value)
-  {
-    assert(answer != NULL && key != NULL && value != NULL);
-
-    try
-    {
-      reinterpret_cast<HttpClient::IAnswer*>(answer)->AddHeader(key, value);
-      return OrthancPluginErrorCode_Success;
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-    }
-    catch (...)
-    {
-      return OrthancPluginErrorCode_Plugin;
-    }
-  }
-#endif
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-  static OrthancPluginErrorCode AnswerAddChunkCallback(void* answer,
-                                                       const void* data,
-                                                       uint32_t size)
-  {
-    assert(answer != NULL);
-
-    try
-    {
-      reinterpret_cast<HttpClient::IAnswer*>(answer)->AddChunk(data, size);
-      return OrthancPluginErrorCode_Success;
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-    }
-    catch (...)
-    {
-      return OrthancPluginErrorCode_Plugin;
-    }
-  }
-#endif
-
-
-  HttpClient::HttpClient() :
-    httpStatus_(0),
-    method_(OrthancPluginHttpMethod_Get),
-    timeout_(0),
-    pkcs11_(false),
-    chunkedBody_(NULL),
-    allowChunkedTransfers_(true)
-  {
-  }
-
-
-  void HttpClient::AddHeaders(const HttpHeaders& headers)
-  {
-    for (HttpHeaders::const_iterator it = headers.begin();
-         it != headers.end(); ++it)
-    {
-      headers_[it->first] = it->second;
-    }
-  }
-
-  
-  void HttpClient::SetCredentials(const std::string& username,
-                                  const std::string& password)
-  {
-    username_ = username;
-    password_ = password;
-  }
-
-  
-  void HttpClient::ClearCredentials()
-  {
-    username_.empty();
-    password_.empty();
-  }
-
-
-  void HttpClient::SetCertificate(const std::string& certificateFile,
-                                  const std::string& keyFile,
-                                  const std::string& keyPassword)
-  {
-    certificateFile_ = certificateFile;
-    certificateKeyFile_ = keyFile;
-    certificateKeyPassword_ = keyPassword;
-  }
-
-  
-  void HttpClient::ClearCertificate()
-  {
-    certificateFile_.clear();
-    certificateKeyFile_.clear();
-    certificateKeyPassword_.clear();
-  }
-
-
-  void HttpClient::ClearBody()
-  {
-    fullBody_.clear();
-    chunkedBody_ = NULL;
-  }
-
-  
-  void HttpClient::SwapBody(std::string& body)
-  {
-    fullBody_.swap(body);
-    chunkedBody_ = NULL;
-  }
-
-  
-  void HttpClient::SetBody(const std::string& body)
-  {
-    fullBody_ = body;
-    chunkedBody_ = NULL;
-  }
-
-  
-  void HttpClient::SetBody(IRequestBody& body)
-  {
-    fullBody_.clear();
-    chunkedBody_ = &body;
-  }
-
-
-  namespace
-  {
-    class HeadersWrapper : public boost::noncopyable
-    {
-    private:
-      std::vector<const char*>  headersKeys_;
-      std::vector<const char*>  headersValues_;
-
-    public:
-      HeadersWrapper(const HttpClient::HttpHeaders& headers)
-      {
-        headersKeys_.reserve(headers.size());
-        headersValues_.reserve(headers.size());
-
-        for (HttpClient::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it)
-        {
-          headersKeys_.push_back(it->first.c_str());
-          headersValues_.push_back(it->second.c_str());
-        }
-      }
-
-      void AddStaticString(const char* key,
-                           const char* value)
-      {
-        headersKeys_.push_back(key);
-        headersValues_.push_back(value);
-      }
-
-      uint32_t GetCount() const
-      {
-        return headersKeys_.size();
-      }
-
-      const char* const* GetKeys() const
-      {
-        return headersKeys_.empty() ? NULL : &headersKeys_[0];
-      }
-
-      const char* const* GetValues() const
-      {
-        return headersValues_.empty() ? NULL : &headersValues_[0];
-      }
-    };
-
-
-    class MemoryRequestBody : public HttpClient::IRequestBody
-    {
-    private:
-      std::string  body_;
-      bool         done_;
-
-    public:
-      MemoryRequestBody(const std::string& body) :
-        body_(body),
-        done_(false)
-      {
-        if (body_.empty())
-        {
-          done_ = true;
-        }
-      }
-
-      virtual bool ReadNextChunk(std::string& chunk)
-      {
-        if (done_)
-        {
-          return false;
-        }
-        else
-        {
-          chunk.swap(body_);
-          done_ = true;
-          return true;
-        }
-      }
-    };
-
-
-    // This class mimics Orthanc::ChunkedBuffer
-    class ChunkedBuffer : public boost::noncopyable
-    {
-    private:
-      typedef std::list<std::string*>  Content;
-
-      Content  content_;
-      size_t   size_;
-
-    public:
-      ChunkedBuffer() :
-        size_(0)
-      {
-      }
-
-      ~ChunkedBuffer()
-      {
-        Clear();
-      }
-
-      void Clear()
-      {
-        for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
-        {
-          assert(*it != NULL);
-          delete *it;
-        }
-
-        content_.clear();
-      }
-
-      void Flatten(std::string& target) const
-      {
-        target.resize(size_);
-
-        size_t pos = 0;
-
-        for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
-        {
-          assert(*it != NULL);
-          size_t s = (*it)->size();
-
-          if (s != 0)
-          {
-            memcpy(&target[pos], (*it)->c_str(), s);
-            pos += s;
-          }
-        }
-
-        assert(size_ == 0 ||
-               pos == target.size());
-      }
-
-      void AddChunk(const void* data,
-                    size_t size)
-      {
-        content_.push_back(new std::string(reinterpret_cast<const char*>(data), size));
-        size_ += size;
-      }
-
-      void AddChunk(const std::string& chunk)
-      {
-        content_.push_back(new std::string(chunk));
-        size_ += chunk.size();
-      }
-    };
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-    class MemoryAnswer : public HttpClient::IAnswer
-    {
-    private:
-      HttpClient::HttpHeaders  headers_;
-      ChunkedBuffer            body_;
-
-    public:
-      const HttpClient::HttpHeaders& GetHeaders() const
-      {
-        return headers_;
-      }
-
-      const ChunkedBuffer& GetBody() const
-      {
-        return body_;
-      }
-
-      virtual void AddHeader(const std::string& key,
-                             const std::string& value)
-      {
-        headers_[key] = value;
-      }
-
-      virtual void AddChunk(const void* data,
-                            size_t size)
-      {
-        body_.AddChunk(data, size);
-      }
-    };
-#endif
-  }
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-  void HttpClient::ExecuteWithStream(uint16_t& httpStatus,
-                                     IAnswer& answer,
-                                     IRequestBody& body) const
-  {
-    HeadersWrapper h(headers_);
-
-    if (method_ == OrthancPluginHttpMethod_Post ||
-        method_ == OrthancPluginHttpMethod_Put)
-    {
-      // Automatically set the "Transfer-Encoding" header if absent
-      bool found = false;
-
-      for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it)
-      {
-        if (boost::iequals(it->first, "Transfer-Encoding"))
-        {
-          found = true;
-          break;
-        }
-      }
-
-      if (!found)
-      {
-        h.AddStaticString("Transfer-Encoding", "chunked");
-      }
-    }
-
-    RequestBodyWrapper request(body);
-        
-    OrthancPluginErrorCode error = OrthancPluginChunkedHttpClient(
-      GetGlobalContext(),
-      &answer,
-      AnswerAddChunkCallback,
-      AnswerAddHeaderCallback,
-      &httpStatus,
-      method_,
-      url_.c_str(),
-      h.GetCount(),
-      h.GetKeys(),
-      h.GetValues(),
-      &request,
-      RequestBodyWrapper::IsDone,
-      RequestBodyWrapper::GetChunkData,
-      RequestBodyWrapper::GetChunkSize,
-      RequestBodyWrapper::Next,
-      username_.empty() ? NULL : username_.c_str(),
-      password_.empty() ? NULL : password_.c_str(),
-      timeout_,
-      certificateFile_.empty() ? NULL : certificateFile_.c_str(),
-      certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
-      certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
-      pkcs11_ ? 1 : 0);
-
-    if (error != OrthancPluginErrorCode_Success)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
-    }
-  }
-#endif    
-
-
-  void HttpClient::ExecuteWithoutStream(uint16_t& httpStatus,
-                                        HttpHeaders& answerHeaders,
-                                        std::string& answerBody,
-                                        const std::string& body) const
-  {
-    HeadersWrapper headers(headers_);
-
-    MemoryBuffer answerBodyBuffer, answerHeadersBuffer;
-
-    OrthancPluginErrorCode error = OrthancPluginHttpClient(
-      GetGlobalContext(),
-      *answerBodyBuffer,
-      *answerHeadersBuffer,
-      &httpStatus,
-      method_,
-      url_.c_str(),
-      headers.GetCount(),
-      headers.GetKeys(),
-      headers.GetValues(),
-      body.empty() ? NULL : body.c_str(),
-      body.size(),
-      username_.empty() ? NULL : username_.c_str(),
-      password_.empty() ? NULL : password_.c_str(),
-      timeout_,
-      certificateFile_.empty() ? NULL : certificateFile_.c_str(),
-      certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
-      certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
-      pkcs11_ ? 1 : 0);
-
-    if (error != OrthancPluginErrorCode_Success)
-    {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
-    }
-
-    Json::Value v;
-    answerHeadersBuffer.ToJson(v);
-
-    if (v.type() != Json::objectValue)
-    {
-      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-    }
-
-    Json::Value::Members members = v.getMemberNames();
-    answerHeaders.clear();
-
-    for (size_t i = 0; i < members.size(); i++)
-    {
-      const Json::Value& h = v[members[i]];
-      if (h.type() != Json::stringValue)
-      {
-        ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-      }
-      else
-      {
-        answerHeaders[members[i]] = h.asString();
-      }
-    }
-
-    answerBodyBuffer.ToString(answerBody);
-  }
-
-
-  void HttpClient::Execute(IAnswer& answer)
-  {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-    if (allowChunkedTransfers_)
-    {
-      if (chunkedBody_ != NULL)
-      {
-        ExecuteWithStream(httpStatus_, answer, *chunkedBody_);
-      }
-      else
-      {
-        MemoryRequestBody wrapper(fullBody_);
-        ExecuteWithStream(httpStatus_, answer, wrapper);
-      }
-
-      return;
-    }
-#endif
-    
-    // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
-    // transfers are disabled. This results in higher memory usage
-    // (all chunks from the answer body are sent at once)
-
-    HttpHeaders answerHeaders;
-    std::string answerBody;
-    Execute(answerHeaders, answerBody);
-
-    for (HttpHeaders::const_iterator it = answerHeaders.begin(); 
-         it != answerHeaders.end(); ++it)
-    {
-      answer.AddHeader(it->first, it->second);      
-    }
-
-    if (!answerBody.empty())
-    {
-      answer.AddChunk(answerBody.c_str(), answerBody.size());
-    }
-  }
-
-
-  void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
-                           std::string& answerBody /* out */)
-  {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-    if (allowChunkedTransfers_)
-    {
-      MemoryAnswer answer;
-      Execute(answer);
-      answerHeaders = answer.GetHeaders();
-      answer.GetBody().Flatten(answerBody);
-      return;
-    }
-#endif
-    
-    // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
-    // transfers are disabled. This results in higher memory usage
-    // (all chunks from the request body are sent at once)
-
-    if (chunkedBody_ != NULL)
-    {
-      ChunkedBuffer buffer;
-      
-      std::string chunk;
-      while (chunkedBody_->ReadNextChunk(chunk))
-      {
-        buffer.AddChunk(chunk);
-      }
-
-      std::string body;
-      buffer.Flatten(body);
-
-      ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, body);
-    }
-    else
-    {
-      ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, fullBody_);
-    }
-  }
-
-
-  void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
-                           Json::Value& answerBody /* out */)
-  {
-    std::string body;
-    Execute(answerHeaders, body);
-    
-    Json::Reader reader;
-    if (!reader.parse(body, answerBody))
-    {
-      LogError("Cannot convert HTTP answer body to JSON");
-      ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
-    }
-  }
-
-
-  void HttpClient::Execute()
-  {
-    HttpHeaders answerHeaders;
-    std::string body;
-    Execute(answerHeaders, body);
-  }
-
-#endif  /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */
-
-
-
-
-
-  /******************************************************************
-   ** CHUNKED HTTP SERVER
-   ******************************************************************/
-
-  namespace Internals
-  {
-    void NullRestCallback(OrthancPluginRestOutput* output,
-                          const char* url,
-                          const OrthancPluginHttpRequest* request)
-    {
-    }
-  
-    IChunkedRequestReader *NullChunkedRestCallback(const char* url,
-                                                   const OrthancPluginHttpRequest* request)
-    {
-      return NULL;
-    }
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
-
-    OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
-      OrthancPluginServerChunkedRequestReader* reader,
-      const void*                              data,
-      uint32_t                                 size)
-    {
-      try
-      {
-        if (reader == NULL)
-        {
-          return OrthancPluginErrorCode_InternalError;
-        }
-
-        reinterpret_cast<IChunkedRequestReader*>(reader)->AddChunk(data, size);
-        return OrthancPluginErrorCode_Success;
-      }
-      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-      {
-        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return OrthancPluginErrorCode_BadFileFormat;
-      }
-      catch (...)
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-    }
-
-    
-    OrthancPluginErrorCode ChunkedRequestReaderExecute(
-      OrthancPluginServerChunkedRequestReader* reader,
-      OrthancPluginRestOutput*                 output)
-    {
-      try
-      {
-        if (reader == NULL)
-        {
-          return OrthancPluginErrorCode_InternalError;
-        }
-
-        reinterpret_cast<IChunkedRequestReader*>(reader)->Execute(output);
-        return OrthancPluginErrorCode_Success;
-      }
-      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-      {
-        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return OrthancPluginErrorCode_BadFileFormat;
-      }
-      catch (...)
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-    }
-
-    
-    void ChunkedRequestReaderFinalize(
-      OrthancPluginServerChunkedRequestReader* reader)
-    {
-      if (reader != NULL)
-      {
-        delete reinterpret_cast<IChunkedRequestReader*>(reader);
-      }
-    }
-
-#else
-    
-    OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
-                                                    const char* url,
-                                                    const OrthancPluginHttpRequest* request,
-                                                    RestCallback         GetHandler,
-                                                    ChunkedRestCallback  PostHandler,
-                                                    RestCallback         DeleteHandler,
-                                                    ChunkedRestCallback  PutHandler)
-    {
-      try
-      {
-        std::string allowed;
-
-        if (GetHandler != Internals::NullRestCallback)
-        {
-          allowed += "GET";
-        }
-
-        if (PostHandler != Internals::NullChunkedRestCallback)
-        {
-          if (!allowed.empty())
-          {
-            allowed += ",";
-          }
-        
-          allowed += "POST";
-        }
-
-        if (DeleteHandler != Internals::NullRestCallback)
-        {
-          if (!allowed.empty())
-          {
-            allowed += ",";
-          }
-        
-          allowed += "DELETE";
-        }
-
-        if (PutHandler != Internals::NullChunkedRestCallback)
-        {
-          if (!allowed.empty())
-          {
-            allowed += ",";
-          }
-        
-          allowed += "PUT";
-        }
-      
-        switch (request->method)
-        {
-          case OrthancPluginHttpMethod_Get:
-            if (GetHandler == Internals::NullRestCallback)
-            {
-              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
-            }
-            else
-            {
-              GetHandler(output, url, request);
-            }
-
-            break;
-
-          case OrthancPluginHttpMethod_Post:
-            if (PostHandler == Internals::NullChunkedRestCallback)
-            {
-              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
-            }
-            else
-            {
-              boost::movelib::unique_ptr<IChunkedRequestReader> reader(PostHandler(url, request));
-              if (reader.get() == NULL)
-              {
-                ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
-              }
-              else
-              {
-                reader->AddChunk(request->body, request->bodySize);
-                reader->Execute(output);
-              }
-            }
-
-            break;
-
-          case OrthancPluginHttpMethod_Delete:
-            if (DeleteHandler == Internals::NullRestCallback)
-            {
-              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
-            }
-            else
-            {
-              DeleteHandler(output, url, request);
-            }
-
-            break;
-
-          case OrthancPluginHttpMethod_Put:
-            if (PutHandler == Internals::NullChunkedRestCallback)
-            {
-              OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
-            }
-            else
-            {
-              boost::movelib::unique_ptr<IChunkedRequestReader> reader(PutHandler(url, request));
-              if (reader.get() == NULL)
-              {
-                ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
-              }
-              else
-              {
-                reader->AddChunk(request->body, request->bodySize);
-                reader->Execute(output);
-              }
-            }
-
-            break;
-
-          default:
-            ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
-        }
-
-        return OrthancPluginErrorCode_Success;
-      }
-      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-      {
-#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
-        if (HasGlobalContext() &&
-            e.HasDetails())
-        {
-          // The "false" instructs Orthanc not to log the detailed
-          // error message. This is to avoid duplicating the details,
-          // because "OrthancException" already does it on construction.
-          OrthancPluginSetHttpErrorDetails
-            (GetGlobalContext(), output, e.GetDetails(), false);
-        }
-#endif
-
-        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return OrthancPluginErrorCode_BadFileFormat;
-      }
-      catch (...)
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-    }
-#endif
-  }
-
-
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
-  OrthancPluginErrorCode IStorageCommitmentScpHandler::Lookup(
-    OrthancPluginStorageCommitmentFailureReason* target,
-    void* rawHandler,
-    const char* sopClassUid,
-    const char* sopInstanceUid)
-  {
-    assert(target != NULL &&
-           rawHandler != NULL);
-      
-    try
-    {
-      IStorageCommitmentScpHandler& handler = *reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
-      *target = handler.Lookup(sopClassUid, sopInstanceUid);
-      return OrthancPluginErrorCode_Success;
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-      return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-    }
-    catch (...)
-    {
-      return OrthancPluginErrorCode_Plugin;
-    }
-  }
-#endif
-
-
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
-  void IStorageCommitmentScpHandler::Destructor(void* rawHandler)
-  {
-    assert(rawHandler != NULL);
-    delete reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
-  }
-#endif
-}
--- a/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1131 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "OrthancPluginException.h"
-
-#include <orthanc/OrthancCPlugin.h>
-#include <boost/noncopyable.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <json/value.h>
-#include <vector>
-#include <list>
-#include <set>
-#include <map>
-
-
-
-#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)
-#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision)        \
-  (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major ||                      \
-   (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major &&                    \
-    (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor ||                    \
-     (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor &&                  \
-      ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
-#endif
-
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
-// The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0
-#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  1
-#else
-#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  0
-#endif
-
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 2)
-#  define HAS_ORTHANC_PLUGIN_PEERS  1
-#  define HAS_ORTHANC_PLUGIN_JOB    1
-#else
-#  define HAS_ORTHANC_PLUGIN_PEERS  0
-#  define HAS_ORTHANC_PLUGIN_JOB    0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
-#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  1
-#else
-#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4)
-#  define HAS_ORTHANC_PLUGIN_METRICS  1
-#else
-#  define HAS_ORTHANC_PLUGIN_METRICS  0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 1, 0)
-#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  1
-#else
-#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  1
-#else
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  1
-#else
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  0
-#endif
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 0)
-#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  1
-#else
-#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  0
-#endif
-
-
-
-namespace OrthancPlugins
-{
-  typedef void (*RestCallback) (OrthancPluginRestOutput* output,
-                                const char* url,
-                                const OrthancPluginHttpRequest* request);
-
-  void SetGlobalContext(OrthancPluginContext* context);
-
-  bool HasGlobalContext();
-
-  OrthancPluginContext* GetGlobalContext();
-
-  
-  class OrthancImage;
-
-
-  class MemoryBuffer : public boost::noncopyable
-  {
-  private:
-    OrthancPluginMemoryBuffer  buffer_;
-
-    void Check(OrthancPluginErrorCode code);
-
-    bool CheckHttp(OrthancPluginErrorCode code);
-
-  public:
-    MemoryBuffer();
-
-    ~MemoryBuffer()
-    {
-      Clear();
-    }
-
-    OrthancPluginMemoryBuffer* operator*()
-    {
-      return &buffer_;
-    }
-
-    // This transfers ownership from "other" to "this"
-    void Assign(OrthancPluginMemoryBuffer& other);
-
-    void Swap(MemoryBuffer& other);
-
-    OrthancPluginMemoryBuffer Release();
-
-    const char* GetData() const
-    {
-      if (buffer_.size > 0)
-      {
-        return reinterpret_cast<const char*>(buffer_.data);
-      }
-      else
-      {
-        return NULL;
-      }
-    }
-
-    size_t GetSize() const
-    {
-      return buffer_.size;
-    }
-
-    bool IsEmpty() const
-    {
-      return GetSize() == 0 || GetData() == NULL;
-    }
-
-    void Clear();
-
-    void ToString(std::string& target) const;
-
-    void ToJson(Json::Value& target) const;
-
-    bool RestApiGet(const std::string& uri,
-                    bool applyPlugins);
-
-    bool RestApiGet(const std::string& uri,
-                    const std::map<std::string, std::string>& httpHeaders,
-                    bool applyPlugins);
-
-    bool RestApiPost(const std::string& uri,
-                     const void* body,
-                     size_t bodySize,
-                     bool applyPlugins);
-
-    bool RestApiPut(const std::string& uri,
-                    const void* body,
-                    size_t bodySize,
-                    bool applyPlugins);
-
-    bool RestApiPost(const std::string& uri,
-                     const Json::Value& body,
-                     bool applyPlugins);
-
-    bool RestApiPut(const std::string& uri,
-                    const Json::Value& body,
-                    bool applyPlugins);
-
-    bool RestApiPost(const std::string& uri,
-                     const std::string& body,
-                     bool applyPlugins)
-    {
-      return RestApiPost(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
-    }
-
-    bool RestApiPut(const std::string& uri,
-                    const std::string& body,
-                    bool applyPlugins)
-    {
-      return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
-    }
-
-    void CreateDicom(const Json::Value& tags,
-                     OrthancPluginCreateDicomFlags flags);
-
-    void CreateDicom(const Json::Value& tags,
-                     const OrthancImage& pixelData,
-                     OrthancPluginCreateDicomFlags flags);
-
-    void ReadFile(const std::string& path);
-
-    void GetDicomQuery(const OrthancPluginWorklistQuery* query);
-
-    void DicomToJson(Json::Value& target,
-                     OrthancPluginDicomToJsonFormat format,
-                     OrthancPluginDicomToJsonFlags flags,
-                     uint32_t maxStringLength);
-
-    bool HttpGet(const std::string& url,
-                 const std::string& username,
-                 const std::string& password);
-
-    bool HttpPost(const std::string& url,
-                  const std::string& body,
-                  const std::string& username,
-                  const std::string& password);
-
-    bool HttpPut(const std::string& url,
-                 const std::string& body,
-                 const std::string& username,
-                 const std::string& password);
-
-    void GetDicomInstance(const std::string& instanceId);
-  };
-
-
-  class OrthancString : public boost::noncopyable
-  {
-  private:
-    char*   str_;
-
-    void Clear();
-
-  public:
-    OrthancString() :
-      str_(NULL)
-    {
-    }
-
-    ~OrthancString()
-    {
-      Clear();
-    }
-
-    // This transfers ownership, warning: The string must have been
-    // allocated by the Orthanc core
-    void Assign(char* str);
-
-    const char* GetContent() const
-    {
-      return str_;
-    }
-
-    void ToString(std::string& target) const;
-
-    void ToJson(Json::Value& target) const;
-  };
-
-
-  class OrthancConfiguration : public boost::noncopyable
-  {
-  private:
-    Json::Value  configuration_;  // Necessarily a Json::objectValue
-    std::string  path_;
-
-    std::string GetPath(const std::string& key) const;
-
-    void LoadConfiguration();
-    
-  public:
-    OrthancConfiguration();
-
-    explicit OrthancConfiguration(bool load);
-
-    const Json::Value& GetJson() const
-    {
-      return configuration_;
-    }
-
-    bool IsSection(const std::string& key) const;
-
-    void GetSection(OrthancConfiguration& target,
-                    const std::string& key) const;
-
-    bool LookupStringValue(std::string& target,
-                           const std::string& key) const;
-    
-    bool LookupIntegerValue(int& target,
-                            const std::string& key) const;
-
-    bool LookupUnsignedIntegerValue(unsigned int& target,
-                                    const std::string& key) const;
-
-    bool LookupBooleanValue(bool& target,
-                            const std::string& key) const;
-
-    bool LookupFloatValue(float& target,
-                          const std::string& key) const;
-
-    bool LookupListOfStrings(std::list<std::string>& target,
-                             const std::string& key,
-                             bool allowSingleString) const;
-
-    bool LookupSetOfStrings(std::set<std::string>& target,
-                            const std::string& key,
-                            bool allowSingleString) const;
-
-    std::string GetStringValue(const std::string& key,
-                               const std::string& defaultValue) const;
-
-    int GetIntegerValue(const std::string& key,
-                        int defaultValue) const;
-
-    unsigned int GetUnsignedIntegerValue(const std::string& key,
-                                         unsigned int defaultValue) const;
-
-    bool GetBooleanValue(const std::string& key,
-                         bool defaultValue) const;
-
-    float GetFloatValue(const std::string& key,
-                        float defaultValue) const;
-
-    void GetDictionary(std::map<std::string, std::string>& target,
-                       const std::string& key) const;
-  };
-
-  class OrthancImage : public boost::noncopyable
-  {
-  private:
-    OrthancPluginImage*    image_;
-
-    void Clear();
-
-    void CheckImageAvailable() const;
-
-  public:
-    OrthancImage();
-
-    explicit OrthancImage(OrthancPluginImage* image);
-
-    OrthancImage(OrthancPluginPixelFormat  format,
-                 uint32_t                  width,
-                 uint32_t                  height);
-
-    OrthancImage(OrthancPluginPixelFormat  format,
-                 uint32_t                  width,
-                 uint32_t                  height,
-                 uint32_t                  pitch,
-                 void*                     buffer
-      );
-
-    ~OrthancImage()
-    {
-      Clear();
-    }
-
-    void UncompressPngImage(const void* data,
-                            size_t size);
-
-    void UncompressJpegImage(const void* data,
-                             size_t size);
-
-    void DecodeDicomImage(const void* data,
-                          size_t size,
-                          unsigned int frame);
-
-    OrthancPluginPixelFormat GetPixelFormat() const;
-
-    unsigned int GetWidth() const;
-
-    unsigned int GetHeight() const;
-
-    unsigned int GetPitch() const;
-    
-    const void* GetBuffer() const;
-
-    const OrthancPluginImage* GetObject() const
-    {
-      return image_;
-    }
-
-    void CompressPngImage(MemoryBuffer& target) const;
-
-    void CompressJpegImage(MemoryBuffer& target,
-                           uint8_t quality) const;
-
-    void AnswerPngImage(OrthancPluginRestOutput* output) const;
-
-    void AnswerJpegImage(OrthancPluginRestOutput* output,
-                         uint8_t quality) const;
-  };
-
-
-#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
-  class FindMatcher : public boost::noncopyable
-  {
-  private:
-    OrthancPluginFindMatcher*          matcher_;
-    const OrthancPluginWorklistQuery*  worklist_;
-
-    void SetupDicom(const void*            query,
-                    uint32_t               size);
-
-  public:
-    explicit FindMatcher(const OrthancPluginWorklistQuery*  worklist);
-
-    FindMatcher(const void*  query,
-                uint32_t     size)
-    {
-      SetupDicom(query, size);
-    }
-
-    explicit FindMatcher(const MemoryBuffer&  dicom)
-    {
-      SetupDicom(dicom.GetData(), dicom.GetSize());
-    }
-
-    ~FindMatcher();
-
-    bool IsMatch(const void*  dicom,
-                 uint32_t     size) const;
-
-    bool IsMatch(const MemoryBuffer& dicom) const
-    {
-      return IsMatch(dicom.GetData(), dicom.GetSize());
-    }
-  };
-#endif
-
-
-  bool RestApiGet(Json::Value& result,
-                  const std::string& uri,
-                  bool applyPlugins);
-
-  bool RestApiGetString(std::string& result,
-                        const std::string& uri,
-                        bool applyPlugins);
-
-  bool RestApiGetString(std::string& result,
-                        const std::string& uri,
-                        const std::map<std::string, std::string>& httpHeaders,
-                        bool applyPlugins);
-
-  bool RestApiPost(std::string& result,
-                   const std::string& uri,
-                   const void* body,
-                   size_t bodySize,
-                   bool applyPlugins);
-
-  bool RestApiPost(Json::Value& result,
-                   const std::string& uri,
-                   const void* body,
-                   size_t bodySize,
-                   bool applyPlugins);
-
-  bool RestApiPost(Json::Value& result,
-                   const std::string& uri,
-                   const Json::Value& body,
-                   bool applyPlugins);
-
-  inline bool RestApiPost(Json::Value& result,
-                          const std::string& uri,
-                          const std::string& body,
-                          bool applyPlugins)
-  {
-    return RestApiPost(result, uri, body.empty() ? NULL : body.c_str(),
-                       body.size(), applyPlugins);
-  }
-
-  inline bool RestApiPost(Json::Value& result,
-                          const std::string& uri,
-                          const MemoryBuffer& body,
-                          bool applyPlugins)
-  {
-    return RestApiPost(result, uri, body.GetData(),
-                       body.GetSize(), applyPlugins);
-  }
-
-  bool RestApiPut(Json::Value& result,
-                  const std::string& uri,
-                  const void* body,
-                  size_t bodySize,
-                  bool applyPlugins);
-
-  bool RestApiPut(Json::Value& result,
-                  const std::string& uri,
-                  const Json::Value& body,
-                  bool applyPlugins);
-
-  inline bool RestApiPut(Json::Value& result,
-                         const std::string& uri,
-                         const std::string& body,
-                         bool applyPlugins)
-  {
-    return RestApiPut(result, uri, body.empty() ? NULL : body.c_str(),
-                      body.size(), applyPlugins);
-  }
-
-  bool RestApiDelete(const std::string& uri,
-                     bool applyPlugins);
-
-  bool HttpDelete(const std::string& url,
-                  const std::string& username,
-                  const std::string& password);
-
-  void AnswerJson(const Json::Value& value,
-                  OrthancPluginRestOutput* output);
-
-  void AnswerString(const std::string& answer,
-                    const char* mimeType,
-                    OrthancPluginRestOutput* output);
-
-  void AnswerHttpError(uint16_t httpError,
-                       OrthancPluginRestOutput* output);
-
-  void AnswerMethodNotAllowed(OrthancPluginRestOutput* output, const char* allowedMethods);
-
-#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
-  const char* AutodetectMimeType(const std::string& path);
-#endif
-
-  void LogError(const std::string& message);
-
-  void LogWarning(const std::string& message);
-
-  void LogInfo(const std::string& message);
-
-  void ReportMinimalOrthancVersion(unsigned int major,
-                                   unsigned int minor,
-                                   unsigned int revision);
-  
-  bool CheckMinimalOrthancVersion(unsigned int major,
-                                  unsigned int minor,
-                                  unsigned int revision);
-
-
-  namespace Internals
-  {
-    template <RestCallback Callback>
-    static OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
-                                          const char* url,
-                                          const OrthancPluginHttpRequest* request)
-    {
-      try
-      {
-        Callback(output, url, request);
-        return OrthancPluginErrorCode_Success;
-      }
-      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-      {
-#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
-        if (HasGlobalContext() &&
-            e.HasDetails())
-        {
-          // The "false" instructs Orthanc not to log the detailed
-          // error message. This is to avoid duplicating the details,
-          // because "OrthancException" already does it on construction.
-          OrthancPluginSetHttpErrorDetails
-            (GetGlobalContext(), output, e.GetDetails(), false);
-        }
-#endif
-
-        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return OrthancPluginErrorCode_BadFileFormat;
-      }
-      catch (...)
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-    }
-  }
-
-  
-  template <RestCallback Callback>
-  void RegisterRestCallback(const std::string& uri,
-                            bool isThreadSafe)
-  {
-    if (isThreadSafe)
-    {
-      OrthancPluginRegisterRestCallbackNoLock
-        (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
-    }
-    else
-    {
-      OrthancPluginRegisterRestCallback
-        (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
-    }
-  }
-
-
-#if HAS_ORTHANC_PLUGIN_PEERS == 1
-  class OrthancPeers : public boost::noncopyable
-  {
-  private:
-    typedef std::map<std::string, uint32_t>   Index;
-
-    OrthancPluginPeers   *peers_;
-    Index                 index_;
-    uint32_t              timeout_;
-
-    size_t GetPeerIndex(const std::string& name) const;
-
-  public:
-    OrthancPeers();
-
-    ~OrthancPeers();
-
-    uint32_t GetTimeout() const
-    {
-      return timeout_;
-    }
-
-    void SetTimeout(uint32_t timeout)
-    {
-      timeout_ = timeout;
-    }
-
-    bool LookupName(size_t& target,
-                    const std::string& name) const;
-
-    std::string GetPeerName(size_t index) const;
-
-    std::string GetPeerUrl(size_t index) const;
-
-    std::string GetPeerUrl(const std::string& name) const;
-
-    size_t GetPeersCount() const
-    {
-      return index_.size();
-    }
-
-    bool LookupUserProperty(std::string& value,
-                            size_t index,
-                            const std::string& key) const;
-
-    bool LookupUserProperty(std::string& value,
-                            const std::string& peer,
-                            const std::string& key) const;
-
-    bool DoGet(MemoryBuffer& target,
-               size_t index,
-               const std::string& uri) const;
-
-    bool DoGet(MemoryBuffer& target,
-               const std::string& name,
-               const std::string& uri) const;
-
-    bool DoGet(Json::Value& target,
-               size_t index,
-               const std::string& uri) const;
-
-    bool DoGet(Json::Value& target,
-               const std::string& name,
-               const std::string& uri) const;
-
-    bool DoPost(MemoryBuffer& target,
-                size_t index,
-                const std::string& uri,
-                const std::string& body) const;
-
-    bool DoPost(MemoryBuffer& target,
-                const std::string& name,
-                const std::string& uri,
-                const std::string& body) const;
-
-    bool DoPost(Json::Value& target,
-                size_t index,
-                const std::string& uri,
-                const std::string& body) const;
-
-    bool DoPost(Json::Value& target,
-                const std::string& name,
-                const std::string& uri,
-                const std::string& body) const;
-
-    bool DoPut(size_t index,
-               const std::string& uri,
-               const std::string& body) const;
-
-    bool DoPut(const std::string& name,
-               const std::string& uri,
-               const std::string& body) const;
-
-    bool DoDelete(size_t index,
-                  const std::string& uri) const;
-
-    bool DoDelete(const std::string& name,
-                  const std::string& uri) const;
-  };
-#endif
-
-
-
-#if HAS_ORTHANC_PLUGIN_JOB == 1
-  class OrthancJob : public boost::noncopyable
-  {
-  private:
-    std::string   jobType_;
-    std::string   content_;
-    bool          hasSerialized_;
-    std::string   serialized_;
-    float         progress_;
-
-    static void CallbackFinalize(void* job);
-
-    static float CallbackGetProgress(void* job);
-
-    static const char* CallbackGetContent(void* job);
-
-    static const char* CallbackGetSerialized(void* job);
-
-    static OrthancPluginJobStepStatus CallbackStep(void* job);
-
-    static OrthancPluginErrorCode CallbackStop(void* job,
-                                               OrthancPluginJobStopReason reason);
-
-    static OrthancPluginErrorCode CallbackReset(void* job);
-
-  protected:
-    void ClearContent();
-
-    void UpdateContent(const Json::Value& content);
-
-    void ClearSerialized();
-
-    void UpdateSerialized(const Json::Value& serialized);
-
-    void UpdateProgress(float progress);
-    
-  public:
-    OrthancJob(const std::string& jobType);
-    
-    virtual ~OrthancJob()
-    {
-    }
-
-    virtual OrthancPluginJobStepStatus Step() = 0;
-
-    virtual void Stop(OrthancPluginJobStopReason reason) = 0;
-    
-    virtual void Reset() = 0;
-
-    static OrthancPluginJob* Create(OrthancJob* job /* takes ownership */);
-
-    static std::string Submit(OrthancJob* job /* takes ownership */,
-                              int priority);
-
-    static void SubmitAndWait(Json::Value& result,
-                              OrthancJob* job /* takes ownership */,
-                              int priority);
-
-    // Submit a job from a POST on the REST API with the same
-    // conventions as in the Orthanc core (according to the
-    // "Synchronous" and "Priority" options)
-    static void SubmitFromRestApiPost(OrthancPluginRestOutput* output,
-                                      const Json::Value& body,
-                                      OrthancJob* job);
-  };
-#endif
-
-
-#if HAS_ORTHANC_PLUGIN_METRICS == 1
-  inline void SetMetricsValue(char* name,
-                              float value)
-  {
-    OrthancPluginSetMetricsValue(GetGlobalContext(), name,
-                                 value, OrthancPluginMetricsType_Default);
-  }
-
-  class MetricsTimer : public boost::noncopyable
-  {
-  private:
-    std::string               name_;
-    boost::posix_time::ptime  start_;
-
-  public:
-    explicit MetricsTimer(const char* name);
-
-    ~MetricsTimer();
-  };
-#endif
-
-
-#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
-  class HttpClient : public boost::noncopyable
-  {
-  public:
-    typedef std::map<std::string, std::string>  HttpHeaders;
-
-    class IRequestBody : public boost::noncopyable
-    {
-    public:
-      virtual ~IRequestBody()
-      {
-      }
-
-      virtual bool ReadNextChunk(std::string& chunk) = 0;
-    };
-
-
-    class IAnswer : public boost::noncopyable
-    {
-    public:
-      virtual ~IAnswer()
-      {
-      }
-
-      virtual void AddHeader(const std::string& key,
-                             const std::string& value) = 0;
-
-      virtual void AddChunk(const void* data,
-                            size_t size) = 0;
-    };
-
-
-  private:
-    class RequestBodyWrapper;
-
-    uint16_t                 httpStatus_;
-    OrthancPluginHttpMethod  method_;
-    std::string              url_;
-    HttpHeaders              headers_;
-    std::string              username_;
-    std::string              password_;
-    uint32_t                 timeout_;
-    std::string              certificateFile_;
-    std::string              certificateKeyFile_;
-    std::string              certificateKeyPassword_;
-    bool                     pkcs11_;
-    std::string              fullBody_;
-    IRequestBody*            chunkedBody_;
-    bool                     allowChunkedTransfers_;
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
-    void ExecuteWithStream(uint16_t& httpStatus,  // out
-                           IAnswer& answer,       // out
-                           IRequestBody& body) const;
-#endif
-
-    void ExecuteWithoutStream(uint16_t& httpStatus,        // out
-                              HttpHeaders& answerHeaders,  // out
-                              std::string& answerBody,     // out
-                              const std::string& body) const;
-    
-  public:
-    HttpClient();
-
-    uint16_t GetHttpStatus() const
-    {
-      return httpStatus_;
-    }
-
-    void SetMethod(OrthancPluginHttpMethod method)
-    {
-      method_ = method;
-    }
-
-    const std::string& GetUrl() const
-    {
-      return url_;
-    }
-
-    void SetUrl(const std::string& url)
-    {
-      url_ = url;
-    }
-
-    void SetHeaders(const HttpHeaders& headers)
-    {
-      headers_ = headers;
-    }
-
-    void AddHeader(const std::string& key,
-                   const std::string& value)
-    {
-      headers_[key] = value;
-    }
-
-    void AddHeaders(const HttpHeaders& headers);
-
-    void SetCredentials(const std::string& username,
-                        const std::string& password);
-
-    void ClearCredentials();
-
-    void SetTimeout(unsigned int timeout)  // 0 for default timeout
-    {
-      timeout_ = timeout;
-    }
-
-    void SetCertificate(const std::string& certificateFile,
-                        const std::string& keyFile,
-                        const std::string& keyPassword);
-
-    void ClearCertificate();
-
-    void SetPkcs11(bool pkcs11)
-    {
-      pkcs11_ = pkcs11;
-    }
-
-    void ClearBody();
-
-    void SwapBody(std::string& body);
-
-    void SetBody(const std::string& body);
-
-    void SetBody(IRequestBody& body);
-
-    // This function can be used to disable chunked transfers if the
-    // remote server is Orthanc with a version <= 1.5.6.
-    void SetChunkedTransfersAllowed(bool allow)
-    {
-      allowChunkedTransfers_ = allow;
-    }
-
-    bool IsChunkedTransfersAllowed() const
-    {
-      return allowChunkedTransfers_;
-    }
-
-    void Execute(IAnswer& answer);
-
-    void Execute(HttpHeaders& answerHeaders /* out */,
-                 std::string& answerBody /* out */);
-
-    void Execute(HttpHeaders& answerHeaders /* out */,
-                 Json::Value& answerBody /* out */);
-
-    void Execute();
-  };
-#endif
-
-
-
-  class IChunkedRequestReader : public boost::noncopyable
-  {
-  public:
-    virtual ~IChunkedRequestReader()
-    {
-    }
-
-    virtual void AddChunk(const void* data,
-                          size_t size) = 0;
-
-    virtual void Execute(OrthancPluginRestOutput* output) = 0;
-  };
-
-
-  typedef IChunkedRequestReader* (*ChunkedRestCallback) (const char* url,
-                                                         const OrthancPluginHttpRequest* request);
-
-
-  namespace Internals
-  {
-    void NullRestCallback(OrthancPluginRestOutput* output,
-                          const char* url,
-                          const OrthancPluginHttpRequest* request);
-  
-    IChunkedRequestReader *NullChunkedRestCallback(const char* url,
-                                                   const OrthancPluginHttpRequest* request);
-
-
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
-    template <ChunkedRestCallback Callback>
-    static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader,
-                                                const char* url,
-                                                const OrthancPluginHttpRequest* request)
-    {
-      try
-      {
-        if (reader == NULL)
-        {
-          return OrthancPluginErrorCode_InternalError;
-        }
-        else
-        {
-          *reader = reinterpret_cast<OrthancPluginServerChunkedRequestReader*>(Callback(url, request));
-          if (*reader == NULL)
-          {
-            return OrthancPluginErrorCode_Plugin;
-          }
-          else
-          {
-            return OrthancPluginErrorCode_Success;
-          }
-        }
-      }
-      catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-      {
-        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return OrthancPluginErrorCode_BadFileFormat;
-      }
-      catch (...)
-      {
-        return OrthancPluginErrorCode_Plugin;
-      }
-    }
-
-    OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
-      OrthancPluginServerChunkedRequestReader* reader,
-      const void*                              data,
-      uint32_t                                 size);
-
-    OrthancPluginErrorCode ChunkedRequestReaderExecute(
-      OrthancPluginServerChunkedRequestReader* reader,
-      OrthancPluginRestOutput*                 output);
-
-    void ChunkedRequestReaderFinalize(
-      OrthancPluginServerChunkedRequestReader* reader);
-
-#else  
-
-    OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
-                                                    const char* url,
-                                                    const OrthancPluginHttpRequest* request,
-                                                    RestCallback GetHandler,
-                                                    ChunkedRestCallback PostHandler,
-                                                    RestCallback DeleteHandler,
-                                                    ChunkedRestCallback PutHandler);
-
-    template<
-      RestCallback         GetHandler,
-      ChunkedRestCallback  PostHandler,
-      RestCallback         DeleteHandler,
-      ChunkedRestCallback  PutHandler
-      >
-    inline OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
-                                                           const char* url,
-                                                           const OrthancPluginHttpRequest* request)
-    {
-      return ChunkedRestCompatibility(output, url, request, GetHandler,
-                                      PostHandler, DeleteHandler, PutHandler);
-    }
-#endif
-  }
-
-
-
-  // NB: We use a templated class instead of a templated function, because
-  // default values are only available in functions since C++11
-  template<
-    RestCallback         GetHandler    = Internals::NullRestCallback,
-    ChunkedRestCallback  PostHandler   = Internals::NullChunkedRestCallback,
-    RestCallback         DeleteHandler = Internals::NullRestCallback,
-    ChunkedRestCallback  PutHandler    = Internals::NullChunkedRestCallback
-    >
-  class ChunkedRestRegistration : public boost::noncopyable
-  {
-  public:
-    static void Apply(const std::string& uri)
-    {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
-      OrthancPluginRegisterChunkedRestCallback(
-        GetGlobalContext(), uri.c_str(),
-        GetHandler == Internals::NullRestCallback         ? NULL : Internals::Protect<GetHandler>,
-        PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PostHandler>,
-        DeleteHandler == Internals::NullRestCallback      ? NULL : Internals::Protect<DeleteHandler>,
-        PutHandler == Internals::NullChunkedRestCallback  ? NULL : Internals::ChunkedProtect<PutHandler>,
-        Internals::ChunkedRequestReaderAddChunk,
-        Internals::ChunkedRequestReaderExecute,
-        Internals::ChunkedRequestReaderFinalize);
-#else
-      OrthancPluginRegisterRestCallbackNoLock(
-        GetGlobalContext(), uri.c_str(), 
-        Internals::ChunkedRestCompatibility<GetHandler, PostHandler, DeleteHandler, PutHandler>);
-#endif
-    }
-  };
-
-  
-
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
-  class IStorageCommitmentScpHandler : public boost::noncopyable
-  {
-  public:
-    virtual ~IStorageCommitmentScpHandler()
-    {
-    }
-    
-    virtual OrthancPluginStorageCommitmentFailureReason Lookup(const std::string& sopClassUid,
-                                                               const std::string& sopInstanceUid) = 0;
-    
-    static OrthancPluginErrorCode Lookup(OrthancPluginStorageCommitmentFailureReason* target,
-                                         void* rawHandler,
-                                         const char* sopClassUid,
-                                         const char* sopInstanceUid);
-
-    static void Destructor(void* rawHandler);
-  };
-#endif
-}
--- a/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginException.h	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#if !defined(HAS_ORTHANC_EXCEPTION)
-#  error The macro HAS_ORTHANC_EXCEPTION must be defined
-#endif
-
-
-#if HAS_ORTHANC_EXCEPTION == 1
-#  include "../../../Core/OrthancException.h"
-#  define ORTHANC_PLUGINS_ERROR_ENUMERATION     ::Orthanc::ErrorCode
-#  define ORTHANC_PLUGINS_EXCEPTION_CLASS       ::Orthanc::OrthancException
-#  define ORTHANC_PLUGINS_GET_ERROR_CODE(code)  ::Orthanc::ErrorCode_ ## code
-#else
-#  include <orthanc/OrthancCPlugin.h>
-#  define ORTHANC_PLUGINS_ERROR_ENUMERATION     ::OrthancPluginErrorCode
-#  define ORTHANC_PLUGINS_EXCEPTION_CLASS       ::OrthancPlugins::PluginException
-#  define ORTHANC_PLUGINS_GET_ERROR_CODE(code)  ::OrthancPluginErrorCode_ ## code
-#endif
-
-
-#define ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code)                   \
-  throw ORTHANC_PLUGINS_EXCEPTION_CLASS(static_cast<ORTHANC_PLUGINS_ERROR_ENUMERATION>(code));
-
-
-#define ORTHANC_PLUGINS_THROW_EXCEPTION(code)                           \
-  throw ORTHANC_PLUGINS_EXCEPTION_CLASS(ORTHANC_PLUGINS_GET_ERROR_CODE(code));
-                                                  
-
-#define ORTHANC_PLUGINS_CHECK_ERROR(code)                           \
-  if (code != ORTHANC_PLUGINS_GET_ERROR_CODE(Success))              \
-  {                                                                 \
-    ORTHANC_PLUGINS_THROW_EXCEPTION(code);                          \
-  }
-
-
-namespace OrthancPlugins
-{
-#if HAS_ORTHANC_EXCEPTION == 0
-  class PluginException
-  {
-  private:
-    OrthancPluginErrorCode  code_;
-
-  public:
-    explicit PluginException(OrthancPluginErrorCode code) : code_(code)
-    {
-    }
-
-    OrthancPluginErrorCode GetErrorCode() const
-    {
-      return code_;
-    }
-
-    const char* What(OrthancPluginContext* context) const
-    {
-      const char* description = OrthancPluginGetErrorDescription(context, code_);
-      if (description)
-      {
-        return description;
-      }
-      else
-      {
-        return "No description available";
-      }
-    }
-  };
-#endif
-}
--- a/Resources/Orthanc/Plugins/Samples/Common/VersionScript.map	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-# This is a version-script for Orthanc plugins
-
-{
-global:
-  OrthancPluginInitialize;
-  OrthancPluginFinalize;
-  OrthancPluginGetName;
-  OrthancPluginGetVersion;
-
-local:
-  *;
-};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/VersionScriptPlugins.map	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,12 @@
+# This is a version-script for Orthanc plugins
+
+{
+global:
+  OrthancPluginInitialize;
+  OrthancPluginFinalize;
+  OrthancPluginGetName;
+  OrthancPluginGetVersion;
+
+local:
+  *;
+};
--- a/Resources/Orthanc/Resources/CMake/BoostConfiguration.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,383 +0,0 @@
-if (STATIC_BUILD OR NOT USE_SYSTEM_BOOST)
-  set(BOOST_STATIC 1)
-else()
-  include(FindBoost)
-
-  set(BOOST_STATIC 0)
-  #set(Boost_DEBUG 1)
-  #set(Boost_USE_STATIC_LIBS ON)
-
-  if (ENABLE_LOCALE)
-    list(APPEND ORTHANC_BOOST_COMPONENTS locale)
-  endif()
-
-  list(APPEND ORTHANC_BOOST_COMPONENTS filesystem thread system date_time regex)
-  find_package(Boost COMPONENTS ${ORTHANC_BOOST_COMPONENTS})
-
-  if (NOT Boost_FOUND)
-    foreach (item ${ORTHANC_BOOST_COMPONENTS})
-      string(TOUPPER ${item} tmp)
-
-      if (Boost_${tmp}_FOUND)
-        set(tmp2 "found")
-      else()
-        set(tmp2 "missing")
-      endif()
-      
-      message("Boost component ${item} - ${tmp2}")
-    endforeach()
-    
-    message(FATAL_ERROR "Unable to locate Boost on this system")
-  endif()
-
-  
-  # Patch by xnox to fix issue #166 (CMake find_boost version is now
-  # broken with newer boost/cmake)
-  # https://bitbucket.org/sjodogne/orthanc/issues/166/
-  if (POLICY CMP0093)
-    set(BOOST144 1.44)
-  else()
-    set(BOOST144 104400)
-  endif()
-  
-  
-  # Boost releases 1.44 through 1.47 supply both V2 and V3 filesystem
-  # http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/index.htm
-  if (${Boost_VERSION} LESS ${BOOST144})
-    add_definitions(
-      -DBOOST_HAS_FILESYSTEM_V3=0
-      )
-  else()
-    add_definitions(
-      -DBOOST_HAS_FILESYSTEM_V3=1
-      -DBOOST_FILESYSTEM_VERSION=3
-      )
-  endif()
-
-  include_directories(${Boost_INCLUDE_DIRS})
-  link_libraries(${Boost_LIBRARIES})
-endif()
-
-
-if (BOOST_STATIC)
-  ##
-  ## Parameters for static compilation of Boost 
-  ##
-  
-  set(BOOST_NAME boost_1_69_0)
-  set(BOOST_VERSION 1.69.0)
-  set(BOOST_BCP_SUFFIX bcpdigest-1.5.6)
-  set(BOOST_MD5 "579bccc0ea4d1a261c1d0c5e27446c3d")
-  set(BOOST_URL "http://orthanc.osimis.io/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
-  set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
-
-  if (IS_DIRECTORY "${BOOST_SOURCES_DIR}")
-    set(FirstRun OFF)
-  else()
-    set(FirstRun ON)
-  endif()
-
-  DownloadPackage(${BOOST_MD5} ${BOOST_URL} "${BOOST_SOURCES_DIR}")
-
-
-  ##
-  ## Patching boost
-  ## 
-
-  execute_process(
-    COMMAND ${PATCH_EXECUTABLE} -p0 -N -i
-    ${ORTHANC_ROOT}/Resources/Patches/boost-${BOOST_VERSION}-linux-standard-base.patch
-    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-    RESULT_VARIABLE Failure
-    )
-
-  if (FirstRun AND Failure)
-    message(FATAL_ERROR "Error while patching a file")
-  endif()
-
-
-  ##
-  ## Generic configuration of Boost
-  ## 
-
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    add_definitions(-isystem ${BOOST_SOURCES_DIR})
-  endif()
-
-  include_directories(
-    BEFORE ${BOOST_SOURCES_DIR}
-    )
-
-  add_definitions(
-    # Static build of Boost
-    -DBOOST_ALL_NO_LIB 
-    -DBOOST_ALL_NOLIB 
-    -DBOOST_DATE_TIME_NO_LIB 
-    -DBOOST_THREAD_BUILD_LIB
-    -DBOOST_PROGRAM_OPTIONS_NO_LIB
-    -DBOOST_REGEX_NO_LIB
-    -DBOOST_SYSTEM_NO_LIB
-    -DBOOST_LOCALE_NO_LIB
-
-    # In static builds, explicitly prevent Boost from using the system
-    # locale in lexical casts. This is notably important if
-    # "boost::lexical_cast<double>()" is applied to strings containing
-    # "," instead of "." as decimal separators. Check out function
-    # "OrthancStone::LinearAlgebra::ParseVector()".
-    -DBOOST_LEXICAL_CAST_ASSUME_C_LOCALE
-    )
-
-  set(BOOST_SOURCES
-    ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
-    )
-
-  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase" OR
-      "${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
-    add_definitions(
-      -DBOOST_SYSTEM_USE_STRERROR=1
-      )
-  endif()
-
-  
-  ##
-  ## Configuration of boost::thread
-  ##
-  
-  if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
-      CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
-      CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
-      CMAKE_SYSTEM_NAME STREQUAL "kFreeBSD" OR
-      CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
-      CMAKE_SYSTEM_NAME STREQUAL "PNaCl" OR
-      CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR
-      CMAKE_SYSTEM_NAME STREQUAL "NaCl64" OR
-      CMAKE_SYSTEM_NAME STREQUAL "Android")
-    list(APPEND BOOST_SOURCES
-      ${BOOST_SOURCES_DIR}/libs/atomic/src/lockpool.cpp
-      ${BOOST_SOURCES_DIR}/libs/thread/src/pthread/once.cpp
-      ${BOOST_SOURCES_DIR}/libs/thread/src/pthread/thread.cpp
-      )
-
-    if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase" OR
-        CMAKE_SYSTEM_NAME STREQUAL "PNaCl" OR
-        CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR
-        CMAKE_SYSTEM_NAME STREQUAL "NaCl64")
-      add_definitions(-DBOOST_HAS_SCHED_YIELD=1)
-    endif()
-
-    # Fix for error: "boost_1_69_0/boost/chrono/detail/inlined/mac/thread_clock.hpp:54:28: 
-    # error: use of undeclared identifier 'pthread_mach_thread_np'"
-    # https://github.com/envoyproxy/envoy/pull/1785
-    if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-      add_definitions(-D_DARWIN_C_SOURCE=1)
-    endif()
-
-  elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
-    list(APPEND BOOST_SOURCES
-      ${BOOST_SOURCES_DIR}/libs/thread/src/win32/tss_dll.cpp
-      ${BOOST_SOURCES_DIR}/libs/thread/src/win32/thread.cpp
-      ${BOOST_SOURCES_DIR}/libs/thread/src/win32/tss_pe.cpp
-      )
-
-  elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
-    # No support for threads in asm.js/WebAssembly
-
-  else()
-    message(FATAL_ERROR "Support your platform here")
-  endif()
-
-
-  ##
-  ## Configuration of boost::regex
-  ##
-  
-  aux_source_directory(${BOOST_SOURCES_DIR}/libs/regex/src BOOST_REGEX_SOURCES)
-
-  list(APPEND BOOST_SOURCES
-    ${BOOST_REGEX_SOURCES}
-    )
-
-
-  ##
-  ## Configuration of boost::datetime
-  ##
-  
-  list(APPEND BOOST_SOURCES
-    ${BOOST_SOURCES_DIR}/libs/date_time/src/gregorian/greg_month.cpp
-    )
-
-
-  ##
-  ## Configuration of boost::filesystem
-  ## 
-
-  if (CMAKE_SYSTEM_NAME STREQUAL "PNaCl" OR
-      CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR
-      CMAKE_SYSTEM_NAME STREQUAL "NaCl64" OR
-      CMAKE_SYSTEM_NAME STREQUAL "Android")
-    # boost::filesystem is not available on PNaCl
-    add_definitions(
-      -DBOOST_HAS_FILESYSTEM_V3=0
-      -D__INTEGRITY=1
-      )
-  else()
-    add_definitions(
-      -DBOOST_HAS_FILESYSTEM_V3=1
-      )
-    list(APPEND BOOST_SOURCES
-      ${BOOST_NAME}/libs/filesystem/src/codecvt_error_category.cpp
-      ${BOOST_NAME}/libs/filesystem/src/operations.cpp
-      ${BOOST_NAME}/libs/filesystem/src/path.cpp
-      ${BOOST_NAME}/libs/filesystem/src/path_traits.cpp
-      )
-
-    if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
-        CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
-        CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
-     list(APPEND BOOST_SOURCES
-        ${BOOST_SOURCES_DIR}/libs/filesystem/src/utf8_codecvt_facet.cpp
-        )
-
-    elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
-      list(APPEND BOOST_SOURCES
-        ${BOOST_NAME}/libs/filesystem/src/windows_file_codecvt.cpp
-        )
-    endif()
-  endif()
-
-
-  ##
-  ## Configuration of boost::locale
-  ## 
-
-  if (NOT ENABLE_LOCALE)
-    message("boost::locale is disabled")
-  else()
-    set(BOOST_ICU_SOURCES
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/boundary.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/codecvt.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/collator.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/conversion.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/date_time.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/formatter.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/icu_backend.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/numeric.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/icu/time_zone.cpp
-      )
-
-    list(APPEND BOOST_SOURCES
-      ${BOOST_SOURCES_DIR}/libs/locale/src/encoding/codepage.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/generator.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/date_time.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/formatting.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/ids.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/localization_backend.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/message.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/shared/mo_lambda.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/util/codecvt_converter.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/util/default_locale.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/util/gregorian.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/util/info.cpp
-      ${BOOST_SOURCES_DIR}/libs/locale/src/util/locale_data.cpp
-      )        
-
-    if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
-        CMAKE_SYSTEM_VERSION STREQUAL "LinuxStandardBase")
-      add_definitions(
-        -DBOOST_LOCALE_NO_WINAPI_BACKEND=1
-        -DBOOST_LOCALE_NO_POSIX_BACKEND=1
-        )
-      
-      list(APPEND BOOST_SOURCES
-        ${BOOST_SOURCES_DIR}/libs/locale/src/std/codecvt.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/std/collate.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/std/converter.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/std/numeric.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/std/std_backend.cpp
-        )
-
-      if (BOOST_LOCALE_BACKEND STREQUAL "gcc" OR
-          BOOST_LOCALE_BACKEND STREQUAL "libiconv")
-        add_definitions(-DBOOST_LOCALE_WITH_ICONV=1)
-      elseif (BOOST_LOCALE_BACKEND STREQUAL "icu")
-        add_definitions(-DBOOST_LOCALE_WITH_ICU=1)
-        list(APPEND BOOST_SOURCES ${BOOST_ICU_SOURCES})
-      else()
-        message(FATAL_ERROR "Unsupported value for BOOST_LOCALE_BACKEND: ${BOOST_LOCALE_BACKEND}")
-      endif()
-
-    elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
-            CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
-            CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
-            CMAKE_SYSTEM_NAME STREQUAL "kFreeBSD" OR
-            CMAKE_SYSTEM_NAME STREQUAL "PNaCl" OR
-            CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR
-            CMAKE_SYSTEM_NAME STREQUAL "NaCl64" OR
-            CMAKE_SYSTEM_NAME STREQUAL "Emscripten") # For WebAssembly or asm.js
-      add_definitions(
-        -DBOOST_LOCALE_NO_WINAPI_BACKEND=1
-        -DBOOST_LOCALE_NO_STD_BACKEND=1
-        )
-      
-      list(APPEND BOOST_SOURCES
-        ${BOOST_SOURCES_DIR}/libs/locale/src/posix/codecvt.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/posix/collate.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/posix/converter.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/posix/numeric.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/posix/posix_backend.cpp
-        )
-
-      if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR
-          BOOST_LOCALE_BACKEND STREQUAL "gcc" OR
-          BOOST_LOCALE_BACKEND STREQUAL "libiconv")
-        # In WebAssembly or asm.js, we rely on the version of iconv
-        # that is shipped with the stdlib
-        add_definitions(-DBOOST_LOCALE_WITH_ICONV=1)
-      elseif (BOOST_LOCALE_BACKEND STREQUAL "icu")
-        add_definitions(-DBOOST_LOCALE_WITH_ICU=1)
-        list(APPEND BOOST_SOURCES ${BOOST_ICU_SOURCES})
-      else()
-        message(FATAL_ERROR "Unsupported value for BOOST_LOCALE_BACKEND: ${BOOST_LOCALE_BACKEND}")
-      endif()
-
-    elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
-      add_definitions(
-        -DBOOST_LOCALE_NO_POSIX_BACKEND=1
-        -DBOOST_LOCALE_NO_STD_BACKEND=1
-        )
-
-      list(APPEND BOOST_SOURCES
-        ${BOOST_SOURCES_DIR}/libs/locale/src/win32/collate.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/win32/converter.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/win32/lcid.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/win32/numeric.cpp
-        ${BOOST_SOURCES_DIR}/libs/locale/src/win32/win_backend.cpp
-        )
-
-      # Starting with release 0.8.2, Orthanc statically links against
-      # libiconv on Windows. Indeed, the "WCONV" library of Windows XP
-      # seems not to support properly several codepages (notably
-      # "Latin3", "Hebrew", and "Arabic"). Set "BOOST_LOCALE_BACKEND"
-      # to "wconv" to use WCONV anyway.
-
-      if (BOOST_LOCALE_BACKEND STREQUAL "libiconv")
-        add_definitions(-DBOOST_LOCALE_WITH_ICONV=1)
-      elseif (BOOST_LOCALE_BACKEND STREQUAL "icu")
-        add_definitions(-DBOOST_LOCALE_WITH_ICU=1)
-        list(APPEND BOOST_SOURCES ${BOOST_ICU_SOURCES})
-      elseif (BOOST_LOCALE_BACKEND STREQUAL "wconv")
-        message("Using Window's wconv")
-        add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
-      else()
-        message(FATAL_ERROR "Unsupported value for BOOST_LOCALE_BACKEND on Windows: ${BOOST_LOCALE_BACKEND}")
-      endif()
-
-    else()
-      message(FATAL_ERROR "Support your platform here")
-    endif()
-  endif()
-
-  
-  source_group(ThirdParty\\boost REGULAR_EXPRESSION ${BOOST_SOURCES_DIR}/.*)
-
-endif()
--- a/Resources/Orthanc/Resources/CMake/Compiler.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,260 +0,0 @@
-# This file sets all the compiler-related flags
-
-if (CMAKE_CROSSCOMPILING OR
-    "${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-  # Cross-compilation necessarily implies standalone and static build
-  SET(STATIC_BUILD ON)
-  SET(STANDALONE_BUILD ON)
-endif()
-
-
-if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-  # Cache the environment variables "LSB_CC" and "LSB_CXX" for further
-  # use by "ExternalProject" in CMake
-  SET(CMAKE_LSB_CC $ENV{LSB_CC} CACHE STRING "")
-  SET(CMAKE_LSB_CXX $ENV{LSB_CXX} CACHE STRING "")
-endif()
-
-
-if (CMAKE_COMPILER_IS_GNUCXX)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long")
-
-  # --std=c99 makes libcurl not to compile
-  # -pedantic gives a lot of warnings on OpenSSL 
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros")
-
-  if (CMAKE_CROSSCOMPILING)
-    # http://stackoverflow.com/a/3543845/881731
-    set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -I<CMAKE_CURRENT_SOURCE_DIR> <SOURCE> <OBJECT>")
-  endif()
-
-elseif (MSVC)
-  # Use static runtime under Visual Studio
-  # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace
-  # http://stackoverflow.com/a/6510446
-  foreach(flag_var
-    CMAKE_C_FLAGS_DEBUG
-    CMAKE_CXX_FLAGS_DEBUG
-    CMAKE_C_FLAGS_RELEASE 
-    CMAKE_CXX_FLAGS_RELEASE
-    CMAKE_C_FLAGS_MINSIZEREL 
-    CMAKE_CXX_FLAGS_MINSIZEREL 
-    CMAKE_C_FLAGS_RELWITHDEBINFO 
-    CMAKE_CXX_FLAGS_RELWITHDEBINFO) 
-    string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
-    string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}")
-  endforeach(flag_var)
-
-  # Add /Zm256 compiler option to Visual Studio to fix PCH errors
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm256")
-
-  # New in Orthanc 1.5.5
-  if (MSVC_MULTIPLE_PROCESSES)
-    # "If you omit the processMax argument in the /MP option, the
-    # compiler obtains the number of effective processors from the
-    # operating system, and then creates one process per effective
-    # processor"
-    # https://blog.kitware.com/cmake-building-with-all-your-cores/
-    # https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
-  endif()
-    
-  add_definitions(
-    -D_CRT_SECURE_NO_WARNINGS=1
-    -D_CRT_SECURE_NO_DEPRECATE=1
-    )
-
-  if (MSVC_VERSION LESS 1600)
-    # Starting with Visual Studio >= 2010 (i.e. macro _MSC_VER >=
-    # 1600), Microsoft ships a standard-compliant <stdint.h>
-    # header. For earlier versions of Visual Studio, give access to a
-    # compatibility header.
-    # http://stackoverflow.com/a/70630/881731
-    # https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h#External_links
-    include_directories(${ORTHANC_ROOT}/Resources/ThirdParty/VisualStudio)
-  endif()
-
-  link_libraries(netapi32)
-endif()
-
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
-    ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-  # In FreeBSD/OpenBSD, the "/usr/local/" folder contains the ports and need to be imported
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/local/include")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/local/include")
-  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/lib")
-  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L/usr/local/lib")
-endif()
-
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
-    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
-    ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
-    ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-
-  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND
-      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
-    # The "--no-undefined" linker flag makes the shared libraries
-    # (plugins ModalityWorklists and ServeFolders) fail to compile on
-    # OpenBSD, and make the PostgreSQL plugin complain about missing
-    # "environ" global variable in FreeBSD
-    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
-    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
-  endif()
-
-  if (NOT DEFINED ENABLE_PLUGINS_VERSION_SCRIPT OR 
-      ENABLE_PLUGINS_VERSION_SCRIPT)
-    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${ORTHANC_ROOT}/Plugins/Samples/Common/VersionScript.map")
-  endif()
-
-  # Remove the "-rdynamic" option
-  # http://www.mail-archive.com/cmake@cmake.org/msg08837.html
-  set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
-  link_libraries(pthread)
-
-  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-    link_libraries(rt)
-  endif()
-
-  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
-      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-    link_libraries(dl)
-  endif()
-
-  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
-      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-    # The "--as-needed" linker flag is not available on FreeBSD and OpenBSD
-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
-    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed")
-    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
-  endif()
-
-  if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND
-      NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-    # FreeBSD/OpenBSD have just one single interface for file
-    # handling, which is 64bit clean, so there is no need to define macro
-    # for LFS (Large File Support).
-    # https://ohse.de/uwe/articles/lfs.html
-    add_definitions(
-      -D_LARGEFILE64_SOURCE=1 
-      -D_FILE_OFFSET_BITS=64
-      )
-  endif()
-
-elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-  if (MSVC)
-    message("MSVC compiler version = " ${MSVC_VERSION} "\n")
-    # Starting Visual Studio 2013 (version 1800), it is not possible
-    # to target Windows XP anymore
-    if (MSVC_VERSION LESS 1800)
-      add_definitions(
-        -DWINVER=0x0501
-        -D_WIN32_WINNT=0x0501
-        )
-    endif()
-  else()
-    add_definitions(
-      -DWINVER=0x0501
-      -D_WIN32_WINNT=0x0501
-      )
-  endif()
-
-  add_definitions(
-    -D_CRT_SECURE_NO_WARNINGS=1
-    )
-  link_libraries(rpcrt4 ws2_32)
-
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    # Some additional C/C++ compiler flags for MinGW
-    SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable")
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
-    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}")
-
-    if (DYNAMIC_MINGW_STDLIB)
-    else()
-      # This is a patch for MinGW64
-      SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
-      SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
-    endif()
-
-    CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD)
-    if (HAVE_WIN_PTHREAD)
-      if (DYNAMIC_MINGW_STDLIB)
-      else()
-        # This line is necessary to compile with recent versions of MinGW,
-        # otherwise "libwinpthread-1.dll" is not statically linked.
-        SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic")
-      endif()
-      add_definitions(-DHAVE_WIN_PTHREAD=1)
-    else()
-      add_definitions(-DHAVE_WIN_PTHREAD=0)
-    endif()
-  endif()
-
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
-  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${ORTHANC_ROOT}/Plugins/Samples/Common/ExportedSymbols.list")
-
-  add_definitions(
-    -D_XOPEN_SOURCE=1
-    )
-  link_libraries(iconv)
-
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
-  message("Building using Emscripten (for WebAssembly or asm.js targets)")
-
-  # The BINARYEN_TRAP_MODE specifies what to do when divisions per
-  # zero (and similar conditions like integer overflows) are
-  # encountered: The "clamp" mode avoids throwing errors, as they
-  # cannot be properly catched by "try {} catch (...)" constructions.
-  # Setting this option to "ON" fixes error: "shared:ERROR:
-  # BINARYEN_TRAP_MODE is not supported by the LLVM wasm backend" if
-  # using the "upstream" backend of Emscripten.
-  if (NOT EMSCRIPTEN_SET_LLVM_WASM_BACKEND)
-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s BINARYEN_TRAP_MODE='\"clamp\"'")
-  endif()
-
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-  
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Android")
-
-else()
-  message("Unknown target platform: ${CMAKE_SYSTEM_NAME}")
-  message(FATAL_ERROR "Support your platform here")
-endif()
-
-
-if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING)
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg")
-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
-    set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pg")
-    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg")
-  else()
-    message(FATAL_ERROR "Don't know how to enable profiling on your configuration")
-  endif()
-endif()
-
-
-if (CMAKE_COMPILER_IS_GNUCXX)
-  # "When creating a static library using binutils (ar) and there
-  # exist a duplicate object name (e.g. a/Foo.cpp.o, b/Foo.cpp.o), the
-  # resulting static library can end up having only one of the
-  # duplicate objects. [...] This bug only happens if there are many
-  # objects." The trick consists in replacing the "r" argument
-  # ("replace") provided to "ar" (as used in CMake < 3.1) by the "q"
-  # argument ("quick append"). This is because of the fact that CMake
-  # will invoke "ar" several times with several batches of ".o"
-  # objects, and using "r" would overwrite symbols defined in
-  # preceding batches. https://cmake.org/Bug/view.php?id=14874
-  set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> <LINK_FLAGS> q <TARGET> <OBJECTS>")
-endif()
-
-
-if (STATIC_BUILD)
-  add_definitions(-DORTHANC_STATIC=1)
-else()
-  add_definitions(-DORTHANC_STATIC=0)
-endif()
--- a/Resources/Orthanc/Resources/CMake/DownloadPackage.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,261 +0,0 @@
-macro(GetUrlFilename TargetVariable Url)
-  string(REGEX REPLACE "^.*/" "" ${TargetVariable} "${Url}")
-endmacro()
-
-
-macro(GetUrlExtension TargetVariable Url)
-  #string(REGEX REPLACE "^.*/[^.]*\\." "" TMP "${Url}")
-  string(REGEX REPLACE "^.*\\." "" TMP "${Url}")
-  string(TOLOWER "${TMP}" "${TargetVariable}")
-endmacro()
-
-
-
-##
-## Setup the patch command-line tool
-##
-
-if (NOT ORTHANC_DISABLE_PATCH)
-  if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-    set(PATCH_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/../ThirdParty/patch/patch.exe)
-    if (NOT EXISTS ${PATCH_EXECUTABLE})
-      message(FATAL_ERROR "Unable to find the patch.exe tool that is shipped with Orthanc")
-    endif()
-
-  else ()
-    find_program(PATCH_EXECUTABLE patch)
-    if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND")
-      message(FATAL_ERROR "Please install the 'patch' standard command-line tool")
-    endif()
-  endif()
-endif()
-
-
-
-##
-## Check the existence of the required decompression tools
-##
-
-if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-  find_program(ZIP_EXECUTABLE 7z 
-    PATHS 
-    "$ENV{ProgramFiles}/7-Zip"
-    "$ENV{ProgramW6432}/7-Zip"
-    )
-
-  if (${ZIP_EXECUTABLE} MATCHES "ZIP_EXECUTABLE-NOTFOUND")
-    message(FATAL_ERROR "Please install the '7-zip' software (http://www.7-zip.org/)")
-  endif()
-
-else()
-  find_program(UNZIP_EXECUTABLE unzip)
-  if (${UNZIP_EXECUTABLE} MATCHES "UNZIP_EXECUTABLE-NOTFOUND")
-    message(FATAL_ERROR "Please install the 'unzip' package")
-  endif()
-
-  find_program(TAR_EXECUTABLE tar)
-  if (${TAR_EXECUTABLE} MATCHES "TAR_EXECUTABLE-NOTFOUND")
-    message(FATAL_ERROR "Please install the 'tar' package")
-  endif()
-
-  find_program(GUNZIP_EXECUTABLE gunzip)
-  if (${GUNZIP_EXECUTABLE} MATCHES "GUNZIP_EXECUTABLE-NOTFOUND")
-    message(FATAL_ERROR "Please install the 'gzip' package")
-  endif()
-endif()
-
-
-macro(DownloadFile MD5 Url)
-  GetUrlFilename(TMP_FILENAME "${Url}")
-
-  set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}")
-  if (NOT EXISTS "${TMP_PATH}")
-    message("Downloading ${Url}")
-
-    # This fixes issue 6: "I think cmake shouldn't download the
-    # packages which are not in the system, it should stop and let
-    # user know."
-    # https://code.google.com/p/orthanc/issues/detail?id=6
-    if (NOT STATIC_BUILD AND NOT ALLOW_DOWNLOADS)
-      message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON")
-    endif()
-
-    if ("${MD5}" STREQUAL "no-check")
-      message(WARNING "Not checking the MD5 of: ${Url}")
-      file(DOWNLOAD "${Url}" "${TMP_PATH}"
-        SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60
-        STATUS Failure)
-    else()
-      file(DOWNLOAD "${Url}" "${TMP_PATH}"
-        SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60
-        EXPECTED_MD5 "${MD5}" STATUS Failure)
-    endif()
-
-    list(GET Failure 0 Status)
-    if (NOT Status EQUAL 0)
-      message(FATAL_ERROR "Cannot download file: ${Url}")
-    endif()
-    
-  else()
-    message("Using local copy of ${Url}")
-
-    if ("${MD5}" STREQUAL "no-check")
-      message(WARNING "Not checking the MD5 of: ${Url}")
-    else()
-      file(MD5 ${TMP_PATH} ActualMD5)
-      if (NOT "${ActualMD5}" STREQUAL "${MD5}")
-        message(FATAL_ERROR "The MD5 hash of a previously download file is invalid: ${TMP_PATH}")
-      endif()
-    endif()
-  endif()
-endmacro()
-
-
-macro(DownloadPackage MD5 Url TargetDirectory)
-  if (NOT IS_DIRECTORY "${TargetDirectory}")
-    DownloadFile("${MD5}" "${Url}")
-    
-    GetUrlExtension(TMP_EXTENSION "${Url}")
-    #message(${TMP_EXTENSION})
-    message("Uncompressing ${TMP_FILENAME}")
-
-    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-      # How to silently extract files using 7-zip
-      # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
-
-      if (("${TMP_EXTENSION}" STREQUAL "gz") OR 
-          ("${TMP_EXTENSION}" STREQUAL "tgz") OR
-          ("${TMP_EXTENSION}" STREQUAL "xz"))
-        execute_process(
-          COMMAND ${ZIP_EXECUTABLE} e -y ${TMP_PATH}
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          OUTPUT_QUIET
-          )
-
-        if (Failure)
-          message(FATAL_ERROR "Error while running the uncompression tool")
-        endif()
-
-        if ("${TMP_EXTENSION}" STREQUAL "tgz")
-          string(REGEX REPLACE ".tgz$" ".tar" TMP_FILENAME2 "${TMP_FILENAME}")
-        elseif ("${TMP_EXTENSION}" STREQUAL "gz")
-          string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}")
-        elseif ("${TMP_EXTENSION}" STREQUAL "xz")
-          string(REGEX REPLACE ".xz" "" TMP_FILENAME2 "${TMP_FILENAME}")
-        endif()
-
-        execute_process(
-          COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_FILENAME2}
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          OUTPUT_QUIET
-          )
-      elseif ("${TMP_EXTENSION}" STREQUAL "zip")
-        execute_process(
-          COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_PATH}
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          OUTPUT_QUIET
-          )
-      else()
-        message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}")
-      endif()
-
-    else()
-      if ("${TMP_EXTENSION}" STREQUAL "zip")
-        execute_process(
-          COMMAND sh -c "${UNZIP_EXECUTABLE} -q ${TMP_PATH}"
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-        )
-      elseif (("${TMP_EXTENSION}" STREQUAL "gz") OR ("${TMP_EXTENSION}" STREQUAL "tgz"))
-        #message("tar xvfz ${TMP_PATH}")
-        execute_process(
-          COMMAND sh -c "${TAR_EXECUTABLE} xfz ${TMP_PATH}"
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          )
-      elseif ("${TMP_EXTENSION}" STREQUAL "bz2")
-        execute_process(
-          COMMAND sh -c "${TAR_EXECUTABLE} xfj ${TMP_PATH}"
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          )
-      elseif ("${TMP_EXTENSION}" STREQUAL "xz")
-        execute_process(
-          COMMAND sh -c "${TAR_EXECUTABLE} xf ${TMP_PATH}"
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          )
-      else()
-        message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}")
-      endif()
-    endif()
-   
-    if (Failure)
-      message(FATAL_ERROR "Error while running the uncompression tool")
-    endif()
-
-    if (NOT IS_DIRECTORY "${TargetDirectory}")
-      message(FATAL_ERROR "The package was not uncompressed at the proper location. Check the CMake instructions.")
-    endif()
-  endif()
-endmacro()
-
-
-
-macro(DownloadCompressedFile MD5 Url TargetFile)
-  message(${MD5})
-  message(${Url})
-  message(${TargetFile})
-  if (NOT EXISTS "${TargetFile}")
-    DownloadFile("${MD5}" "${Url}")
-    
-    GetUrlExtension(TMP_EXTENSION "${Url}")
-    #message(${TMP_EXTENSION})
-    message("Uncompressing ${TMP_FILENAME}")
-
-    if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-      # How to silently extract files using 7-zip
-      # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
-
-      if ("${TMP_EXTENSION}" STREQUAL "gz")
-        execute_process(
-          # "-so" writes uncompressed file to stdout
-          COMMAND ${ZIP_EXECUTABLE} e -so -y ${TMP_PATH}
-          OUTPUT_FILE "${TargetFile}"
-          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-          RESULT_VARIABLE Failure
-          OUTPUT_QUIET
-          )
-
-        if (Failure)
-          message(FATAL_ERROR "Error while running the uncompression tool")
-        endif()
-
-      else()
-        message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}")
-      endif()
-
-    else()
-      if ("${TMP_EXTENSION}" STREQUAL "gz")
-        execute_process(
-          COMMAND sh -c "${GUNZIP_EXECUTABLE} -c ${TMP_PATH}"
-          OUTPUT_FILE "${TargetFile}"
-          RESULT_VARIABLE Failure
-          )
-      else()
-        message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}")
-      endif()
-    endif()
-   
-    if (Failure)
-      message(FATAL_ERROR "Error while running the uncompression tool")
-    endif()
-
-    if (NOT EXISTS "${TargetFile}")
-      message(FATAL_ERROR "The file was not uncompressed at the proper location. Check the CMake instructions.")
-    endif()
-  endif()
-endmacro()
--- a/Resources/Orthanc/Resources/CMake/JsonCppConfiguration.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-set(JSONCPP_CXX11 OFF)
-
-if (STATIC_BUILD OR NOT USE_SYSTEM_JSONCPP)
-  if (USE_LEGACY_JSONCPP)
-    set(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-0.10.7)
-    set(JSONCPP_URL "http://orthanc.osimis.io/ThirdPartyDownloads/jsoncpp-0.10.7.tar.gz")
-    set(JSONCPP_MD5 "3a8072ca6a1fa9cbaf7715ae625f134f")
-    add_definitions(-DORTHANC_LEGACY_JSONCPP=1)
-  else()
-    set(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-1.8.4)
-    set(JSONCPP_URL "http://orthanc.osimis.io/ThirdPartyDownloads/jsoncpp-1.8.4.tar.gz")
-    set(JSONCPP_MD5 "fa47a3ab6b381869b6a5f20811198662")
-    add_definitions(-DORTHANC_LEGACY_JSONCPP=0)
-    set(JSONCPP_CXX11 ON)
-  endif()
-
-  DownloadPackage(${JSONCPP_MD5} ${JSONCPP_URL} "${JSONCPP_SOURCES_DIR}")
-
-  set(JSONCPP_SOURCES
-    ${JSONCPP_SOURCES_DIR}/src/lib_json/json_reader.cpp
-    ${JSONCPP_SOURCES_DIR}/src/lib_json/json_value.cpp
-    ${JSONCPP_SOURCES_DIR}/src/lib_json/json_writer.cpp
-    )
-
-  include_directories(
-    ${JSONCPP_SOURCES_DIR}/include
-    )
-
-  if (NOT ENABLE_LOCALE)
-    add_definitions(-DJSONCPP_NO_LOCALE_SUPPORT=1)
-  endif()
-    
-  source_group(ThirdParty\\JsonCpp REGULAR_EXPRESSION ${JSONCPP_SOURCES_DIR}/.*)
-
-else()
-  find_path(JSONCPP_INCLUDE_DIR json/reader.h
-    /usr/include/jsoncpp
-    /usr/local/include/jsoncpp
-    )
-
-  message("JsonCpp include dir: ${JSONCPP_INCLUDE_DIR}")
-  include_directories(${JSONCPP_INCLUDE_DIR})
-  link_libraries(jsoncpp)
-
-  CHECK_INCLUDE_FILE_CXX(${JSONCPP_INCLUDE_DIR}/json/reader.h HAVE_JSONCPP_H)
-  if (NOT HAVE_JSONCPP_H)
-    message(FATAL_ERROR "Please install the libjsoncpp-dev package")
-  endif()
-
-  # Switch to the C++11 standard if the version of JsonCpp is 1.y.z
-  if (EXISTS ${JSONCPP_INCLUDE_DIR}/json/version.h)
-    file(STRINGS
-      "${JSONCPP_INCLUDE_DIR}/json/version.h" 
-      JSONCPP_VERSION_MAJOR1 REGEX
-      ".*define JSONCPP_VERSION_MAJOR.*")
-
-    if (NOT JSONCPP_VERSION_MAJOR1)
-      message(FATAL_ERROR "Unable to extract the major version of JsonCpp")
-    endif()
-    
-    string(REGEX REPLACE
-      ".*JSONCPP_VERSION_MAJOR.*([0-9]+)$" "\\1" 
-      JSONCPP_VERSION_MAJOR ${JSONCPP_VERSION_MAJOR1})
-    message("JsonCpp major version: ${JSONCPP_VERSION_MAJOR}")
-
-    if (JSONCPP_VERSION_MAJOR GREATER 0)
-      set(JSONCPP_CXX11 ON)
-    endif()
-  else()
-    message("Unable to detect the major version of JsonCpp, assuming < 1.0.0")
-  endif()
-endif()
-
-
-if (JSONCPP_CXX11)
-  # Osimis has encountered problems when this macro is left at its
-  # default value (1000), so we increase this limit
-  # https://gitlab.kitware.com/third-party/jsoncpp/commit/56df2068470241f9043b676bfae415ed62a0c172
-  add_definitions(-DJSONCPP_DEPRECATED_STACK_LIMIT=5000)
-
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    message("Switching to C++11 standard in gcc, as version of JsonCpp is >= 1.0.0")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wno-deprecated-declarations")
-  elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-    message("Switching to C++11 standard in clang, as version of JsonCpp is >= 1.0.0")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated-declarations")
-  endif()
-endif()
--- a/Resources/Orthanc/Resources/LinuxStandardBaseToolchain.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-#
-# Full build, as used on the BuildBot CIS:
-#
-#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu -DENABLE_PKCS11=ON -G Ninja
-#
-# Or, more lightweight version (without libp11 and ICU):
-#
-#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -G Ninja
-#
-
-INCLUDE(CMakeForceCompiler)
-
-SET(LSB_PATH $ENV{LSB_PATH})
-SET(LSB_CC $ENV{LSB_CC})
-SET(LSB_CXX $ENV{LSB_CXX})
-SET(LSB_TARGET_VERSION "4.0")
-
-IF ("${LSB_PATH}" STREQUAL "")
-  SET(LSB_PATH "/opt/lsb")
-ENDIF()
-
-IF (EXISTS ${LSB_PATH}/lib64)
-  SET(LSB_TARGET_PROCESSOR "x86_64")
-  SET(LSB_LIBPATH ${LSB_PATH}/lib64-${LSB_TARGET_VERSION})
-ELSEIF (EXISTS ${LSB_PATH}/lib)
-  SET(LSB_TARGET_PROCESSOR "x86")
-  SET(LSB_LIBPATH ${LSB_PATH}/lib-${LSB_TARGET_VERSION})
-ELSE()
-  MESSAGE(FATAL_ERROR "Unable to detect the target processor architecture. Check the LSB_PATH environment variable.")
-ENDIF()
-
-SET(LSB_CPPPATH ${LSB_PATH}/include)
-SET(PKG_CONFIG_PATH ${LSB_LIBPATH}/pkgconfig/)
-
-# the name of the target operating system
-SET(CMAKE_SYSTEM_NAME Linux)
-SET(CMAKE_SYSTEM_VERSION LinuxStandardBase)
-SET(CMAKE_SYSTEM_PROCESSOR ${LSB_TARGET_PROCESSOR})
-
-# which compilers to use for C and C++
-SET(CMAKE_C_COMPILER ${LSB_PATH}/bin/lsbcc)
-
-if (${CMAKE_VERSION} VERSION_LESS "3.6.0") 
-  CMAKE_FORCE_CXX_COMPILER(${LSB_PATH}/bin/lsbc++ GNU)
-else()
-  SET(CMAKE_CXX_COMPILER ${LSB_PATH}/bin/lsbc++)
-endif()
-
-# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH ${LSB_PATH})
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search 
-# programs in the host environment
-SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
-SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
-
-SET(CMAKE_CROSSCOMPILING OFF)
-
-
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include" CACHE INTERNAL "" FORCE)
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward" CACHE INTERNAL "" FORCE)
-SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE)
-SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE)
-
-if (NOT "${LSB_CXX}" STREQUAL "")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cxx=${LSB_CXX}")
-  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}")
-  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}")
-endif()
-
-if (NOT "${LSB_CC}" STREQUAL "")
-  SET(CMAKE_C_FLAGS "${CMAKE_CC_FLAGS} --lsb-cc=${LSB_CC}")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cc=${LSB_CC}")
-  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cc=${LSB_CC}")
-  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cc=${LSB_CC}")
-endif()
-
--- a/Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-# the name of the target operating system
-set(CMAKE_SYSTEM_NAME Windows)
-
-# which compilers to use for C and C++
-set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
-set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
-set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
-
-# here is the target environment located
-set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search 
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- a/Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-# the name of the target operating system
-set(CMAKE_SYSTEM_NAME Windows)
-
-# which compilers to use for C and C++
-set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
-set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
-set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
-
-# here is the target environment located
-set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search 
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- a/Resources/Orthanc/Resources/MinGWToolchain.cmake	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-# the name of the target operating system
-set(CMAKE_SYSTEM_NAME Windows)
-
-# which compilers to use for C and C++
-set(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
-set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++)
-set(CMAKE_RC_COMPILER i586-mingw32msvc-windres)
-
-# here is the target environment located
-set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search 
-# programs in the host environment
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE)
--- a/Resources/Orthanc/Resources/Patches/boost-1.69.0-linux-standard-base.patch	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-diff -urEb boost_1_69_0.orig/boost/move/adl_move_swap.hpp boost_1_69_0/boost/move/adl_move_swap.hpp
---- boost_1_69_0.orig/boost/move/adl_move_swap.hpp	2019-02-22 15:05:32.682359994 +0100
-+++ boost_1_69_0/boost/move/adl_move_swap.hpp	2019-02-22 15:05:48.426358034 +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_69_0.orig/boost/system/detail/system_category_win32.hpp boost_1_69_0/boost/system/detail/system_category_win32.hpp
---- boost_1_69_0.orig/boost/system/detail/system_category_win32.hpp	2019-02-22 15:05:32.722359989 +0100
-+++ boost_1_69_0/boost/system/detail/system_category_win32.hpp	2019-02-22 15:06:31.922352713 +0100
-@@ -26,7 +26,7 @@
- namespace detail
- {
- 
--#if ( defined(_MSC_VER) && _MSC_VER < 1900 ) || ( defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) )
-+#if ( defined(_MSC_VER) && _MSC_VER < 1900 ) || ( defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) ) || 1  /* std::snprintf() does not seem to exist on Visual Studio 2015 */
- 
- inline char const * unknown_message_win32( int ev, char * buffer, std::size_t len )
- {
-diff -urEb boost_1_69_0.orig/boost/thread/detail/config.hpp boost_1_69_0/boost/thread/detail/config.hpp
---- boost_1_69_0.orig/boost/thread/detail/config.hpp	2019-02-22 15:05:32.598360004 +0100
-+++ boost_1_69_0/boost/thread/detail/config.hpp	2019-02-22 15:05:48.426358034 +0100
-@@ -418,7 +418,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(__ANDROID__) || defined(__LSB_VERSION__)
-   #define BOOST_THREAD_HAS_MONO_CLOCK
-   #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
-     #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO
-diff -urEb boost_1_69_0.orig/boost/type_traits/detail/has_postfix_operator.hpp boost_1_69_0/boost/type_traits/detail/has_postfix_operator.hpp
---- boost_1_69_0.orig/boost/type_traits/detail/has_postfix_operator.hpp	2019-02-22 15:05:32.650359998 +0100
-+++ boost_1_69_0/boost/type_traits/detail/has_postfix_operator.hpp	2019-02-22 15:05:48.426358034 +0100
-@@ -85,8 +85,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
-@@ -234,7 +237,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_69_0.orig/boost/type_traits/detail/has_prefix_operator.hpp boost_1_69_0/boost/type_traits/detail/has_prefix_operator.hpp
---- boost_1_69_0.orig/boost/type_traits/detail/has_prefix_operator.hpp	2019-02-22 15:05:32.650359998 +0100
-+++ boost_1_69_0/boost/type_traits/detail/has_prefix_operator.hpp	2019-02-22 15:05:48.426358034 +0100
-@@ -114,8 +114,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
-@@ -263,7 +266,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 -urEb boost_1_69_0.orig/libs/filesystem/src/operations.cpp boost_1_69_0/libs/filesystem/src/operations.cpp
---- boost_1_69_0.orig/libs/filesystem/src/operations.cpp	2019-02-22 15:05:32.566360008 +0100
-+++ boost_1_69_0/libs/filesystem/src/operations.cpp	2019-02-22 18:04:17.346573047 +0100
-@@ -2111,9 +2111,16 @@
-     std::size_t path_size (0);  // initialization quiets gcc warning (ticket #3509)
-     error_code ec = path_max(path_size);
-     if (ec)return ec;
--    dirent de;
--    buffer = std::malloc((sizeof(dirent) - sizeof(de.d_name))
--      +  path_size + 1); // + 1 for "/0"
-+
-+    // Fixed possible use of uninitialized dirent::d_type in dir_iterator
-+    // https://github.com/boostorg/filesystem/commit/bbe9d1771e5d679b3f10c42a58fc81f7e8c024a9
-+    const std::size_t buffer_size = (sizeof(dirent) - sizeof(dirent().d_name))
-+      +  path_size + 1; // + 1 for "\0"
-+    buffer = std::malloc(buffer_size);
-+    if (BOOST_UNLIKELY(!buffer))
-+      return make_error_code(boost::system::errc::not_enough_memory);
-+    std::memset(buffer, 0, buffer_size);
-+    
-     return ok;
-   }  
- 
-@@ -2142,6 +2149,13 @@
-     *result = 0;
-     if ((p = ::readdir(dirp))== 0)
-       return errno;
-+
-+    // Fixed possible use of uninitialized dirent::d_type in dir_iterator
-+    // https://github.com/boostorg/filesystem/commit/bbe9d1771e5d679b3f10c42a58fc81f7e8c024a9    
-+#   ifdef BOOST_FILESYSTEM_STATUS_CACHE
-+    entry->d_type = p->d_type;
-+#   endif
-+
-     std::strcpy(entry->d_name, p->d_name);
-     *result = entry;
-     return 0;
--- a/Resources/Orthanc/Resources/ThirdParty/patch/NOTES.txt	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-===========
-INFORMATION
-===========
-
-This is a precompiled version of the "patch" standard tool for
-Windows. It was compiled using the MSYS framework.
-
-The binaries originate from the "Git for Windows 1.9.5" package
-(https://msysgit.github.io/). The build instructions have been
-provided on the discussion group of Git for Windows [1]. They are
-copied/pasted below for reference.
-
-
-
-================
-UPSTREAM PROJECT
-================
-
-URL to the upstream project:
-http://savannah.gnu.org/projects/patch/
-
-License of patch: GPLv2 (GNU General Public License v2)
-
-Copyright (C) 1988 Larry Wall "with lots o' patches by Paul Eggert"
-Copyright (C) 1997 Free Software Foundation, Inc.
-
-
-
-======================
-BUILD INSTRUCTIONS [1]
-======================
-
-The easiest way to find out about this is to install the Git SDK, then 
-run 
-
-     pacman -Qu $(which patch.exe) 
-
-to find out which package contains the `patch.exe` binary. It so happens 
-to be patch.2.7.5-1 at the moment. Since this is an MSys2 package (not a 
-MinGW one, otherwise the patch utility would be in /mingw64/bin/, not 
-/usr/bin/), this package is built from the recipes in 
-
-     https://github.com/msys2/MSYS2-packages 
-
-The `patch` package is obviously built from the subdirectory 
-
-     https://github.com/Alexpux/MSYS2-packages/tree/master/patch 
-
-and the PKGBUILD file specifies that the source is fetched from 
-ftp://ftp.gnu.org/gnu/patch/patch-2.7.5.tar.xz: 
-
-https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/PKGBUILD#L14 
-
-and then these two patches are applied before building: 
-     
-https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/msys2-patch-2.7.1.patch 
-
-and 
-      
-https://github.com/Alexpux/MSYS2-packages/blob/900744becd072f687029b0f830ab6fe95cf533d6/patch/msys2-patch-manifest.patch 
-
-As you can see, some light changes are applied, i.e. `patch.exe` will 
-always write in binary mode with MSys2, and the executable will have a 
-manifest embedded that allows it to run as non-administrator. 
-
-Ciao, 
-Johannes Schindelin
-
-
-[1] https://groups.google.com/d/msg/git-for-windows/xWyVr4z6Ri0/6RKeV028EAAJ
Binary file Resources/Orthanc/Resources/ThirdParty/patch/msys-1.0.dll has changed
Binary file Resources/Orthanc/Resources/ThirdParty/patch/patch.exe has changed
--- a/Resources/Orthanc/Resources/ThirdParty/patch/patch.exe.manifest	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <assemblyIdentity version="7.95.0.0"
-     processorArchitecture="X86"
-     name="patch.exe"
-     type="win32"/>
-
-  <!-- Identify the application security requirements. -->
-  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
-    <security>
-      <requestedPrivileges>
-        <requestedExecutionLevel
-          level="asInvoker"
-          uiAccess="false"/>
-        </requestedPrivileges>
-       </security>
-  </trustInfo>
-</assembly>
-
--- a/Resources/Orthanc/Resources/WindowsResources.py	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#!/usr/bin/python
-
-# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
-# Department, University Hospital of Liege, Belgium
-# Copyright (C) 2017-2020 Osimis S.A., Belgium
-#
-# This program is free software: you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders of this
-# program give permission to link the code of its release with the
-# OpenSSL project's "OpenSSL" library (or with modified versions of it
-# that use the same license as the "OpenSSL" library), and distribute
-# the linked executables. You must obey the GNU General Public License
-# in all respects for all of the code used other than "OpenSSL". If you
-# modify file(s) with this exception, you may extend this exception to
-# your version of the file(s), but you are not obligated to do so. If
-# you do not wish to do so, delete this exception statement from your
-# version. If you delete this exception statement from all source files
-# in the program, then also delete it here.
-# 
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import os
-import sys
-import datetime
-
-if len(sys.argv) != 5:
-    sys.stderr.write('Usage: %s <Version> <ProductName> <Filename> <Description>\n\n' % sys.argv[0])
-    sys.stderr.write('Example: %s 0.9.1 Orthanc Orthanc.exe "Lightweight, RESTful DICOM server for medical imaging"\n' % sys.argv[0])
-    sys.exit(-1)
-
-SOURCE = os.path.join(os.path.dirname(__file__), 'WindowsResources.rc')
-
-VERSION = sys.argv[1]
-PRODUCT = sys.argv[2]
-FILENAME = sys.argv[3]
-DESCRIPTION = sys.argv[4]
-
-if VERSION == 'mainline':
-    VERSION = '999.999.999'
-    RELEASE = 'This is a mainline build, not an official release'
-else:
-    RELEASE = 'Release %s' % VERSION
-
-v = VERSION.split('.')
-if len(v) != 2 and len(v) != 3:
-    sys.stderr.write('Bad version number: %s\n' % VERSION)
-    sys.exit(-1)
-
-if len(v) == 2:
-    v.append('0')
-
-extension = os.path.splitext(FILENAME)[1]
-if extension.lower() == '.dll':
-    BLOCK = '040904E4'
-    TYPE = 'VFT_DLL'
-elif extension.lower() == '.exe':
-    #BLOCK = '040904B0'   # LANG_ENGLISH/SUBLANG_ENGLISH_US,
-    BLOCK = '040904E4'   # Lang=US English, CharSet=Windows Multilingual
-    TYPE = 'VFT_APP'
-else:
-    sys.stderr.write('Unsupported extension (.EXE or .DLL only): %s\n' % extension)
-    sys.exit(-1)
-
-
-with open(SOURCE, 'r') as source:
-    content = source.read()
-    content = content.replace('${VERSION_MAJOR}', v[0])
-    content = content.replace('${VERSION_MINOR}', v[1])
-    content = content.replace('${VERSION_PATCH}', v[2])
-    content = content.replace('${RELEASE}', RELEASE)
-    content = content.replace('${DESCRIPTION}', DESCRIPTION)
-    content = content.replace('${PRODUCT}', PRODUCT)   
-    content = content.replace('${FILENAME}', FILENAME)   
-    content = content.replace('${YEAR}', str(datetime.datetime.now().year))
-    content = content.replace('${BLOCK}', BLOCK)
-    content = content.replace('${TYPE}', TYPE)
-
-    sys.stdout.write(content)
--- a/Resources/Orthanc/Resources/WindowsResources.rc	Thu May 28 21:02:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
-   FILEVERSION ${VERSION_MAJOR},${VERSION_MINOR},0,${VERSION_PATCH}
-   PRODUCTVERSION ${VERSION_MAJOR},${VERSION_MINOR},0,0
-   FILEOS VOS_NT_WINDOWS32
-   FILETYPE ${TYPE}
-   BEGIN
-      BLOCK "StringFileInfo"
-      BEGIN
-         BLOCK "${BLOCK}"
-         BEGIN
-            VALUE "Comments", "${RELEASE}"
-            VALUE "CompanyName", "Osimis SA, Belgium"
-            VALUE "FileDescription", "${DESCRIPTION}"
-            VALUE "FileVersion", "${VERSION_MAJOR}.${VERSION_MINOR}.0.${VERSION_PATCH}"
-            VALUE "InternalName", "${PRODUCT}"
-            VALUE "LegalCopyright", "(c) 2012-${YEAR}, Sebastien Jodogne, University Hospital of Liege, and Osimis SA, Belgium"
-            VALUE "LegalTrademarks", "Licensing information is available at http://www.orthanc-server.com/"
-            VALUE "OriginalFilename", "${FILENAME}"
-            VALUE "ProductName", "${PRODUCT}"
-            VALUE "ProductVersion", "${VERSION_MAJOR}.${VERSION_MINOR}"
-         END
-      END
-
-      BLOCK "VarFileInfo"
-      BEGIN
-        VALUE "Translation", 0x409, 1252  // U.S. English
-      END
-   END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Toolchains/LinuxStandardBaseToolchain.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,79 @@
+#
+# Full build, as used on the BuildBot CIS:
+#
+#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu -DENABLE_PKCS11=ON -G Ninja
+#
+# Or, more lightweight version (without libp11 and ICU):
+#
+#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -G Ninja
+#
+
+INCLUDE(CMakeForceCompiler)
+
+SET(LSB_PATH $ENV{LSB_PATH} CACHE STRING "")
+SET(LSB_CC $ENV{LSB_CC} CACHE STRING "")
+SET(LSB_CXX $ENV{LSB_CXX} CACHE STRING "")
+SET(LSB_TARGET_VERSION "4.0" CACHE STRING "")
+
+IF ("${LSB_PATH}" STREQUAL "")
+  SET(LSB_PATH "/opt/lsb")
+ENDIF()
+
+IF (EXISTS ${LSB_PATH}/lib64)
+  SET(LSB_TARGET_PROCESSOR "x86_64")
+  SET(LSB_LIBPATH ${LSB_PATH}/lib64-${LSB_TARGET_VERSION})
+ELSEIF (EXISTS ${LSB_PATH}/lib)
+  SET(LSB_TARGET_PROCESSOR "x86")
+  SET(LSB_LIBPATH ${LSB_PATH}/lib-${LSB_TARGET_VERSION})
+ELSE()
+  MESSAGE(FATAL_ERROR "Unable to detect the target processor architecture. Check the LSB_PATH environment variable.")
+ENDIF()
+
+SET(LSB_CPPPATH ${LSB_PATH}/include)
+SET(PKG_CONFIG_PATH ${LSB_LIBPATH}/pkgconfig/)
+
+# the name of the target operating system
+SET(CMAKE_SYSTEM_NAME Linux)
+SET(CMAKE_SYSTEM_VERSION LinuxStandardBase)
+SET(CMAKE_SYSTEM_PROCESSOR ${LSB_TARGET_PROCESSOR})
+
+# which compilers to use for C and C++
+SET(CMAKE_C_COMPILER ${LSB_PATH}/bin/lsbcc)
+
+if (${CMAKE_VERSION} VERSION_LESS "3.6.0") 
+  CMAKE_FORCE_CXX_COMPILER(${LSB_PATH}/bin/lsbc++ GNU)
+else()
+  SET(CMAKE_CXX_COMPILER ${LSB_PATH}/bin/lsbc++)
+endif()
+
+# here is the target environment located
+SET(CMAKE_FIND_ROOT_PATH ${LSB_PATH})
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
+
+SET(CMAKE_CROSSCOMPILING OFF)
+
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include" CACHE INTERNAL "" FORCE)
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward" CACHE INTERNAL "" FORCE)
+SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE)
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE)
+
+if (NOT "${LSB_CXX}" STREQUAL "")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cxx=${LSB_CXX}")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}")
+  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}")
+endif()
+
+if (NOT "${LSB_CC}" STREQUAL "")
+  SET(CMAKE_C_FLAGS "${CMAKE_CC_FLAGS} --lsb-cc=${LSB_CC}")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cc=${LSB_CC}")
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cc=${LSB_CC}")
+  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cc=${LSB_CC}")
+endif()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,17 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,17 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Toolchains/MinGWToolchain.cmake	Mon Jul 06 17:37:30 2020 +0200
@@ -0,0 +1,20 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
+set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++)
+set(CMAKE_RC_COMPILER i586-mingw32msvc-windres)
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE)
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE)
--- a/Resources/SyncOrthancFolder.py	Thu May 28 21:02:23 2020 +0200
+++ b/Resources/SyncOrthancFolder.py	Mon Jul 06 17:37:30 2020 +0200
@@ -16,26 +16,22 @@
 REPOSITORY = 'https://hg.orthanc-server.com/orthanc/raw-file'
 
 FILES = [
-    'Core/Compatibility.h',
-    'Plugins/Samples/Common/OrthancPluginCppWrapper.cpp',
-    'Plugins/Samples/Common/OrthancPluginCppWrapper.h',
-    'Plugins/Samples/Common/OrthancPluginException.h',
-    'Plugins/Samples/Common/VersionScript.map',
-    'Resources/CMake/BoostConfiguration.cmake',
-    'Resources/CMake/Compiler.cmake',
-    'Resources/CMake/DownloadPackage.cmake',
-    'Resources/CMake/JsonCppConfiguration.cmake',
-    'Resources/LinuxStandardBaseToolchain.cmake',
-    'Resources/MinGW-W64-Toolchain32.cmake',
-    'Resources/MinGW-W64-Toolchain64.cmake',
-    'Resources/MinGWToolchain.cmake',
-    'Resources/Patches/boost-1.69.0-linux-standard-base.patch',
-    'Resources/ThirdParty/patch/NOTES.txt',
-    'Resources/ThirdParty/patch/msys-1.0.dll',
-    'Resources/ThirdParty/patch/patch.exe',
-    'Resources/ThirdParty/patch/patch.exe.manifest',
-    'Resources/WindowsResources.py',
-    'Resources/WindowsResources.rc',
+    ('OrthancFramework/Resources/CMake/AutoGeneratedCode.cmake', 'CMake'),
+    ('OrthancFramework/Resources/CMake/Compiler.cmake', 'CMake'),
+    ('OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake', 'CMake'),
+    ('OrthancFramework/Resources/CMake/DownloadPackage.cmake', 'CMake'),
+
+    ('OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake', 'Toolchains'),
+    ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake', 'Toolchains'),
+    ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain64.cmake', 'Toolchains'),
+    ('OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake', 'Toolchains'),
+
+    ('OrthancServer/Plugins/Samples/Common/ExportedSymbolsPlugins.list', 'Plugins'),
+    ('OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp', 'Plugins'),
+    ('OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h', 'Plugins'),
+    ('OrthancServer/Plugins/Samples/Common/OrthancPluginException.h', 'Plugins'),
+    ('OrthancServer/Plugins/Samples/Common/OrthancPluginsExports.cmake', 'Plugins'),
+    ('OrthancServer/Plugins/Samples/Common/VersionScriptPlugins.map', 'Plugins'),
 ]
 
 SDK = [
@@ -55,19 +51,17 @@
         pass
 
     url = '%s/%s/%s' % (REPOSITORY, branch, source)
-    print('Downloading %s' % url)
 
     with open(target, 'w') as f:
         f.write(urllib2.urlopen(url).read())
 
-    print('Done %s' % url)
-        
-
 
 commands = []
 
 for f in FILES:
-    commands.append([ 'default', f, f ])
+    commands.append([ 'default',
+                      f[0],
+                      os.path.join(f[1], os.path.basename(f[0])) ])
 
 for f in SDK:
     commands.append([
--- a/Sources/Autogenerated/sdk.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/Autogenerated/sdk.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -21,7 +21,7 @@
 
 #include "../PythonLock.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 #include "./sdk_OrthancPluginInstanceOrigin.impl.h"
 #include "./sdk_OrthancPluginJobStepStatus.impl.h"
--- a/Sources/OnChangeCallback.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/OnChangeCallback.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -21,7 +21,9 @@
 
 #include "PythonObject.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
+
+#include <Compatibility.h>  // For std::unique_ptr<>
 
 #include <boost/thread.hpp>
 
@@ -116,7 +118,7 @@
       }
     }
 
-    std::auto_ptr<PendingChange> change(queue_.front());
+    std::unique_ptr<PendingChange> change(queue_.front());
     queue_.pop_front();
 
     return change.release();
--- a/Sources/OnStoredInstanceCallback.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/OnStoredInstanceCallback.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -22,7 +22,7 @@
 #include "PythonObject.h"
 #include "Autogenerated/sdk.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 
 static PyObject*   storedInstanceCallback_ = NULL;
--- a/Sources/Plugin.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/Plugin.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -31,7 +31,7 @@
 
 #include "Autogenerated/sdk.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/filesystem.hpp>
--- a/Sources/PythonFunction.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonFunction.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -21,7 +21,7 @@
 
 #include "PythonModule.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 
 PythonObject* PythonFunction::CallUnchecked(PyObject* args)
--- a/Sources/PythonFunction.h	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonFunction.h	Mon Jul 06 17:37:30 2020 +0200
@@ -19,9 +19,10 @@
 
 #pragma once
 
-#include "../Resources/Orthanc/Core/Compatibility.h"
 #include "PythonObject.h"
 
+#include <Compatibility.h>
+
 #include <memory>
 
 class PythonModule;
--- a/Sources/PythonLock.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonLock.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -22,7 +22,9 @@
 #include "PythonFunction.h"
 #include "PythonModule.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
+
+#include <Compatibility.h>  // For std::unique_ptr<>
 
 #include <boost/thread/mutex.hpp>
 
@@ -137,7 +139,7 @@
         PythonObject args(*this, PyTuple_New(1));
         PyTuple_SetItem(args.GetPyObject(), 0, traceback);
 
-        std::auto_ptr<PythonObject> value(f.CallUnchecked(args.GetPyObject()));
+        std::unique_ptr<PythonObject> value(f.CallUnchecked(args.GetPyObject()));
 
         if (value->IsValid())
         {
--- a/Sources/PythonModule.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonModule.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -21,7 +21,7 @@
 
 #include "PythonString.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 #include <boost/thread/mutex.hpp>
 #include <set>
--- a/Sources/PythonModule.h	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonModule.h	Mon Jul 06 17:37:30 2020 +0200
@@ -1,5 +1,5 @@
 /**
- * Python plugin for Orthanc
+2 * Python plugin for Orthanc
  * Copyright (C) 2017-2020 Osimis S.A., Belgium
  *
  * This program is free software: you can redistribute it and/or
@@ -19,9 +19,10 @@
 
 #pragma once
 
-#include "../Resources/Orthanc/Core/Compatibility.h"
 #include "PythonObject.h"
 
+#include <Compatibility.h>
+
 #include <memory>
 
 class PythonModule : public boost::noncopyable
--- a/Sources/PythonObject.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonObject.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -22,7 +22,7 @@
 
 #include "PythonLock.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 
 PythonObject::PythonObject(PythonLock& lock,
--- a/Sources/PythonString.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/PythonString.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -19,7 +19,7 @@
 
 #include "PythonString.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 PythonString::PythonString(PythonLock& lock,
                            const std::string& utf8) :
--- a/Sources/RestCallbacks.cpp	Thu May 28 21:02:23 2020 +0200
+++ b/Sources/RestCallbacks.cpp	Mon Jul 06 17:37:30 2020 +0200
@@ -22,7 +22,7 @@
 #include "PythonObject.h"
 #include "Autogenerated/sdk.h"
 
-#include <OrthancPluginCppWrapper.h>
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
 #include <boost/regex.hpp>