# HG changeset patch # User Sebastien Jodogne # Date 1594049850 -7200 # Node ID fd58eb5749ed77517c30f6a7fe4ffe5642c3c423 # Parent 77f4e165e5351154240d29efa6a278811edb0815 CMake simplification using DownloadOrthancFramework.cmake diff -r 77f4e165e535 -r fd58eb5749ed CMakeLists.txt --- 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} diff -r 77f4e165e535 -r fd58eb5749ed CodeAnalysis/sdk.cpp.mustache --- 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 +#include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" {{#enumerations}} #include "./sdk_{{name}}.impl.h" diff -r 77f4e165e535 -r fd58eb5749ed NEWS --- 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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/DebianStable/Dockerfile-BuildEnvironment --- 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/* diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/Dockerfile-MinGW-BuildEnvironment --- 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/* diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW32-Python2.7/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW32-Python3.7/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW32-Python3.8/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW64-Python2.7/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW64-Python3.7/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Builders/MinGW64-Python3.8/docker-internal.sh --- 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` diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/CMake/AutoGeneratedCode.cmake --- /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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/CMake/Compiler.cmake --- /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 " -O coff -I ") + 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 + # 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 " q ") +endif() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/CMake/DownloadOrthancFramework.cmake --- /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 . + + + +## +## 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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/CMake/DownloadPackage.cmake --- /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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Core/Compatibility.h --- 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 . - **/ - - -#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 - -namespace std -{ - template - class unique_ptr : public boost::movelib::unique_ptr - { - public: - explicit unique_ptr() : - boost::movelib::unique_ptr() - { - } - - explicit unique_ptr(T* p) : - boost::movelib::unique_ptr(p) - { - } - }; -} -#else -//# pragma message("C++ 11 support is present.") -# include -#endif diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/ExportedSymbolsPlugins.list --- /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 diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp --- /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 . + **/ + + +#include "OrthancPluginCppWrapper.h" + +#include +#include +#include +#include +#include + + +#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(size); + if (static_cast(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(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(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& httpHeaders, + bool applyPlugins) + { + Clear(); + + std::vector headersKeys; + std::vector headersValues; + + for (std::map::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(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(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(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(configuration_[key].asInt()); + return true; + + case Json::uintValue: + target = static_cast(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& 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& target, + const std::string& key, + bool allowSingleString) const + { + std::list lst; + + if (LookupListOfStrings(lst, key, allowSingleString)) + { + target.clear(); + + for (std::list::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& 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& 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(major) + "." + + boost::lexical_cast(minor) + "." + + boost::lexical_cast(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(aa); + unsigned int b = static_cast(bb); + unsigned int c = static_cast(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(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(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(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(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(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(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(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(job); + } + } + + + float OrthancJob::CallbackGetProgress(void* job) + { + assert(job != NULL); + + try + { + return reinterpret_cast(job)->progress_; + } + catch (...) + { + return 0; + } + } + + + const char* OrthancJob::CallbackGetContent(void* job) + { + assert(job != NULL); + + try + { + return reinterpret_cast(job)->content_.c_str(); + } + catch (...) + { + return 0; + } + } + + + const char* OrthancJob::CallbackGetSerialized(void* job) + { + assert(job != NULL); + + try + { + const OrthancJob& tmp = *reinterpret_cast(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(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(job)->Stop(reason); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + + + OrthancPluginErrorCode OrthancJob::CallbackReset(void* job) + { + assert(job != NULL); + + try + { + reinterpret_cast(job)->Reset(); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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(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 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(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(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(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(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(answer)->AddHeader(key, value); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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(answer)->AddChunk(data, size); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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 headersKeys_; + std::vector 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 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(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(reader)->AddChunk(data, size); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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(reader)->Execute(output); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (boost::bad_lexical_cast&) + { + return OrthancPluginErrorCode_BadFileFormat; + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + + + void ChunkedRequestReaderFinalize( + OrthancPluginServerChunkedRequestReader* reader) + { + if (reader != NULL) + { + delete reinterpret_cast(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 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 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(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(rawHandler); + *target = handler.Lookup(sopClassUid, sopInstanceUid); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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(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(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 result(new DicomInstance(instance)); + result->toFree_ = true; + return result.release(); + } + } +#endif +} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h --- /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 . + **/ + + +#pragma once + +#include "OrthancPluginException.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * 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(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& 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& target, + const std::string& key, + bool allowSingleString) const; + + bool LookupSetOfStrings(std::set& 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& 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& 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 + 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(e.GetErrorCode()); + } + catch (boost::bad_lexical_cast&) + { + return OrthancPluginErrorCode_BadFileFormat; + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + } + + + template + void RegisterRestCallback(const std::string& uri, + bool isThreadSafe) + { + if (isThreadSafe) + { + OrthancPluginRegisterRestCallbackNoLock + (GetGlobalContext(), uri.c_str(), Internals::Protect); + } + else + { + OrthancPluginRegisterRestCallback + (GetGlobalContext(), uri.c_str(), Internals::Protect); + } + } + + +#if HAS_ORTHANC_PLUGIN_PEERS == 1 + class OrthancPeers : public boost::noncopyable + { + private: + typedef std::map 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 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 + static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader, + const char* url, + const OrthancPluginHttpRequest* request) + { + try + { + if (reader == NULL) + { + return OrthancPluginErrorCode_InternalError; + } + else + { + *reader = reinterpret_cast(Callback(url, request)); + if (*reader == NULL) + { + return OrthancPluginErrorCode_Plugin; + } + else + { + return OrthancPluginErrorCode_Success; + } + } + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(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, + PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect, + DeleteHandler == Internals::NullRestCallback ? NULL : Internals::Protect, + PutHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect, + Internals::ChunkedRequestReaderAddChunk, + Internals::ChunkedRequestReaderExecute, + Internals::ChunkedRequestReaderFinalize); +#else + OrthancPluginRegisterRestCallbackNoLock( + GetGlobalContext(), uri.c_str(), + Internals::ChunkedRestCompatibility); +#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(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 + }; +} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/OrthancPluginException.h --- /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 . + **/ + + +#pragma once + +#if !defined(HAS_ORTHANC_EXCEPTION) +# error The macro HAS_ORTHANC_EXCEPTION must be defined +#endif + + +#if HAS_ORTHANC_EXCEPTION == 1 +# include +# 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 +# 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(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 +} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/OrthancPluginsExports.cmake --- /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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp --- 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 . - **/ - - -#include "OrthancPluginCppWrapper.h" - -#include -#include -#include -#include -#include - - -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(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(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& httpHeaders, - bool applyPlugins) - { - Clear(); - - std::vector headersKeys; - std::vector headersValues; - - for (std::map::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(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(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(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(configuration_[key].asInt()); - return true; - - case Json::uintValue: - target = static_cast(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& 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& target, - const std::string& key, - bool allowSingleString) const - { - std::list lst; - - if (LookupListOfStrings(lst, key, allowSingleString)) - { - target.clear(); - - for (std::list::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& 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& 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(major) + "." + - boost::lexical_cast(minor) + "." + - boost::lexical_cast(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(aa); - unsigned int b = static_cast(bb); - unsigned int c = static_cast(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(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(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(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(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(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(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(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(job); - } - } - - - float OrthancJob::CallbackGetProgress(void* job) - { - assert(job != NULL); - - try - { - return reinterpret_cast(job)->progress_; - } - catch (...) - { - return 0; - } - } - - - const char* OrthancJob::CallbackGetContent(void* job) - { - assert(job != NULL); - - try - { - return reinterpret_cast(job)->content_.c_str(); - } - catch (...) - { - return 0; - } - } - - - const char* OrthancJob::CallbackGetSerialized(void* job) - { - assert(job != NULL); - - try - { - const OrthancJob& tmp = *reinterpret_cast(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(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(job)->Stop(reason); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(e.GetErrorCode()); - } - catch (...) - { - return OrthancPluginErrorCode_Plugin; - } - } - - - OrthancPluginErrorCode OrthancJob::CallbackReset(void* job) - { - assert(job != NULL); - - try - { - reinterpret_cast(job)->Reset(); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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(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 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(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(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(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(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(answer)->AddHeader(key, value); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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(answer)->AddChunk(data, size); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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 headersKeys_; - std::vector 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 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(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(reader)->AddChunk(data, size); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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(reader)->Execute(output); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(e.GetErrorCode()); - } - catch (boost::bad_lexical_cast&) - { - return OrthancPluginErrorCode_BadFileFormat; - } - catch (...) - { - return OrthancPluginErrorCode_Plugin; - } - } - - - void ChunkedRequestReaderFinalize( - OrthancPluginServerChunkedRequestReader* reader) - { - if (reader != NULL) - { - delete reinterpret_cast(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 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 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(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(rawHandler); - *target = handler.Lookup(sopClassUid, sopInstanceUid); - return OrthancPluginErrorCode_Success; - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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(rawHandler); - } -#endif -} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h --- 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 . - **/ - - -#pragma once - -#include "OrthancPluginException.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -#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(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& 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& target, - const std::string& key, - bool allowSingleString) const; - - bool LookupSetOfStrings(std::set& 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& 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& 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 - 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(e.GetErrorCode()); - } - catch (boost::bad_lexical_cast&) - { - return OrthancPluginErrorCode_BadFileFormat; - } - catch (...) - { - return OrthancPluginErrorCode_Plugin; - } - } - } - - - template - void RegisterRestCallback(const std::string& uri, - bool isThreadSafe) - { - if (isThreadSafe) - { - OrthancPluginRegisterRestCallbackNoLock - (GetGlobalContext(), uri.c_str(), Internals::Protect); - } - else - { - OrthancPluginRegisterRestCallback - (GetGlobalContext(), uri.c_str(), Internals::Protect); - } - } - - -#if HAS_ORTHANC_PLUGIN_PEERS == 1 - class OrthancPeers : public boost::noncopyable - { - private: - typedef std::map 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 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 - static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader, - const char* url, - const OrthancPluginHttpRequest* request) - { - try - { - if (reader == NULL) - { - return OrthancPluginErrorCode_InternalError; - } - else - { - *reader = reinterpret_cast(Callback(url, request)); - if (*reader == NULL) - { - return OrthancPluginErrorCode_Plugin; - } - else - { - return OrthancPluginErrorCode_Success; - } - } - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - return static_cast(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, - PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect, - DeleteHandler == Internals::NullRestCallback ? NULL : Internals::Protect, - PutHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect, - Internals::ChunkedRequestReaderAddChunk, - Internals::ChunkedRequestReaderExecute, - Internals::ChunkedRequestReaderFinalize); -#else - OrthancPluginRegisterRestCallbackNoLock( - GetGlobalContext(), uri.c_str(), - Internals::ChunkedRestCompatibility); -#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 -} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/Samples/Common/OrthancPluginException.h --- 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 . - **/ - - -#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 -# 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(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 -} diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/Samples/Common/VersionScript.map --- 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: - *; -}; diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Plugins/VersionScriptPlugins.map --- /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: + *; +}; diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/CMake/BoostConfiguration.cmake --- 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()" 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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/CMake/Compiler.cmake --- 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 " -O coff -I ") - 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 - # 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 " q ") -endif() - - -if (STATIC_BUILD) - add_definitions(-DORTHANC_STATIC=1) -else() - add_definitions(-DORTHANC_STATIC=0) -endif() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/CMake/DownloadPackage.cmake --- 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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/CMake/JsonCppConfiguration.cmake --- 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() diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/LinuxStandardBaseToolchain.cmake --- 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() - diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/MinGW-W64-Toolchain32.cmake --- 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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/MinGW-W64-Toolchain64.cmake --- 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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/MinGWToolchain.cmake --- 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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/Patches/boost-1.69.0-linux-standard-base.patch --- 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 , as it's quite big - #if defined(_MSC_VER) && defined(BOOST_DINKUMWARE_STDLIB) - #include //Dinkum libraries define std::swap in utility which is lighter than algorithm -+#elif defined(__LSB_VERSION__) -+# include - #elif defined(BOOST_GNU_STDLIB) - //For non-GCC compilers, where GNUC version is not very reliable, or old GCC versions - //use the good old stl_algobase header, which is quite lightweight -diff -urEb boost_1_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; diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/ThirdParty/patch/NOTES.txt --- 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 diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/ThirdParty/patch/msys-1.0.dll Binary file Resources/Orthanc/Resources/ThirdParty/patch/msys-1.0.dll has changed diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/ThirdParty/patch/patch.exe Binary file Resources/Orthanc/Resources/ThirdParty/patch/patch.exe has changed diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/ThirdParty/patch/patch.exe.manifest --- 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 @@ - - - - - - - - - - - - - - diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/WindowsResources.py --- 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 . - - -import os -import sys -import datetime - -if len(sys.argv) != 5: - sys.stderr.write('Usage: %s \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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Resources/WindowsResources.rc --- 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 - -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 diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Toolchains/LinuxStandardBaseToolchain.cmake --- /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() + diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake --- /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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake --- /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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/Orthanc/Toolchains/MinGWToolchain.cmake --- /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) diff -r 77f4e165e535 -r fd58eb5749ed Resources/SyncOrthancFolder.py --- 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([ diff -r 77f4e165e535 -r fd58eb5749ed Sources/Autogenerated/sdk.cpp --- 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 +#include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" #include "./sdk_OrthancPluginInstanceOrigin.impl.h" #include "./sdk_OrthancPluginJobStepStatus.impl.h" diff -r 77f4e165e535 -r fd58eb5749ed Sources/OnChangeCallback.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" + +#include // For std::unique_ptr<> #include @@ -116,7 +118,7 @@ } } - std::auto_ptr change(queue_.front()); + std::unique_ptr change(queue_.front()); queue_.pop_front(); return change.release(); diff -r 77f4e165e535 -r fd58eb5749ed Sources/OnStoredInstanceCallback.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" static PyObject* storedInstanceCallback_ = NULL; diff -r 77f4e165e535 -r fd58eb5749ed Sources/Plugin.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" #include #include diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonFunction.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" PythonObject* PythonFunction::CallUnchecked(PyObject* args) diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonFunction.h --- 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 + #include class PythonModule; diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonLock.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" + +#include // For std::unique_ptr<> #include @@ -137,7 +139,7 @@ PythonObject args(*this, PyTuple_New(1)); PyTuple_SetItem(args.GetPyObject(), 0, traceback); - std::auto_ptr value(f.CallUnchecked(args.GetPyObject())); + std::unique_ptr value(f.CallUnchecked(args.GetPyObject())); if (value->IsValid()) { diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonModule.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" #include #include diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonModule.h --- 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 + #include class PythonModule : public boost::noncopyable diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonObject.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" PythonObject::PythonObject(PythonLock& lock, diff -r 77f4e165e535 -r fd58eb5749ed Sources/PythonString.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" PythonString::PythonString(PythonLock& lock, const std::string& utf8) : diff -r 77f4e165e535 -r fd58eb5749ed Sources/RestCallbacks.cpp --- 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 +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" #include