Mercurial > hg > orthanc-gdcm
changeset 67:8b65e8ef0cb1
sync orthanc folder
line wrap: on
line diff
--- a/CMakeLists.txt Fri Jan 06 09:43:41 2023 +0100 +++ b/CMakeLists.txt Mon Nov 13 20:53:53 2023 +0100 @@ -154,6 +154,8 @@ VERSION ${PLUGIN_VERSION} SOVERSION ${PLUGIN_VERSION}) +DefineSourceBasenameForTarget(OrthancGdcm) + install( TARGETS OrthancGdcm RUNTIME DESTINATION lib # Destination for Windows
--- a/Resources/Orthanc/CMake/AutoGeneratedCode.cmake Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/CMake/AutoGeneratedCode.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License
--- a/Resources/Orthanc/CMake/Compiler.cmake Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/CMake/Compiler.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -237,7 +237,8 @@ if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING) - if (CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_COMPILER_IS_GNUCXX OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 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") @@ -262,3 +263,24 @@ # preceding batches. https://cmake.org/Bug/view.php?id=14874 set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> <LINK_FLAGS> q <TARGET> <OBJECTS>") endif() + + +# This function defines macro "__ORTHANC_FILE__" as a replacement to +# macro "__FILE__", as the latter leaks the full path of the source +# files in the binaries +# https://stackoverflow.com/questions/8487986/file-macro-shows-full-path +# https://twitter.com/wget42/status/1676877802375634944?s=20 +function(DefineSourceBasenameForTarget targetname) + # Microsoft Visual Studio is extremely slow if using + # "set_property()", we only enable this feature for gcc and clang + if (CMAKE_COMPILER_IS_GNUCXX OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + get_target_property(source_files "${targetname}" SOURCES) + foreach(sourcefile ${source_files}) + get_filename_component(basename "${sourcefile}" NAME) + set_property( + SOURCE "${sourcefile}" APPEND + PROPERTY COMPILE_DEFINITIONS "__ORTHANC_FILE__=\"${basename}\"") + endforeach() + endif() +endfunction()
--- a/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -146,6 +146,18 @@ set(ORTHANC_FRAMEWORK_MD5 "8610c82d9153f22e929f2110f8f60279") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.10.1") set(ORTHANC_FRAMEWORK_MD5 "caf667fc5ea452b3d0c2f70bfd02599c") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.0") + set(ORTHANC_FRAMEWORK_MD5 "962c4a4a706a2ef28b390d8515dd7091") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.1") + set(ORTHANC_FRAMEWORK_MD5 "a39661c406adf22cf574fde290cf4bbf") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.2") + set(ORTHANC_FRAMEWORK_MD5 "ede3de356493a8868545f8cb4b8bc8b5") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.3") + set(ORTHANC_FRAMEWORK_MD5 "f941c0f5771db7616e7b7961026a60e2") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.0") + set(ORTHANC_FRAMEWORK_MD5 "d32a0cde03b6eb603d8dd2b33d38bf1b") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.1") + set(ORTHANC_FRAMEWORK_MD5 "8a435140efc8ff4a01d8242f092f21de") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc @@ -169,6 +181,9 @@ elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "b2e08d83e21d") # WSI 1.1 (framework pre-1.10.0), to remove "-std=c++11" set(ORTHANC_FRAMEWORK_MD5 "2eaa073cbb4b44ffba199ad93393b2b1") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "daf4807631c5") + # DICOMweb 1.15 (framework pre-1.12.2) + set(ORTHANC_FRAMEWORK_MD5 "c644aff2817306b3207c98c92e43f35f") endif() endif() endif() @@ -256,7 +271,7 @@ else() message("Forking the Orthanc source repository using Mercurial") execute_process( - COMMAND ${ORTHANC_FRAMEWORK_HG} clone "https://hg.orthanc-server.com/orthanc/" + COMMAND ${ORTHANC_FRAMEWORK_HG} clone "https://orthanc.uclouvain.be/hg/orthanc/" WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE Failure ) @@ -305,7 +320,7 @@ 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}") + set(ORTHANC_FRAMEWORK_URL "https://orthanc.uclouvain.be/third-party-downloads/orthanc-framework/${ORTHANC_FRAMEMORK_FILENAME}") endif() set(ORTHANC_FRAMEWORK_ARCHIVE "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${ORTHANC_FRAMEMORK_FILENAME}")
--- a/Resources/Orthanc/CMake/DownloadPackage.cmake Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/CMake/DownloadPackage.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -101,19 +101,26 @@ 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() + foreach (retry RANGE 1 5) # Retries 5 times + if ("${MD5}" STREQUAL "no-check") + message(WARNING "Not checking the MD5 of: ${Url}") + file(DOWNLOAD "${Url}" "${TMP_PATH}" + SHOW_PROGRESS TIMEOUT 30 INACTIVITY_TIMEOUT 10 + STATUS Failure) + else() + file(DOWNLOAD "${Url}" "${TMP_PATH}" + SHOW_PROGRESS TIMEOUT 30 INACTIVITY_TIMEOUT 10 + EXPECTED_MD5 "${MD5}" STATUS Failure) + endif() - list(GET Failure 0 Status) + list(GET Failure 0 Status) + if (Status EQUAL 0) + break() # Successful download + endif() + endforeach() + if (NOT Status EQUAL 0) + file(REMOVE ${TMP_PATH}) message(FATAL_ERROR "Cannot download file: ${Url}") endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/CMake/EmbedResources.py Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,445 @@ +#!/usr/bin/python + +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +import sys +import os +import os.path +import pprint +import re + +UPCASE_CHECK = True +USE_SYSTEM_EXCEPTION = False +EXCEPTION_CLASS = 'OrthancException' +OUT_OF_RANGE_EXCEPTION = '::Orthanc::OrthancException(::Orthanc::ErrorCode_ParameterOutOfRange)' +INEXISTENT_PATH_EXCEPTION = '::Orthanc::OrthancException(::Orthanc::ErrorCode_InexistentItem)' +NAMESPACE = 'Orthanc.EmbeddedResources' +FRAMEWORK_PATH = None + +ARGS = [] +for i in range(len(sys.argv)): + if not sys.argv[i].startswith('--'): + ARGS.append(sys.argv[i]) + elif sys.argv[i].lower() == '--no-upcase-check': + UPCASE_CHECK = False + elif sys.argv[i].lower() == '--system-exception': + USE_SYSTEM_EXCEPTION = True + EXCEPTION_CLASS = '::std::runtime_error' + OUT_OF_RANGE_EXCEPTION = '%s("Parameter out of range")' % EXCEPTION_CLASS + INEXISTENT_PATH_EXCEPTION = '%s("Unknown path in a directory resource")' % EXCEPTION_CLASS + elif sys.argv[i].startswith('--namespace='): + NAMESPACE = sys.argv[i][sys.argv[i].find('=') + 1 : ] + elif sys.argv[i].startswith('--framework-path='): + FRAMEWORK_PATH = sys.argv[i][sys.argv[i].find('=') + 1 : ] + +if len(ARGS) < 2 or len(ARGS) % 2 != 0: + print ('Usage:') + print ('python %s [--no-upcase-check] [--system-exception] [--namespace=<Namespace>] <TargetBaseFilename> [ <Name> <Source> ]*' % sys.argv[0]) + exit(-1) + +TARGET_BASE_FILENAME = ARGS[1] +SOURCES = ARGS[2:] + +try: + # Make sure the destination directory exists + os.makedirs(os.path.normpath(os.path.join(TARGET_BASE_FILENAME, '..'))) +except: + pass + + +##################################################################### +## Read each resource file +##################################################################### + +def CheckNoUpcase(s): + global UPCASE_CHECK + if (UPCASE_CHECK and + re.search('[A-Z]', s) != None): + raise Exception("Path in a directory with an upcase letter: %s" % s) + +resources = {} + +counter = 0 +i = 0 +while i < len(SOURCES): + resourceName = SOURCES[i].upper() + pathName = SOURCES[i + 1] + + if not os.path.exists(pathName): + raise Exception("Non existing path: %s" % pathName) + + if resourceName in resources: + raise Exception("Twice the same resource: " + resourceName) + + if os.path.isdir(pathName): + # The resource is a directory: Recursively explore its files + content = {} + for root, dirs, files in os.walk(pathName): + dirs.sort() + files.sort() + base = os.path.relpath(root, pathName) + + # Fix issue #24 (Build fails on OSX when directory has .DS_Store files): + # Ignore folders whose name starts with a dot (".") + if base.find('/.') != -1: + print('Ignoring folder: %s' % root) + continue + + for f in files: + if f.find('~') == -1: # Ignore Emacs backup files + if base == '.': + r = f + else: + r = os.path.join(base, f) + + CheckNoUpcase(r) + r = '/' + r.replace('\\', '/') + if r in content: + raise Exception("Twice the same filename (check case): " + r) + + content[r] = { + 'Filename' : os.path.join(root, f), + 'Index' : counter + } + counter += 1 + + resources[resourceName] = { + 'Type' : 'Directory', + 'Files' : content + } + + elif os.path.isfile(pathName): + resources[resourceName] = { + 'Type' : 'File', + 'Index' : counter, + 'Filename' : pathName + } + counter += 1 + + else: + raise Exception("Not a regular file, nor a directory: " + pathName) + + i += 2 + +#pprint.pprint(resources) + + +##################################################################### +## Write .h header +##################################################################### + +header = open(TARGET_BASE_FILENAME + '.h', 'w') + +header.write(""" +#pragma once + +#include <string> +#include <list> + +#if defined(_MSC_VER) +# pragma warning(disable: 4065) // "Switch statement contains 'default' but no 'case' labels" +#endif + +""") + + +for ns in NAMESPACE.split('.'): + header.write('namespace %s {\n' % ns) + + +header.write(""" + enum FileResourceId + { +""") + +isFirst = True +for name in resources: + if resources[name]['Type'] == 'File': + if isFirst: + isFirst = False + else: + header.write(',\n') + header.write(' %s' % name) + +header.write(""" + }; + + enum DirectoryResourceId + { +""") + +isFirst = True +for name in resources: + if resources[name]['Type'] == 'Directory': + if isFirst: + isFirst = False + else: + header.write(',\n') + header.write(' %s' % name) + +header.write(""" + }; + + const void* GetFileResourceBuffer(FileResourceId id); + size_t GetFileResourceSize(FileResourceId id); + void GetFileResource(std::string& result, FileResourceId id); + + const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path); + size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path); + void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path); + + void ListResources(std::list<std::string>& result, DirectoryResourceId id); + +""") + + +for ns in NAMESPACE.split('.'): + header.write('}\n') + +header.close() + + + +##################################################################### +## Write the resource content in the .cpp source +##################################################################### + +PYTHON_MAJOR_VERSION = sys.version_info[0] + +def WriteResource(cpp, item): + cpp.write(' static const uint8_t resource%dBuffer[] = {' % item['Index']) + + f = open(item['Filename'], "rb") + content = f.read() + f.close() + + # http://stackoverflow.com/a/1035360 + pos = 0 + buffer = [] # instead of appending a few bytes at a time to the cpp file, + # we first append each chunk to a list, join it and write it + # to the file. We've measured that it was 2-3 times faster in python3. + # Note that speed is important since if generation is too slow, + # cmake might try to compile the EmbeddedResources.cpp file while it is + # still being generated ! + for b in content: + if PYTHON_MAJOR_VERSION == 2: + c = ord(b[0]) + else: + c = b + + if pos > 0: + buffer.append(",") + + if (pos % 16) == 0: + buffer.append("\n") + + if c < 0: + raise Exception("Internal error") + + buffer.append("0x%02x" % c) + pos += 1 + + cpp.write("".join(buffer)) + # Zero-size array are disallowed, so we put one single void character in it. + if pos == 0: + cpp.write(' 0') + + cpp.write(' };\n') + cpp.write(' static const size_t resource%dSize = %d;\n' % (item['Index'], pos)) + + +cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w') + +cpp.write('#include "%s.h"\n' % os.path.basename(TARGET_BASE_FILENAME)) + +if USE_SYSTEM_EXCEPTION: + cpp.write('#include <stdexcept>') +elif FRAMEWORK_PATH != None: + cpp.write('#include "%s/OrthancException.h"' % FRAMEWORK_PATH) +else: + cpp.write('#include <OrthancException.h>') + +cpp.write(""" +#include <stdint.h> +#include <string.h> + +""") + +for ns in NAMESPACE.split('.'): + cpp.write('namespace %s {\n' % ns) + + +for name in resources: + if resources[name]['Type'] == 'File': + WriteResource(cpp, resources[name]) + else: + for f in resources[name]['Files']: + WriteResource(cpp, resources[name]['Files'][f]) + + + +##################################################################### +## Write the accessors to the file resources in .cpp +##################################################################### + +cpp.write(""" + const void* GetFileResourceBuffer(FileResourceId id) + { + switch (id) + { +""") +for name in resources: + if resources[name]['Type'] == 'File': + cpp.write(' case %s:\n' % name) + cpp.write(' return resource%dBuffer;\n' % resources[name]['Index']) + +cpp.write(""" + default: + throw %s; + } + } + + size_t GetFileResourceSize(FileResourceId id) + { + switch (id) + { +""" % OUT_OF_RANGE_EXCEPTION) + +for name in resources: + if resources[name]['Type'] == 'File': + cpp.write(' case %s:\n' % name) + cpp.write(' return resource%dSize;\n' % resources[name]['Index']) + +cpp.write(""" + default: + throw %s; + } + } +""" % OUT_OF_RANGE_EXCEPTION) + + + +##################################################################### +## Write the accessors to the directory resources in .cpp +##################################################################### + +cpp.write(""" + const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path) + { + switch (id) + { +""") + +for name in resources: + if resources[name]['Type'] == 'Directory': + cpp.write(' case %s:\n' % name) + isFirst = True + for path in resources[name]['Files']: + cpp.write(' if (!strcmp(path, "%s"))\n' % path) + cpp.write(' return resource%dBuffer;\n' % resources[name]['Files'][path]['Index']) + cpp.write(' throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION) + +cpp.write(""" default: + throw %s; + } + } + + size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path) + { + switch (id) + { +""" % OUT_OF_RANGE_EXCEPTION) + +for name in resources: + if resources[name]['Type'] == 'Directory': + cpp.write(' case %s:\n' % name) + isFirst = True + for path in resources[name]['Files']: + cpp.write(' if (!strcmp(path, "%s"))\n' % path) + cpp.write(' return resource%dSize;\n' % resources[name]['Files'][path]['Index']) + cpp.write(' throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION) + +cpp.write(""" default: + throw %s; + } + } +""" % OUT_OF_RANGE_EXCEPTION) + + + + +##################################################################### +## List the resources in a directory +##################################################################### + +cpp.write(""" + void ListResources(std::list<std::string>& result, DirectoryResourceId id) + { + result.clear(); + + switch (id) + { +""") + +for name in resources: + if resources[name]['Type'] == 'Directory': + cpp.write(' case %s:\n' % name) + for path in sorted(resources[name]['Files']): + cpp.write(' result.push_back("%s");\n' % path) + cpp.write(' break;\n\n') + +cpp.write(""" default: + throw %s; + } + } +""" % OUT_OF_RANGE_EXCEPTION) + + + + +##################################################################### +## Write the convenience wrappers in .cpp +##################################################################### + +cpp.write(""" + void GetFileResource(std::string& result, FileResourceId id) + { + size_t size = GetFileResourceSize(id); + result.resize(size); + if (size > 0) + memcpy(&result[0], GetFileResourceBuffer(id), size); + } + + void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path) + { + size_t size = GetDirectoryResourceSize(id, path); + result.resize(size); + if (size > 0) + memcpy(&result[0], GetDirectoryResourceBuffer(id, path), size); + } +""") + + +for ns in NAMESPACE.split('.'): + cpp.write('}\n') + +cpp.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/CMake/GoogleTestConfiguration.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,90 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +if (USE_GOOGLE_TEST_DEBIAN_PACKAGE) + find_path(GOOGLE_TEST_DEBIAN_SOURCES_DIR + NAMES src/gtest-all.cc + PATHS + ${CROSSTOOL_NG_IMAGE}/usr/src/gtest + ${CROSSTOOL_NG_IMAGE}/usr/src/googletest/googletest + PATH_SUFFIXES src + ) + + find_path(GOOGLE_TEST_DEBIAN_INCLUDE_DIR + NAMES gtest.h + PATHS + ${CROSSTOOL_NG_IMAGE}/usr/include/gtest + ) + + message("Path to the Debian Google Test sources: ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}") + message("Path to the Debian Google Test includes: ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}") + + set(GOOGLE_TEST_SOURCES + ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc + ) + + include_directories(${GOOGLE_TEST_DEBIAN_SOURCES_DIR}) + + if (NOT EXISTS ${GOOGLE_TEST_SOURCES} OR + NOT EXISTS ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}/gtest.h) + message(FATAL_ERROR "Please install the libgtest-dev package") + endif() + +elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST) + set(GOOGLE_TEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/googletest-release-1.8.1) + set(GOOGLE_TEST_URL "https://orthanc.uclouvain.be/third-party-downloads/gtest-1.8.1.tar.gz") + set(GOOGLE_TEST_MD5 "2e6fbeb6a91310a16efe181886c59596") + + DownloadPackage(${GOOGLE_TEST_MD5} ${GOOGLE_TEST_URL} "${GOOGLE_TEST_SOURCES_DIR}") + + include_directories( + ${GOOGLE_TEST_SOURCES_DIR}/googletest + ${GOOGLE_TEST_SOURCES_DIR}/googletest/include + ${GOOGLE_TEST_SOURCES_DIR} + ) + + set(GOOGLE_TEST_SOURCES + ${GOOGLE_TEST_SOURCES_DIR}/googletest/src/gtest-all.cc + ) + + # https://code.google.com/p/googletest/issues/detail?id=412 + if (MSVC) # VS2012 does not support tuples correctly yet + add_definitions(/D _VARIADIC_MAX=10) + endif() + + if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") + add_definitions(-DGTEST_HAS_CLONE=0) + endif() + + source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GOOGLE_TEST_SOURCES_DIR}/.*) + +else() + include(FindGTest) + if (NOT GTEST_FOUND) + message(FATAL_ERROR "Unable to find GoogleTest") + endif() + + include_directories(${GTEST_INCLUDE_DIRS}) + + # The variable GTEST_LIBRARIES contains the shared library of + # Google Test, create an alias for more uniformity + set(GOOGLE_TEST_LIBRARIES ${GTEST_LIBRARIES}) +endif()
--- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp Mon Nov 13 20:53:53 2023 +0100 @@ -2,8 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2022 Osimis S.A., Belgium - * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -79,6 +79,10 @@ } } + void ResetGlobalContext() + { + globalContext_ = NULL; + } bool HasGlobalContext() { @@ -250,26 +254,53 @@ } } + // helper class to convert std::map of headers to the plugin SDK C structure + class PluginHttpHeaders + { + private: + std::vector<const char*> headersKeys_; + std::vector<const char*> headersValues_; + + public: + explicit PluginHttpHeaders(const std::map<std::string, std::string>& httpHeaders) + { + for (std::map<std::string, std::string>::const_iterator + it = httpHeaders.begin(); it != httpHeaders.end(); ++it) + { + headersKeys_.push_back(it->first.c_str()); + headersValues_.push_back(it->second.c_str()); + } + } + + const char* const* GetKeys() + { + return (headersKeys_.empty() ? NULL : &headersKeys_[0]); + } + + const char* const* GetValues() + { + return (headersValues_.empty() ? NULL : &headersValues_[0]); + } + + uint32_t GetSize() + { + return static_cast<uint32_t>(headersKeys_.size()); + } + }; + bool MemoryBuffer::RestApiGet(const std::string& uri, const std::map<std::string, std::string>& httpHeaders, bool applyPlugins) { Clear(); - std::vector<const char*> headersKeys; - std::vector<const char*> headersValues; - - for (std::map<std::string, std::string>::const_iterator - it = httpHeaders.begin(); it != httpHeaders.end(); it++) - { - headersKeys.push_back(it->first.c_str()); - headersValues.push_back(it->second.c_str()); - } + PluginHttpHeaders headers(httpHeaders); return CheckHttp(OrthancPluginRestApiGet2( - GetGlobalContext(), &buffer_, uri.c_str(), httpHeaders.size(), - (headersKeys.empty() ? NULL : &headersKeys[0]), - (headersValues.empty() ? NULL : &headersValues[0]), applyPlugins)); + GetGlobalContext(), &buffer_, uri.c_str(), + headers.GetSize(), + headers.GetKeys(), + headers.GetValues(), applyPlugins)); } bool MemoryBuffer::RestApiPost(const std::string& uri, @@ -292,6 +323,41 @@ } } +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + + bool MemoryBuffer::RestApiPost(const std::string& uri, + const void* body, + size_t bodySize, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins) + { + MemoryBuffer answerHeaders; + uint16_t httpStatus; + + PluginHttpHeaders headers(httpHeaders); + + return CheckHttp(OrthancPluginCallRestApi(GetGlobalContext(), + &buffer_, + *answerHeaders, + &httpStatus, + OrthancPluginHttpMethod_Post, + uri.c_str(), + headers.GetSize(), headers.GetKeys(), headers.GetValues(), + body, bodySize, + applyPlugins)); + } + + + bool MemoryBuffer::RestApiPost(const std::string& uri, + const Json::Value& body, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins) + { + std::string s; + WriteFastJson(s, body); + return RestApiPost(uri, s.c_str(), s.size(), httpHeaders, applyPlugins); + } +#endif bool MemoryBuffer::RestApiPut(const std::string& uri, const void* body, @@ -689,6 +755,12 @@ } } + OrthancConfiguration::OrthancConfiguration(const Json::Value& configuration, const std::string& path) : + configuration_(configuration), + path_(path) + { + } + std::string OrthancConfiguration::GetPath(const std::string& key) const { @@ -1044,7 +1116,7 @@ if (configuration_[key].type() != Json::objectValue) { LogError("The configuration option \"" + GetPath(key) + - "\" is not a string as expected"); + "\" is not an object as expected"); ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); } @@ -1390,6 +1462,27 @@ } + bool RestApiGet(Json::Value& result, + const std::string& uri, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins) + { + MemoryBuffer answer; + + if (!answer.RestApiGet(uri, httpHeaders, applyPlugins)) + { + return false; + } + else + { + if (!answer.IsEmpty()) + { + answer.ToJson(result); + } + return true; + } + } + bool RestApiGet(Json::Value& result, const std::string& uri, @@ -1457,6 +1550,30 @@ } } +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(Json::Value& result, + const std::string& uri, + const Json::Value& body, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins) + { + MemoryBuffer answer; + + if (!answer.RestApiPost(uri, body, httpHeaders, applyPlugins)) + { + return false; + } + else + { + if (!answer.IsEmpty()) + { + answer.ToJson(result); + } + return true; + } + } +#endif + bool RestApiPost(Json::Value& result, const std::string& uri, @@ -1546,32 +1663,27 @@ " is required)"); } - - bool CheckMinimalOrthancVersion(unsigned int major, - unsigned int minor, - unsigned int revision) + bool CheckMinimalVersion(const char* version, + 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")) + if (!strcmp(version, "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 +#define ORTHANC_SCANF sscanf_s #else - sscanf +#define ORTHANC_SCANF sscanf #endif - (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || + + // Parse the version + int aa, bb, cc = 0; + if ((ORTHANC_SCANF(version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 && + ORTHANC_SCANF(version, "%4d.%4d", &aa, &bb) != 2) || aa < 0 || bb < 0 || cc < 0) @@ -1595,7 +1707,6 @@ return false; } - // Check the minor version number assert(a == major); @@ -1623,6 +1734,21 @@ } + bool CheckMinimalOrthancVersion(unsigned int major, + unsigned int minor, + unsigned int revision) + { + if (!HasGlobalContext()) + { + LogError("Bad Orthanc context in the plugin"); + return false; + } + + return CheckMinimalVersion(GetGlobalContext()->orthancVersion, + major, minor, revision); + } + + #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0) const char* AutodetectMimeType(const std::string& path) { @@ -1792,7 +1918,8 @@ bool OrthancPeers::DoGet(MemoryBuffer& target, size_t index, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { if (index >= index_.size()) { @@ -1801,10 +1928,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast<uint32_t>(index), OrthancPluginHttpMethod_Get, uri.c_str(), - 0, NULL, NULL, NULL, 0, timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1820,21 +1949,23 @@ bool OrthancPeers::DoGet(MemoryBuffer& target, const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { size_t index; return (LookupName(index, name) && - DoGet(target, index, uri)); + DoGet(target, index, uri, headers)); } bool OrthancPeers::DoGet(Json::Value& target, size_t index, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { MemoryBuffer buffer; - if (DoGet(buffer, index, uri)) + if (DoGet(buffer, index, uri, headers)) { buffer.ToJson(target); return true; @@ -1848,11 +1979,12 @@ bool OrthancPeers::DoGet(Json::Value& target, const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { MemoryBuffer buffer; - if (DoGet(buffer, name, uri)) + if (DoGet(buffer, name, uri, headers)) { buffer.ToJson(target); return true; @@ -1867,22 +1999,24 @@ bool OrthancPeers::DoPost(MemoryBuffer& target, const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { size_t index; return (LookupName(index, name) && - DoPost(target, index, uri, body)); + DoPost(target, index, uri, body, headers)); } bool OrthancPeers::DoPost(Json::Value& target, size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { MemoryBuffer buffer; - if (DoPost(buffer, index, uri, body)) + if (DoPost(buffer, index, uri, body, headers)) { buffer.ToJson(target); return true; @@ -1897,11 +2031,12 @@ bool OrthancPeers::DoPost(Json::Value& target, const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { MemoryBuffer buffer; - if (DoPost(buffer, name, uri, body)) + if (DoPost(buffer, name, uri, body, headers)) { buffer.ToJson(target); return true; @@ -1916,7 +2051,8 @@ bool OrthancPeers::DoPost(MemoryBuffer& target, size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { if (index >= index_.size()) { @@ -1931,10 +2067,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast<uint32_t>(index), OrthancPluginHttpMethod_Post, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1950,7 +2088,8 @@ bool OrthancPeers::DoPut(size_t index, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { if (index >= index_.size()) { @@ -1965,10 +2104,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast<uint32_t>(index), OrthancPluginHttpMethod_Put, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1983,16 +2124,18 @@ bool OrthancPeers::DoPut(const std::string& name, const std::string& uri, - const std::string& body) const + const std::string& body, + const std::map<std::string, std::string>& headers) const { size_t index; return (LookupName(index, name) && - DoPut(index, uri, body)); + DoPut(index, uri, body, headers)); } bool OrthancPeers::DoDelete(size_t index, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { if (index >= index_.size()) { @@ -2001,10 +2144,12 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; + PluginHttpHeaders pluginHeaders(headers); + OrthancPluginErrorCode code = OrthancPluginCallPeerApi (GetGlobalContext(), *answer, NULL, &status, peers_, static_cast<uint32_t>(index), OrthancPluginHttpMethod_Delete, uri.c_str(), - 0, NULL, NULL, NULL, 0, timeout_); + pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -2018,11 +2163,12 @@ bool OrthancPeers::DoDelete(const std::string& name, - const std::string& uri) const + const std::string& uri, + const std::map<std::string, std::string>& headers) const { size_t index; return (LookupName(index, name) && - DoDelete(index, uri)); + DoDelete(index, uri, headers)); } #endif @@ -2059,6 +2205,36 @@ } +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static OrthancPluginErrorCode CopyStringToMemoryBuffer(OrthancPluginMemoryBuffer* target, + const std::string& source) + { + if (OrthancPluginCreateMemoryBuffer(globalContext_, target, source.size()) != OrthancPluginErrorCode_Success) + { + return OrthancPluginErrorCode_NotEnoughMemory; + } + else + { + if (!source.empty()) + { + memcpy(target->data, source.c_str(), source.size()); + } + + return OrthancPluginErrorCode_Success; + } + } +#endif + + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + OrthancPluginErrorCode OrthancJob::CallbackGetContent(OrthancPluginMemoryBuffer* target, + void* job) + { + assert(job != NULL); + OrthancJob& that = *reinterpret_cast<OrthancJob*>(job); + return CopyStringToMemoryBuffer(target, that.content_); + } +#else const char* OrthancJob::CallbackGetContent(void* job) { assert(job != NULL); @@ -2072,8 +2248,33 @@ return 0; } } - - +#endif + + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + int32_t OrthancJob::CallbackGetSerialized(OrthancPluginMemoryBuffer* target, + void* job) + { + assert(job != NULL); + OrthancJob& that = *reinterpret_cast<OrthancJob*>(job); + + if (that.hasSerialized_) + { + if (CopyStringToMemoryBuffer(target, that.serialized_) == OrthancPluginErrorCode_Success) + { + return 1; + } + else + { + return -1; + } + } + else + { + return 0; + } + } +#else const char* OrthancJob::CallbackGetSerialized(void* job) { assert(job != NULL); @@ -2096,6 +2297,7 @@ return 0; } } +#endif OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job) @@ -2227,10 +2429,15 @@ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer); } - OrthancPluginJob* orthanc = OrthancPluginCreateJob( - GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), - CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, - CallbackStep, CallbackStop, CallbackReset); + OrthancPluginJob* orthanc = +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + OrthancPluginCreateJob2 +#else + OrthancPluginCreateJob +#endif + (GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), + CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, + CallbackStep, CallbackStop, CallbackReset); if (orthanc == NULL) { @@ -3550,6 +3757,27 @@ #endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 1) + DicomInstance* DicomInstance::Load(const std::string& instanceId, + OrthancPluginLoadDicomInstanceMode mode) + { + OrthancPluginDicomInstance* instance = OrthancPluginLoadDicomInstance( + GetGlobalContext(), instanceId.c_str(), mode); + + if (instance == NULL) + { + ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin); + } + else + { + boost::movelib::unique_ptr<DicomInstance> result(new DicomInstance(instance)); + result->toFree_ = true; + return result.release(); + } + } +#endif + + #if HAS_ORTHANC_PLUGIN_WEBDAV == 1 static std::vector<std::string> WebDavConvertPath(uint32_t pathSize, const char* const* pathItems) @@ -3703,7 +3931,13 @@ try { - *isReadOnly = (that.StoreFile(WebDavConvertPath(pathSize, pathItems), data, size) ? 1 : 0); + if (static_cast<uint64_t>(static_cast<size_t>(size)) != size) + { + ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory); + } + + *isReadOnly = (that.StoreFile(WebDavConvertPath(pathSize, pathItems), data, + static_cast<size_t>(size)) ? 1 : 0); return OrthancPluginErrorCode_Success; } catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) @@ -3782,4 +4016,14 @@ } } #endif + + void GetHttpHeaders(std::map<std::string, std::string>& result, const OrthancPluginHttpRequest* request) + { + result.clear(); + + for (uint32_t i = 0; i < request->headersCount; ++i) + { + result[request->headersKeys[i]] = request->headersValues[i]; + } + } }
--- a/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h Mon Nov 13 20:53:53 2023 +0100 @@ -2,8 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2022 Osimis S.A., Belgium - * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -115,6 +115,12 @@ # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 0 #endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) +# define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 1 +#else +# define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API 0 +#endif + #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 1) # define HAS_ORTHANC_PLUGIN_WEBDAV 1 #else @@ -131,6 +137,8 @@ void SetGlobalContext(OrthancPluginContext* context); + void ResetGlobalContext(); + bool HasGlobalContext(); OrthancPluginContext* GetGlobalContext(); @@ -224,6 +232,19 @@ const Json::Value& body, bool applyPlugins); +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(const std::string& uri, + const Json::Value& body, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins); + + bool RestApiPost(const std::string& uri, + const void* body, + size_t bodySize, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins); +#endif + bool RestApiPut(const std::string& uri, const Json::Value& body, bool applyPlugins); @@ -303,6 +324,11 @@ return str_; } + bool IsNullOrEmpty() const + { + return str_ == NULL || str_[0] == 0; + } + void ToString(std::string& target) const; void ToJson(Json::Value& target) const; @@ -322,10 +348,12 @@ void LoadConfiguration(); public: - OrthancConfiguration(); + OrthancConfiguration(); // loads the full Orthanc configuration explicit OrthancConfiguration(bool load); + explicit OrthancConfiguration(const Json::Value& configuration, const std::string& path); // e.g. to load a section from a default json content + const Json::Value& GetJson() const { return configuration_; @@ -509,6 +537,11 @@ const std::string& uri, bool applyPlugins); + bool RestApiGet(Json::Value& result, + const std::string& uri, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins); + bool RestApiGetString(std::string& result, const std::string& uri, bool applyPlugins); @@ -530,6 +563,14 @@ size_t bodySize, bool applyPlugins); +#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1 + bool RestApiPost(Json::Value& result, + const std::string& uri, + const Json::Value& body, + const std::map<std::string, std::string>& httpHeaders, + bool applyPlugins); +#endif + bool RestApiPost(Json::Value& result, const std::string& uri, const Json::Value& body, @@ -610,6 +651,10 @@ unsigned int minor, unsigned int revision); + bool CheckMinimalVersion(const char* version, + unsigned int major, + unsigned int minor, + unsigned int revision); namespace Internals { @@ -719,53 +764,65 @@ bool DoGet(MemoryBuffer& target, size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; bool DoGet(MemoryBuffer& target, const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; bool DoGet(Json::Value& target, size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; bool DoGet(Json::Value& target, const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; bool DoPost(MemoryBuffer& target, size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoPost(MemoryBuffer& target, const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoPost(Json::Value& target, size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoPost(Json::Value& target, const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoPut(size_t index, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoPut(const std::string& name, const std::string& uri, - const std::string& body) const; + const std::string& body, + const std::map<std::string, std::string>& headers) const; bool DoDelete(size_t index, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; bool DoDelete(const std::string& name, - const std::string& uri) const; + const std::string& uri, + const std::map<std::string, std::string>& headers) const; }; #endif @@ -785,9 +842,19 @@ static float CallbackGetProgress(void* job); +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static OrthancPluginErrorCode CallbackGetContent(OrthancPluginMemoryBuffer* target, + void* job); +#else static const char* CallbackGetContent(void* job); +#endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3) + static int32_t CallbackGetSerialized(OrthancPluginMemoryBuffer* target, + void* job); +#else static const char* CallbackGetSerialized(void* job); +#endif static OrthancPluginJobStepStatus CallbackStep(void* job); @@ -1199,6 +1266,11 @@ ~DicomInstance(); + const OrthancPluginDicomInstance* GetObject() const + { + return instance_; + } + std::string GetRemoteAet() const; const void* GetBuffer() const @@ -1253,9 +1325,15 @@ size_t size, const std::string& transferSyntax); #endif + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 1) + static DicomInstance* Load(const std::string& instanceId, + OrthancPluginLoadDicomInstanceMode mode); +#endif }; - +// helper method to convert Http headers from the plugin SDK to a std::map +void GetHttpHeaders(std::map<std::string, std::string>& result, const OrthancPluginHttpRequest* request); #if HAS_ORTHANC_PLUGIN_WEBDAV == 1 class IWebDavCollection : public boost::noncopyable
--- a/Resources/Orthanc/Plugins/OrthancPluginException.h Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginException.h Mon Nov 13 20:53:53 2023 +0100 @@ -2,8 +2,8 @@ * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2022 Osimis S.A., Belgium - * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as
--- a/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/Orthanc/Plugins/OrthancPluginsExports.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/Toolchains/LinuxStandardBaseToolchain.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,100 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +# +# Full build, as used on the BuildBot CIS: +# +# $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake ../OrthancServer/ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DUSE_LEGACY_BOOST=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 ../OrthancServer/ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_BOOST=ON -G Ninja +# + +INCLUDE(CMakeForceCompiler) + +SET(LSB_PATH $ENV{LSB_PATH} CACHE STRING "") +SET(LSB_CC $ENV{LSB_CC} CACHE STRING "") +SET(LSB_CXX $ENV{LSB_CXX} CACHE STRING "") +SET(LSB_TARGET_VERSION "4.0" CACHE STRING "") + +IF ("${LSB_PATH}" STREQUAL "") + SET(LSB_PATH "/opt/lsb") +ENDIF() + +IF (EXISTS ${LSB_PATH}/lib64) + SET(LSB_TARGET_PROCESSOR "x86_64") + SET(LSB_LIBPATH ${LSB_PATH}/lib64-${LSB_TARGET_VERSION}) +ELSEIF (EXISTS ${LSB_PATH}/lib) + SET(LSB_TARGET_PROCESSOR "x86") + SET(LSB_LIBPATH ${LSB_PATH}/lib-${LSB_TARGET_VERSION}) +ELSE() + MESSAGE(FATAL_ERROR "Unable to detect the target processor architecture. Check the LSB_PATH environment variable.") +ENDIF() + +SET(LSB_CPPPATH ${LSB_PATH}/include) +SET(PKG_CONFIG_PATH ${LSB_LIBPATH}/pkgconfig/) + +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_VERSION LinuxStandardBase) +SET(CMAKE_SYSTEM_PROCESSOR ${LSB_TARGET_PROCESSOR}) + +# which compilers to use for C and C++ +SET(CMAKE_C_COMPILER ${LSB_PATH}/bin/lsbcc) + +if (${CMAKE_VERSION} VERSION_LESS "3.6.0") + CMAKE_FORCE_CXX_COMPILER(${LSB_PATH}/bin/lsbc++ GNU) +else() + SET(CMAKE_CXX_COMPILER ${LSB_PATH}/bin/lsbc++) +endif() + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH ${LSB_PATH}) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + +SET(CMAKE_CROSSCOMPILING OFF) + + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include" CACHE INTERNAL "" FORCE) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward" CACHE INTERNAL "" FORCE) +SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE) +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE) + +if (NOT "${LSB_CXX}" STREQUAL "") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cxx=${LSB_CXX}") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}") +endif() + +if (NOT "${LSB_CC}" STREQUAL "") + SET(CMAKE_C_FLAGS "${CMAKE_CC_FLAGS} --lsb-cc=${LSB_CC}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cc=${LSB_CC}") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cc=${LSB_CC}") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cc=${LSB_CC}") +endif() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain32.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,38 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) +set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/Toolchains/MinGW-W64-Toolchain64.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,38 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Orthanc/Toolchains/MinGWToolchain.cmake Mon Nov 13 20:53:53 2023 +0100 @@ -0,0 +1,41 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/>. + + +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER i586-mingw32msvc-gcc) +set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++) +set(CMAKE_RC_COMPILER i586-mingw32msvc-windres) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE)
--- a/Resources/SyncOrthancFolder.py Fri Jan 06 09:43:41 2023 +0100 +++ b/Resources/SyncOrthancFolder.py Mon Nov 13 20:53:53 2023 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # This maintenance script updates the content of the "Orthanc" folder @@ -8,21 +8,23 @@ import multiprocessing import os import stat -import urllib2 +import urllib.request TARGET = os.path.join(os.path.dirname(__file__), 'Orthanc') -PLUGIN_SDK_VERSIONS = [ '0.9.5', '1.7.0' ] -REPOSITORY = 'https://hg.orthanc-server.com/orthanc/raw-file' +PLUGIN_SDK_VERSION = '1.7.0' +REPOSITORY = 'https://orthanc.uclouvain.be/hg/orthanc/raw-file' FILES = [ ('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', '.'), - ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake', '.'), - ('OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain64.cmake', '.'), - ('OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake', '.'), + ('OrthancFramework/Resources/CMake/GoogleTestConfiguration.cmake', 'CMake'), + ('OrthancFramework/Resources/EmbedResources.py', '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'), @@ -40,7 +42,7 @@ branch = x[0] source = x[1] target = os.path.join(TARGET, x[2]) - print target + print(target) try: os.makedirs(os.path.dirname(target)) @@ -49,8 +51,12 @@ url = '%s/%s/%s' % (REPOSITORY, branch, source) - with open(target, 'w') as f: - f.write(urllib2.urlopen(url).read()) + with open(target, 'wb') as f: + try: + f.write(urllib.request.urlopen(url).read()) + except: + print('ERROR %s' % url) + raise commands = [] @@ -61,12 +67,11 @@ os.path.join(f[1], os.path.basename(f[0])) ]) for f in SDK: - for version in PLUGIN_SDK_VERSIONS: - commands.append([ - 'Orthanc-%s' % version, - 'Plugins/Include/%s' % f, - 'Sdk-%s/%s' % (version, f) - ]) + commands.append([ + 'Orthanc-%s' % PLUGIN_SDK_VERSION, + 'OrthancServer/Plugins/Include/%s' % f, + 'Sdk-%s/%s' % (PLUGIN_SDK_VERSION, f) + ]) pool = multiprocessing.Pool(10) # simultaneous downloads