Orthanc-1.0.0/.hg_archival.txt0000644000000000000000000000023412634042176014326 0ustar 00000000000000repo: 3959d33612ccaadc0d4d707227fbed09ac35e5fe node: 74cf1f350b45473e847fed4116c1b428fa3d7352 branch: Orthanc-1.0.0 latesttag: null latesttagdistance: 1626 Orthanc-1.0.0/.travis.yml0000644000000000000000000000410012634042176013345 0ustar 00000000000000language: cpp env: - TRAVIS_MINGW=OFF - TRAVIS_MINGW=ON compiler: - gcc - clang os: - osx - linux osx_image: xcode61 matrix: exclude: # This excludes OSX builds from the build matrix for gcc - os: osx compiler: gcc # Do not compile for OS X or clang when MinGW is enabled - os: osx env: TRAVIS_MINGW=ON - compiler: clang env: TRAVIS_MINGW=ON before_install: - if [ $TRAVIS_OS_NAME == linux ]; then sudo apt-get update -qq && sudo apt-get install -qq build-essential unzip cmake mercurial uuid-dev libcurl4-openssl-dev liblua5.1-0-dev libgtest-dev libpng-dev libsqlite3-dev libssl-dev zlib1g-dev libdcmtk2-dev libwrap0-dev libcharls-dev; fi - if [ $TRAVIS_OS_NAME == linux -a $TRAVIS_MINGW == ON ]; then sudo apt-get install mingw32; fi before_script: - mkdir Build - cd Build - if [ $TRAVIS_OS_NAME == linux -a $TRAVIS_MINGW == OFF ]; then cmake -DCMAKE_BUILD_TYPE=Debug "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" -DALLOW_DOWNLOADS=ON -DUSE_SYSTEM_BOOST=OFF -DUSE_SYSTEM_MONGOOSE=OFF -DUSE_SYSTEM_JSONCPP=OFF -DUSE_SYSTEM_GOOGLE_LOG=OFF -DUSE_SYSTEM_PUGIXML=OFF -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON ..; fi - if [ $TRAVIS_OS_NAME == linux -a $TRAVIS_MINGW == ON ]; then cmake -DCMAKE_BUILD_TYPE=Debug -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DALLOW_DOWNLOADS=ON -DCMAKE_TOOLCHAIN_FILE=Resources/MinGWToolchain.cmake ..; fi - if [ $TRAVIS_OS_NAME == osx ]; then cmake -DCMAKE_BUILD_TYPE=Debug -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DALLOW_DOWNLOADS=ON ..; fi script: make && if [ $TRAVIS_MINGW == OFF ]; then ./UnitTests; fi #script: cp ../README Orthanc #deploy: # provider: releases # api_key: # secure: WU+niKLAKMoJHST5EK23BayK4qXSrXELKlJYc8wRjMO4ay1KSgvzlY2UGKeW1EPClBfZZ0Uh5VKF8l34exsfirFuwCX2qceozduZproUszZ4Z88X8wt8Ctu8tBuuKLZYFc9iNH4zw+QZyRuPyXK9iWpS0L9O20pqy5upTsagM3o= # file_glob: true # file: # - 'Build/Orthanc' # - 'Build/UnitTests' # - 'BuildMinGW32/Orthanc.exe' # - 'BuildMinGW32/UnitTests.exe' # skip_cleanup: true # on: # all_branches: true Orthanc-1.0.0/AUTHORS0000644000000000000000000000142612634042176012314 0ustar 00000000000000Orthanc - A Lightweight, RESTful DICOM Server ============================================= Authors of Orthanc ------------------ * Sebastien Jodogne Department of Medical Physics University Hospital of Liege Belgium Overall design and lead developer. Client library -------------- The client library of Orthanc is automatically wrapped from the C++ code using the LAAW software. LAAW is the Lightweight, Automated API Wrapper from the Jomago team, that comes from the following authors: * Sebastien Jodogne * Alain Mazy * Benjamin Golinvaux LAAW should be soon released as a separate open-source project. Contributors ------------ See the file "THANKS" for the occasional contributors. Orthanc-1.0.0/CMakeLists.txt0000644000000000000000000005250712634042176014012 0ustar 00000000000000cmake_minimum_required(VERSION 2.8) project(Orthanc) # Version of the build, should always be "mainline" except in release branches set(ORTHANC_VERSION "1.0.0") # Version of the database schema. History: # * Orthanc 0.1.0 -> Orthanc 0.3.0 = no versioning # * Orthanc 0.3.1 = version 2 # * Orthanc 0.4.0 -> Orthanc 0.7.2 = version 3 # * Orthanc 0.7.3 -> Orthanc 0.8.4 = version 4 # * Orthanc 0.8.5 -> Orthanc 0.9.4 = version 5 # * Orthanc 0.9.5 -> mainline = version 6 set(ORTHANC_DATABASE_VERSION 6) ##################################################################### ## CMake parameters tunable at the command line ##################################################################### # Parameters of the build SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") SET(STANDALONE_BUILD ON CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)") SET(ENABLE_SSL ON CACHE BOOL "Include support for SSL") SET(DCMTK_DICTIONARY_DIR "" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (only when using system version of DCMTK)") SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests") SET(ENABLE_GOOGLE_LOG OFF CACHE BOOL "Enable Google Log (otherwise, an internal logger is used)") SET(ENABLE_JPEG ON CACHE BOOL "Enable JPEG decompression") SET(ENABLE_JPEG_LOSSLESS ON CACHE BOOL "Enable JPEG-LS (Lossless) decompression") SET(ENABLE_PLUGINS ON CACHE BOOL "Enable plugins") SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Build the ServeFolders plugin") SET(BUILD_MODALITY_WORKLISTS ON CACHE BOOL "Build the sample plugin to serve modality worklists") # Advanced parameters to fine-tune linking against system libraries SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp") SET(USE_SYSTEM_GOOGLE_LOG ON CACHE BOOL "Use the system version of Google Log") SET(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test") SET(USE_SYSTEM_SQLITE ON CACHE BOOL "Use the system version of SQLite") SET(USE_SYSTEM_MONGOOSE ON CACHE BOOL "Use the system version of Mongoose") SET(USE_SYSTEM_LUA ON CACHE BOOL "Use the system version of Lua") SET(USE_SYSTEM_DCMTK ON CACHE BOOL "Use the system version of DCMTK") SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost") SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng") SET(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg") SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl") SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL") SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib") SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml)") # Experimental options SET(USE_PUGIXML ON CACHE BOOL "Use the Pugixml parser (turn off only for debug)") # Distribution-specific settings SET(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)") SET(USE_BOOST_ICONV ON CACHE BOOL "Use iconv instead of wconv (Windows only)") mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE) mark_as_advanced(SYSTEM_MONGOOSE_USE_CALLBACKS) mark_as_advanced(USE_BOOST_ICONV) mark_as_advanced(USE_PUGIXML) # Path to the root folder of the Orthanc distribution set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}) # Some basic inclusions include(CheckIncludeFiles) include(CheckIncludeFileCXX) include(CheckLibraryExists) include(FindPythonInterp) include(${CMAKE_SOURCE_DIR}/Resources/CMake/AutoGeneratedCode.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/DownloadPackage.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/Compiler.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/VisualStudioPrecompiledHeaders.cmake) ##################################################################### ## List of source files ##################################################################### set(ORTHANC_CORE_SOURCES Core/Cache/MemoryCache.cpp Core/Cache/SharedArchive.cpp Core/ChunkedBuffer.cpp Core/Compression/DeflateBaseCompressor.cpp Core/Compression/GzipCompressor.cpp Core/Compression/HierarchicalZipWriter.cpp Core/Compression/ZipWriter.cpp Core/Compression/ZlibCompressor.cpp Core/DicomFormat/DicomArray.cpp Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomTag.cpp Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomInstanceHasher.cpp Core/DicomFormat/DicomValue.cpp Core/Enumerations.cpp Core/FileStorage/FilesystemStorage.cpp Core/FileStorage/StorageAccessor.cpp Core/HttpClient.cpp Core/HttpServer/BufferHttpSender.cpp Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpToolbox.cpp Core/HttpServer/HttpOutput.cpp Core/HttpServer/StringHttpOutput.cpp Core/HttpServer/MongooseServer.cpp Core/HttpServer/HttpFileSender.cpp Core/HttpServer/FilesystemHttpSender.cpp Core/HttpServer/HttpContentNegociation.cpp Core/HttpServer/HttpStreamTranscoder.cpp Core/Logging.cpp Core/RestApi/RestApiCall.cpp Core/RestApi/RestApiGetCall.cpp Core/RestApi/RestApiHierarchy.cpp Core/RestApi/RestApiPath.cpp Core/RestApi/RestApiOutput.cpp Core/RestApi/RestApi.cpp Core/MultiThreading/Mutex.cpp Core/MultiThreading/ReaderWriterLock.cpp Core/MultiThreading/RunnableWorkersPool.cpp Core/MultiThreading/Semaphore.cpp Core/MultiThreading/SharedMessageQueue.cpp Core/Images/Font.cpp Core/Images/FontRegistry.cpp Core/Images/ImageAccessor.cpp Core/Images/ImageBuffer.cpp Core/Images/ImageProcessing.cpp Core/Images/JpegErrorManager.cpp Core/Images/JpegReader.cpp Core/Images/JpegWriter.cpp Core/Images/PngReader.cpp Core/Images/PngWriter.cpp Core/SQLite/Connection.cpp Core/SQLite/FunctionContext.cpp Core/SQLite/Statement.cpp Core/SQLite/StatementId.cpp Core/SQLite/StatementReference.cpp Core/SQLite/Transaction.cpp Core/Toolbox.cpp Core/Uuid.cpp Core/Lua/LuaContext.cpp Core/Lua/LuaFunctionCall.cpp ) set(ORTHANC_SERVER_SOURCES OrthancServer/DatabaseWrapper.cpp OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DicomDirWriter.cpp OrthancServer/DicomModification.cpp OrthancServer/DicomProtocol/DicomFindAnswers.cpp OrthancServer/DicomProtocol/DicomServer.cpp OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/DicomProtocol/RemoteModalityParameters.cpp OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp OrthancServer/ExportedResource.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/CommandDispatcher.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/FindScp.cpp OrthancServer/Internals/MoveScp.cpp OrthancServer/Internals/StoreScp.cpp OrthancServer/LuaScripting.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancHttpHandler.cpp OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancMoveRequestHandler.cpp OrthancServer/OrthancPeerParameters.cpp OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/OrthancRestApi/OrthancRestChanges.cpp OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/QueryRetrieveHandler.cpp OrthancServer/Search/HierarchicalMatcher.cpp OrthancServer/Search/IFindConstraint.cpp OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/LookupResource.cpp OrthancServer/Search/SetOfResources.cpp OrthancServer/Search/ListConstraint.cpp OrthancServer/Search/RangeConstraint.cpp OrthancServer/Search/ValueConstraint.cpp OrthancServer/Search/WildcardConstraint.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerEnumerations.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerToolbox.cpp OrthancServer/SliceOrdering.cpp OrthancServer/ToDcmtkBridge.cpp # From "lua-scripting" branch OrthancServer/DicomInstanceToStore.cpp OrthancServer/Scheduler/DeleteInstanceCommand.cpp OrthancServer/Scheduler/ModifyInstanceCommand.cpp OrthancServer/Scheduler/ServerCommandInstance.cpp OrthancServer/Scheduler/ServerJob.cpp OrthancServer/Scheduler/ServerScheduler.cpp OrthancServer/Scheduler/StorePeerCommand.cpp OrthancServer/Scheduler/StoreScuCommand.cpp OrthancServer/Scheduler/CallSystemCommand.cpp ) set(ORTHANC_UNIT_TESTS_SOURCES UnitTestsSources/DicomMapTests.cpp UnitTestsSources/FileStorageTests.cpp UnitTestsSources/FromDcmtkTests.cpp UnitTestsSources/MemoryCacheTests.cpp UnitTestsSources/ImageTests.cpp UnitTestsSources/RestApiTests.cpp UnitTestsSources/SQLiteTests.cpp UnitTestsSources/SQLiteChromiumTests.cpp UnitTestsSources/ServerIndexTests.cpp UnitTestsSources/VersionsTests.cpp UnitTestsSources/ZipTests.cpp UnitTestsSources/LuaTests.cpp UnitTestsSources/MultiThreadingTests.cpp UnitTestsSources/UnitTestsMain.cpp UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/JpegLosslessTests.cpp UnitTestsSources/StreamTests.cpp ) if (ENABLE_PLUGINS) list(APPEND ORTHANC_SERVER_SOURCES Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/PluginsEnumerations.cpp Plugins/Engine/PluginsErrorDictionary.cpp Plugins/Engine/PluginsManager.cpp Plugins/Engine/SharedLibrary.cpp ) list(APPEND ORTHANC_UNIT_TESTS_SOURCES UnitTestsSources/PluginsTests.cpp ) endif() set(ORTHANC_EMBEDDED_FILES PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql UPGRADE_DATABASE_3_TO_4 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql UPGRADE_DATABASE_4_TO_5 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade4To5.sql CONFIGURATION_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json DICOM_CONFORMANCE_STATEMENT ${CMAKE_CURRENT_SOURCE_DIR}/Resources/DicomConformanceStatement.txt LUA_TOOLBOX ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua FONT_UBUNTU_MONO_BOLD_16 ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Fonts/UbuntuMonoBold-16.json ) ##################################################################### ## Inclusion of third-party dependencies ##################################################################### if (ENABLE_GOOGLE_LOG) include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake) endif() include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibJpegConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake) # These are the two most heavyweight dependencies. We put them as the # last includes to quickly spot problems when configuring static # builds. include(${CMAKE_SOURCE_DIR}/Resources/CMake/BoostConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/DcmtkConfiguration.cmake) if (ENABLE_SSL) add_definitions(-DORTHANC_SSL_ENABLED=1) include(${CMAKE_SOURCE_DIR}/Resources/CMake/OpenSslConfiguration.cmake) else() add_definitions(-DORTHANC_SSL_ENABLED=0) endif() if (ENABLE_JPEG) add_definitions(-DORTHANC_JPEG_ENABLED=1) else() add_definitions(-DORTHANC_JPEG_ENABLED=0) endif() if (ENABLE_JPEG_LOSSLESS) add_definitions(-DORTHANC_JPEG_LOSSLESS_ENABLED=1) else() add_definitions(-DORTHANC_JPEG_LOSSLESS_ENABLED=0) endif() if (ENABLE_PLUGINS) add_definitions(-DORTHANC_PLUGINS_ENABLED=1) else() add_definitions(-DORTHANC_PLUGINS_ENABLED=0) endif() ##################################################################### ## Autogeneration of files ##################################################################### if (STANDALONE_BUILD) # We embed all the resources in the binaries for standalone builds add_definitions(-DORTHANC_STANDALONE=1) EmbedResources( ${ORTHANC_EMBEDDED_FILES} ORTHANC_EXPLORER ${CMAKE_CURRENT_SOURCE_DIR}/OrthancExplorer ${DCMTK_DICTIONARIES} ) else() add_definitions( -DORTHANC_STANDALONE=0 -DORTHANC_PATH=\"${CMAKE_SOURCE_DIR}\" ) EmbedResources( ${ORTHANC_EMBEDDED_FILES} ) endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") execute_process( COMMAND ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py ${ORTHANC_VERSION} Orthanc Orthanc.exe "Lightweight, RESTful DICOM server for medical imaging" ERROR_VARIABLE Failure OUTPUT_FILE ${AUTOGENERATED_DIR}/Orthanc.rc ) if (Failure) message(FATAL_ERROR "Error while computing the version information: ${Failure}") endif() list(APPEND ORTHANC_RESOURCES ${AUTOGENERATED_DIR}/Orthanc.rc) endif() ##################################################################### ## Build the core of Orthanc ##################################################################### # Setup precompiled headers for Microsoft Visual Studio if (MSVC) add_definitions(-DORTHANC_USE_PRECOMPILED_HEADERS=1) ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS( "PrecompiledHeaders.h" "Core/PrecompiledHeaders.cpp" ORTHANC_CORE_SOURCES) ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS( "PrecompiledHeadersServer.h" "OrthancServer/PrecompiledHeadersServer.cpp" ORTHANC_SERVER_SOURCES) ADD_VISUAL_STUDIO_PRECOMPILED_HEADERS( "PrecompiledHeadersUnitTests.h" "UnitTestsSources/PrecompiledHeadersUnitTests.cpp" ORTHANC_UNIT_TESTS_SOURCES) endif() add_definitions( -DORTHANC_VERSION="${ORTHANC_VERSION}" -DORTHANC_DATABASE_VERSION=${ORTHANC_DATABASE_VERSION} -DORTHANC_ENABLE_LOGGING=1 -DORTHANC_MAXIMUM_TAG_LENGTH=256 ) list(LENGTH OPENSSL_SOURCES OPENSSL_SOURCES_LENGTH) if (${OPENSSL_SOURCES_LENGTH} GREATER 0) add_library(OpenSSL STATIC ${OPENSSL_SOURCES}) endif() add_library(CoreLibrary STATIC ${ORTHANC_CORE_SOURCES} ${AUTOGENERATED_SOURCES} ${BOOST_SOURCES} ${CURL_SOURCES} ${GOOGLE_LOG_SOURCES} ${JSONCPP_SOURCES} ${LIBPNG_SOURCES} ${LIBJPEG_SOURCES} ${LUA_SOURCES} ${MONGOOSE_SOURCES} ${PUGIXML_SOURCES} ${SQLITE_SOURCES} ${ZLIB_SOURCES} ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp # This is the minizip distribution to create ZIP files using zlib ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/ioapi.c ${ORTHANC_ROOT}/Resources/ThirdParty/minizip/zip.c ) ##################################################################### ## Build the Orthanc server ##################################################################### add_library(ServerLibrary STATIC ${DCMTK_SOURCES} ${ORTHANC_SERVER_SOURCES} ) # Ensure autogenerated code is built before building ServerLibrary add_dependencies(ServerLibrary CoreLibrary) add_executable(Orthanc OrthancServer/main.cpp ${ORTHANC_RESOURCES} ) target_link_libraries(Orthanc ServerLibrary CoreLibrary ${DCMTK_LIBRARIES}) if (${OPENSSL_SOURCES_LENGTH} GREATER 0) target_link_libraries(Orthanc OpenSSL) endif() install( TARGETS Orthanc RUNTIME DESTINATION sbin ) ##################################################################### ## Build the unit tests ##################################################################### if (UNIT_TESTS_WITH_HTTP_CONNEXIONS) add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1) else() add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=0) endif() add_definitions( -DORTHANC_BUILD_UNIT_TESTS=1 ) include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake) add_executable(UnitTests ${GTEST_SOURCES} ${ORTHANC_UNIT_TESTS_SOURCES} ) target_link_libraries(UnitTests ServerLibrary CoreLibrary ${DCMTK_LIBRARIES}) if (${OPENSSL_SOURCES_LENGTH} GREATER 0) target_link_libraries(UnitTests OpenSSL) endif() ##################################################################### ## Build the "ServeFolders" plugin ##################################################################### if (ENABLE_PLUGINS AND BUILD_SERVE_FOLDERS) execute_process( COMMAND ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py ${ORTHANC_VERSION} ServeFolders ServeFolders.dll "Orthanc plugin to serve additional folders" ERROR_VARIABLE Failure OUTPUT_FILE ${AUTOGENERATED_DIR}/ServeFolders.rc ) if (Failure) message(FATAL_ERROR "Error while computing the version information: ${Failure}") endif() add_definitions(-DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}") include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include) add_library(ServeFolders SHARED ${BOOST_SOURCES} ${JSONCPP_SOURCES} Plugins/Samples/ServeFolders/Plugin.cpp ${AUTOGENERATED_DIR}/ServeFolders.rc ) set_target_properties( ServeFolders PROPERTIES VERSION ${ORTHANC_VERSION} SOVERSION ${ORTHANC_VERSION} ) install( TARGETS ServeFolders RUNTIME DESTINATION lib # Destination for Windows LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux ) endif() ##################################################################### ## Build the "ModalityWorklists" plugin ##################################################################### if (ENABLE_PLUGINS AND BUILD_MODALITY_WORKLISTS) execute_process( COMMAND ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py ${ORTHANC_VERSION} ModalityWorklists ModalityWorklists.dll "Sample Orthanc plugin to serve modality worklists" ERROR_VARIABLE Failure OUTPUT_FILE ${AUTOGENERATED_DIR}/ModalityWorklists.rc ) if (Failure) message(FATAL_ERROR "Error while computing the version information: ${Failure}") endif() add_definitions(-DMODALITY_WORKLISTS_VERSION="${ORTHANC_VERSION}") include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include) add_library(ModalityWorklists SHARED ${BOOST_SOURCES} ${JSONCPP_SOURCES} Plugins/Samples/ModalityWorklists/Plugin.cpp ${AUTOGENERATED_DIR}/ModalityWorklists.rc ) set_target_properties( ModalityWorklists PROPERTIES VERSION ${ORTHANC_VERSION} SOVERSION ${ORTHANC_VERSION} ) install( TARGETS ModalityWorklists RUNTIME DESTINATION lib # Destination for Windows LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux ) endif() ##################################################################### ## Generate the documentation if Doxygen is present ##################################################################### find_package(Doxygen) if (DOXYGEN_FOUND) configure_file( ${CMAKE_SOURCE_DIR}/Resources/Orthanc.doxygen ${CMAKE_CURRENT_BINARY_DIR}/Orthanc.doxygen @ONLY) configure_file( ${CMAKE_SOURCE_DIR}/Resources/OrthancPlugin.doxygen ${CMAKE_CURRENT_BINARY_DIR}/OrthancPlugin.doxygen @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Orthanc.doxygen COMMENT "Generating internal documentation with Doxygen" VERBATIM ) add_custom_command(TARGET Orthanc POST_BUILD COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancPlugin.doxygen WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating plugin documentation with Doxygen" VERBATIM ) install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/OrthancPluginDocumentation/doc/ DESTINATION share/doc/orthanc/OrthancPlugin ) else() message("Doxygen not found. The documentation will not be built.") endif() ##################################################################### ## Install the plugin SDK ##################################################################### if (ENABLE_PLUGINS) install( FILES Plugins/Include/orthanc/OrthancCPlugin.h Plugins/Include/orthanc/OrthancCDatabasePlugin.h Plugins/Include/orthanc/OrthancCppDatabasePlugin.h DESTINATION include/orthanc ) endif() ##################################################################### ## Prepare the "uninstall" target ## http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F ##################################################################### configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/Resources/CMake/Uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) Orthanc-1.0.0/COPYING0000644000000000000000000010451312634042176012300 0ustar 00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . Orthanc-1.0.0/Core/Cache/ICachePageProvider.h0000644000000000000000000000344212634042176016734 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include "../IDynamicObject.h" namespace Orthanc { class ICachePageProvider { public: virtual ~ICachePageProvider() { } virtual IDynamicObject* Provide(const std::string& id) = 0; }; } Orthanc-1.0.0/Core/Cache/LeastRecentlyUsedIndex.h0000644000000000000000000002113312634042176017674 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #include #include "../OrthancException.h" #include "../Toolbox.h" namespace Orthanc { /** * This class implements the index of a cache with least recently * used (LRU) recycling policy. All the items of the cache index * can be associated with a payload. * Reference: http://stackoverflow.com/a/2504317 **/ template class LeastRecentlyUsedIndex : public boost::noncopyable { private: typedef std::list< std::pair > Queue; typedef std::map Index; Index index_; Queue queue_; /** * Internal method for debug builds to check whether the internal * data structures are not corrupted. **/ void CheckInvariants() const; public: /** * Add a new element to the cache index, and make it the most * recent element. * \param id The ID of the element. * \param payload The payload of the element. **/ void Add(T id, Payload payload = Payload()); void AddOrMakeMostRecent(T id, Payload payload = Payload()); /** * When accessing an element of the cache, this method tags the * element as the most recently used. * \param id The most recently accessed item. **/ void MakeMostRecent(T id); void MakeMostRecent(T id, Payload updatedPayload); /** * Remove an element from the cache index. * \param id The item to remove. **/ Payload Invalidate(T id); /** * Get the oldest element in the cache and remove it. * \return The oldest item. **/ T RemoveOldest(); /** * Get the oldest element in the cache, remove it and return the * associated payload. * \param payload Where to store the associated payload. * \return The oldest item. **/ T RemoveOldest(Payload& payload); /** * Check whether an element is contained in the cache. * \param id The item. * \return \c true iff the item is indexed by the cache. **/ bool Contains(T id) const { return index_.find(id) != index_.end(); } bool Contains(T id, Payload& payload) const { typename Index::const_iterator it = index_.find(id); if (it == index_.end()) { return false; } else { payload = it->second->second; return true; } } /** * Return the number of elements in the cache. * \return The number of elements. **/ size_t GetSize() const { assert(index_.size() == queue_.size()); return queue_.size(); } /** * Check whether the cache index is empty. * \return \c true iff the cache is empty. **/ bool IsEmpty() const { return index_.empty(); } const T& GetOldest() const; const Payload& GetOldestPayload() const; }; /****************************************************************** ** Implementation of the template ******************************************************************/ template void LeastRecentlyUsedIndex::CheckInvariants() const { #ifndef NDEBUG assert(index_.size() == queue_.size()); for (typename Index::const_iterator it = index_.begin(); it != index_.end(); it++) { assert(it->second != queue_.end()); assert(it->second->first == it->first); } #endif } template void LeastRecentlyUsedIndex::Add(T id, Payload payload) { if (Contains(id)) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } queue_.push_front(std::make_pair(id, payload)); index_[id] = queue_.begin(); CheckInvariants(); } template void LeastRecentlyUsedIndex::MakeMostRecent(T id) { if (!Contains(id)) { throw OrthancException(ErrorCode_InexistentItem); } typename Index::iterator it = index_.find(id); assert(it != index_.end()); std::pair item = *(it->second); queue_.erase(it->second); queue_.push_front(item); index_[id] = queue_.begin(); CheckInvariants(); } template void LeastRecentlyUsedIndex::AddOrMakeMostRecent(T id, Payload payload) { typename Index::iterator it = index_.find(id); if (it != index_.end()) { // Already existing. Make it most recent. std::pair item = *(it->second); item.second = payload; queue_.erase(it->second); queue_.push_front(item); } else { // New item queue_.push_front(std::make_pair(id, payload)); } index_[id] = queue_.begin(); CheckInvariants(); } template void LeastRecentlyUsedIndex::MakeMostRecent(T id, Payload updatedPayload) { if (!Contains(id)) { throw OrthancException(ErrorCode_InexistentItem); } typename Index::iterator it = index_.find(id); assert(it != index_.end()); std::pair item = *(it->second); item.second = updatedPayload; queue_.erase(it->second); queue_.push_front(item); index_[id] = queue_.begin(); CheckInvariants(); } template Payload LeastRecentlyUsedIndex::Invalidate(T id) { if (!Contains(id)) { throw OrthancException(ErrorCode_InexistentItem); } typename Index::iterator it = index_.find(id); assert(it != index_.end()); Payload payload = it->second->second; queue_.erase(it->second); index_.erase(it); CheckInvariants(); return payload; } template T LeastRecentlyUsedIndex::RemoveOldest(Payload& payload) { if (IsEmpty()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } std::pair item = queue_.back(); T oldest = item.first; payload = item.second; queue_.pop_back(); assert(index_.find(oldest) != index_.end()); index_.erase(oldest); CheckInvariants(); return oldest; } template T LeastRecentlyUsedIndex::RemoveOldest() { if (IsEmpty()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } std::pair item = queue_.back(); T oldest = item.first; queue_.pop_back(); assert(index_.find(oldest) != index_.end()); index_.erase(oldest); CheckInvariants(); return oldest; } template const T& LeastRecentlyUsedIndex::GetOldest() const { if (IsEmpty()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } return queue_.back().first; } template const Payload& LeastRecentlyUsedIndex::GetOldestPayload() const { if (IsEmpty()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } return queue_.back().second; } } Orthanc-1.0.0/Core/Cache/MemoryCache.cpp0000644000000000000000000000567112634042176016045 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "MemoryCache.h" #include "../Logging.h" namespace Orthanc { MemoryCache::Page& MemoryCache::Load(const std::string& id) { // Reuse the cache entry if it already exists Page* p = NULL; if (index_.Contains(id, p)) { VLOG(1) << "Reusing a cache page"; assert(p != NULL); index_.MakeMostRecent(id); return *p; } // The id is not in the cache yet. Make some room if the cache // is full. if (index_.GetSize() == cacheSize_) { VLOG(1) << "Dropping the oldest cache page"; index_.RemoveOldest(p); delete p; } // Create a new cache page std::auto_ptr result(new Page); result->id_ = id; result->content_.reset(provider_.Provide(id)); // Add the newly create page to the cache VLOG(1) << "Registering new data in a cache page"; p = result.release(); index_.Add(id, p); return *p; } MemoryCache::MemoryCache(ICachePageProvider& provider, size_t cacheSize) : provider_(provider), cacheSize_(cacheSize) { } MemoryCache::~MemoryCache() { while (!index_.IsEmpty()) { Page* element = NULL; index_.RemoveOldest(element); assert(element != NULL); delete element; } } IDynamicObject& MemoryCache::Access(const std::string& id) { return *Load(id).content_; } } Orthanc-1.0.0/Core/Cache/MemoryCache.h0000644000000000000000000000425612634042176015510 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include "LeastRecentlyUsedIndex.h" #include "ICachePageProvider.h" namespace Orthanc { /** * WARNING: This class is NOT thread-safe. **/ class MemoryCache { private: struct Page { std::string id_; std::auto_ptr content_; }; ICachePageProvider& provider_; size_t cacheSize_; LeastRecentlyUsedIndex index_; Page& Load(const std::string& id); public: MemoryCache(ICachePageProvider& provider, size_t cacheSize); ~MemoryCache(); IDynamicObject& Access(const std::string& id); }; } Orthanc-1.0.0/Core/Cache/SharedArchive.cpp0000644000000000000000000000672412634042176016361 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "SharedArchive.h" #include "../Uuid.h" namespace Orthanc { void SharedArchive::RemoveInternal(const std::string& id) { Archive::iterator it = archive_.find(id); if (it != archive_.end()) { delete it->second; archive_.erase(it); } } SharedArchive::Accessor::Accessor(SharedArchive& that, const std::string& id) : lock_(that.mutex_) { Archive::iterator it = that.archive_.find(id); if (it == that.archive_.end()) { throw OrthancException(ErrorCode_InexistentItem); } else { that.lru_.MakeMostRecent(id); item_ = it->second; } } SharedArchive::SharedArchive(size_t maxSize) : maxSize_(maxSize) { if (maxSize == 0) { throw OrthancException(ErrorCode_ParameterOutOfRange); } } SharedArchive::~SharedArchive() { for (Archive::iterator it = archive_.begin(); it != archive_.end(); ++it) { delete it->second; } } std::string SharedArchive::Add(IDynamicObject* obj) { boost::mutex::scoped_lock lock(mutex_); if (archive_.size() == maxSize_) { // The quota has been reached, remove the oldest element std::string oldest = lru_.RemoveOldest(); RemoveInternal(oldest); } std::string id = Toolbox::GenerateUuid(); RemoveInternal(id); // Should never be useful because of UUID archive_[id] = obj; lru_.Add(id); return id; } void SharedArchive::Remove(const std::string& id) { boost::mutex::scoped_lock lock(mutex_); RemoveInternal(id); lru_.Invalidate(id); } void SharedArchive::List(std::list& items) { items.clear(); boost::mutex::scoped_lock lock(mutex_); for (Archive::const_iterator it = archive_.begin(); it != archive_.end(); ++it) { items.push_back(it->first); } } } Orthanc-1.0.0/Core/Cache/SharedArchive.h0000644000000000000000000000505512634042176016022 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "LeastRecentlyUsedIndex.h" #include "../IDynamicObject.h" #include #include namespace Orthanc { class SharedArchive : public boost::noncopyable { private: typedef std::map Archive; size_t maxSize_; boost::mutex mutex_; Archive archive_; Orthanc::LeastRecentlyUsedIndex lru_; void RemoveInternal(const std::string& id); public: class Accessor : public boost::noncopyable { private: boost::mutex::scoped_lock lock_; IDynamicObject* item_; public: Accessor(SharedArchive& that, const std::string& id); IDynamicObject& GetItem() const { return *item_; } }; SharedArchive(size_t maxSize); ~SharedArchive(); std::string Add(IDynamicObject* obj); // Takes the ownership void Remove(const std::string& id); void List(std::list& items); }; } Orthanc-1.0.0/Core/ChunkedBuffer.cpp0000644000000000000000000000524112634042176015352 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "ChunkedBuffer.h" #include #include namespace Orthanc { void ChunkedBuffer::Clear() { numBytes_ = 0; for (Chunks::iterator it = chunks_.begin(); it != chunks_.end(); ++it) { delete *it; } } void ChunkedBuffer::AddChunk(const char* chunkData, size_t chunkSize) { if (chunkSize == 0) { return; } assert(chunkData != NULL); chunks_.push_back(new std::string(chunkData, chunkSize)); numBytes_ += chunkSize; } void ChunkedBuffer::AddChunk(const std::string& chunk) { if (chunk.size() > 0) { AddChunk(&chunk[0], chunk.size()); } } void ChunkedBuffer::Flatten(std::string& result) { result.resize(numBytes_); size_t pos = 0; for (Chunks::iterator it = chunks_.begin(); it != chunks_.end(); ++it) { assert(*it != NULL); size_t s = (*it)->size(); if (s != 0) { memcpy(&result[pos], (*it)->c_str(), s); pos += s; } delete *it; } chunks_.clear(); } } Orthanc-1.0.0/Core/ChunkedBuffer.h0000644000000000000000000000414012634042176015014 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include namespace Orthanc { class ChunkedBuffer { private: typedef std::list Chunks; size_t numBytes_; Chunks chunks_; void Clear(); public: ChunkedBuffer() : numBytes_(0) { } ~ChunkedBuffer() { Clear(); } size_t GetNumBytes() const { return numBytes_; } void AddChunk(const char* chunkData, size_t chunkSize); void AddChunk(const std::string& chunk); void Flatten(std::string& result); }; } Orthanc-1.0.0/Core/Compression/DeflateBaseCompressor.cpp0000644000000000000000000000477512634042176021367 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DeflateBaseCompressor.h" #include "../OrthancException.h" #include "../Logging.h" #include namespace Orthanc { void DeflateBaseCompressor::SetCompressionLevel(uint8_t level) { if (level >= 10) { LOG(ERROR) << "Zlib compression level must be between 0 (no compression) and 9 (highest compression)"; throw OrthancException(ErrorCode_ParameterOutOfRange); } compressionLevel_ = level; } uint64_t DeflateBaseCompressor::ReadUncompressedSizePrefix(const void* compressed, size_t compressedSize) { if (compressedSize == 0) { return 0; } if (compressedSize < sizeof(uint64_t)) { LOG(ERROR) << "The compressed buffer is ill-formed"; throw OrthancException(ErrorCode_CorruptedFile); } uint64_t size; memcpy(&size, compressed, sizeof(uint64_t)); return size; } } Orthanc-1.0.0/Core/Compression/DeflateBaseCompressor.h0000644000000000000000000000460012634042176021017 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IBufferCompressor.h" #include namespace Orthanc { class DeflateBaseCompressor : public IBufferCompressor { private: uint8_t compressionLevel_; bool prefixWithUncompressedSize_; protected: uint64_t ReadUncompressedSizePrefix(const void* compressed, size_t compressedSize); public: DeflateBaseCompressor() : compressionLevel_(6), prefixWithUncompressedSize_(false) { } void SetCompressionLevel(uint8_t level); void SetPrefixWithUncompressedSize(bool prefix) { prefixWithUncompressedSize_ = prefix; } bool HasPrefixWithUncompressedSize() const { return prefixWithUncompressedSize_; } uint8_t GetCompressionLevel() const { return compressionLevel_; } }; } Orthanc-1.0.0/Core/Compression/GzipCompressor.cpp0000644000000000000000000002153712634042176020134 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "GzipCompressor.h" #include #include #include #include "../OrthancException.h" #include "../Logging.h" namespace Orthanc { uint64_t GzipCompressor::GuessUncompressedSize(const void* compressed, size_t compressedSize) { /** * "Is there a way to find out the size of the original file which * is inside a GZIP file? [...] There is no truly reliable way, * other than gunzipping the stream. You do not need to save the * result of the decompression, so you can determine the size by * simply reading and decoding the entire file without taking up * space with the decompressed result. * * There is an unreliable way to determine the uncompressed size, * which is to look at the last four bytes of the gzip file, which * is the uncompressed length of that entry modulo 232 in little * endian order. * * It is unreliable because a) the uncompressed data may be longer * than 2^32 bytes, and b) the gzip file may consist of multiple * gzip streams, in which case you would find the length of only * the last of those streams. * * If you are in control of the source of the gzip files, you know * that they consist of single gzip streams, and you know that * they are less than 2^32 bytes uncompressed, then and only then * can you use those last four bytes with confidence." * * http://stackoverflow.com/a/9727599/881731 **/ if (compressedSize < 4) { throw OrthancException(ErrorCode_BadFileFormat); } const uint8_t* p = reinterpret_cast(compressed) + compressedSize - 4; return ((static_cast(p[0]) << 0) + (static_cast(p[1]) << 8) + (static_cast(p[2]) << 16) + (static_cast(p[3]) << 24)); } void GzipCompressor::Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize) { uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */; if (compressedSize == 0) { compressedSize = 1; } uint8_t* target; if (HasPrefixWithUncompressedSize()) { compressed.resize(compressedSize + sizeof(uint64_t)); target = reinterpret_cast(&compressed[0]) + sizeof(uint64_t); } else { compressed.resize(compressedSize); target = reinterpret_cast(&compressed[0]); } z_stream stream; memset(&stream, 0, sizeof(stream)); stream.next_in = const_cast(reinterpret_cast(uncompressed)); stream.next_out = reinterpret_cast(target); stream.avail_in = static_cast(uncompressedSize); stream.avail_out = static_cast(compressedSize); // Ensure no overflow (if the buffer is too large for the current archicture) if (static_cast(stream.avail_in) != uncompressedSize || static_cast(stream.avail_out) != compressedSize) { throw OrthancException(ErrorCode_NotEnoughMemory); } // Initialize the compression engine int error = deflateInit2(&stream, GetCompressionLevel(), Z_DEFLATED, MAX_WBITS + 16, // ask for gzip output 8, // default memory level Z_DEFAULT_STRATEGY); if (error != Z_OK) { // Cannot initialize zlib compressed.clear(); throw OrthancException(ErrorCode_InternalError); } // Compress the input buffer error = deflate(&stream, Z_FINISH); if (error != Z_STREAM_END) { deflateEnd(&stream); compressed.clear(); switch (error) { case Z_MEM_ERROR: throw OrthancException(ErrorCode_NotEnoughMemory); default: throw OrthancException(ErrorCode_InternalError); } } size_t size = stream.total_out; if (deflateEnd(&stream) != Z_OK) { throw OrthancException(ErrorCode_InternalError); } // The compression was successful if (HasPrefixWithUncompressedSize()) { uint64_t s = static_cast(uncompressedSize); memcpy(&compressed[0], &s, sizeof(uint64_t)); compressed.resize(size + sizeof(uint64_t)); } else { compressed.resize(size); } } void GzipCompressor::Uncompress(std::string& uncompressed, const void* compressed, size_t compressedSize) { uint64_t uncompressedSize; const uint8_t* source = reinterpret_cast(compressed); if (HasPrefixWithUncompressedSize()) { uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize); source += sizeof(uint64_t); compressedSize -= sizeof(uint64_t); } else { uncompressedSize = GuessUncompressedSize(compressed, compressedSize); } try { uncompressed.resize(static_cast(uncompressedSize)); } catch (...) { throw OrthancException(ErrorCode_NotEnoughMemory); } z_stream stream; memset(&stream, 0, sizeof(stream)); char dummy = '\0'; // zlib does not like NULL output buffers (even if the uncompressed data is empty) stream.next_in = const_cast(source); stream.next_out = reinterpret_cast(uncompressedSize == 0 ? &dummy : &uncompressed[0]); stream.avail_in = static_cast(compressedSize); stream.avail_out = static_cast(uncompressedSize); // Ensure no overflow (if the buffer is too large for the current archicture) if (static_cast(stream.avail_in) != compressedSize || static_cast(stream.avail_out) != uncompressedSize) { throw OrthancException(ErrorCode_NotEnoughMemory); } // Initialize the compression engine int error = inflateInit2(&stream, MAX_WBITS + 16); // this is a gzip input if (error != Z_OK) { // Cannot initialize zlib uncompressed.clear(); throw OrthancException(ErrorCode_InternalError); } // Uncompress the input buffer error = inflate(&stream, Z_FINISH); if (error != Z_STREAM_END) { inflateEnd(&stream); uncompressed.clear(); switch (error) { case Z_MEM_ERROR: throw OrthancException(ErrorCode_NotEnoughMemory); case Z_BUF_ERROR: case Z_NEED_DICT: throw OrthancException(ErrorCode_BadFileFormat); default: throw OrthancException(ErrorCode_InternalError); } } size_t size = stream.total_out; if (inflateEnd(&stream) != Z_OK) { uncompressed.clear(); throw OrthancException(ErrorCode_InternalError); } if (size != uncompressedSize) { uncompressed.clear(); // The uncompressed size was not that properly guess, presumably // because of a file size over 4GB. Should fallback to // stream-based decompression. LOG(ERROR) << "The uncompressed size of a gzip-encoded buffer was not properly guessed"; throw OrthancException(ErrorCode_NotImplemented); } } } Orthanc-1.0.0/Core/Compression/GzipCompressor.h0000644000000000000000000000431312634042176017572 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DeflateBaseCompressor.h" namespace Orthanc { class GzipCompressor : public DeflateBaseCompressor { private: uint64_t GuessUncompressedSize(const void* compressed, size_t compressedSize); public: GzipCompressor() { SetPrefixWithUncompressedSize(false); } virtual void Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize); virtual void Uncompress(std::string& uncompressed, const void* compressed, size_t compressedSize); }; } Orthanc-1.0.0/Core/Compression/HierarchicalZipWriter.cpp0000644000000000000000000001117512634042176021401 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HierarchicalZipWriter.h" #include "../Toolbox.h" #include "../OrthancException.h" #include namespace Orthanc { std::string HierarchicalZipWriter::Index::KeepAlphanumeric(const std::string& source) { std::string result; bool lastSpace = false; result.reserve(source.size()); for (size_t i = 0; i < source.size(); i++) { char c = source[i]; if (c == '^') c = ' '; if (c <= 127 && c >= 0) { if (isspace(c)) { if (!lastSpace) { lastSpace = true; result.push_back(' '); } } else if (isalnum(c) || c == '.' || c == '_') { result.push_back(c); lastSpace = false; } } } return Toolbox::StripSpaces(result); } std::string HierarchicalZipWriter::Index::GetCurrentDirectoryPath() const { std::string result; Stack::const_iterator it = stack_.begin(); ++it; // Skip the root node (to avoid absolute paths) while (it != stack_.end()) { result += (*it)->name_ + "/"; ++it; } return result; } std::string HierarchicalZipWriter::Index::EnsureUniqueFilename(const char* filename) { std::string standardized = KeepAlphanumeric(filename); Directory& d = *stack_.back(); Directory::Content::iterator it = d.content_.find(standardized); if (it == d.content_.end()) { d.content_[standardized] = 1; return standardized; } else { it->second++; return standardized + "-" + boost::lexical_cast(it->second); } } HierarchicalZipWriter::Index::Index() { stack_.push_back(new Directory); } HierarchicalZipWriter::Index::~Index() { for (Stack::iterator it = stack_.begin(); it != stack_.end(); ++it) { delete *it; } } std::string HierarchicalZipWriter::Index::OpenFile(const char* name) { return GetCurrentDirectoryPath() + EnsureUniqueFilename(name); } void HierarchicalZipWriter::Index::OpenDirectory(const char* name) { std::string d = EnsureUniqueFilename(name); // Push the new directory onto the stack stack_.push_back(new Directory); stack_.back()->name_ = d; } void HierarchicalZipWriter::Index::CloseDirectory() { if (IsRoot()) { // Cannot close the root node throw OrthancException(ErrorCode_BadSequenceOfCalls); } delete stack_.back(); stack_.pop_back(); } HierarchicalZipWriter::HierarchicalZipWriter(const char* path) { writer_.SetOutputPath(path); writer_.Open(); } HierarchicalZipWriter::~HierarchicalZipWriter() { writer_.Close(); } void HierarchicalZipWriter::OpenFile(const char* name) { std::string p = indexer_.OpenFile(name); writer_.OpenFile(p.c_str()); } void HierarchicalZipWriter::OpenDirectory(const char* name) { indexer_.OpenDirectory(name); } void HierarchicalZipWriter::CloseDirectory() { indexer_.CloseDirectory(); } } Orthanc-1.0.0/Core/Compression/HierarchicalZipWriter.h0000644000000000000000000000712012634042176021041 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ZipWriter.h" #include #include #include namespace Orthanc { class HierarchicalZipWriter { #if ORTHANC_BUILD_UNIT_TESTS == 1 FRIEND_TEST(HierarchicalZipWriter, Index); FRIEND_TEST(HierarchicalZipWriter, Filenames); #endif private: class Index { private: struct Directory { typedef std::map Content; std::string name_; Content content_; }; typedef std::list Stack; Stack stack_; std::string EnsureUniqueFilename(const char* filename); public: Index(); ~Index(); bool IsRoot() const { return stack_.size() == 1; } std::string OpenFile(const char* name); void OpenDirectory(const char* name); void CloseDirectory(); std::string GetCurrentDirectoryPath() const; static std::string KeepAlphanumeric(const std::string& source); }; Index indexer_; ZipWriter writer_; public: HierarchicalZipWriter(const char* path); ~HierarchicalZipWriter(); void SetZip64(bool isZip64) { writer_.SetZip64(isZip64); } bool IsZip64() const { return writer_.IsZip64(); } void SetCompressionLevel(uint8_t level) { writer_.SetCompressionLevel(level); } uint8_t GetCompressionLevel() const { return writer_.GetCompressionLevel(); } void SetAppendToExisting(bool append) { writer_.SetAppendToExisting(append); } bool IsAppendToExisting() const { return writer_.IsAppendToExisting(); } void OpenFile(const char* name); void OpenDirectory(const char* name); void CloseDirectory(); std::string GetCurrentDirectoryPath() const { return indexer_.GetCurrentDirectoryPath(); } void Write(const char* data, size_t length) { writer_.Write(data, length); } void Write(const std::string& data) { writer_.Write(data); } }; } Orthanc-1.0.0/Core/Compression/IBufferCompressor.h0000644000000000000000000000537712634042176020216 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include namespace Orthanc { class IBufferCompressor : public boost::noncopyable { public: virtual ~IBufferCompressor() { } virtual void Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize) = 0; virtual void Uncompress(std::string& uncompressed, const void* compressed, size_t compressedSize) = 0; static void Compress(std::string& compressed, IBufferCompressor& compressor, const std::string& uncompressed) { compressor.Compress(compressed, uncompressed.size() == 0 ? NULL : uncompressed.c_str(), uncompressed.size()); } static void Uncompress(std::string& uncompressed, IBufferCompressor& compressor, const std::string& compressed) { compressor.Uncompress(uncompressed, compressed.size() == 0 ? NULL : compressed.c_str(), compressed.size()); } }; } Orthanc-1.0.0/Core/Compression/ZipWriter.cpp0000644000000000000000000001447612634042176017111 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #ifndef NOMINMAX #define NOMINMAX #endif #include "ZipWriter.h" #include #include #include #include "../../Resources/ThirdParty/minizip/zip.h" #include "../OrthancException.h" #include "../Logging.h" static void PrepareFileInfo(zip_fileinfo& zfi) { memset(&zfi, 0, sizeof(zfi)); using namespace boost::posix_time; ptime now = second_clock::local_time(); boost::gregorian::date today = now.date(); ptime midnight(today); time_duration sinceMidnight = now - midnight; zfi.tmz_date.tm_sec = sinceMidnight.seconds(); // seconds after the minute - [0,59] zfi.tmz_date.tm_min = sinceMidnight.minutes(); // minutes after the hour - [0,59] zfi.tmz_date.tm_hour = sinceMidnight.hours(); // hours since midnight - [0,23] // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_day.html zfi.tmz_date.tm_mday = today.day(); // day of the month - [1,31] // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_month.html zfi.tmz_date.tm_mon = today.month() - 1; // months since January - [0,11] // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_year.html zfi.tmz_date.tm_year = today.year(); // years - [1980..2044] } namespace Orthanc { struct ZipWriter::PImpl { zipFile file_; PImpl() : file_(NULL) { } }; ZipWriter::ZipWriter() : pimpl_(new PImpl), isZip64_(false), hasFileInZip_(false), append_(false), compressionLevel_(6) { } ZipWriter::~ZipWriter() { Close(); } void ZipWriter::Close() { if (IsOpen()) { zipClose(pimpl_->file_, "Created by Orthanc"); pimpl_->file_ = NULL; hasFileInZip_ = false; } } bool ZipWriter::IsOpen() const { return pimpl_->file_ != NULL; } void ZipWriter::Open() { if (IsOpen()) { return; } if (path_.size() == 0) { LOG(ERROR) << "Please call SetOutputPath() before creating the file"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } hasFileInZip_ = false; int mode = APPEND_STATUS_CREATE; if (append_ && boost::filesystem::exists(path_)) { mode = APPEND_STATUS_ADDINZIP; } if (isZip64_) { pimpl_->file_ = zipOpen64(path_.c_str(), mode); } else { pimpl_->file_ = zipOpen(path_.c_str(), mode); } if (!pimpl_->file_) { throw OrthancException(ErrorCode_CannotWriteFile); } } void ZipWriter::SetOutputPath(const char* path) { Close(); path_ = path; } void ZipWriter::SetZip64(bool isZip64) { Close(); isZip64_ = isZip64; } void ZipWriter::SetCompressionLevel(uint8_t level) { if (level >= 10) { LOG(ERROR) << "ZIP compression level must be between 0 (no compression) and 9 (highest compression)"; throw OrthancException(ErrorCode_ParameterOutOfRange); } Close(); compressionLevel_ = level; } void ZipWriter::OpenFile(const char* path) { Open(); zip_fileinfo zfi; PrepareFileInfo(zfi); int result; if (isZip64_) { result = zipOpenNewFileInZip64(pimpl_->file_, path, &zfi, NULL, 0, NULL, 0, "", // Comment Z_DEFLATED, compressionLevel_, 1); } else { result = zipOpenNewFileInZip(pimpl_->file_, path, &zfi, NULL, 0, NULL, 0, "", // Comment Z_DEFLATED, compressionLevel_); } if (result != 0) { throw OrthancException(ErrorCode_CannotWriteFile); } hasFileInZip_ = true; } void ZipWriter::Write(const std::string& data) { if (data.size()) { Write(&data[0], data.size()); } } void ZipWriter::Write(const char* data, size_t length) { if (!hasFileInZip_) { LOG(ERROR) << "Call first OpenFile()"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } const size_t maxBytesInAStep = std::numeric_limits::max(); while (length > 0) { int bytes = static_cast(length <= maxBytesInAStep ? length : maxBytesInAStep); if (zipWriteInFileInZip(pimpl_->file_, data, bytes)) { throw OrthancException(ErrorCode_CannotWriteFile); } data += bytes; length -= bytes; } } void ZipWriter::SetAppendToExisting(bool append) { Close(); append_ = append; } } Orthanc-1.0.0/Core/Compression/ZipWriter.h0000644000000000000000000000517112634042176016546 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #if ORTHANC_BUILD_UNIT_TESTS == 1 #include #endif namespace Orthanc { class ZipWriter { private: struct PImpl; boost::shared_ptr pimpl_; bool isZip64_; bool hasFileInZip_; bool append_; uint8_t compressionLevel_; std::string path_; public: ZipWriter(); ~ZipWriter(); void SetZip64(bool isZip64); bool IsZip64() const { return isZip64_; } void SetCompressionLevel(uint8_t level); uint8_t GetCompressionLevel() const { return compressionLevel_; } void SetAppendToExisting(bool append); bool IsAppendToExisting() const { return append_; } void Open(); void Close(); bool IsOpen() const; void SetOutputPath(const char* path); const std::string& GetOutputPath() const { return path_; } void OpenFile(const char* path); void Write(const char* data, size_t length); void Write(const std::string& data); }; } Orthanc-1.0.0/Core/Compression/ZlibCompressor.cpp0000644000000000000000000001116212634042176020114 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "ZlibCompressor.h" #include "../OrthancException.h" #include "../Logging.h" #include #include #include namespace Orthanc { void ZlibCompressor::Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize) { if (uncompressedSize == 0) { compressed.clear(); return; } uLongf compressedSize = compressBound(uncompressedSize) + 1024 /* security margin */; if (compressedSize == 0) { compressedSize = 1; } uint8_t* target; if (HasPrefixWithUncompressedSize()) { compressed.resize(compressedSize + sizeof(uint64_t)); target = reinterpret_cast(&compressed[0]) + sizeof(uint64_t); } else { compressed.resize(compressedSize); target = reinterpret_cast(&compressed[0]); } int error = compress2(target, &compressedSize, const_cast(static_cast(uncompressed)), uncompressedSize, GetCompressionLevel()); if (error != Z_OK) { compressed.clear(); switch (error) { case Z_MEM_ERROR: throw OrthancException(ErrorCode_NotEnoughMemory); default: throw OrthancException(ErrorCode_InternalError); } } // The compression was successful if (HasPrefixWithUncompressedSize()) { uint64_t s = static_cast(uncompressedSize); memcpy(&compressed[0], &s, sizeof(uint64_t)); compressed.resize(compressedSize + sizeof(uint64_t)); } else { compressed.resize(compressedSize); } } void ZlibCompressor::Uncompress(std::string& uncompressed, const void* compressed, size_t compressedSize) { if (compressedSize == 0) { uncompressed.clear(); return; } if (!HasPrefixWithUncompressedSize()) { LOG(ERROR) << "Cannot guess the uncompressed size of a zlib-encoded buffer"; throw OrthancException(ErrorCode_InternalError); } uint64_t uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize); try { uncompressed.resize(static_cast(uncompressedSize)); } catch (...) { throw OrthancException(ErrorCode_NotEnoughMemory); } uLongf tmp = static_cast(uncompressedSize); int error = uncompress (reinterpret_cast(&uncompressed[0]), &tmp, reinterpret_cast(compressed) + sizeof(uint64_t), compressedSize - sizeof(uint64_t)); if (error != Z_OK) { uncompressed.clear(); switch (error) { case Z_DATA_ERROR: throw OrthancException(ErrorCode_CorruptedFile); case Z_MEM_ERROR: throw OrthancException(ErrorCode_NotEnoughMemory); default: throw OrthancException(ErrorCode_InternalError); } } } } Orthanc-1.0.0/Core/Compression/ZlibCompressor.h0000644000000000000000000000411012634042176017554 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DeflateBaseCompressor.h" namespace Orthanc { class ZlibCompressor : public DeflateBaseCompressor { public: ZlibCompressor() { SetPrefixWithUncompressedSize(true); } virtual void Compress(std::string& compressed, const void* uncompressed, size_t uncompressedSize); virtual void Uncompress(std::string& uncompressed, const void* compressed, size_t compressedSize); }; } Orthanc-1.0.0/Core/DicomFormat/DicomArray.cpp0000644000000000000000000000461612634042176017102 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DicomArray.h" #include namespace Orthanc { DicomArray::DicomArray(const DicomMap& map) { elements_.reserve(map.map_.size()); for (DicomMap::Map::const_iterator it = map.map_.begin(); it != map.map_.end(); ++it) { elements_.push_back(new DicomElement(it->first, *it->second)); } } DicomArray::~DicomArray() { for (size_t i = 0; i < elements_.size(); i++) { delete elements_[i]; } } void DicomArray::Print(FILE* fp) const { for (size_t i = 0; i < elements_.size(); i++) { DicomTag t = elements_[i]->GetTag(); const DicomValue& v = elements_[i]->GetValue(); std::string s = v.IsNull() ? "(null)" : v.GetContent(); printf("0x%04x 0x%04x [%s]\n", t.GetGroup(), t.GetElement(), s.c_str()); } } } Orthanc-1.0.0/Core/DicomFormat/DicomArray.h0000644000000000000000000000405612634042176016545 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomElement.h" #include "DicomMap.h" #include namespace Orthanc { class DicomArray : public boost::noncopyable { private: typedef std::vector Elements; Elements elements_; public: DicomArray(const DicomMap& map); ~DicomArray(); size_t GetSize() const { return elements_.size(); } const DicomElement& GetElement(size_t i) const { return *elements_[i]; } void Print(FILE* fp) const; }; } Orthanc-1.0.0/Core/DicomFormat/DicomElement.h0000644000000000000000000000500612634042176017054 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomValue.h" #include "DicomTag.h" namespace Orthanc { class DicomElement : public boost::noncopyable { private: DicomTag tag_; DicomValue* value_; public: DicomElement(uint16_t group, uint16_t element, const DicomValue& value) : tag_(group, element), value_(value.Clone()) { } DicomElement(const DicomTag& tag, const DicomValue& value) : tag_(tag), value_(value.Clone()) { } ~DicomElement() { delete value_; } const DicomTag& GetTag() const { return tag_; } const DicomValue& GetValue() const { return *value_; } uint16_t GetTagGroup() const { return tag_.GetGroup(); } uint16_t GetTagElement() const { return tag_.GetElement(); } bool operator< (const DicomElement& other) const { return GetTag() < other.GetTag(); } }; } Orthanc-1.0.0/Core/DicomFormat/DicomImageInformation.cpp0000644000000000000000000001675512634042176021263 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #ifndef NOMINMAX #define NOMINMAX #endif #include "DicomImageInformation.h" #include "../OrthancException.h" #include "../Toolbox.h" #include #include #include #include namespace Orthanc { DicomImageInformation::DicomImageInformation(const DicomMap& values) { unsigned int pixelRepresentation; unsigned int planarConfiguration = 0; try { std::string p = values.GetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION).GetContent(); Toolbox::ToUpperCase(p); if (p == "RGB") { photometric_ = PhotometricInterpretation_RGB; } else if (p == "MONOCHROME1") { photometric_ = PhotometricInterpretation_Monochrome1; } else if (p == "MONOCHROME2") { photometric_ = PhotometricInterpretation_Monochrome2; } else if (p == "PALETTE COLOR") { photometric_ = PhotometricInterpretation_Palette; } else if (p == "HSV") { photometric_ = PhotometricInterpretation_HSV; } else if (p == "ARGB") { photometric_ = PhotometricInterpretation_ARGB; } else if (p == "CMYK") { photometric_ = PhotometricInterpretation_CMYK; } else if (p == "YBR_FULL") { photometric_ = PhotometricInterpretation_YBRFull; } else if (p == "YBR_FULL_422") { photometric_ = PhotometricInterpretation_YBRFull422; } else if (p == "YBR_PARTIAL_420") { photometric_ = PhotometricInterpretation_YBRPartial420; } else if (p == "YBR_PARTIAL_422") { photometric_ = PhotometricInterpretation_YBRPartial422; } else if (p == "YBR_ICT") { photometric_ = PhotometricInterpretation_YBR_ICT; } else if (p == "YBR_RCT") { photometric_ = PhotometricInterpretation_YBR_RCT; } else { photometric_ = PhotometricInterpretation_Unknown; } width_ = boost::lexical_cast(values.GetValue(DICOM_TAG_COLUMNS).GetContent()); height_ = boost::lexical_cast(values.GetValue(DICOM_TAG_ROWS).GetContent()); bitsAllocated_ = boost::lexical_cast(values.GetValue(DICOM_TAG_BITS_ALLOCATED).GetContent()); try { samplesPerPixel_ = boost::lexical_cast(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).GetContent()); } catch (OrthancException&) { samplesPerPixel_ = 1; // Assume 1 color channel } try { bitsStored_ = boost::lexical_cast(values.GetValue(DICOM_TAG_BITS_STORED).GetContent()); } catch (OrthancException&) { bitsStored_ = bitsAllocated_; } try { highBit_ = boost::lexical_cast(values.GetValue(DICOM_TAG_HIGH_BIT).GetContent()); } catch (OrthancException&) { highBit_ = bitsStored_ - 1; } try { pixelRepresentation = boost::lexical_cast(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).GetContent()); } catch (OrthancException&) { pixelRepresentation = 0; // Assume unsigned pixels } if (samplesPerPixel_ > 1) { // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1 // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/ try { planarConfiguration = boost::lexical_cast(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).GetContent()); } catch (OrthancException&) { planarConfiguration = 0; // Assume interleaved color channels } } } catch (boost::bad_lexical_cast&) { throw OrthancException(ErrorCode_NotImplemented); } catch (OrthancException&) { throw OrthancException(ErrorCode_NotImplemented); } try { numberOfFrames_ = boost::lexical_cast(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).GetContent()); } catch (OrthancException&) { // If the tag "NumberOfFrames" is absent, assume there is a single frame numberOfFrames_ = 1; } catch (boost::bad_lexical_cast&) { throw OrthancException(ErrorCode_NotImplemented); } if ((bitsAllocated_ != 8 && bitsAllocated_ != 16 && bitsAllocated_ != 24 && bitsAllocated_ != 32) || numberOfFrames_ == 0 || (planarConfiguration != 0 && planarConfiguration != 1)) { throw OrthancException(ErrorCode_NotImplemented); } if (bitsAllocated_ > 32 || bitsStored_ >= 32) { // Not available, as the accessor internally uses int32_t values throw OrthancException(ErrorCode_NotImplemented); } if (samplesPerPixel_ == 0) { throw OrthancException(ErrorCode_NotImplemented); } bytesPerValue_ = bitsAllocated_ / 8; isPlanar_ = (planarConfiguration != 0 ? true : false); isSigned_ = (pixelRepresentation != 0 ? true : false); } bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const { if (photometric_ == PhotometricInterpretation_Monochrome1 || photometric_ == PhotometricInterpretation_Monochrome2) { if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) { format = PixelFormat_Grayscale8; return true; } if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && !IsSigned()) { format = PixelFormat_Grayscale16; return true; } if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && IsSigned()) { format = PixelFormat_SignedGrayscale16; return true; } } if (GetBitsStored() == 8 && GetChannelCount() == 3 && !IsSigned() && photometric_ == PhotometricInterpretation_RGB) { format = PixelFormat_RGB24; return true; } return false; } } Orthanc-1.0.0/Core/DicomFormat/DicomImageInformation.h0000644000000000000000000000603312634042176020714 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomMap.h" #include namespace Orthanc { class DicomImageInformation { private: unsigned int width_; unsigned int height_; unsigned int samplesPerPixel_; unsigned int numberOfFrames_; bool isPlanar_; bool isSigned_; size_t bytesPerValue_; unsigned int bitsAllocated_; unsigned int bitsStored_; unsigned int highBit_; PhotometricInterpretation photometric_; public: DicomImageInformation(const DicomMap& values); unsigned int GetWidth() const { return width_; } unsigned int GetHeight() const { return height_; } unsigned int GetNumberOfFrames() const { return numberOfFrames_; } unsigned int GetChannelCount() const { return samplesPerPixel_; } unsigned int GetBitsStored() const { return bitsStored_; } size_t GetBytesPerValue() const { return bytesPerValue_; } bool IsSigned() const { return isSigned_; } unsigned int GetBitsAllocated() const { return bitsAllocated_; } unsigned int GetHighBit() const { return highBit_; } bool IsPlanar() const { return isPlanar_; } unsigned int GetShift() const { return highBit_ + 1 - bitsStored_; } PhotometricInterpretation GetPhotometricInterpretation() const { return photometric_; } bool ExtractPixelFormat(PixelFormat& format) const; }; } Orthanc-1.0.0/Core/DicomFormat/DicomInstanceHasher.cpp0000644000000000000000000000700212634042176020713 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DicomInstanceHasher.h" #include "../OrthancException.h" #include "../Toolbox.h" namespace Orthanc { void DicomInstanceHasher::Setup(const std::string& patientId, const std::string& studyUid, const std::string& seriesUid, const std::string& instanceUid) { patientId_ = patientId; studyUid_ = studyUid; seriesUid_ = seriesUid; instanceUid_ = instanceUid; if (studyUid_.size() == 0 || seriesUid_.size() == 0 || instanceUid_.size() == 0) { throw OrthancException(ErrorCode_BadFileFormat); } } DicomInstanceHasher::DicomInstanceHasher(const DicomMap& instance) { const DicomValue* patientId = instance.TestAndGetValue(DICOM_TAG_PATIENT_ID); Setup(patientId == NULL ? "" : patientId->GetContent(), instance.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).GetContent(), instance.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).GetContent(), instance.GetValue(DICOM_TAG_SOP_INSTANCE_UID).GetContent()); } const std::string& DicomInstanceHasher::HashPatient() { if (patientHash_.size() == 0) { Toolbox::ComputeSHA1(patientHash_, patientId_); } return patientHash_; } const std::string& DicomInstanceHasher::HashStudy() { if (studyHash_.size() == 0) { Toolbox::ComputeSHA1(studyHash_, patientId_ + "|" + studyUid_); } return studyHash_; } const std::string& DicomInstanceHasher::HashSeries() { if (seriesHash_.size() == 0) { Toolbox::ComputeSHA1(seriesHash_, patientId_ + "|" + studyUid_ + "|" + seriesUid_); } return seriesHash_; } const std::string& DicomInstanceHasher::HashInstance() { if (instanceHash_.size() == 0) { Toolbox::ComputeSHA1(instanceHash_, patientId_ + "|" + studyUid_ + "|" + seriesUid_ + "|" + instanceUid_); } return instanceHash_; } } Orthanc-1.0.0/Core/DicomFormat/DicomInstanceHasher.h0000644000000000000000000000646612634042176020375 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomMap.h" namespace Orthanc { /** * This class implements the hashing mechanism that is used to * convert DICOM unique identifiers to Orthanc identifiers. Any * Orthanc identifier for a DICOM resource corresponds to the SHA-1 * hash of the DICOM identifiers. * \note SHA-1 hash is used because it is less sensitive to * collision attacks than MD5. [Reference] **/ class DicomInstanceHasher { private: std::string patientId_; std::string studyUid_; std::string seriesUid_; std::string instanceUid_; std::string patientHash_; std::string studyHash_; std::string seriesHash_; std::string instanceHash_; void Setup(const std::string& patientId, const std::string& studyUid, const std::string& seriesUid, const std::string& instanceUid); public: DicomInstanceHasher(const DicomMap& instance); DicomInstanceHasher(const std::string& patientId, const std::string& studyUid, const std::string& seriesUid, const std::string& instanceUid) { Setup(patientId, studyUid, seriesUid, instanceUid); } const std::string& GetPatientId() const { return patientId_; } const std::string& GetStudyUid() const { return studyUid_; } const std::string& GetSeriesUid() const { return seriesUid_; } const std::string& GetInstanceUid() const { return instanceUid_; } const std::string& HashPatient(); const std::string& HashStudy(); const std::string& HashSeries(); const std::string& HashInstance(); }; } Orthanc-1.0.0/Core/DicomFormat/DicomIntegerPixelAccessor.cpp0000644000000000000000000001457712634042176022115 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #ifndef NOMINMAX #define NOMINMAX #endif #include "DicomIntegerPixelAccessor.h" #include "../OrthancException.h" #include #include #include #include namespace Orthanc { DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values, const void* pixelData, size_t size) : information_(values), pixelData_(pixelData), size_(size) { frame_ = 0; frameOffset_ = (information_.GetHeight() * information_.GetWidth() * information_.GetBytesPerValue() * information_.GetChannelCount()); if (information_.GetNumberOfFrames() * frameOffset_ > size) { throw OrthancException(ErrorCode_BadFileFormat); } if (information_.IsSigned()) { // Pixels are signed mask_ = (1 << (information_.GetBitsStored() - 1)) - 1; signMask_ = (1 << (information_.GetBitsStored() - 1)); } else { // Pixels are unsigned mask_ = (1 << information_.GetBitsStored()) - 1; signMask_ = 0; } if (information_.IsPlanar()) { /** * Each color plane shall be sent contiguously. For RGB images, * this means the order of the pixel values sent is R1, R2, R3, * ..., G1, G2, G3, ..., B1, B2, B3, etc. **/ rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue(); } else { /** * The sample values for the first pixel are followed by the * sample values for the second pixel, etc. For RGB images, this * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue() * information_.GetChannelCount(); } } void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min, int32_t& max) const { if (information_.GetHeight() == 0 || information_.GetWidth() == 0) { min = max = 0; return; } min = std::numeric_limits::max(); max = std::numeric_limits::min(); for (unsigned int y = 0; y < information_.GetHeight(); y++) { for (unsigned int x = 0; x < information_.GetWidth(); x++) { for (unsigned int c = 0; c < information_.GetChannelCount(); c++) { int32_t v = GetValue(x, y); if (v < min) min = v; if (v > max) max = v; } } } } int32_t DicomIntegerPixelAccessor::GetValue(unsigned int x, unsigned int y, unsigned int channel) const { assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetChannelCount()); const uint8_t* pixel = reinterpret_cast(pixelData_) + y * rowOffset_ + frame_ * frameOffset_; // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/ if (information_.IsPlanar()) { /** * Each color plane shall be sent contiguously. For RGB images, * this means the order of the pixel values sent is R1, R2, R3, * ..., G1, G2, G3, ..., B1, B2, B3, etc. **/ assert(frameOffset_ % information_.GetChannelCount() == 0); pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerValue(); } else { /** * The sample values for the first pixel are followed by the * sample values for the second pixel, etc. For RGB images, this * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ pixel += channel * information_.GetBytesPerValue() + x * information_.GetChannelCount() * information_.GetBytesPerValue(); } uint32_t v; v = pixel[0]; if (information_.GetBytesPerValue() >= 2) v = v + (static_cast(pixel[1]) << 8); if (information_.GetBytesPerValue() >= 3) v = v + (static_cast(pixel[2]) << 16); if (information_.GetBytesPerValue() >= 4) v = v + (static_cast(pixel[3]) << 24); v = v >> information_.GetShift(); if (v & signMask_) { // Signed value // http://en.wikipedia.org/wiki/Two%27s_complement#Subtraction_from_2N return -static_cast(mask_) + static_cast(v & mask_) - 1; } else { // Unsigned value return static_cast(v & mask_); } } void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame) { if (frame >= information_.GetNumberOfFrames()) { throw OrthancException(ErrorCode_ParameterOutOfRange); } frame_ = frame; } } Orthanc-1.0.0/Core/DicomFormat/DicomIntegerPixelAccessor.h0000644000000000000000000000513012634042176021543 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomMap.h" #include "DicomImageInformation.h" #include namespace Orthanc { class DicomIntegerPixelAccessor { private: DicomImageInformation information_; uint32_t signMask_; uint32_t mask_; const void* pixelData_; size_t size_; unsigned int frame_; size_t frameOffset_; size_t rowOffset_; public: DicomIntegerPixelAccessor(const DicomMap& values, const void* pixelData, size_t size); const DicomImageInformation GetInformation() const { return information_; } unsigned int GetCurrentFrame() const { return frame_; } void SetCurrentFrame(unsigned int frame); void GetExtremeValues(int32_t& min, int32_t& max) const; int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const; const void* GetPixelData() const { return pixelData_; } size_t GetSize() const { return size_; } }; } Orthanc-1.0.0/Core/DicomFormat/DicomMap.cpp0000644000000000000000000003225712634042176016543 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DicomMap.h" #include #include #include "DicomArray.h" #include "../OrthancException.h" namespace Orthanc { static DicomTag patientTags[] = { //DicomTag(0x0010, 0x1010), // PatientAge //DicomTag(0x0010, 0x1040) // PatientAddress DicomTag(0x0010, 0x0010), // PatientName DicomTag(0x0010, 0x0030), // PatientBirthDate DicomTag(0x0010, 0x0040), // PatientSex DicomTag(0x0010, 0x1000), // OtherPatientIDs DICOM_TAG_PATIENT_ID }; static DicomTag studyTags[] = { //DicomTag(0x0010, 0x1020), // PatientSize //DicomTag(0x0010, 0x1030) // PatientWeight DICOM_TAG_STUDY_DATE, DicomTag(0x0008, 0x0030), // StudyTime DicomTag(0x0020, 0x0010), // StudyID DICOM_TAG_STUDY_DESCRIPTION, DICOM_TAG_ACCESSION_NUMBER, DICOM_TAG_STUDY_INSTANCE_UID, DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION, // New in db v6 DICOM_TAG_INSTITUTION_NAME, // New in db v6 DICOM_TAG_REQUESTING_PHYSICIAN, // New in db v6 DICOM_TAG_REFERRING_PHYSICIAN_NAME // New in db v6 }; static DicomTag seriesTags[] = { //DicomTag(0x0010, 0x1080), // MilitaryRank DicomTag(0x0008, 0x0021), // SeriesDate DicomTag(0x0008, 0x0031), // SeriesTime DICOM_TAG_MODALITY, DicomTag(0x0008, 0x0070), // Manufacturer DicomTag(0x0008, 0x1010), // StationName DICOM_TAG_SERIES_DESCRIPTION, DicomTag(0x0018, 0x0015), // BodyPartExamined DicomTag(0x0018, 0x0024), // SequenceName DicomTag(0x0018, 0x1030), // ProtocolName DicomTag(0x0020, 0x0011), // SeriesNumber DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES, DICOM_TAG_IMAGES_IN_ACQUISITION, DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, DICOM_TAG_NUMBER_OF_SLICES, DICOM_TAG_NUMBER_OF_TIME_SLICES, DICOM_TAG_SERIES_INSTANCE_UID, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, // New in db v6 DICOM_TAG_SERIES_TYPE, // New in db v6 DICOM_TAG_OPERATOR_NAME, // New in db v6 DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION, // New in db v6 DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION, // New in db v6 DICOM_TAG_CONTRAST_BOLUS_AGENT // New in db v6 }; static DicomTag instanceTags[] = { DicomTag(0x0008, 0x0012), // InstanceCreationDate DicomTag(0x0008, 0x0013), // InstanceCreationTime DicomTag(0x0020, 0x0012), // AcquisitionNumber DICOM_TAG_IMAGE_INDEX, DICOM_TAG_INSTANCE_NUMBER, DICOM_TAG_NUMBER_OF_FRAMES, DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, DICOM_TAG_SOP_INSTANCE_UID, DICOM_TAG_IMAGE_POSITION_PATIENT, // New in db v6 DICOM_TAG_IMAGE_COMMENTS // New in db v6 }; void DicomMap::LoadMainDicomTags(const DicomTag*& tags, size_t& size, ResourceType level) { switch (level) { case ResourceType_Patient: tags = patientTags; size = sizeof(patientTags) / sizeof(DicomTag); break; case ResourceType_Study: tags = studyTags; size = sizeof(studyTags) / sizeof(DicomTag); break; case ResourceType_Series: tags = seriesTags; size = sizeof(seriesTags) / sizeof(DicomTag); break; case ResourceType_Instance: tags = instanceTags; size = sizeof(instanceTags) / sizeof(DicomTag); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } void DicomMap::SetValue(uint16_t group, uint16_t element, DicomValue* value) { DicomTag tag(group, element); Map::iterator it = map_.find(tag); if (it != map_.end()) { delete it->second; it->second = value; } else { map_.insert(std::make_pair(tag, value)); } } void DicomMap::SetValue(DicomTag tag, DicomValue* value) { SetValue(tag.GetGroup(), tag.GetElement(), value); } void DicomMap::Clear() { for (Map::iterator it = map_.begin(); it != map_.end(); ++it) { delete it->second; } map_.clear(); } void DicomMap::ExtractTags(DicomMap& result, const DicomTag* tags, size_t count) const { result.Clear(); for (unsigned int i = 0; i < count; i++) { Map::const_iterator it = map_.find(tags[i]); if (it != map_.end()) { result.SetValue(it->first, it->second->Clone()); } } } void DicomMap::ExtractPatientInformation(DicomMap& result) const { ExtractTags(result, patientTags, sizeof(patientTags) / sizeof(DicomTag)); } void DicomMap::ExtractStudyInformation(DicomMap& result) const { ExtractTags(result, studyTags, sizeof(studyTags) / sizeof(DicomTag)); } void DicomMap::ExtractSeriesInformation(DicomMap& result) const { ExtractTags(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag)); } void DicomMap::ExtractInstanceInformation(DicomMap& result) const { ExtractTags(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag)); } DicomMap* DicomMap::Clone() const { std::auto_ptr result(new DicomMap); for (Map::const_iterator it = map_.begin(); it != map_.end(); ++it) { result->map_.insert(std::make_pair(it->first, it->second->Clone())); } return result.release(); } void DicomMap::Assign(const DicomMap& other) { Clear(); for (Map::const_iterator it = other.map_.begin(); it != other.map_.end(); ++it) { map_.insert(std::make_pair(it->first, it->second->Clone())); } } const DicomValue& DicomMap::GetValue(const DicomTag& tag) const { const DicomValue* value = TestAndGetValue(tag); if (value) { return *value; } else { throw OrthancException(ErrorCode_InexistentTag); } } const DicomValue* DicomMap::TestAndGetValue(const DicomTag& tag) const { Map::const_iterator it = map_.find(tag); if (it == map_.end()) { return NULL; } else { return it->second; } } void DicomMap::Remove(const DicomTag& tag) { Map::iterator it = map_.find(tag); if (it != map_.end()) { delete it->second; map_.erase(it); } } static void SetupFindTemplate(DicomMap& result, const DicomTag* tags, size_t count) { result.Clear(); for (size_t i = 0; i < count; i++) { result.SetValue(tags[i], ""); } } void DicomMap::SetupFindPatientTemplate(DicomMap& result) { SetupFindTemplate(result, patientTags, sizeof(patientTags) / sizeof(DicomTag)); } void DicomMap::SetupFindStudyTemplate(DicomMap& result) { SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag)); result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); result.SetValue(DICOM_TAG_PATIENT_ID, ""); // These main DICOM tags are only indirectly related to the // General Study Module, remove them result.Remove(DICOM_TAG_INSTITUTION_NAME); result.Remove(DICOM_TAG_REQUESTING_PHYSICIAN); result.Remove(DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION); } void DicomMap::SetupFindSeriesTemplate(DicomMap& result) { SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag)); result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); result.SetValue(DICOM_TAG_PATIENT_ID, ""); result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); // These tags are considered as "main" by Orthanc, but are not in the Series module result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer result.Remove(DicomTag(0x0008, 0x1010)); // Station name result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES); result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION); result.Remove(DICOM_TAG_NUMBER_OF_SLICES); result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS); result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES); result.Remove(DICOM_TAG_IMAGE_ORIENTATION_PATIENT); result.Remove(DICOM_TAG_SERIES_TYPE); result.Remove(DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION); result.Remove(DICOM_TAG_CONTRAST_BOLUS_AGENT); } void DicomMap::SetupFindInstanceTemplate(DicomMap& result) { SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag)); result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); result.SetValue(DICOM_TAG_PATIENT_ID, ""); result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, ""); } void DicomMap::CopyTagIfExists(const DicomMap& source, const DicomTag& tag) { if (source.HasTag(tag)) { SetValue(tag, source.GetValue(tag)); } } bool DicomMap::IsMainDicomTag(const DicomTag& tag, ResourceType level) { DicomTag *tags = NULL; size_t size; switch (level) { case ResourceType_Patient: tags = patientTags; size = sizeof(patientTags) / sizeof(DicomTag); break; case ResourceType_Study: tags = studyTags; size = sizeof(studyTags) / sizeof(DicomTag); break; case ResourceType_Series: tags = seriesTags; size = sizeof(seriesTags) / sizeof(DicomTag); break; case ResourceType_Instance: tags = instanceTags; size = sizeof(instanceTags) / sizeof(DicomTag); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } for (size_t i = 0; i < size; i++) { if (tags[i] == tag) { return true; } } return false; } bool DicomMap::IsMainDicomTag(const DicomTag& tag) { return (IsMainDicomTag(tag, ResourceType_Patient) || IsMainDicomTag(tag, ResourceType_Study) || IsMainDicomTag(tag, ResourceType_Series) || IsMainDicomTag(tag, ResourceType_Instance)); } void DicomMap::GetMainDicomTagsInternal(std::set& result, ResourceType level) { DicomTag *tags = NULL; size_t size; switch (level) { case ResourceType_Patient: tags = patientTags; size = sizeof(patientTags) / sizeof(DicomTag); break; case ResourceType_Study: tags = studyTags; size = sizeof(studyTags) / sizeof(DicomTag); break; case ResourceType_Series: tags = seriesTags; size = sizeof(seriesTags) / sizeof(DicomTag); break; case ResourceType_Instance: tags = instanceTags; size = sizeof(instanceTags) / sizeof(DicomTag); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } for (size_t i = 0; i < size; i++) { result.insert(tags[i]); } } void DicomMap::GetMainDicomTags(std::set& result, ResourceType level) { result.clear(); GetMainDicomTagsInternal(result, level); } void DicomMap::GetMainDicomTags(std::set& result) { result.clear(); GetMainDicomTagsInternal(result, ResourceType_Patient); GetMainDicomTagsInternal(result, ResourceType_Study); GetMainDicomTagsInternal(result, ResourceType_Series); GetMainDicomTagsInternal(result, ResourceType_Instance); } void DicomMap::Print(FILE* fp) const { DicomArray a(*this); a.Print(fp); } void DicomMap::GetTags(std::set& tags) const { tags.clear(); for (Map::const_iterator it = map_.begin(); it != map_.end(); ++it) { tags.insert(it->first); } } } Orthanc-1.0.0/Core/DicomFormat/DicomMap.h0000644000000000000000000001204512634042176016201 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "DicomTag.h" #include "DicomValue.h" #include "../Enumerations.h" #include #include #include namespace Orthanc { class DicomMap : public boost::noncopyable { private: friend class DicomArray; friend class FromDcmtkBridge; friend class ToDcmtkBridge; typedef std::map Map; Map map_; // Warning: This takes the ownership of "value" void SetValue(uint16_t group, uint16_t element, DicomValue* value); void SetValue(DicomTag tag, DicomValue* value); void ExtractTags(DicomMap& source, const DicomTag* tags, size_t count) const; static void GetMainDicomTagsInternal(std::set& result, ResourceType level); public: DicomMap() { } ~DicomMap() { Clear(); } size_t GetSize() const { return map_.size(); } DicomMap* Clone() const; void Assign(const DicomMap& other); void Clear(); void SetValue(uint16_t group, uint16_t element, const DicomValue& value) { SetValue(group, element, value.Clone()); } void SetValue(const DicomTag& tag, const DicomValue& value) { SetValue(tag, value.Clone()); } void SetValue(const DicomTag& tag, const std::string& str) { SetValue(tag, new DicomValue(str, false)); } void SetValue(uint16_t group, uint16_t element, const std::string& str) { SetValue(group, element, new DicomValue(str, false)); } bool HasTag(uint16_t group, uint16_t element) const { return HasTag(DicomTag(group, element)); } bool HasTag(const DicomTag& tag) const { return map_.find(tag) != map_.end(); } const DicomValue& GetValue(uint16_t group, uint16_t element) const { return GetValue(DicomTag(group, element)); } const DicomValue& GetValue(const DicomTag& tag) const; // DO NOT delete the returned value! const DicomValue* TestAndGetValue(uint16_t group, uint16_t element) const { return TestAndGetValue(DicomTag(group, element)); } // DO NOT delete the returned value! const DicomValue* TestAndGetValue(const DicomTag& tag) const; void Remove(const DicomTag& tag); void ExtractPatientInformation(DicomMap& result) const; void ExtractStudyInformation(DicomMap& result) const; void ExtractSeriesInformation(DicomMap& result) const; void ExtractInstanceInformation(DicomMap& result) const; static void SetupFindPatientTemplate(DicomMap& result); static void SetupFindStudyTemplate(DicomMap& result); static void SetupFindSeriesTemplate(DicomMap& result); static void SetupFindInstanceTemplate(DicomMap& result); void CopyTagIfExists(const DicomMap& source, const DicomTag& tag); static bool IsMainDicomTag(const DicomTag& tag, ResourceType level); static bool IsMainDicomTag(const DicomTag& tag); static void GetMainDicomTags(std::set& result, ResourceType level); static void GetMainDicomTags(std::set& result); void Print(FILE* fp) const; void GetTags(std::set& tags) const; static void LoadMainDicomTags(const DicomTag*& tags, size_t& size, ResourceType level); }; } Orthanc-1.0.0/Core/DicomFormat/DicomTag.cpp0000644000000000000000000002727512634042176016545 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DicomTag.h" #include "../OrthancException.h" #include #include #include namespace Orthanc { bool DicomTag::operator< (const DicomTag& other) const { if (group_ < other.group_) return true; if (group_ > other.group_) return false; return element_ < other.element_; } std::ostream& operator<< (std::ostream& o, const DicomTag& tag) { using namespace std; ios_base::fmtflags state = o.flags(); o.flags(ios::right | ios::hex); o << "(" << setfill('0') << setw(4) << tag.GetGroup() << "," << setw(4) << tag.GetElement() << ")"; o.flags(state); return o; } std::string DicomTag::Format() const { char b[16]; sprintf(b, "%04x,%04x", group_, element_); return std::string(b); } const char* DicomTag::GetMainTagsName() const { if (*this == DICOM_TAG_ACCESSION_NUMBER) return "AccessionNumber"; if (*this == DICOM_TAG_SOP_INSTANCE_UID) return "SOPInstanceUID"; if (*this == DICOM_TAG_PATIENT_ID) return "PatientID"; if (*this == DICOM_TAG_SERIES_INSTANCE_UID) return "SeriesInstanceUID"; if (*this == DICOM_TAG_STUDY_INSTANCE_UID) return "StudyInstanceUID"; if (*this == DICOM_TAG_PIXEL_DATA) return "PixelData"; if (*this == DICOM_TAG_IMAGE_INDEX) return "ImageIndex"; if (*this == DICOM_TAG_INSTANCE_NUMBER) return "InstanceNumber"; if (*this == DICOM_TAG_NUMBER_OF_SLICES) return "NumberOfSlices"; if (*this == DICOM_TAG_NUMBER_OF_FRAMES) return "NumberOfFrames"; if (*this == DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES) return "CardiacNumberOfImages"; if (*this == DICOM_TAG_IMAGES_IN_ACQUISITION) return "ImagesInAcquisition"; if (*this == DICOM_TAG_PATIENT_NAME) return "PatientName"; if (*this == DICOM_TAG_IMAGE_POSITION_PATIENT) return "ImagePositionPatient"; if (*this == DICOM_TAG_IMAGE_ORIENTATION_PATIENT) return "ImageOrientationPatient"; return ""; } void DicomTag::AddTagsForModule(std::set& target, DicomModule module) { // REFERENCE: 11_03pu.pdf, DICOM PS 3.3 2011 - Information Object Definitions switch (module) { case DicomModule_Patient: // This is Table C.7-1 "Patient Module Attributes" (p. 373) target.insert(DicomTag(0x0010, 0x0010)); // Patient's name target.insert(DicomTag(0x0010, 0x0020)); // Patient ID target.insert(DicomTag(0x0010, 0x0030)); // Patient's birth date target.insert(DicomTag(0x0010, 0x0040)); // Patient's sex target.insert(DicomTag(0x0008, 0x1120)); // Referenced patient sequence target.insert(DicomTag(0x0010, 0x0032)); // Patient's birth time target.insert(DicomTag(0x0010, 0x1000)); // Other patient IDs target.insert(DicomTag(0x0010, 0x1002)); // Other patient IDs sequence target.insert(DicomTag(0x0010, 0x1001)); // Other patient names target.insert(DicomTag(0x0010, 0x2160)); // Ethnic group target.insert(DicomTag(0x0010, 0x4000)); // Patient comments target.insert(DicomTag(0x0010, 0x2201)); // Patient species description target.insert(DicomTag(0x0010, 0x2202)); // Patient species code sequence target.insert(DicomTag(0x0010, 0x2292)); // Patient breed description target.insert(DicomTag(0x0010, 0x2293)); // Patient breed code sequence target.insert(DicomTag(0x0010, 0x2294)); // Breed registration sequence target.insert(DicomTag(0x0010, 0x2297)); // Responsible person target.insert(DicomTag(0x0010, 0x2298)); // Responsible person role target.insert(DicomTag(0x0010, 0x2299)); // Responsible organization target.insert(DicomTag(0x0012, 0x0062)); // Patient identity removed target.insert(DicomTag(0x0012, 0x0063)); // De-identification method target.insert(DicomTag(0x0012, 0x0064)); // De-identification method code sequence // Table 10-18 ISSUER OF PATIENT ID MACRO (p. 112) target.insert(DicomTag(0x0010, 0x0021)); // Issuer of Patient ID target.insert(DicomTag(0x0010, 0x0024)); // Issuer of Patient ID qualifiers sequence break; case DicomModule_Study: // This is Table C.7-3 "General Study Module Attributes" (p. 378) target.insert(DicomTag(0x0020, 0x000d)); // Study instance UID target.insert(DicomTag(0x0008, 0x0020)); // Study date target.insert(DicomTag(0x0008, 0x0030)); // Study time target.insert(DicomTag(0x0008, 0x0090)); // Referring physician's name target.insert(DicomTag(0x0008, 0x0096)); // Referring physician identification sequence target.insert(DicomTag(0x0020, 0x0010)); // Study ID target.insert(DicomTag(0x0008, 0x0050)); // Accession number target.insert(DicomTag(0x0008, 0x0051)); // Issuer of accession number sequence target.insert(DicomTag(0x0008, 0x1030)); // Study description target.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of record target.insert(DicomTag(0x0008, 0x1049)); // Physician(s) of record identification sequence target.insert(DicomTag(0x0008, 0x1060)); // Name of physician(s) reading study target.insert(DicomTag(0x0008, 0x1062)); // Physician(s) reading study identification sequence target.insert(DicomTag(0x0032, 0x1034)); // Requesting service code sequence target.insert(DicomTag(0x0008, 0x1110)); // Referenced study sequence target.insert(DicomTag(0x0008, 0x1032)); // Procedure code sequence target.insert(DicomTag(0x0040, 0x1012)); // Reason for performed procedure code sequence break; case DicomModule_Series: // This is Table C.7-5 "General Series Module Attributes" (p. 385) target.insert(DicomTag(0x0008, 0x0060)); // Modality target.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID target.insert(DicomTag(0x0020, 0x0011)); // Series Number target.insert(DicomTag(0x0020, 0x0060)); // Laterality target.insert(DicomTag(0x0008, 0x0021)); // Series Date target.insert(DicomTag(0x0008, 0x0031)); // Series Time target.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians’ Name target.insert(DicomTag(0x0008, 0x1052)); // Performing Physician Identification Sequence target.insert(DicomTag(0x0018, 0x1030)); // Protocol Name target.insert(DicomTag(0x0008, 0x103e)); // Series Description target.insert(DicomTag(0x0008, 0x103f)); // Series Description Code Sequence target.insert(DicomTag(0x0008, 0x1070)); // Operators' Name target.insert(DicomTag(0x0008, 0x1072)); // Operator Identification Sequence target.insert(DicomTag(0x0008, 0x1111)); // Referenced Performed Procedure Step Sequence target.insert(DicomTag(0x0008, 0x1250)); // Related Series Sequence target.insert(DicomTag(0x0018, 0x0015)); // Body Part Examined target.insert(DicomTag(0x0018, 0x5100)); // Patient Position target.insert(DicomTag(0x0028, 0x0108)); // Smallest Pixel Value in Series target.insert(DicomTag(0x0029, 0x0109)); // Largest Pixel Value in Series target.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence target.insert(DicomTag(0x0010, 0x2210)); // Anatomical Orientation Type // Table 10-16 PERFORMED PROCEDURE STEP SUMMARY MACRO ATTRIBUTES target.insert(DicomTag(0x0040, 0x0253)); // Performed Procedure Step ID target.insert(DicomTag(0x0040, 0x0244)); // Performed Procedure Step Start Date target.insert(DicomTag(0x0040, 0x0245)); // Performed Procedure Step Start Time target.insert(DicomTag(0x0040, 0x0254)); // Performed Procedure Step Description target.insert(DicomTag(0x0040, 0x0260)); // Performed Protocol Code Sequence target.insert(DicomTag(0x0040, 0x0280)); // Comments on the Performed Procedure Step break; case DicomModule_Instance: // This is Table C.12-1 "SOP Common Module Attributes" (p. 1207) target.insert(DicomTag(0x0008, 0x0016)); // SOP Class UID target.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID target.insert(DicomTag(0x0008, 0x0005)); // Specific Character Set target.insert(DicomTag(0x0008, 0x0012)); // Instance Creation Date target.insert(DicomTag(0x0008, 0x0013)); // Instance Creation Time target.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID target.insert(DicomTag(0x0008, 0x001a)); // Related General SOP Class UID target.insert(DicomTag(0x0008, 0x001b)); // Original Specialized SOP Class UID target.insert(DicomTag(0x0008, 0x0110)); // Coding Scheme Identification Sequence target.insert(DicomTag(0x0008, 0x0201)); // Timezone Offset From UTC target.insert(DicomTag(0x0018, 0xa001)); // Contributing Equipment Sequence target.insert(DicomTag(0x0020, 0x0013)); // Instance Number target.insert(DicomTag(0x0100, 0x0410)); // SOP Instance Status target.insert(DicomTag(0x0100, 0x0420)); // SOP Authorization DateTime target.insert(DicomTag(0x0100, 0x0424)); // SOP Authorization Comment target.insert(DicomTag(0x0100, 0x0426)); // Authorization Equipment Certification Number target.insert(DicomTag(0x0400, 0x0500)); // Encrypted Attributes Sequence target.insert(DicomTag(0x0400, 0x0561)); // Original Attributes Sequence target.insert(DicomTag(0x0040, 0xa390)); // HL7 Structured Document Reference Sequence target.insert(DicomTag(0x0028, 0x0303)); // Longitudinal Temporal Information Modified // Table C.12-6 "DIGITAL SIGNATURES MACRO ATTRIBUTES" (p. 1216) target.insert(DicomTag(0x4ffe, 0x0001)); // MAC Parameters sequence target.insert(DicomTag(0xfffa, 0xfffa)); // Digital signatures sequence break; // TODO IMAGE MODULE? default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } } Orthanc-1.0.0/Core/DicomFormat/DicomTag.h0000644000000000000000000001601612634042176016201 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #include "../Enumerations.h" namespace Orthanc { class DicomTag { // This must stay a POD (plain old data structure) private: uint16_t group_; uint16_t element_; public: DicomTag(uint16_t group, uint16_t element) : group_(group), element_(element) { } uint16_t GetGroup() const { return group_; } uint16_t GetElement() const { return element_; } bool IsPrivate() const { return group_ % 2 == 1; } const char* GetMainTagsName() const; bool operator< (const DicomTag& other) const; bool operator== (const DicomTag& other) const { return group_ == other.group_ && element_ == other.element_; } bool operator!= (const DicomTag& other) const { return !(*this == other); } std::string Format() const; friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag); static void AddTagsForModule(std::set& target, DicomModule module); }; // Aliases for the most useful tags static const DicomTag DICOM_TAG_ACCESSION_NUMBER(0x0008, 0x0050); static const DicomTag DICOM_TAG_SOP_INSTANCE_UID(0x0008, 0x0018); static const DicomTag DICOM_TAG_PATIENT_ID(0x0010, 0x0020); static const DicomTag DICOM_TAG_SERIES_INSTANCE_UID(0x0020, 0x000e); static const DicomTag DICOM_TAG_STUDY_INSTANCE_UID(0x0020, 0x000d); static const DicomTag DICOM_TAG_PIXEL_DATA(0x7fe0, 0x0010); static const DicomTag DICOM_TAG_IMAGE_INDEX(0x0054, 0x1330); static const DicomTag DICOM_TAG_INSTANCE_NUMBER(0x0020, 0x0013); static const DicomTag DICOM_TAG_NUMBER_OF_SLICES(0x0054, 0x0081); static const DicomTag DICOM_TAG_NUMBER_OF_TIME_SLICES(0x0054, 0x0101); static const DicomTag DICOM_TAG_NUMBER_OF_FRAMES(0x0028, 0x0008); static const DicomTag DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES(0x0018, 0x1090); static const DicomTag DICOM_TAG_IMAGES_IN_ACQUISITION(0x0020, 0x1002); static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010); static const DicomTag DICOM_TAG_ENCAPSULATED_DOCUMENT(0x0042, 0x0011); static const DicomTag DICOM_TAG_STUDY_DESCRIPTION(0x0008, 0x1030); static const DicomTag DICOM_TAG_SERIES_DESCRIPTION(0x0008, 0x103e); static const DicomTag DICOM_TAG_MODALITY(0x0008, 0x0060); // The following is used for "modify/anonymize" operations static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016); static const DicomTag DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID(0x0002, 0x0002); static const DicomTag DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID(0x0002, 0x0003); static const DicomTag DICOM_TAG_DEIDENTIFICATION_METHOD(0x0012, 0x0063); // DICOM tags used for fMRI (thanks to Will Ryder) static const DicomTag DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS(0x0020, 0x0105); static const DicomTag DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER(0x0020, 0x0100); // Tags for C-FIND and C-MOVE static const DicomTag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005); static const DicomTag DICOM_TAG_QUERY_RETRIEVE_LEVEL(0x0008, 0x0052); static const DicomTag DICOM_TAG_MODALITIES_IN_STUDY(0x0008, 0x0061); // Tags for images static const DicomTag DICOM_TAG_COLUMNS(0x0028, 0x0011); static const DicomTag DICOM_TAG_ROWS(0x0028, 0x0010); static const DicomTag DICOM_TAG_SAMPLES_PER_PIXEL(0x0028, 0x0002); static const DicomTag DICOM_TAG_BITS_ALLOCATED(0x0028, 0x0100); static const DicomTag DICOM_TAG_BITS_STORED(0x0028, 0x0101); static const DicomTag DICOM_TAG_HIGH_BIT(0x0028, 0x0102); static const DicomTag DICOM_TAG_PIXEL_REPRESENTATION(0x0028, 0x0103); static const DicomTag DICOM_TAG_PLANAR_CONFIGURATION(0x0028, 0x0006); static const DicomTag DICOM_TAG_PHOTOMETRIC_INTERPRETATION(0x0028, 0x0004); static const DicomTag DICOM_TAG_IMAGE_ORIENTATION_PATIENT(0x0020, 0x0037); static const DicomTag DICOM_TAG_IMAGE_POSITION_PATIENT(0x0020, 0x0032); // Tags related to date and time static const DicomTag DICOM_TAG_ACQUISITION_DATE(0x0008, 0x0022); static const DicomTag DICOM_TAG_ACQUISITION_TIME(0x0008, 0x0032); static const DicomTag DICOM_TAG_CONTENT_DATE(0x0008, 0x0023); static const DicomTag DICOM_TAG_CONTENT_TIME(0x0008, 0x0033); static const DicomTag DICOM_TAG_INSTANCE_CREATION_DATE(0x0008, 0x0012); static const DicomTag DICOM_TAG_INSTANCE_CREATION_TIME(0x0008, 0x0013); static const DicomTag DICOM_TAG_PATIENT_BIRTH_DATE(0x0010, 0x0030); static const DicomTag DICOM_TAG_PATIENT_BIRTH_TIME(0x0010, 0x0032); static const DicomTag DICOM_TAG_SERIES_DATE(0x0008, 0x0021); static const DicomTag DICOM_TAG_SERIES_TIME(0x0008, 0x0031); static const DicomTag DICOM_TAG_STUDY_DATE(0x0008, 0x0020); static const DicomTag DICOM_TAG_STUDY_TIME(0x0008, 0x0030); // Various tags static const DicomTag DICOM_TAG_SERIES_TYPE(0x0054, 0x1000); static const DicomTag DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION(0x0032, 0x1060); static const DicomTag DICOM_TAG_INSTITUTION_NAME(0x0008, 0x0080); static const DicomTag DICOM_TAG_REQUESTING_PHYSICIAN(0x0032, 0x1032); static const DicomTag DICOM_TAG_REFERRING_PHYSICIAN_NAME(0x0008, 0x0090); static const DicomTag DICOM_TAG_OPERATOR_NAME(0x0008, 0x1070); static const DicomTag DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION(0x0040, 0x0254); static const DicomTag DICOM_TAG_IMAGE_COMMENTS(0x0020, 0x4000); static const DicomTag DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION(0x0018, 0x1400); static const DicomTag DICOM_TAG_CONTRAST_BOLUS_AGENT(0x0018, 0x0010); } Orthanc-1.0.0/Core/DicomFormat/DicomValue.cpp0000644000000000000000000000543112634042176017074 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "DicomValue.h" #include "../OrthancException.h" #include "../Toolbox.h" namespace Orthanc { DicomValue::DicomValue(const DicomValue& other) : type_(other.type_), content_(other.content_) { } DicomValue::DicomValue(const std::string& content, bool isBinary) : type_(isBinary ? Type_Binary : Type_String), content_(content) { } DicomValue::DicomValue(const char* data, size_t size, bool isBinary) : type_(isBinary ? Type_Binary : Type_String) { content_.assign(data, size); } const std::string& DicomValue::GetContent() const { if (type_ == Type_Null) { throw OrthancException(ErrorCode_BadParameterType); } else { return content_; } } DicomValue* DicomValue::Clone() const { return new DicomValue(*this); } #if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void DicomValue::FormatDataUriScheme(std::string& target, const std::string& mime) const { Toolbox::EncodeBase64(target, GetContent()); target.insert(0, "data:" + mime + ";base64,"); } #endif } Orthanc-1.0.0/Core/DicomFormat/DicomValue.h0000644000000000000000000000520512634042176016540 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include namespace Orthanc { class DicomValue : public boost::noncopyable { private: enum Type { Type_Null, Type_String, Type_Binary }; Type type_; std::string content_; DicomValue(const DicomValue& other); public: DicomValue() : type_(Type_Null) { } DicomValue(const std::string& content, bool isBinary); DicomValue(const char* data, size_t size, bool isBinary); const std::string& GetContent() const; bool IsNull() const { return type_ == Type_Null; } bool IsBinary() const { return type_ == Type_Binary; } DicomValue* Clone() const; #if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void FormatDataUriScheme(std::string& target, const std::string& mime) const; void FormatDataUriScheme(std::string& target) const { FormatDataUriScheme(target, "application/octet-stream"); } #endif }; } Orthanc-1.0.0/Core/EnumerationDictionary.h0000644000000000000000000000765012634042176016626 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "OrthancException.h" #include "Toolbox.h" #include #include #include namespace Orthanc { namespace Toolbox { template class EnumerationDictionary { private: typedef std::map EnumerationToString; typedef std::map StringToEnumeration; EnumerationToString enumerationToString_; StringToEnumeration stringToEnumeration_; public: void Clear() { enumerationToString_.clear(); stringToEnumeration_.clear(); } bool Contains(Enumeration value) const { return enumerationToString_.find(value) != enumerationToString_.end(); } void Add(Enumeration value, const std::string& str) { // Check if these values are free if (enumerationToString_.find(value) != enumerationToString_.end() || stringToEnumeration_.find(str) != stringToEnumeration_.end() || Toolbox::IsInteger(str) /* Prevent the registration of a number */) { throw OrthancException(ErrorCode_BadRequest); } // OK, the string is free and is not a number enumerationToString_[value] = str; stringToEnumeration_[str] = value; stringToEnumeration_[boost::lexical_cast(static_cast(value))] = value; } Enumeration Translate(const std::string& str) const { try { int value = boost::lexical_cast(str); return static_cast(value); } catch (boost::bad_lexical_cast) { } typename StringToEnumeration::const_iterator found = stringToEnumeration_.find(str); if (found == stringToEnumeration_.end()) { throw OrthancException(ErrorCode_InexistentItem); } else { return found->second; } } std::string Translate(Enumeration e) const { typename EnumerationToString::const_iterator found = enumerationToString_.find(e); if (found == enumerationToString_.end()) { // No name for this item return boost::lexical_cast(static_cast(e)); } else { return found->second; } } }; } } Orthanc-1.0.0/Core/Enumerations.cpp0000644000000000000000000006731612634042176015323 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "Enumerations.h" #include "OrthancException.h" #include "Toolbox.h" #include namespace Orthanc { // This function is autogenerated by the script // "Resources/GenerateErrorCodes.py" const char* EnumerationToString(ErrorCode error) { switch (error) { case ErrorCode_InternalError: return "Internal error"; case ErrorCode_Success: return "Success"; case ErrorCode_Plugin: return "Error encountered within the plugin engine"; case ErrorCode_NotImplemented: return "Not implemented yet"; case ErrorCode_ParameterOutOfRange: return "Parameter out of range"; case ErrorCode_NotEnoughMemory: return "Not enough memory"; case ErrorCode_BadParameterType: return "Bad type for a parameter"; case ErrorCode_BadSequenceOfCalls: return "Bad sequence of calls"; case ErrorCode_InexistentItem: return "Accessing an inexistent item"; case ErrorCode_BadRequest: return "Bad request"; case ErrorCode_NetworkProtocol: return "Error in the network protocol"; case ErrorCode_SystemCommand: return "Error while calling a system command"; case ErrorCode_Database: return "Error with the database engine"; case ErrorCode_UriSyntax: return "Badly formatted URI"; case ErrorCode_InexistentFile: return "Inexistent file"; case ErrorCode_CannotWriteFile: return "Cannot write to file"; case ErrorCode_BadFileFormat: return "Bad file format"; case ErrorCode_Timeout: return "Timeout"; case ErrorCode_UnknownResource: return "Unknown resource"; case ErrorCode_IncompatibleDatabaseVersion: return "Incompatible version of the database"; case ErrorCode_FullStorage: return "The file storage is full"; case ErrorCode_CorruptedFile: return "Corrupted file (e.g. inconsistent MD5 hash)"; case ErrorCode_InexistentTag: return "Inexistent tag"; case ErrorCode_ReadOnly: return "Cannot modify a read-only data structure"; case ErrorCode_IncompatibleImageFormat: return "Incompatible format of the images"; case ErrorCode_IncompatibleImageSize: return "Incompatible size of the images"; case ErrorCode_SharedLibrary: return "Error while using a shared library (plugin)"; case ErrorCode_UnknownPluginService: return "Plugin invoking an unknown service"; case ErrorCode_UnknownDicomTag: return "Unknown DICOM tag"; case ErrorCode_BadJson: return "Cannot parse a JSON document"; case ErrorCode_Unauthorized: return "Bad credentials were provided to an HTTP request"; case ErrorCode_BadFont: return "Badly formatted font file"; case ErrorCode_DatabasePlugin: return "The plugin implementing a custom database back-end does not fulfill the proper interface"; case ErrorCode_StorageAreaPlugin: return "Error in the plugin implementing a custom storage area"; case ErrorCode_EmptyRequest: return "The request is empty"; case ErrorCode_NotAcceptable: return "Cannot send a response which is acceptable according to the Accept HTTP header"; case ErrorCode_SQLiteNotOpened: return "SQLite: The database is not opened"; case ErrorCode_SQLiteAlreadyOpened: return "SQLite: Connection is already open"; case ErrorCode_SQLiteCannotOpen: return "SQLite: Unable to open the database"; case ErrorCode_SQLiteStatementAlreadyUsed: return "SQLite: This cached statement is already being referred to"; case ErrorCode_SQLiteExecute: return "SQLite: Cannot execute a command"; case ErrorCode_SQLiteRollbackWithoutTransaction: return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)"; case ErrorCode_SQLiteCommitWithoutTransaction: return "SQLite: Committing a nonexistent transaction"; case ErrorCode_SQLiteRegisterFunction: return "SQLite: Unable to register a function"; case ErrorCode_SQLiteFlush: return "SQLite: Unable to flush the database"; case ErrorCode_SQLiteCannotRun: return "SQLite: Cannot run a cached statement"; case ErrorCode_SQLiteCannotStep: return "SQLite: Cannot step over a cached statement"; case ErrorCode_SQLiteBindOutOfRange: return "SQLite: Bing a value while out of range (serious error)"; case ErrorCode_SQLitePrepareStatement: return "SQLite: Cannot prepare a cached statement"; case ErrorCode_SQLiteTransactionAlreadyStarted: return "SQLite: Beginning the same transaction twice"; case ErrorCode_SQLiteTransactionCommit: return "SQLite: Failure when committing the transaction"; case ErrorCode_SQLiteTransactionBegin: return "SQLite: Cannot start a transaction"; case ErrorCode_DirectoryOverFile: return "The directory to be created is already occupied by a regular file"; case ErrorCode_FileStorageCannotWrite: return "Unable to create a subdirectory or a file in the file storage"; case ErrorCode_DirectoryExpected: return "The specified path does not point to a directory"; case ErrorCode_HttpPortInUse: return "The TCP port of the HTTP server is already in use"; case ErrorCode_DicomPortInUse: return "The TCP port of the DICOM server is already in use"; case ErrorCode_BadHttpStatusInRest: return "This HTTP status is not allowed in a REST API"; case ErrorCode_RegularFileExpected: return "The specified path does not point to a regular file"; case ErrorCode_PathToExecutable: return "Unable to get the path to the executable"; case ErrorCode_MakeDirectory: return "Cannot create a directory"; case ErrorCode_BadApplicationEntityTitle: return "An application entity title (AET) cannot be empty or be longer than 16 characters"; case ErrorCode_NoCFindHandler: return "No request handler factory for DICOM C-FIND SCP"; case ErrorCode_NoCMoveHandler: return "No request handler factory for DICOM C-MOVE SCP"; case ErrorCode_NoCStoreHandler: return "No request handler factory for DICOM C-STORE SCP"; case ErrorCode_NoApplicationEntityFilter: return "No application entity filter"; case ErrorCode_NoSopClassOrInstance: return "DicomUserConnection: Unable to find the SOP class and instance"; case ErrorCode_NoPresentationContext: return "DicomUserConnection: No acceptable presentation context for modality"; case ErrorCode_DicomFindUnavailable: return "DicomUserConnection: The C-FIND command is not supported by the remote SCP"; case ErrorCode_DicomMoveUnavailable: return "DicomUserConnection: The C-MOVE command is not supported by the remote SCP"; case ErrorCode_CannotStoreInstance: return "Cannot store an instance"; case ErrorCode_CreateDicomNotString: return "Only string values are supported when creating DICOM instances"; case ErrorCode_CreateDicomOverrideTag: return "Trying to override a value inherited from a parent module"; case ErrorCode_CreateDicomUseContent: return "Use \"Content\" to inject an image into a new DICOM instance"; case ErrorCode_CreateDicomNoPayload: return "No payload is present for one instance in the series"; case ErrorCode_CreateDicomUseDataUriScheme: return "The payload of the DICOM instance must be specified according to Data URI scheme"; case ErrorCode_CreateDicomBadParent: return "Trying to attach a new DICOM instance to an inexistent resource"; case ErrorCode_CreateDicomParentIsInstance: return "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)"; case ErrorCode_CreateDicomParentEncoding: return "Unable to get the encoding of the parent resource"; case ErrorCode_UnknownModality: return "Unknown modality"; case ErrorCode_BadJobOrdering: return "Bad ordering of filters in a job"; case ErrorCode_JsonToLuaTable: return "Cannot convert the given JSON object to a Lua table"; case ErrorCode_CannotCreateLua: return "Cannot create the Lua context"; case ErrorCode_CannotExecuteLua: return "Cannot execute a Lua command"; case ErrorCode_LuaAlreadyExecuted: return "Arguments cannot be pushed after the Lua function is executed"; case ErrorCode_LuaBadOutput: return "The Lua function does not give the expected number of outputs"; case ErrorCode_NotLuaPredicate: return "The Lua function is not a predicate (only true/false outputs allowed)"; case ErrorCode_LuaReturnsNoString: return "The Lua function does not return a string"; case ErrorCode_StorageAreaAlreadyRegistered: return "Another plugin has already registered a custom storage area"; case ErrorCode_DatabaseBackendAlreadyRegistered: return "Another plugin has already registered a custom database back-end"; case ErrorCode_DatabaseNotInitialized: return "Plugin trying to call the database during its initialization"; case ErrorCode_SslDisabled: return "Orthanc has been built without SSL support"; case ErrorCode_CannotOrderSlices: return "Unable to order the slices of the series"; case ErrorCode_NoWorklistHandler: return "No request handler factory for DICOM C-Find Modality SCP"; default: if (error >= ErrorCode_START_PLUGINS) { return "Error encountered within some plugin"; } else { return "Unknown error code"; } } } const char* EnumerationToString(HttpMethod method) { switch (method) { case HttpMethod_Get: return "GET"; case HttpMethod_Post: return "POST"; case HttpMethod_Delete: return "DELETE"; case HttpMethod_Put: return "PUT"; default: return "?"; } } const char* EnumerationToString(HttpStatus status) { switch (status) { case HttpStatus_100_Continue: return "Continue"; case HttpStatus_101_SwitchingProtocols: return "Switching Protocols"; case HttpStatus_102_Processing: return "Processing"; case HttpStatus_200_Ok: return "OK"; case HttpStatus_201_Created: return "Created"; case HttpStatus_202_Accepted: return "Accepted"; case HttpStatus_203_NonAuthoritativeInformation: return "Non-Authoritative Information"; case HttpStatus_204_NoContent: return "No Content"; case HttpStatus_205_ResetContent: return "Reset Content"; case HttpStatus_206_PartialContent: return "Partial Content"; case HttpStatus_207_MultiStatus: return "Multi-Status"; case HttpStatus_208_AlreadyReported: return "Already Reported"; case HttpStatus_226_IMUsed: return "IM Used"; case HttpStatus_300_MultipleChoices: return "Multiple Choices"; case HttpStatus_301_MovedPermanently: return "Moved Permanently"; case HttpStatus_302_Found: return "Found"; case HttpStatus_303_SeeOther: return "See Other"; case HttpStatus_304_NotModified: return "Not Modified"; case HttpStatus_305_UseProxy: return "Use Proxy"; case HttpStatus_307_TemporaryRedirect: return "Temporary Redirect"; case HttpStatus_400_BadRequest: return "Bad Request"; case HttpStatus_401_Unauthorized: return "Unauthorized"; case HttpStatus_402_PaymentRequired: return "Payment Required"; case HttpStatus_403_Forbidden: return "Forbidden"; case HttpStatus_404_NotFound: return "Not Found"; case HttpStatus_405_MethodNotAllowed: return "Method Not Allowed"; case HttpStatus_406_NotAcceptable: return "Not Acceptable"; case HttpStatus_407_ProxyAuthenticationRequired: return "Proxy Authentication Required"; case HttpStatus_408_RequestTimeout: return "Request Timeout"; case HttpStatus_409_Conflict: return "Conflict"; case HttpStatus_410_Gone: return "Gone"; case HttpStatus_411_LengthRequired: return "Length Required"; case HttpStatus_412_PreconditionFailed: return "Precondition Failed"; case HttpStatus_413_RequestEntityTooLarge: return "Request Entity Too Large"; case HttpStatus_414_RequestUriTooLong: return "Request-URI Too Long"; case HttpStatus_415_UnsupportedMediaType: return "Unsupported Media Type"; case HttpStatus_416_RequestedRangeNotSatisfiable: return "Requested Range Not Satisfiable"; case HttpStatus_417_ExpectationFailed: return "Expectation Failed"; case HttpStatus_422_UnprocessableEntity: return "Unprocessable Entity"; case HttpStatus_423_Locked: return "Locked"; case HttpStatus_424_FailedDependency: return "Failed Dependency"; case HttpStatus_426_UpgradeRequired: return "Upgrade Required"; case HttpStatus_500_InternalServerError: return "Internal Server Error"; case HttpStatus_501_NotImplemented: return "Not Implemented"; case HttpStatus_502_BadGateway: return "Bad Gateway"; case HttpStatus_503_ServiceUnavailable: return "Service Unavailable"; case HttpStatus_504_GatewayTimeout: return "Gateway Timeout"; case HttpStatus_505_HttpVersionNotSupported: return "HTTP Version Not Supported"; case HttpStatus_506_VariantAlsoNegotiates: return "Variant Also Negotiates"; case HttpStatus_507_InsufficientStorage: return "Insufficient Storage"; case HttpStatus_509_BandwidthLimitExceeded: return "Bandwidth Limit Exceeded"; case HttpStatus_510_NotExtended: return "Not Extended"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(ResourceType type) { switch (type) { case ResourceType_Patient: return "Patient"; case ResourceType_Study: return "Study"; case ResourceType_Series: return "Series"; case ResourceType_Instance: return "Instance"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(ImageFormat format) { switch (format) { case ImageFormat_Png: return "Png"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(Encoding encoding) { switch (encoding) { case Encoding_Ascii: return "Ascii"; case Encoding_Utf8: return "Utf8"; case Encoding_Latin1: return "Latin1"; case Encoding_Latin2: return "Latin2"; case Encoding_Latin3: return "Latin3"; case Encoding_Latin4: return "Latin4"; case Encoding_Latin5: return "Latin5"; case Encoding_Cyrillic: return "Cyrillic"; case Encoding_Windows1251: return "Windows1251"; case Encoding_Arabic: return "Arabic"; case Encoding_Greek: return "Greek"; case Encoding_Hebrew: return "Hebrew"; case Encoding_Thai: return "Thai"; case Encoding_Japanese: return "Japanese"; case Encoding_Chinese: return "Chinese"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(PhotometricInterpretation photometric) { switch (photometric) { case PhotometricInterpretation_RGB: return "RGB"; case PhotometricInterpretation_Monochrome1: return "Monochrome1"; case PhotometricInterpretation_Monochrome2: return "Monochrome2"; case PhotometricInterpretation_ARGB: return "ARGB"; case PhotometricInterpretation_CMYK: return "CMYK"; case PhotometricInterpretation_HSV: return "HSV"; case PhotometricInterpretation_Palette: return "Palette color"; case PhotometricInterpretation_YBRFull: return "YBR full"; case PhotometricInterpretation_YBRFull422: return "YBR full 422"; case PhotometricInterpretation_YBRPartial420: return "YBR partial 420"; case PhotometricInterpretation_YBRPartial422: return "YBR partial 422"; case PhotometricInterpretation_YBR_ICT: return "YBR ICT"; case PhotometricInterpretation_YBR_RCT: return "YBR RCT"; case PhotometricInterpretation_Unknown: return "Unknown"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(RequestOrigin origin) { switch (origin) { case RequestOrigin_Unknown: return "Unknown"; case RequestOrigin_DicomProtocol: return "DicomProtocol"; case RequestOrigin_RestApi: return "RestApi"; case RequestOrigin_Plugins: return "Plugins"; case RequestOrigin_Lua: return "Lua"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* EnumerationToString(LogLevel level) { switch (level) { case LogLevel_Error: return "ERROR"; case LogLevel_Warning: return "WARNING"; case LogLevel_Info: return "INFO"; case LogLevel_Trace: return "TRACE"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } Encoding StringToEncoding(const char* encoding) { std::string s(encoding); Toolbox::ToUpperCase(s); if (s == "UTF8") { return Encoding_Utf8; } if (s == "ASCII") { return Encoding_Ascii; } if (s == "LATIN1") { return Encoding_Latin1; } if (s == "LATIN2") { return Encoding_Latin2; } if (s == "LATIN3") { return Encoding_Latin3; } if (s == "LATIN4") { return Encoding_Latin4; } if (s == "LATIN5") { return Encoding_Latin5; } if (s == "CYRILLIC") { return Encoding_Cyrillic; } if (s == "WINDOWS1251") { return Encoding_Windows1251; } if (s == "ARABIC") { return Encoding_Arabic; } if (s == "GREEK") { return Encoding_Greek; } if (s == "HEBREW") { return Encoding_Hebrew; } if (s == "THAI") { return Encoding_Thai; } if (s == "JAPANESE") { return Encoding_Japanese; } if (s == "CHINESE") { return Encoding_Chinese; } throw OrthancException(ErrorCode_ParameterOutOfRange); } ResourceType StringToResourceType(const char* type) { std::string s(type); Toolbox::ToUpperCase(s); if (s == "PATIENT" || s == "PATIENTS") { return ResourceType_Patient; } else if (s == "STUDY" || s == "STUDIES") { return ResourceType_Study; } else if (s == "SERIES") { return ResourceType_Series; } else if (s == "INSTANCE" || s == "IMAGE" || s == "INSTANCES" || s == "IMAGES") { return ResourceType_Instance; } throw OrthancException(ErrorCode_ParameterOutOfRange); } ImageFormat StringToImageFormat(const char* format) { std::string s(format); Toolbox::ToUpperCase(s); if (s == "PNG") { return ImageFormat_Png; } throw OrthancException(ErrorCode_ParameterOutOfRange); } LogLevel StringToLogLevel(const char *level) { if (strcmp(level, "ERROR") == 0) { return LogLevel_Error; } else if (strcmp(level, "WARNING") == 0) { return LogLevel_Warning; } else if (strcmp(level, "INFO") == 0) { return LogLevel_Info; } else if (strcmp(level, "TRACE") == 0) { return LogLevel_Trace; } else { throw OrthancException(ErrorCode_InternalError); } } unsigned int GetBytesPerPixel(PixelFormat format) { switch (format) { case PixelFormat_Grayscale8: return 1; case PixelFormat_Grayscale16: case PixelFormat_SignedGrayscale16: return 2; case PixelFormat_RGB24: return 3; case PixelFormat_RGBA32: return 4; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } bool GetDicomEncoding(Encoding& encoding, const char* specificCharacterSet) { std::string s = specificCharacterSet; Toolbox::ToUpperCase(s); // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java if (s == "ISO_IR 6" || s == "ISO_IR 192" || s == "ISO 2022 IR 6") { encoding = Encoding_Utf8; } else if (s == "ISO_IR 100" || s == "ISO 2022 IR 100") { encoding = Encoding_Latin1; } else if (s == "ISO_IR 101" || s == "ISO 2022 IR 101") { encoding = Encoding_Latin2; } else if (s == "ISO_IR 109" || s == "ISO 2022 IR 109") { encoding = Encoding_Latin3; } else if (s == "ISO_IR 110" || s == "ISO 2022 IR 110") { encoding = Encoding_Latin4; } else if (s == "ISO_IR 148" || s == "ISO 2022 IR 148") { encoding = Encoding_Latin5; } else if (s == "ISO_IR 144" || s == "ISO 2022 IR 144") { encoding = Encoding_Cyrillic; } else if (s == "ISO_IR 127" || s == "ISO 2022 IR 127") { encoding = Encoding_Arabic; } else if (s == "ISO_IR 126" || s == "ISO 2022 IR 126") { encoding = Encoding_Greek; } else if (s == "ISO_IR 138" || s == "ISO 2022 IR 138") { encoding = Encoding_Hebrew; } else if (s == "ISO_IR 166" || s == "ISO 2022 IR 166") { encoding = Encoding_Thai; } else if (s == "ISO_IR 13" || s == "ISO 2022 IR 13") { encoding = Encoding_Japanese; } else if (s == "GB18030") { encoding = Encoding_Chinese; } /* else if (s == "ISO 2022 IR 149") { TODO } else if (s == "ISO 2022 IR 159") { TODO } else if (s == "ISO 2022 IR 87") { TODO } */ else { return false; } // The encoding was properly detected return true; } ResourceType GetChildResourceType(ResourceType type) { switch (type) { case ResourceType_Patient: return ResourceType_Study; case ResourceType_Study: return ResourceType_Series; case ResourceType_Series: return ResourceType_Instance; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } ResourceType GetParentResourceType(ResourceType type) { switch (type) { case ResourceType_Study: return ResourceType_Patient; case ResourceType_Series: return ResourceType_Study; case ResourceType_Instance: return ResourceType_Series; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } DicomModule GetModule(ResourceType type) { switch (type) { case ResourceType_Patient: return DicomModule_Patient; case ResourceType_Study: return DicomModule_Study; case ResourceType_Series: return DicomModule_Series; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } const char* GetDicomSpecificCharacterSet(Encoding encoding) { // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ switch (encoding) { case Encoding_Utf8: case Encoding_Ascii: return "ISO_IR 192"; case Encoding_Latin1: return "ISO_IR 100"; case Encoding_Latin2: return "ISO_IR 101"; case Encoding_Latin3: return "ISO_IR 109"; case Encoding_Latin4: return "ISO_IR 110"; case Encoding_Latin5: return "ISO_IR 148"; case Encoding_Cyrillic: return "ISO_IR 144"; case Encoding_Arabic: return "ISO_IR 127"; case Encoding_Greek: return "ISO_IR 126"; case Encoding_Hebrew: return "ISO_IR 138"; case Encoding_Japanese: return "ISO_IR 13"; case Encoding_Chinese: return "GB18030"; case Encoding_Thai: return "ISO_IR 166"; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } // This function is autogenerated by the script // "Resources/GenerateErrorCodes.py" HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error) { switch (error) { case ErrorCode_Success: return HttpStatus_200_Ok; case ErrorCode_ParameterOutOfRange: return HttpStatus_400_BadRequest; case ErrorCode_BadParameterType: return HttpStatus_400_BadRequest; case ErrorCode_InexistentItem: return HttpStatus_404_NotFound; case ErrorCode_BadRequest: return HttpStatus_400_BadRequest; case ErrorCode_UriSyntax: return HttpStatus_400_BadRequest; case ErrorCode_InexistentFile: return HttpStatus_404_NotFound; case ErrorCode_BadFileFormat: return HttpStatus_400_BadRequest; case ErrorCode_UnknownResource: return HttpStatus_404_NotFound; case ErrorCode_InexistentTag: return HttpStatus_404_NotFound; case ErrorCode_BadJson: return HttpStatus_400_BadRequest; case ErrorCode_Unauthorized: return HttpStatus_401_Unauthorized; case ErrorCode_NotAcceptable: return HttpStatus_406_NotAcceptable; default: return HttpStatus_500_InternalServerError; } } bool IsUserContentType(FileContentType type) { return (type >= FileContentType_StartUser && type <= FileContentType_EndUser); } } Orthanc-1.0.0/Core/Enumerations.h0000644000000000000000000004700212634042176014756 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 namespace Orthanc { enum Endianness { Endianness_Unknown, Endianness_Big, Endianness_Little }; // This enumeration is autogenerated by the script // "Resources/GenerateErrorCodes.py" enum ErrorCode { ErrorCode_InternalError = -1 /*!< Internal error */, ErrorCode_Success = 0 /*!< Success */, ErrorCode_Plugin = 1 /*!< Error encountered within the plugin engine */, ErrorCode_NotImplemented = 2 /*!< Not implemented yet */, ErrorCode_ParameterOutOfRange = 3 /*!< Parameter out of range */, ErrorCode_NotEnoughMemory = 4 /*!< Not enough memory */, ErrorCode_BadParameterType = 5 /*!< Bad type for a parameter */, ErrorCode_BadSequenceOfCalls = 6 /*!< Bad sequence of calls */, ErrorCode_InexistentItem = 7 /*!< Accessing an inexistent item */, ErrorCode_BadRequest = 8 /*!< Bad request */, ErrorCode_NetworkProtocol = 9 /*!< Error in the network protocol */, ErrorCode_SystemCommand = 10 /*!< Error while calling a system command */, ErrorCode_Database = 11 /*!< Error with the database engine */, ErrorCode_UriSyntax = 12 /*!< Badly formatted URI */, ErrorCode_InexistentFile = 13 /*!< Inexistent file */, ErrorCode_CannotWriteFile = 14 /*!< Cannot write to file */, ErrorCode_BadFileFormat = 15 /*!< Bad file format */, ErrorCode_Timeout = 16 /*!< Timeout */, ErrorCode_UnknownResource = 17 /*!< Unknown resource */, ErrorCode_IncompatibleDatabaseVersion = 18 /*!< Incompatible version of the database */, ErrorCode_FullStorage = 19 /*!< The file storage is full */, ErrorCode_CorruptedFile = 20 /*!< Corrupted file (e.g. inconsistent MD5 hash) */, ErrorCode_InexistentTag = 21 /*!< Inexistent tag */, ErrorCode_ReadOnly = 22 /*!< Cannot modify a read-only data structure */, ErrorCode_IncompatibleImageFormat = 23 /*!< Incompatible format of the images */, ErrorCode_IncompatibleImageSize = 24 /*!< Incompatible size of the images */, ErrorCode_SharedLibrary = 25 /*!< Error while using a shared library (plugin) */, ErrorCode_UnknownPluginService = 26 /*!< Plugin invoking an unknown service */, ErrorCode_UnknownDicomTag = 27 /*!< Unknown DICOM tag */, ErrorCode_BadJson = 28 /*!< Cannot parse a JSON document */, ErrorCode_Unauthorized = 29 /*!< Bad credentials were provided to an HTTP request */, ErrorCode_BadFont = 30 /*!< Badly formatted font file */, ErrorCode_DatabasePlugin = 31 /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */, ErrorCode_StorageAreaPlugin = 32 /*!< Error in the plugin implementing a custom storage area */, ErrorCode_EmptyRequest = 33 /*!< The request is empty */, ErrorCode_NotAcceptable = 34 /*!< Cannot send a response which is acceptable according to the Accept HTTP header */, ErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, ErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, ErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, ErrorCode_SQLiteStatementAlreadyUsed = 1003 /*!< SQLite: This cached statement is already being referred to */, ErrorCode_SQLiteExecute = 1004 /*!< SQLite: Cannot execute a command */, ErrorCode_SQLiteRollbackWithoutTransaction = 1005 /*!< SQLite: Rolling back a nonexistent transaction (have you called Begin()?) */, ErrorCode_SQLiteCommitWithoutTransaction = 1006 /*!< SQLite: Committing a nonexistent transaction */, ErrorCode_SQLiteRegisterFunction = 1007 /*!< SQLite: Unable to register a function */, ErrorCode_SQLiteFlush = 1008 /*!< SQLite: Unable to flush the database */, ErrorCode_SQLiteCannotRun = 1009 /*!< SQLite: Cannot run a cached statement */, ErrorCode_SQLiteCannotStep = 1010 /*!< SQLite: Cannot step over a cached statement */, ErrorCode_SQLiteBindOutOfRange = 1011 /*!< SQLite: Bing a value while out of range (serious error) */, ErrorCode_SQLitePrepareStatement = 1012 /*!< SQLite: Cannot prepare a cached statement */, ErrorCode_SQLiteTransactionAlreadyStarted = 1013 /*!< SQLite: Beginning the same transaction twice */, ErrorCode_SQLiteTransactionCommit = 1014 /*!< SQLite: Failure when committing the transaction */, ErrorCode_SQLiteTransactionBegin = 1015 /*!< SQLite: Cannot start a transaction */, ErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */, ErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */, ErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */, ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is already in use */, ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is already in use */, ErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */, ErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */, ErrorCode_PathToExecutable = 2007 /*!< Unable to get the path to the executable */, ErrorCode_MakeDirectory = 2008 /*!< Cannot create a directory */, ErrorCode_BadApplicationEntityTitle = 2009 /*!< An application entity title (AET) cannot be empty or be longer than 16 characters */, ErrorCode_NoCFindHandler = 2010 /*!< No request handler factory for DICOM C-FIND SCP */, ErrorCode_NoCMoveHandler = 2011 /*!< No request handler factory for DICOM C-MOVE SCP */, ErrorCode_NoCStoreHandler = 2012 /*!< No request handler factory for DICOM C-STORE SCP */, ErrorCode_NoApplicationEntityFilter = 2013 /*!< No application entity filter */, ErrorCode_NoSopClassOrInstance = 2014 /*!< DicomUserConnection: Unable to find the SOP class and instance */, ErrorCode_NoPresentationContext = 2015 /*!< DicomUserConnection: No acceptable presentation context for modality */, ErrorCode_DicomFindUnavailable = 2016 /*!< DicomUserConnection: The C-FIND command is not supported by the remote SCP */, ErrorCode_DicomMoveUnavailable = 2017 /*!< DicomUserConnection: The C-MOVE command is not supported by the remote SCP */, ErrorCode_CannotStoreInstance = 2018 /*!< Cannot store an instance */, ErrorCode_CreateDicomNotString = 2019 /*!< Only string values are supported when creating DICOM instances */, ErrorCode_CreateDicomOverrideTag = 2020 /*!< Trying to override a value inherited from a parent module */, ErrorCode_CreateDicomUseContent = 2021 /*!< Use \"Content\" to inject an image into a new DICOM instance */, ErrorCode_CreateDicomNoPayload = 2022 /*!< No payload is present for one instance in the series */, ErrorCode_CreateDicomUseDataUriScheme = 2023 /*!< The payload of the DICOM instance must be specified according to Data URI scheme */, ErrorCode_CreateDicomBadParent = 2024 /*!< Trying to attach a new DICOM instance to an inexistent resource */, ErrorCode_CreateDicomParentIsInstance = 2025 /*!< Trying to attach a new DICOM instance to an instance (must be a series, study or patient) */, ErrorCode_CreateDicomParentEncoding = 2026 /*!< Unable to get the encoding of the parent resource */, ErrorCode_UnknownModality = 2027 /*!< Unknown modality */, ErrorCode_BadJobOrdering = 2028 /*!< Bad ordering of filters in a job */, ErrorCode_JsonToLuaTable = 2029 /*!< Cannot convert the given JSON object to a Lua table */, ErrorCode_CannotCreateLua = 2030 /*!< Cannot create the Lua context */, ErrorCode_CannotExecuteLua = 2031 /*!< Cannot execute a Lua command */, ErrorCode_LuaAlreadyExecuted = 2032 /*!< Arguments cannot be pushed after the Lua function is executed */, ErrorCode_LuaBadOutput = 2033 /*!< The Lua function does not give the expected number of outputs */, ErrorCode_NotLuaPredicate = 2034 /*!< The Lua function is not a predicate (only true/false outputs allowed) */, ErrorCode_LuaReturnsNoString = 2035 /*!< The Lua function does not return a string */, ErrorCode_StorageAreaAlreadyRegistered = 2036 /*!< Another plugin has already registered a custom storage area */, ErrorCode_DatabaseBackendAlreadyRegistered = 2037 /*!< Another plugin has already registered a custom database back-end */, ErrorCode_DatabaseNotInitialized = 2038 /*!< Plugin trying to call the database during its initialization */, ErrorCode_SslDisabled = 2039 /*!< Orthanc has been built without SSL support */, ErrorCode_CannotOrderSlices = 2040 /*!< Unable to order the slices of the series */, ErrorCode_NoWorklistHandler = 2041 /*!< No request handler factory for DICOM C-Find Modality SCP */, ErrorCode_START_PLUGINS = 1000000 }; enum LogLevel { LogLevel_Error, LogLevel_Warning, LogLevel_Info, LogLevel_Trace }; /** * {summary}{The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.} **/ enum PixelFormat { /** * {summary}{Color image in RGB24 format.} * {description}{This format describes a color image. The pixels are stored in 3 * consecutive bytes. The memory layout is RGB.} **/ PixelFormat_RGB24 = 1, /** * {summary}{Color image in RGBA32 format.} * {description}{This format describes a color image. The pixels are stored in 4 * consecutive bytes. The memory layout is RGBA.} **/ PixelFormat_RGBA32 = 2, /** * {summary}{Graylevel 8bpp image.} * {description}{The image is graylevel. Each pixel is unsigned and stored in one byte.} **/ PixelFormat_Grayscale8 = 3, /** * {summary}{Graylevel, unsigned 16bpp image.} * {description}{The image is graylevel. Each pixel is unsigned and stored in two bytes.} **/ PixelFormat_Grayscale16 = 4, /** * {summary}{Graylevel, signed 16bpp image.} * {description}{The image is graylevel. Each pixel is signed and stored in two bytes.} **/ PixelFormat_SignedGrayscale16 = 5 }; /** * {summary}{The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.} **/ enum ImageExtractionMode { /** * {summary}{Rescaled to 8bpp.} * {description}{The minimum value of the image is set to 0, and its maximum value is set to 255.} **/ ImageExtractionMode_Preview = 1, /** * {summary}{Truncation to the [0, 255] range.} **/ ImageExtractionMode_UInt8 = 2, /** * {summary}{Truncation to the [0, 65535] range.} **/ ImageExtractionMode_UInt16 = 3, /** * {summary}{Truncation to the [-32768, 32767] range.} **/ ImageExtractionMode_Int16 = 4 }; /** * Most common, non-joke and non-experimental HTTP status codes * http://en.wikipedia.org/wiki/List_of_HTTP_status_codes **/ enum HttpStatus { HttpStatus_None = -1, // 1xx Informational HttpStatus_100_Continue = 100, HttpStatus_101_SwitchingProtocols = 101, HttpStatus_102_Processing = 102, // 2xx Success HttpStatus_200_Ok = 200, HttpStatus_201_Created = 201, HttpStatus_202_Accepted = 202, HttpStatus_203_NonAuthoritativeInformation = 203, HttpStatus_204_NoContent = 204, HttpStatus_205_ResetContent = 205, HttpStatus_206_PartialContent = 206, HttpStatus_207_MultiStatus = 207, HttpStatus_208_AlreadyReported = 208, HttpStatus_226_IMUsed = 226, // 3xx Redirection HttpStatus_300_MultipleChoices = 300, HttpStatus_301_MovedPermanently = 301, HttpStatus_302_Found = 302, HttpStatus_303_SeeOther = 303, HttpStatus_304_NotModified = 304, HttpStatus_305_UseProxy = 305, HttpStatus_307_TemporaryRedirect = 307, // 4xx Client Error HttpStatus_400_BadRequest = 400, HttpStatus_401_Unauthorized = 401, HttpStatus_402_PaymentRequired = 402, HttpStatus_403_Forbidden = 403, HttpStatus_404_NotFound = 404, HttpStatus_405_MethodNotAllowed = 405, HttpStatus_406_NotAcceptable = 406, HttpStatus_407_ProxyAuthenticationRequired = 407, HttpStatus_408_RequestTimeout = 408, HttpStatus_409_Conflict = 409, HttpStatus_410_Gone = 410, HttpStatus_411_LengthRequired = 411, HttpStatus_412_PreconditionFailed = 412, HttpStatus_413_RequestEntityTooLarge = 413, HttpStatus_414_RequestUriTooLong = 414, HttpStatus_415_UnsupportedMediaType = 415, HttpStatus_416_RequestedRangeNotSatisfiable = 416, HttpStatus_417_ExpectationFailed = 417, HttpStatus_422_UnprocessableEntity = 422, HttpStatus_423_Locked = 423, HttpStatus_424_FailedDependency = 424, HttpStatus_426_UpgradeRequired = 426, // 5xx Server Error HttpStatus_500_InternalServerError = 500, HttpStatus_501_NotImplemented = 501, HttpStatus_502_BadGateway = 502, HttpStatus_503_ServiceUnavailable = 503, HttpStatus_504_GatewayTimeout = 504, HttpStatus_505_HttpVersionNotSupported = 505, HttpStatus_506_VariantAlsoNegotiates = 506, HttpStatus_507_InsufficientStorage = 507, HttpStatus_509_BandwidthLimitExceeded = 509, HttpStatus_510_NotExtended = 510 }; enum HttpMethod { HttpMethod_Get = 0, HttpMethod_Post = 1, HttpMethod_Delete = 2, HttpMethod_Put = 3 }; enum ImageFormat { ImageFormat_Png = 1 }; // https://en.wikipedia.org/wiki/HTTP_compression enum HttpCompression { HttpCompression_None, HttpCompression_Deflate, HttpCompression_Gzip }; // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ enum Encoding { Encoding_Ascii, Encoding_Utf8, Encoding_Latin1, Encoding_Latin2, Encoding_Latin3, Encoding_Latin4, Encoding_Latin5, // Turkish Encoding_Cyrillic, Encoding_Windows1251, // Windows-1251 (commonly used for Cyrillic) Encoding_Arabic, Encoding_Greek, Encoding_Hebrew, Encoding_Thai, // TIS 620-2533 Encoding_Japanese, // JIS X 0201 (Shift JIS): Katakana Encoding_Chinese // GB18030 - Chinese simplified //Encoding_JapaneseKanji, // Multibyte - JIS X 0208: Kanji //Encoding_JapaneseSupplementaryKanji, // Multibyte - JIS X 0212: Supplementary Kanji set //Encoding_Korean, // Multibyte - KS X 1001: Hangul and Hanja }; // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.2/ enum PhotometricInterpretation { PhotometricInterpretation_ARGB, // Retired PhotometricInterpretation_CMYK, // Retired PhotometricInterpretation_HSV, // Retired PhotometricInterpretation_Monochrome1, PhotometricInterpretation_Monochrome2, PhotometricInterpretation_Palette, PhotometricInterpretation_RGB, PhotometricInterpretation_YBRFull, PhotometricInterpretation_YBRFull422, PhotometricInterpretation_YBRPartial420, PhotometricInterpretation_YBRPartial422, PhotometricInterpretation_YBR_ICT, PhotometricInterpretation_YBR_RCT, PhotometricInterpretation_Unknown }; enum DicomModule { DicomModule_Patient, DicomModule_Study, DicomModule_Series, DicomModule_Instance, DicomModule_Image }; enum RequestOrigin { RequestOrigin_Unknown, RequestOrigin_DicomProtocol, RequestOrigin_RestApi, RequestOrigin_Plugins, RequestOrigin_Lua }; /** * WARNING: Do not change the explicit values in the enumerations * below this point. This would result in incompatible databases * between versions of Orthanc! **/ enum CompressionType { /** * Buffer/file that is stored as-is, in a raw fashion, without * compression. **/ CompressionType_None = 1, /** * Buffer that is compressed using the "deflate" algorithm (RFC * 1951), wrapped inside the zlib data format (RFC 1950), prefixed * with a "uint64_t" (8 bytes) that encodes the size of the * uncompressed buffer. If the compressed buffer is empty, its * represents an empty uncompressed buffer. This format is * internal to Orthanc. If the 8 first bytes are skipped AND the * buffer is non-empty, the buffer is compatible with the * "deflate" HTTP compression. **/ CompressionType_ZlibWithSize = 2 }; enum FileContentType { // If you add a value below, insert it in "PluginStorageArea" in // the file "Plugins/Engine/OrthancPlugins.cpp" FileContentType_Unknown = 0, FileContentType_Dicom = 1, FileContentType_DicomAsJson = 2, // Make sure that the value "65535" can be stored into this enumeration FileContentType_StartUser = 1024, FileContentType_EndUser = 65535 }; enum ResourceType { ResourceType_Patient = 1, ResourceType_Study = 2, ResourceType_Series = 3, ResourceType_Instance = 4 }; const char* EnumerationToString(ErrorCode code); const char* EnumerationToString(HttpMethod method); const char* EnumerationToString(HttpStatus status); const char* EnumerationToString(ResourceType type); const char* EnumerationToString(ImageFormat format); const char* EnumerationToString(Encoding encoding); const char* EnumerationToString(PhotometricInterpretation photometric); const char* EnumerationToString(LogLevel level); const char* EnumerationToString(RequestOrigin origin); Encoding StringToEncoding(const char* encoding); ResourceType StringToResourceType(const char* type); ImageFormat StringToImageFormat(const char* format); LogLevel StringToLogLevel(const char* format); unsigned int GetBytesPerPixel(PixelFormat format); bool GetDicomEncoding(Encoding& encoding, const char* specificCharacterSet); ResourceType GetChildResourceType(ResourceType type); ResourceType GetParentResourceType(ResourceType type); DicomModule GetModule(ResourceType type); const char* GetDicomSpecificCharacterSet(Encoding encoding); HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error); bool IsUserContentType(FileContentType type); } Orthanc-1.0.0/Core/FileStorage/FileInfo.h0000644000000000000000000000711312634042176016203 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include "../Enumerations.h" namespace Orthanc { struct FileInfo { private: std::string uuid_; FileContentType contentType_; uint64_t uncompressedSize_; std::string uncompressedMD5_; CompressionType compressionType_; uint64_t compressedSize_; std::string compressedMD5_; public: FileInfo() { } /** * Constructor for an uncompressed attachment. **/ FileInfo(const std::string& uuid, FileContentType contentType, uint64_t size, const std::string& md5) : uuid_(uuid), contentType_(contentType), uncompressedSize_(size), uncompressedMD5_(md5), compressionType_(CompressionType_None), compressedSize_(size), compressedMD5_(md5) { } /** * Constructor for a compressed attachment. **/ FileInfo(const std::string& uuid, FileContentType contentType, uint64_t uncompressedSize, const std::string& uncompressedMD5, CompressionType compressionType, uint64_t compressedSize, const std::string& compressedMD5) : uuid_(uuid), contentType_(contentType), uncompressedSize_(uncompressedSize), uncompressedMD5_(uncompressedMD5), compressionType_(compressionType), compressedSize_(compressedSize), compressedMD5_(compressedMD5) { } const std::string& GetUuid() const { return uuid_; } FileContentType GetContentType() const { return contentType_; } uint64_t GetUncompressedSize() const { return uncompressedSize_; } CompressionType GetCompressionType() const { return compressionType_; } uint64_t GetCompressedSize() const { return compressedSize_; } const std::string& GetCompressedMD5() const { return compressedMD5_; } const std::string& GetUncompressedMD5() const { return uncompressedMD5_; } }; } Orthanc-1.0.0/Core/FileStorage/FilesystemStorage.cpp0000644000000000000000000001554412634042176020523 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "FilesystemStorage.h" // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images #include "../Logging.h" #include "../OrthancException.h" #include "../Toolbox.h" #include "../Uuid.h" #include static std::string ToString(const boost::filesystem::path& p) { #if BOOST_HAS_FILESYSTEM_V3 == 1 return p.filename().string(); #else return p.filename(); #endif } namespace Orthanc { boost::filesystem::path FilesystemStorage::GetPath(const std::string& uuid) const { namespace fs = boost::filesystem; if (!Toolbox::IsUuid(uuid)) { throw OrthancException(ErrorCode_ParameterOutOfRange); } fs::path path = root_; path /= std::string(&uuid[0], &uuid[2]); path /= std::string(&uuid[2], &uuid[4]); path /= uuid; #if BOOST_HAS_FILESYSTEM_V3 == 1 path.make_preferred(); #endif return path; } FilesystemStorage::FilesystemStorage(std::string root) { //root_ = boost::filesystem::absolute(root).string(); root_ = root; Toolbox::MakeDirectory(root); } void FilesystemStorage::Create(const std::string& uuid, const void* content, size_t size, FileContentType /*type*/) { boost::filesystem::path path; path = GetPath(uuid); if (boost::filesystem::exists(path)) { // Extremely unlikely case: This Uuid has already been created // in the past. throw OrthancException(ErrorCode_InternalError); } if (boost::filesystem::exists(path.parent_path())) { if (!boost::filesystem::is_directory(path.parent_path())) { throw OrthancException(ErrorCode_DirectoryOverFile); } } else { if (!boost::filesystem::create_directories(path.parent_path())) { throw OrthancException(ErrorCode_FileStorageCannotWrite); } } boost::filesystem::ofstream f; f.open(path, std::ofstream::out | std::ios::binary); if (!f.good()) { throw OrthancException(ErrorCode_FileStorageCannotWrite); } if (size != 0) { f.write(static_cast(content), size); if (!f.good()) { f.close(); throw OrthancException(ErrorCode_FileStorageCannotWrite); } } f.close(); } void FilesystemStorage::Read(std::string& content, const std::string& uuid, FileContentType /*type*/) { content.clear(); Toolbox::ReadFile(content, GetPath(uuid).string()); } uintmax_t FilesystemStorage::GetSize(const std::string& uuid) const { boost::filesystem::path path = GetPath(uuid); return boost::filesystem::file_size(path); } void FilesystemStorage::ListAllFiles(std::set& result) const { namespace fs = boost::filesystem; result.clear(); if (fs::exists(root_) && fs::is_directory(root_)) { for (fs::recursive_directory_iterator current(root_), end; current != end ; ++current) { if (fs::is_regular_file(current->status())) { try { fs::path d = current->path(); std::string uuid = ToString(d); if (Toolbox::IsUuid(uuid)) { fs::path p0 = d.parent_path().parent_path().parent_path(); std::string p1 = ToString(d.parent_path().parent_path()); std::string p2 = ToString(d.parent_path()); if (p1.length() == 2 && p2.length() == 2 && p1 == uuid.substr(0, 2) && p2 == uuid.substr(2, 2) && p0 == root_) { result.insert(uuid); } } } catch (fs::filesystem_error) { } } } } } void FilesystemStorage::Clear() { namespace fs = boost::filesystem; typedef std::set List; List result; ListAllFiles(result); for (List::const_iterator it = result.begin(); it != result.end(); ++it) { Remove(*it, FileContentType_Unknown /*ignored in this class*/); } } void FilesystemStorage::Remove(const std::string& uuid, FileContentType /*type*/) { #if ORTHANC_ENABLE_GOOGLE_LOG == 1 LOG(INFO) << "Deleting file " << uuid; #endif namespace fs = boost::filesystem; fs::path p = GetPath(uuid); try { fs::remove(p); } catch (...) { // Ignore the error } // Remove the two parent directories, ignoring the error code if // these directories are not empty try { #if BOOST_HAS_FILESYSTEM_V3 == 1 boost::system::error_code err; fs::remove(p.parent_path(), err); fs::remove(p.parent_path().parent_path(), err); #else fs::remove(p.parent_path()); fs::remove(p.parent_path().parent_path()); #endif } catch (...) { // Ignore the error } } uintmax_t FilesystemStorage::GetCapacity() const { return boost::filesystem::space(root_).capacity; } uintmax_t FilesystemStorage::GetAvailableSpace() const { return boost::filesystem::space(root_).available; } } Orthanc-1.0.0/Core/FileStorage/FilesystemStorage.h0000644000000000000000000000515312634042176020163 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IStorageArea.h" #include #include #include namespace Orthanc { class FilesystemStorage : public IStorageArea { // TODO REMOVE THIS friend class FilesystemHttpSender; friend class FileStorageAccessor; private: boost::filesystem::path root_; boost::filesystem::path GetPath(const std::string& uuid) const; public: FilesystemStorage(std::string root); virtual void Create(const std::string& uuid, const void* content, size_t size, FileContentType type); virtual void Read(std::string& content, const std::string& uuid, FileContentType type); virtual void Remove(const std::string& uuid, FileContentType type); void ListAllFiles(std::set& result) const; uintmax_t GetSize(const std::string& uuid) const; void Clear(); uintmax_t GetCapacity() const; uintmax_t GetAvailableSpace() const; }; } Orthanc-1.0.0/Core/FileStorage/IStorageArea.h0000644000000000000000000000427212634042176017021 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Enumerations.h" #include #include namespace Orthanc { class IStorageArea : public boost::noncopyable { public: virtual ~IStorageArea() { } virtual void Create(const std::string& uuid, const void* content, size_t size, FileContentType type) = 0; virtual void Read(std::string& content, const std::string& uuid, FileContentType type) = 0; virtual void Remove(const std::string& uuid, FileContentType type) = 0; }; } Orthanc-1.0.0/Core/FileStorage/StorageAccessor.cpp0000644000000000000000000001261512634042176020135 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "StorageAccessor.h" #include "../Compression/ZlibCompressor.h" #include "../OrthancException.h" #include "../HttpServer/HttpStreamTranscoder.h" namespace Orthanc { FileInfo StorageAccessor::Write(const void* data, size_t size, FileContentType type, CompressionType compression, bool storeMd5) { std::string uuid = Toolbox::GenerateUuid(); std::string md5; if (storeMd5) { Toolbox::ComputeMD5(md5, data, size); } switch (compression) { case CompressionType_None: { area_.Create(uuid, data, size, type); return FileInfo(uuid, type, size, md5); } case CompressionType_ZlibWithSize: { ZlibCompressor zlib; std::string compressed; zlib.Compress(compressed, data, size); std::string compressedMD5; if (storeMd5) { Toolbox::ComputeMD5(compressedMD5, compressed); } if (compressed.size() > 0) { area_.Create(uuid, &compressed[0], compressed.size(), type); } else { area_.Create(uuid, NULL, 0, type); } return FileInfo(uuid, type, size, md5, CompressionType_ZlibWithSize, compressed.size(), compressedMD5); } default: throw OrthancException(ErrorCode_NotImplemented); } } void StorageAccessor::Read(std::string& content, const FileInfo& info) { switch (info.GetCompressionType()) { case CompressionType_None: { area_.Read(content, info.GetUuid(), info.GetContentType()); break; } case CompressionType_ZlibWithSize: { ZlibCompressor zlib; std::string compressed; area_.Read(compressed, info.GetUuid(), info.GetContentType()); IBufferCompressor::Uncompress(content, zlib, compressed); break; } default: { throw OrthancException(ErrorCode_NotImplemented); } } // TODO Check the validity of the uncompressed MD5? } void StorageAccessor::Read(Json::Value& content, const FileInfo& info) { std::string s; Read(s, info); Json::Reader reader; if (!reader.parse(s, content)) { throw OrthancException(ErrorCode_BadFileFormat); } } void StorageAccessor::SetupSender(BufferHttpSender& sender, const FileInfo& info, const std::string& mime) { area_.Read(sender.GetBuffer(), info.GetUuid(), info.GetContentType()); sender.SetContentType(mime); const char* extension; switch (info.GetContentType()) { case FileContentType_Dicom: extension = ".dcm"; break; case FileContentType_DicomAsJson: extension = ".json"; break; default: // Non-standard content type extension = ""; } sender.SetContentFilename(info.GetUuid() + std::string(extension)); } void StorageAccessor::AnswerFile(HttpOutput& output, const FileInfo& info, const std::string& mime) { BufferHttpSender sender; SetupSender(sender, info, mime); HttpStreamTranscoder transcoder(sender, info.GetCompressionType()); output.Answer(transcoder); } void StorageAccessor::AnswerFile(RestApiOutput& output, const FileInfo& info, const std::string& mime) { BufferHttpSender sender; SetupSender(sender, info, mime); HttpStreamTranscoder transcoder(sender, info.GetCompressionType()); output.AnswerStream(transcoder); } } Orthanc-1.0.0/Core/FileStorage/StorageAccessor.h0000644000000000000000000000616312634042176017603 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IStorageArea.h" #include "FileInfo.h" #include "../HttpServer/BufferHttpSender.h" #include "../RestApi/RestApiOutput.h" #include #include #include #include #include namespace Orthanc { class StorageAccessor : boost::noncopyable { private: IStorageArea& area_; void SetupSender(BufferHttpSender& sender, const FileInfo& info, const std::string& mime); public: StorageAccessor(IStorageArea& area) : area_(area) { } FileInfo Write(const void* data, size_t size, FileContentType type, CompressionType compression, bool storeMd5); FileInfo Write(const std::string& data, FileContentType type, CompressionType compression, bool storeMd5) { return Write((data.size() == 0 ? NULL : data.c_str()), data.size(), type, compression, storeMd5); } void Read(std::string& content, const FileInfo& info); void Read(Json::Value& content, const FileInfo& info); void Remove(const FileInfo& info) { area_.Remove(info.GetUuid(), info.GetContentType()); } void AnswerFile(HttpOutput& output, const FileInfo& info, const std::string& mime); void AnswerFile(RestApiOutput& output, const FileInfo& info, const std::string& mime); }; } Orthanc-1.0.0/Core/HttpClient.cpp0000644000000000000000000002650712634042176014725 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "HttpClient.h" #include "Toolbox.h" #include "OrthancException.h" #include "Logging.h" #include #include #include static std::string globalCACertificates_; static bool globalVerifyPeers_ = true; static long globalTimeout_ = 0; extern "C" { static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) { if (code == CURLE_OK) { code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); return code; } else { *status = 0; return code; } } // This is a dummy wrapper function to suppress any OpenSSL-related // problem in valgrind. Inlining is prevented. #if defined(__GNUC__) || defined(__clang__) __attribute__((noinline)) #endif static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status) { return GetHttpStatus(curl_easy_perform(curl), curl, status); } } namespace Orthanc { struct HttpClient::PImpl { CURL* curl_; struct curl_slist *postHeaders_; }; static void ThrowException(HttpStatus status) { switch (status) { case HttpStatus_400_BadRequest: throw OrthancException(ErrorCode_BadRequest); case HttpStatus_401_Unauthorized: throw OrthancException(ErrorCode_Unauthorized); case HttpStatus_404_NotFound: throw OrthancException(ErrorCode_InexistentItem); default: throw OrthancException(ErrorCode_NetworkProtocol); } } static CURLcode CheckCode(CURLcode code) { if (code != CURLE_OK) { LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code)); throw OrthancException(ErrorCode_NetworkProtocol); } return code; } static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *payload) { std::string& target = *(static_cast(payload)); size_t length = size * nmemb; if (length == 0) return 0; size_t pos = target.size(); target.resize(pos + length); memcpy(&target.at(pos), buffer, length); return length; } void HttpClient::Setup() { pimpl_->postHeaders_ = NULL; if ((pimpl_->postHeaders_ = curl_slist_append(pimpl_->postHeaders_, "Expect:")) == NULL) { throw OrthancException(ErrorCode_NotEnoughMemory); } pimpl_->curl_ = curl_easy_init(); if (!pimpl_->curl_) { curl_slist_free_all(pimpl_->postHeaders_); throw OrthancException(ErrorCode_NotEnoughMemory); } CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlCallback)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); // This fixes the "longjmp causes uninitialized stack frame" crash // that happens on modern Linux versions. // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1)); url_ = ""; method_ = HttpMethod_Get; lastStatus_ = HttpStatus_200_Ok; isVerbose_ = false; timeout_ = globalTimeout_; verifyPeers_ = globalVerifyPeers_; } HttpClient::HttpClient() : pimpl_(new PImpl) { Setup(); } HttpClient::HttpClient(const HttpClient& other) : pimpl_(new PImpl) { Setup(); if (other.IsVerbose()) { SetVerbose(true); } if (other.credentials_.size() != 0) { credentials_ = other.credentials_; } } HttpClient::~HttpClient() { curl_easy_cleanup(pimpl_->curl_); curl_slist_free_all(pimpl_->postHeaders_); } void HttpClient::SetVerbose(bool isVerbose) { isVerbose_ = isVerbose; if (isVerbose_) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1)); } else { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0)); } } bool HttpClient::Apply(std::string& answer) { answer.clear(); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer)); // Setup HTTPS-related options #if ORTHANC_SSL_ENABLED == 1 if (IsHttpsVerifyPeers()) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, GetHttpsCACertificates().c_str())); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); } else { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); } #endif // Reset the parameters from previous calls to Apply() CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 0L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 0L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, NULL)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, NULL)); // Set timeouts if (timeout_ <= 0) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, 10)); /* default: 10 seconds */ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, 10)); /* default: 10 seconds */ } else { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, timeout_)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, timeout_)); } if (credentials_.size() != 0) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str())); } if (proxy_.size() != 0) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, proxy_.c_str())); } switch (method_) { case HttpMethod_Get: CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L)); break; case HttpMethod_Post: CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_)); break; case HttpMethod_Delete: CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE")); break; case HttpMethod_Put: // http://stackoverflow.com/a/7570281/881731: Don't use // CURLOPT_PUT if there is a body // CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L)); curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_)); break; default: throw OrthancException(ErrorCode_InternalError); } if (method_ == HttpMethod_Post || method_ == HttpMethod_Put) { if (body_.size() > 0) { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str())); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size())); } else { CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); } } // Do the actual request CURLcode code; long status = 0; if (boost::starts_with(url_, "https://")) { code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status); } else { code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status); } CheckCode(code); if (status == 0) { // This corresponds to a call to an inexistent host lastStatus_ = HttpStatus_500_InternalServerError; } else { lastStatus_ = static_cast(status); } return (status >= 200 && status < 300); } bool HttpClient::Apply(Json::Value& answer) { std::string s; if (Apply(s)) { Json::Reader reader; return reader.parse(s, answer); } else { return false; } } void HttpClient::SetCredentials(const char* username, const char* password) { credentials_ = std::string(username) + ":" + std::string(password); } const std::string& HttpClient::GetHttpsCACertificates() const { if (caCertificates_.empty()) { return globalCACertificates_; } else { return caCertificates_; } } void HttpClient::GlobalInitialize(bool httpsVerifyPeers, const std::string& httpsVerifyCertificates) { globalVerifyPeers_ = httpsVerifyPeers; globalCACertificates_ = httpsVerifyCertificates; #if ORTHANC_SSL_ENABLED == 1 if (httpsVerifyPeers) { if (globalCACertificates_.empty()) { LOG(WARNING) << "No certificates are provided to validate peers, " << "set \"HttpsCACertificates\" if you need to do HTTPS requests"; } else { LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << globalCACertificates_; } } else { LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!"; } #endif CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT)); } void HttpClient::GlobalFinalize() { curl_global_cleanup(); } void HttpClient::SetDefaultTimeout(long timeout) { LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds"; globalTimeout_ = timeout; } void HttpClient::ApplyAndThrowException(std::string& answer) { if (!Apply(answer)) { ThrowException(GetLastStatus()); } } void HttpClient::ApplyAndThrowException(Json::Value& answer) { if (!Apply(answer)) { ThrowException(GetLastStatus()); } } } Orthanc-1.0.0/Core/HttpClient.h0000644000000000000000000000774712634042176014377 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "Enumerations.h" #include #include #include namespace Orthanc { class HttpClient { private: struct PImpl; boost::shared_ptr pimpl_; std::string url_; std::string credentials_; HttpMethod method_; HttpStatus lastStatus_; std::string body_; // This only makes sense for POST and PUT requests bool isVerbose_; long timeout_; std::string proxy_; bool verifyPeers_; std::string caCertificates_; void Setup(); void operator= (const HttpClient&); // Forbidden public: HttpClient(const HttpClient& base); HttpClient(); ~HttpClient(); void SetUrl(const char* url) { url_ = std::string(url); } void SetUrl(const std::string& url) { url_ = url; } const std::string& GetUrl() const { return url_; } void SetMethod(HttpMethod method) { method_ = method; } HttpMethod GetMethod() const { return method_; } void SetTimeout(long seconds) { timeout_ = seconds; } long GetTimeout() const { return timeout_; } void SetBody(const std::string& data) { body_ = data; } std::string& GetBody() { return body_; } const std::string& GetBody() const { return body_; } void SetVerbose(bool isVerbose); bool IsVerbose() const { return isVerbose_; } bool Apply(std::string& answer); bool Apply(Json::Value& answer); HttpStatus GetLastStatus() const { return lastStatus_; } void SetCredentials(const char* username, const char* password); void SetProxy(const std::string& proxy) { proxy_ = proxy; } void SetHttpsVerifyPeers(bool verify) { verifyPeers_ = verify; } bool IsHttpsVerifyPeers() const { return verifyPeers_; } void SetHttpsCACertificates(const std::string& certificates) { caCertificates_ = certificates; } const std::string& GetHttpsCACertificates() const; static void GlobalInitialize(bool httpsVerifyPeers, const std::string& httpsCACertificates); static void GlobalFinalize(); static void SetDefaultTimeout(long timeout); void ApplyAndThrowException(std::string& answer); void ApplyAndThrowException(Json::Value& answer); }; } Orthanc-1.0.0/Core/HttpServer/BufferHttpSender.cpp0000644000000000000000000000463412634042176020164 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "BufferHttpSender.h" #include "../OrthancException.h" #include namespace Orthanc { BufferHttpSender::BufferHttpSender() : position_(0), chunkSize_(0), currentChunkSize_(0) { } bool BufferHttpSender::ReadNextChunk() { assert(position_ + currentChunkSize_ <= buffer_.size()); position_ += currentChunkSize_; if (position_ == buffer_.size()) { return false; } else { currentChunkSize_ = buffer_.size() - position_; if (chunkSize_ != 0 && currentChunkSize_ > chunkSize_) { currentChunkSize_ = chunkSize_; } return true; } } const char* BufferHttpSender::GetChunkContent() { return buffer_.c_str() + position_; } size_t BufferHttpSender::GetChunkSize() { return currentChunkSize_; } } Orthanc-1.0.0/Core/HttpServer/BufferHttpSender.h0000644000000000000000000000467312634042176017634 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "HttpFileSender.h" namespace Orthanc { class BufferHttpSender : public HttpFileSender { private: std::string buffer_; size_t position_; size_t chunkSize_; size_t currentChunkSize_; public: BufferHttpSender(); std::string& GetBuffer() { return buffer_; } const std::string& GetBuffer() const { return buffer_; } // This is for test purpose. If "chunkSize" is set to "0" (the // default), the entire buffer is consumed at once. void SetChunkSize(size_t chunkSize) { chunkSize_ = chunkSize; } /** * Implementation of the IHttpStreamAnswer interface. **/ virtual uint64_t GetContentLength() { return buffer_.size(); } virtual bool ReadNextChunk(); virtual const char* GetChunkContent(); virtual size_t GetChunkSize(); }; } Orthanc-1.0.0/Core/HttpServer/EmbeddedResourceHttpHandler.cpp0000644000000000000000000000625312634042176022310 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "EmbeddedResourceHttpHandler.h" #include "../Logging.h" #include "../OrthancException.h" #include "HttpOutput.h" #include namespace Orthanc { EmbeddedResourceHttpHandler::EmbeddedResourceHttpHandler( const std::string& baseUri, EmbeddedResources::DirectoryResourceId resourceId) { Toolbox::SplitUriComponents(baseUri_, baseUri); resourceId_ = resourceId; } bool EmbeddedResourceHttpHandler::Handle( HttpOutput& output, RequestOrigin /*origin*/, const char* /*remoteIp*/, const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const char* /*bodyData*/, size_t /*bodySize*/) { if (!Toolbox::IsChildUri(baseUri_, uri)) { // This URI is not served by this handler return false; } if (method != HttpMethod_Get) { output.SendMethodNotAllowed("GET"); return true; } std::string resourcePath = Toolbox::FlattenUri(uri, baseUri_.size()); std::string contentType = Toolbox::AutodetectMimeType(resourcePath); try { const void* buffer = EmbeddedResources::GetDirectoryResourceBuffer(resourceId_, resourcePath.c_str()); size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str()); output.SetContentType(contentType.c_str()); output.Answer(buffer, size); } catch (OrthancException&) { LOG(WARNING) << "Unable to find HTTP resource: " << resourcePath; output.SendStatus(HttpStatus_404_NotFound); } return true; } } Orthanc-1.0.0/Core/HttpServer/EmbeddedResourceHttpHandler.h0000644000000000000000000000446212634042176021755 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IHttpHandler.h" #include // Autogenerated file #include namespace Orthanc { class EmbeddedResourceHttpHandler : public IHttpHandler { private: UriComponents baseUri_; EmbeddedResources::DirectoryResourceId resourceId_; public: EmbeddedResourceHttpHandler( const std::string& baseUri, EmbeddedResources::DirectoryResourceId resourceId); virtual bool Handle( HttpOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const char* /*bodyData*/, size_t /*bodySize*/); }; } Orthanc-1.0.0/Core/HttpServer/FilesystemHttpHandler.cpp0000644000000000000000000001201212634042176021221 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "FilesystemHttpHandler.h" #include "../OrthancException.h" #include "FilesystemHttpSender.h" #include namespace Orthanc { struct FilesystemHttpHandler::PImpl { UriComponents baseUri_; boost::filesystem::path root_; }; static void OutputDirectoryContent(HttpOutput& output, const IHttpHandler::Arguments& headers, const UriComponents& uri, const boost::filesystem::path& p) { namespace fs = boost::filesystem; std::string s; s += ""; s += " "; s += "

Subdirectories

"; s += "
    "; if (uri.size() > 0) { std::string h = Toolbox::FlattenUri(uri) + "/.."; s += "
  • ..
  • "; } fs::directory_iterator end; for (fs::directory_iterator it(p) ; it != end; ++it) { #if BOOST_HAS_FILESYSTEM_V3 == 1 std::string f = it->path().filename().string(); #else std::string f = it->path().filename(); #endif std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (fs::is_directory(it->status())) s += "
  • " + f + "
  • "; } s += "
"; s += "

Files

"; s += "
    "; for (fs::directory_iterator it(p) ; it != end; ++it) { #if BOOST_HAS_FILESYSTEM_V3 == 1 std::string f = it->path().filename().string(); #else std::string f = it->path().filename(); #endif std::string h = Toolbox::FlattenUri(uri) + "/" + f; if (fs::is_regular_file(it->status())) s += "
  • " + f + "
  • "; } s += "
"; s += " "; s += ""; output.SetContentType("text/html"); output.Answer(s); } FilesystemHttpHandler::FilesystemHttpHandler(const std::string& baseUri, const std::string& root) : pimpl_(new PImpl) { Toolbox::SplitUriComponents(pimpl_->baseUri_, baseUri); pimpl_->root_ = root; listDirectoryContent_ = false; namespace fs = boost::filesystem; if (!fs::exists(pimpl_->root_) || !fs::is_directory(pimpl_->root_)) { throw OrthancException(ErrorCode_DirectoryExpected); } } bool FilesystemHttpHandler::Handle( HttpOutput& output, RequestOrigin /*origin*/, const char* /*remoteIp*/, const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const char* /*bodyData*/, size_t /*bodySize*/) { if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri)) { // This URI is not served by this handler return false; } if (method != HttpMethod_Get) { output.SendMethodNotAllowed("GET"); return true; } namespace fs = boost::filesystem; fs::path p = pimpl_->root_; for (size_t i = pimpl_->baseUri_.size(); i < uri.size(); i++) { p /= uri[i]; } if (fs::exists(p) && fs::is_regular_file(p)) { FilesystemHttpSender sender(p); output.Answer(sender); // TODO COMPRESSION } else if (listDirectoryContent_ && fs::exists(p) && fs::is_directory(p)) { OutputDirectoryContent(output, headers, uri, p); } else { output.SendStatus(HttpStatus_404_NotFound); } return true; } } Orthanc-1.0.0/Core/HttpServer/FilesystemHttpHandler.h0000644000000000000000000000500012634042176020665 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IHttpHandler.h" #include namespace Orthanc { class FilesystemHttpHandler : public IHttpHandler { private: // PImpl idiom to avoid the inclusion of boost::filesystem // throughout the software struct PImpl; boost::shared_ptr pimpl_; bool listDirectoryContent_; public: FilesystemHttpHandler(const std::string& baseUri, const std::string& root); virtual bool Handle( HttpOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& arguments, const char* /*bodyData*/, size_t /*bodySize*/); bool IsListDirectoryContent() const { return listDirectoryContent_; } void SetListDirectoryContent(bool enabled) { listDirectoryContent_ = enabled; } }; } Orthanc-1.0.0/Core/HttpServer/FilesystemHttpSender.cpp0000644000000000000000000000475212634042176021100 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "FilesystemHttpSender.h" #include "../OrthancException.h" static const size_t CHUNK_SIZE = 64 * 1024; // Use 64KB chunks namespace Orthanc { void FilesystemHttpSender::Initialize(const boost::filesystem::path& path) { SetContentFilename(path.filename().string()); file_.open(path.string().c_str(), std::ifstream::binary); if (!file_.is_open()) { throw OrthancException(ErrorCode_InexistentFile); } file_.seekg(0, file_.end); size_ = file_.tellg(); file_.seekg(0, file_.beg); } bool FilesystemHttpSender::ReadNextChunk() { if (chunk_.size() == 0) { chunk_.resize(CHUNK_SIZE); } file_.read(&chunk_[0], chunk_.size()); if ((file_.flags() & std::istream::failbit) || file_.gcount() < 0) { throw OrthancException(ErrorCode_CorruptedFile); } chunkSize_ = file_.gcount(); return chunkSize_ > 0; } } Orthanc-1.0.0/Core/HttpServer/FilesystemHttpSender.h0000644000000000000000000000520412634042176020536 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "HttpFileSender.h" #include "BufferHttpSender.h" #include "../FileStorage/FilesystemStorage.h" #include namespace Orthanc { class FilesystemHttpSender : public HttpFileSender { private: std::ifstream file_; uint64_t size_; std::string chunk_; size_t chunkSize_; void Initialize(const boost::filesystem::path& path); public: FilesystemHttpSender(const std::string& path) { Initialize(path); } FilesystemHttpSender(const boost::filesystem::path& path) { Initialize(path); } FilesystemHttpSender(const FilesystemStorage& storage, const std::string& uuid) { Initialize(storage.GetPath(uuid)); } /** * Implementation of the IHttpStreamAnswer interface. **/ virtual uint64_t GetContentLength() { return size_; } virtual bool ReadNextChunk(); virtual const char* GetChunkContent() { return chunk_.c_str(); } virtual size_t GetChunkSize() { return chunkSize_; } }; } Orthanc-1.0.0/Core/HttpServer/HttpContentNegociation.cpp0000644000000000000000000001567312634042176021411 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HttpContentNegociation.h" #include "../Logging.h" #include "../OrthancException.h" #include "../Toolbox.h" #include namespace Orthanc { HttpContentNegociation::Handler::Handler(const std::string& type, const std::string& subtype, IHandler& handler) : type_(type), subtype_(subtype), handler_(handler) { } bool HttpContentNegociation::Handler::IsMatch(const std::string& type, const std::string& subtype) const { if (type == "*" && subtype == "*") { return true; } if (subtype == "*" && type == type_) { return true; } return type == type_ && subtype == subtype_; } struct HttpContentNegociation::Reference : public boost::noncopyable { const Handler& handler_; uint8_t level_; float quality_; Reference(const Handler& handler, const std::string& type, const std::string& subtype, float quality) : handler_(handler), quality_(quality) { if (type == "*" && subtype == "*") { level_ = 0; } else if (subtype == "*") { level_ = 1; } else { level_ = 2; } } bool operator< (const Reference& other) const { if (level_ < other.level_) { return true; } if (level_ > other.level_) { return false; } return quality_ < other.quality_; } }; bool HttpContentNegociation::SplitPair(std::string& first /* out */, std::string& second /* out */, const std::string& source, char separator) { size_t pos = source.find(separator); if (pos == std::string::npos) { return false; } else { first = Toolbox::StripSpaces(source.substr(0, pos)); second = Toolbox::StripSpaces(source.substr(pos + 1)); return true; } } float HttpContentNegociation::GetQuality(const Tokens& parameters) { for (size_t i = 1; i < parameters.size(); i++) { std::string key, value; if (SplitPair(key, value, parameters[i], '=') && key == "q") { float quality; bool ok = false; try { quality = boost::lexical_cast(value); ok = (quality >= 0.0f && quality <= 1.0f); } catch (boost::bad_lexical_cast&) { } if (ok) { return quality; } else { LOG(ERROR) << "Quality parameter out of range in a HTTP request (must be between 0 and 1): " << value; throw OrthancException(ErrorCode_BadRequest); } } } return 1.0f; // Default quality } void HttpContentNegociation::SelectBestMatch(std::auto_ptr& best, const Handler& handler, const std::string& type, const std::string& subtype, float quality) { std::auto_ptr match(new Reference(handler, type, subtype, quality)); if (best.get() == NULL || *best < *match) { best = match; } } void HttpContentNegociation::Register(const std::string& mime, IHandler& handler) { std::string type, subtype; if (SplitPair(type, subtype, mime, '/') && type != "*" && subtype != "*") { handlers_.push_back(Handler(type, subtype, handler)); } else { throw OrthancException(ErrorCode_ParameterOutOfRange); } } bool HttpContentNegociation::Apply(const HttpHeaders& headers) { HttpHeaders::const_iterator accept = headers.find("accept"); if (accept != headers.end()) { return Apply(accept->second); } else { return Apply("*/*"); } } bool HttpContentNegociation::Apply(const std::string& accept) { // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 // https://en.wikipedia.org/wiki/Content_negotiation // http://www.newmediacampaigns.com/blog/browser-rest-http-accept-headers Tokens mediaRanges; Toolbox::TokenizeString(mediaRanges, accept, ','); std::auto_ptr bestMatch; for (Tokens::const_iterator it = mediaRanges.begin(); it != mediaRanges.end(); ++it) { Tokens parameters; Toolbox::TokenizeString(parameters, *it, ';'); if (parameters.size() > 0) { float quality = GetQuality(parameters); std::string type, subtype; if (SplitPair(type, subtype, parameters[0], '/')) { for (Handlers::const_iterator it2 = handlers_.begin(); it2 != handlers_.end(); ++it2) { if (it2->IsMatch(type, subtype)) { SelectBestMatch(bestMatch, *it2, type, subtype, quality); } } } } } if (bestMatch.get() == NULL) // No match was found { return false; } else { bestMatch->handler_.Call(); return true; } } } Orthanc-1.0.0/Core/HttpServer/HttpContentNegociation.h0000644000000000000000000000654212634042176021051 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #include #include #include #include namespace Orthanc { class HttpContentNegociation : public boost::noncopyable { public: typedef std::map HttpHeaders; class IHandler : public boost::noncopyable { public: virtual ~IHandler() { } virtual void Handle(const std::string& type, const std::string& subtype) = 0; }; private: struct Handler { std::string type_; std::string subtype_; IHandler& handler_; Handler(const std::string& type, const std::string& subtype, IHandler& handler); bool IsMatch(const std::string& type, const std::string& subtype) const; void Call() const { handler_.Handle(type_, subtype_); } }; struct Reference; typedef std::vector Tokens; typedef std::list Handlers; Handlers handlers_; static bool SplitPair(std::string& first /* out */, std::string& second /* out */, const std::string& source, char separator); static float GetQuality(const Tokens& parameters); static void SelectBestMatch(std::auto_ptr& best, const Handler& handler, const std::string& type, const std::string& subtype, float quality); public: void Register(const std::string& mime, IHandler& handler); bool Apply(const HttpHeaders& headers); bool Apply(const std::string& accept); }; } Orthanc-1.0.0/Core/HttpServer/HttpFileSender.cpp0000644000000000000000000000447312634042176017633 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HttpFileSender.h" #include "../OrthancException.h" #include "../Toolbox.h" #include namespace Orthanc { void HttpFileSender::SetContentFilename(const std::string& filename) { filename_ = filename; if (contentType_.empty()) { contentType_ = Toolbox::AutodetectMimeType(filename); } } bool HttpFileSender::HasContentFilename(std::string& filename) { if (filename_.empty()) { return false; } else { filename = filename_; return true; } } std::string HttpFileSender::GetContentType() { if (contentType_.empty()) { return "application/octet-stream"; } else { return contentType_; } } } Orthanc-1.0.0/Core/HttpServer/HttpFileSender.h0000644000000000000000000000467712634042176017306 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "HttpOutput.h" namespace Orthanc { class HttpFileSender : public IHttpStreamAnswer { private: std::string contentType_; std::string filename_; public: void SetContentType(const std::string& contentType) { contentType_ = contentType; } const std::string& GetContentType() const { return contentType_; } void SetContentFilename(const std::string& filename); const std::string& GetContentFilename() const { return filename_; } /** * Implementation of the IHttpStreamAnswer interface. **/ virtual HttpCompression SetupHttpCompression(bool /*gzipAllowed*/, bool /*deflateAllowed*/) { return HttpCompression_None; } virtual bool HasContentFilename(std::string& filename); virtual std::string GetContentType(); }; } Orthanc-1.0.0/Core/HttpServer/HttpOutput.cpp0000644000000000000000000003561512634042176017115 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HttpOutput.h" #include "../Logging.h" #include "../OrthancException.h" #include "../Toolbox.h" #include "../Compression/GzipCompressor.h" #include "../Compression/ZlibCompressor.h" #include #include #include #include namespace Orthanc { HttpOutput::StateMachine::StateMachine(IHttpOutputStream& stream, bool isKeepAlive) : stream_(stream), state_(State_WritingHeader), status_(HttpStatus_200_Ok), hasContentLength_(false), contentPosition_(0), keepAlive_(isKeepAlive) { } HttpOutput::StateMachine::~StateMachine() { if (state_ != State_Done) { //asm volatile ("int3;"); //LOG(ERROR) << "This HTTP answer does not contain any body"; } if (hasContentLength_ && contentPosition_ != contentLength_) { LOG(ERROR) << "This HTTP answer has not sent the proper number of bytes in its body"; } } void HttpOutput::StateMachine::SetHttpStatus(HttpStatus status) { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } status_ = status; } void HttpOutput::StateMachine::SetContentLength(uint64_t length) { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } hasContentLength_ = true; contentLength_ = length; } void HttpOutput::StateMachine::SetContentType(const char* contentType) { AddHeader("Content-Type", contentType); } void HttpOutput::StateMachine::SetContentFilename(const char* filename) { // TODO Escape double quotes AddHeader("Content-Disposition", "filename=\"" + std::string(filename) + "\""); } void HttpOutput::StateMachine::SetCookie(const std::string& cookie, const std::string& value) { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } // TODO Escape "=" characters AddHeader("Set-Cookie", cookie + "=" + value); } void HttpOutput::StateMachine::AddHeader(const std::string& header, const std::string& value) { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } headers_.push_back(header + ": " + value + "\r\n"); } void HttpOutput::StateMachine::ClearHeaders() { if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } headers_.clear(); } void HttpOutput::StateMachine::SendBody(const void* buffer, size_t length) { if (state_ == State_Done) { if (length == 0) { return; } else { LOG(ERROR) << "Because of keep-alive connections, the entire body must be sent at once or Content-Length must be given"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } } if (state_ == State_WritingMultipart) { throw OrthancException(ErrorCode_InternalError); } if (state_ == State_WritingHeader) { // Send the HTTP header before writing the body stream_.OnHttpStatusReceived(status_); std::string s = "HTTP/1.1 " + boost::lexical_cast(status_) + " " + std::string(EnumerationToString(status_)) + "\r\n"; if (keepAlive_) { s += "Connection: keep-alive\r\n"; } for (std::list::const_iterator it = headers_.begin(); it != headers_.end(); ++it) { s += *it; } if (status_ != HttpStatus_200_Ok) { hasContentLength_ = false; } uint64_t contentLength = (hasContentLength_ ? contentLength_ : length); s += "Content-Length: " + boost::lexical_cast(contentLength) + "\r\n\r\n"; stream_.Send(true, s.c_str(), s.size()); state_ = State_WritingBody; } if (hasContentLength_ && contentPosition_ + length > contentLength_) { LOG(ERROR) << "The body size exceeds what was declared with SetContentSize()"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (length > 0) { stream_.Send(false, buffer, length); contentPosition_ += length; } if (!hasContentLength_ || contentPosition_ == contentLength_) { state_ = State_Done; } } void HttpOutput::StateMachine::CloseBody() { switch (state_) { case State_WritingHeader: SetContentLength(0); SendBody(NULL, 0); break; case State_WritingBody: if (!hasContentLength_ || contentPosition_ == contentLength_) { state_ = State_Done; } else { LOG(ERROR) << "The body size has not reached what was declared with SetContentSize()"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } break; case State_WritingMultipart: LOG(ERROR) << "Cannot invoke CloseBody() with multipart outputs"; throw OrthancException(ErrorCode_BadSequenceOfCalls); case State_Done: return; // Ignore default: throw OrthancException(ErrorCode_InternalError); } } HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const { #if 0 // TODO Do not compress small files? if (bodySize < 512) { return HttpCompression_None; } #endif // Prefer "gzip" over "deflate" if the choice is offered if (isGzipAllowed_) { return HttpCompression_Gzip; } else if (isDeflateAllowed_) { return HttpCompression_Deflate; } else { return HttpCompression_None; } } void HttpOutput::SendMethodNotAllowed(const std::string& allowed) { stateMachine_.ClearHeaders(); stateMachine_.SetHttpStatus(HttpStatus_405_MethodNotAllowed); stateMachine_.AddHeader("Allow", allowed); stateMachine_.SendBody(NULL, 0); } void HttpOutput::SendStatus(HttpStatus status, const char* message, size_t messageSize) { if (status == HttpStatus_200_Ok || status == HttpStatus_301_MovedPermanently || status == HttpStatus_401_Unauthorized || status == HttpStatus_405_MethodNotAllowed) { LOG(ERROR) << "Please use the dedicated methods to this HTTP status code in HttpOutput"; throw OrthancException(ErrorCode_ParameterOutOfRange); } stateMachine_.ClearHeaders(); stateMachine_.SetHttpStatus(status); stateMachine_.SendBody(message, messageSize); } void HttpOutput::Redirect(const std::string& path) { stateMachine_.ClearHeaders(); stateMachine_.SetHttpStatus(HttpStatus_301_MovedPermanently); stateMachine_.AddHeader("Location", path); stateMachine_.SendBody(NULL, 0); } void HttpOutput::SendUnauthorized(const std::string& realm) { stateMachine_.ClearHeaders(); stateMachine_.SetHttpStatus(HttpStatus_401_Unauthorized); stateMachine_.AddHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); stateMachine_.SendBody(NULL, 0); } void HttpOutput::Answer(const void* buffer, size_t length) { if (length == 0) { AnswerEmpty(); return; } HttpCompression compression = GetPreferredCompression(length); if (compression == HttpCompression_None) { stateMachine_.SetContentLength(length); stateMachine_.SendBody(buffer, length); return; } std::string compressed, encoding; switch (compression) { case HttpCompression_Deflate: { encoding = "deflate"; ZlibCompressor compressor; // Do not prefix the buffer with its uncompressed size, to be compatible with "deflate" compressor.SetPrefixWithUncompressedSize(false); compressor.Compress(compressed, buffer, length); break; } case HttpCompression_Gzip: { encoding = "gzip"; GzipCompressor compressor; compressor.Compress(compressed, buffer, length); break; } default: throw OrthancException(ErrorCode_InternalError); } LOG(TRACE) << "Compressing a HTTP answer using " << encoding; // The body is empty, do not use HTTP compression if (compressed.size() == 0) { AnswerEmpty(); } else { stateMachine_.AddHeader("Content-Encoding", encoding); stateMachine_.SetContentLength(compressed.size()); stateMachine_.SendBody(compressed.c_str(), compressed.size()); } stateMachine_.CloseBody(); } void HttpOutput::Answer(const std::string& str) { Answer(str.size() == 0 ? NULL : str.c_str(), str.size()); } void HttpOutput::AnswerEmpty() { stateMachine_.CloseBody(); } void HttpOutput::StateMachine::StartMultipart(const std::string& subType, const std::string& contentType) { if (subType != "mixed" && subType != "related") { throw OrthancException(ErrorCode_ParameterOutOfRange); } if (keepAlive_) { LOG(ERROR) << "Multipart answers are not implemented together with keep-alive connections"; throw OrthancException(ErrorCode_NotImplemented); } if (state_ != State_WritingHeader) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (status_ != HttpStatus_200_Ok) { SendBody(NULL, 0); return; } stream_.OnHttpStatusReceived(status_); std::string header = "HTTP/1.1 200 OK\r\n"; // Possibly add the cookies for (std::list::const_iterator it = headers_.begin(); it != headers_.end(); ++it) { if (!Toolbox::StartsWith(*it, "Set-Cookie: ")) { LOG(ERROR) << "The only headers that can be set in multipart answers are Set-Cookie (here: " << *it << " is set)"; throw OrthancException(ErrorCode_BadSequenceOfCalls); } header += *it; } multipartBoundary_ = Toolbox::GenerateUuid(); multipartContentType_ = contentType; header += "Content-Type: multipart/" + subType + "; type=" + contentType + "; boundary=" + multipartBoundary_ + "\r\n\r\n"; stream_.Send(true, header.c_str(), header.size()); state_ = State_WritingMultipart; } void HttpOutput::StateMachine::SendMultipartItem(const void* item, size_t length, const std::map& headers) { if (state_ != State_WritingMultipart) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } std::string header = "--" + multipartBoundary_ + "\r\n"; bool hasContentType = false; bool hasContentLength = false; bool hasMimeVersion = false; for (std::map::const_iterator it = headers.begin(); it != headers.end(); ++it) { header += it->first + ": " + it->second + "\r\n"; std::string tmp; Toolbox::ToLowerCase(tmp, it->first); if (tmp == "content-type") { hasContentType = true; } if (tmp == "content-length") { hasContentLength = true; } if (tmp == "mime-version") { hasMimeVersion = true; } } if (!hasContentType) { header += "Content-Type: " + multipartContentType_ + "\r\n"; } if (!hasContentLength) { header += "Content-Length: " + boost::lexical_cast(length) + "\r\n"; } if (!hasMimeVersion) { header += "MIME-Version: 1.0\r\n\r\n"; } stream_.Send(false, header.c_str(), header.size()); if (length > 0) { stream_.Send(false, item, length); } stream_.Send(false, "\r\n", 2); } void HttpOutput::StateMachine::CloseMultipart() { if (state_ != State_WritingMultipart) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } // The two lines below might throw an exception, if the client has // closed the connection. Such an error is ignored. try { std::string header = "--" + multipartBoundary_ + "--\r\n"; stream_.Send(false, header.c_str(), header.size()); } catch (OrthancException&) { } state_ = State_Done; } void HttpOutput::Answer(IHttpStreamAnswer& stream) { HttpCompression compression = stream.SetupHttpCompression(isGzipAllowed_, isDeflateAllowed_); switch (compression) { case HttpCompression_None: break; case HttpCompression_Gzip: stateMachine_.AddHeader("Content-Encoding", "gzip"); break; case HttpCompression_Deflate: stateMachine_.AddHeader("Content-Encoding", "deflate"); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } stateMachine_.SetContentLength(stream.GetContentLength()); std::string contentType = stream.GetContentType(); if (contentType.empty()) { contentType = "application/octet-stream"; } stateMachine_.SetContentType(contentType.c_str()); std::string filename; if (stream.HasContentFilename(filename)) { SetContentFilename(filename.c_str()); } while (stream.ReadNextChunk()) { stateMachine_.SendBody(stream.GetChunkContent(), stream.GetChunkSize()); } stateMachine_.CloseBody(); } } Orthanc-1.0.0/Core/HttpServer/HttpOutput.h0000644000000000000000000001355212634042176016556 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #include "../Enumerations.h" #include "IHttpOutputStream.h" #include "IHttpStreamAnswer.h" #include "../Uuid.h" namespace Orthanc { class HttpOutput : public boost::noncopyable { private: typedef std::list< std::pair > Header; class StateMachine : public boost::noncopyable { public: enum State { State_WritingHeader, State_WritingBody, State_WritingMultipart, State_Done }; private: IHttpOutputStream& stream_; State state_; HttpStatus status_; bool hasContentLength_; uint64_t contentLength_; uint64_t contentPosition_; bool keepAlive_; std::list headers_; std::string multipartBoundary_; std::string multipartContentType_; public: StateMachine(IHttpOutputStream& stream, bool isKeepAlive); ~StateMachine(); void SetHttpStatus(HttpStatus status); void SetContentLength(uint64_t length); void SetContentType(const char* contentType); void SetContentFilename(const char* filename); void SetCookie(const std::string& cookie, const std::string& value); void AddHeader(const std::string& header, const std::string& value); void ClearHeaders(); void SendBody(const void* buffer, size_t length); void StartMultipart(const std::string& subType, const std::string& contentType); void SendMultipartItem(const void* item, size_t length, const std::map& headers); void CloseMultipart(); void CloseBody(); State GetState() const { return state_; } }; StateMachine stateMachine_; bool isDeflateAllowed_; bool isGzipAllowed_; HttpCompression GetPreferredCompression(size_t bodySize) const; public: HttpOutput(IHttpOutputStream& stream, bool isKeepAlive) : stateMachine_(stream, isKeepAlive), isDeflateAllowed_(false), isGzipAllowed_(false) { } void SetDeflateAllowed(bool allowed) { isDeflateAllowed_ = allowed; } bool IsDeflateAllowed() const { return isDeflateAllowed_; } void SetGzipAllowed(bool allowed) { isGzipAllowed_ = allowed; } bool IsGzipAllowed() const { return isGzipAllowed_; } void SendStatus(HttpStatus status, const char* message, size_t messageSize); void SendStatus(HttpStatus status) { SendStatus(status, NULL, 0); } void SendStatus(HttpStatus status, const std::string& message) { SendStatus(status, message.c_str(), message.size()); } void SetContentType(const char* contentType) { stateMachine_.SetContentType(contentType); } void SetContentFilename(const char* filename) { stateMachine_.SetContentFilename(filename); } void SetCookie(const std::string& cookie, const std::string& value) { stateMachine_.SetCookie(cookie, value); } void AddHeader(const std::string& key, const std::string& value) { stateMachine_.AddHeader(key, value); } void Answer(const void* buffer, size_t length); void Answer(const std::string& str); void AnswerEmpty(); void SendMethodNotAllowed(const std::string& allowed); void Redirect(const std::string& path); void SendUnauthorized(const std::string& realm); void StartMultipart(const std::string& subType, const std::string& contentType) { stateMachine_.StartMultipart(subType, contentType); } void SendMultipartItem(const void* item, size_t size, const std::map& headers) { stateMachine_.SendMultipartItem(item, size, headers); } void CloseMultipart() { stateMachine_.CloseMultipart(); } bool IsWritingMultipart() const { return stateMachine_.GetState() == StateMachine::State_WritingMultipart; } void Answer(IHttpStreamAnswer& stream); }; } Orthanc-1.0.0/Core/HttpServer/HttpStreamTranscoder.cpp0000644000000000000000000001450512634042176021070 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HttpStreamTranscoder.h" #include "../OrthancException.h" #include "../Compression/ZlibCompressor.h" #include // For memcpy() #include #include namespace Orthanc { void HttpStreamTranscoder::ReadSource(std::string& buffer) { if (source_.SetupHttpCompression(false, false) != HttpCompression_None) { throw OrthancException(ErrorCode_InternalError); } uint64_t size = source_.GetContentLength(); if (static_cast(static_cast(size)) != size) { throw OrthancException(ErrorCode_NotEnoughMemory); } buffer.resize(static_cast(size)); size_t offset = 0; while (source_.ReadNextChunk()) { size_t chunkSize = static_cast(source_.GetChunkSize()); memcpy(&buffer[offset], source_.GetChunkContent(), chunkSize); offset += chunkSize; } if (offset != size) { throw OrthancException(ErrorCode_InternalError); } } HttpCompression HttpStreamTranscoder::SetupZlibCompression(bool deflateAllowed) { uint64_t size = source_.GetContentLength(); if (size == 0) { return HttpCompression_None; } if (size < sizeof(uint64_t)) { throw OrthancException(ErrorCode_CorruptedFile); } if (deflateAllowed) { bytesToSkip_ = sizeof(uint64_t); return HttpCompression_Deflate; } else { // TODO Use stream-based zlib decoding to reduce memory usage std::string compressed; ReadSource(compressed); uncompressed_.reset(new BufferHttpSender); ZlibCompressor compressor; IBufferCompressor::Uncompress(uncompressed_->GetBuffer(), compressor, compressed); return HttpCompression_None; } } HttpCompression HttpStreamTranscoder::SetupHttpCompression(bool gzipAllowed, bool deflateAllowed) { if (ready_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } ready_ = true; switch (sourceCompression_) { case CompressionType_None: return HttpCompression_None; case CompressionType_ZlibWithSize: return SetupZlibCompression(deflateAllowed); default: throw OrthancException(ErrorCode_NotImplemented); } } uint64_t HttpStreamTranscoder::GetContentLength() { if (!ready_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (uncompressed_.get() != NULL) { return uncompressed_->GetContentLength(); } else { uint64_t length = source_.GetContentLength(); if (length < bytesToSkip_) { throw OrthancException(ErrorCode_InternalError); } return length - bytesToSkip_; } } bool HttpStreamTranscoder::ReadNextChunk() { if (!ready_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (uncompressed_.get() != NULL) { return uncompressed_->ReadNextChunk(); } assert(skipped_ <= bytesToSkip_); if (skipped_ == bytesToSkip_) { // We have already skipped the first bytes of the stream currentChunkOffset_ = 0; return source_.ReadNextChunk(); } // This condition can only be true on the first call to "ReadNextChunk()" for (;;) { assert(skipped_ < bytesToSkip_); bool ok = source_.ReadNextChunk(); if (!ok) { throw OrthancException(ErrorCode_CorruptedFile); } size_t remaining = static_cast(bytesToSkip_ - skipped_); size_t s = source_.GetChunkSize(); if (s < remaining) { skipped_ += s; } else if (s == remaining) { // We have skipped enough bytes, but we must read a new chunk currentChunkOffset_ = 0; skipped_ = bytesToSkip_; return source_.ReadNextChunk(); } else { // We have skipped enough bytes, and we have enough data in the current chunk assert(s > remaining); currentChunkOffset_ = remaining; skipped_ = bytesToSkip_; return true; } } } const char* HttpStreamTranscoder::GetChunkContent() { if (!ready_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (uncompressed_.get() != NULL) { return uncompressed_->GetChunkContent(); } else { return source_.GetChunkContent() + currentChunkOffset_; } } size_t HttpStreamTranscoder::GetChunkSize() { if (!ready_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } if (uncompressed_.get() != NULL) { return uncompressed_->GetChunkSize(); } else { return static_cast(source_.GetChunkSize() - currentChunkOffset_); } } } Orthanc-1.0.0/Core/HttpServer/HttpStreamTranscoder.h0000644000000000000000000000562012634042176020533 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "BufferHttpSender.h" #include // For std::auto_ptr namespace Orthanc { class HttpStreamTranscoder : public IHttpStreamAnswer { private: IHttpStreamAnswer& source_; CompressionType sourceCompression_; uint64_t bytesToSkip_; uint64_t skipped_; uint64_t currentChunkOffset_; bool ready_; std::auto_ptr uncompressed_; void ReadSource(std::string& buffer); HttpCompression SetupZlibCompression(bool deflateAllowed); public: HttpStreamTranscoder(IHttpStreamAnswer& source, CompressionType compression) : source_(source), sourceCompression_(compression), bytesToSkip_(0), skipped_(0), ready_(false) { } // This is the first method to be called virtual HttpCompression SetupHttpCompression(bool gzipAllowed, bool deflateAllowed); virtual bool HasContentFilename(std::string& filename) { return source_.HasContentFilename(filename); } virtual std::string GetContentType() { return source_.GetContentType(); } virtual uint64_t GetContentLength(); virtual bool ReadNextChunk(); virtual const char* GetChunkContent(); virtual size_t GetChunkSize(); }; } Orthanc-1.0.0/Core/HttpServer/HttpToolbox.cpp0000644000000000000000000002177112634042176017241 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "HttpToolbox.h" #include #include #include #include "HttpOutput.h" #include "StringHttpOutput.h" static const char* LOCALHOST = "localhost"; namespace Orthanc { static void SplitGETNameValue(IHttpHandler::GetArguments& result, const char* start, const char* end) { std::string name, value; const char* equal = strchr(start, '='); if (equal == NULL || equal >= end) { name = std::string(start, end - start); //value = ""; } else { name = std::string(start, equal - start); value = std::string(equal + 1, end); } Toolbox::UrlDecode(name); Toolbox::UrlDecode(value); result.push_back(std::make_pair(name, value)); } void HttpToolbox::ParseGetArguments(IHttpHandler::GetArguments& result, const char* query) { const char* pos = query; while (pos != NULL) { const char* ampersand = strchr(pos, '&'); if (ampersand) { SplitGETNameValue(result, pos, ampersand); pos = ampersand + 1; } else { // No more ampersand, this is the last argument SplitGETNameValue(result, pos, pos + strlen(pos)); pos = NULL; } } } void HttpToolbox::ParseGetQuery(UriComponents& uri, IHttpHandler::GetArguments& getArguments, const char* query) { const char *questionMark = ::strchr(query, '?'); if (questionMark == NULL) { // No question mark in the string Toolbox::SplitUriComponents(uri, query); getArguments.clear(); } else { Toolbox::SplitUriComponents(uri, std::string(query, questionMark)); HttpToolbox::ParseGetArguments(getArguments, questionMark + 1); } } std::string HttpToolbox::GetArgument(const IHttpHandler::Arguments& getArguments, const std::string& name, const std::string& defaultValue) { IHttpHandler::Arguments::const_iterator it = getArguments.find(name); if (it == getArguments.end()) { return defaultValue; } else { return it->second; } } std::string HttpToolbox::GetArgument(const IHttpHandler::GetArguments& getArguments, const std::string& name, const std::string& defaultValue) { for (size_t i = 0; i < getArguments.size(); i++) { if (getArguments[i].first == name) { return getArguments[i].second; } } return defaultValue; } void HttpToolbox::ParseCookies(IHttpHandler::Arguments& result, const IHttpHandler::Arguments& httpHeaders) { result.clear(); IHttpHandler::Arguments::const_iterator it = httpHeaders.find("cookie"); if (it != httpHeaders.end()) { const std::string& cookies = it->second; size_t pos = 0; while (pos != std::string::npos) { size_t nextSemicolon = cookies.find(";", pos); std::string cookie; if (nextSemicolon == std::string::npos) { cookie = cookies.substr(pos); pos = std::string::npos; } else { cookie = cookies.substr(pos, nextSemicolon - pos); pos = nextSemicolon + 1; } size_t equal = cookie.find("="); if (equal != std::string::npos) { std::string name = Toolbox::StripSpaces(cookie.substr(0, equal)); std::string value = Toolbox::StripSpaces(cookie.substr(equal + 1)); result[name] = value; } } } } void HttpToolbox::CompileGetArguments(IHttpHandler::Arguments& compiled, const IHttpHandler::GetArguments& source) { compiled.clear(); for (size_t i = 0; i < source.size(); i++) { compiled[source[i].first] = source[i].second; } } bool HttpToolbox::SimpleGet(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const IHttpHandler::Arguments& httpHeaders) { UriComponents curi; IHttpHandler::GetArguments getArguments; ParseGetQuery(curi, getArguments, uri.c_str()); StringHttpOutput stream; HttpOutput http(stream, false /* no keep alive */); if (handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Get, curi, httpHeaders, getArguments, NULL /* no body for GET */, 0)) { stream.GetOutput(result); return true; } else { return false; } } bool HttpToolbox::SimpleGet(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri) { IHttpHandler::Arguments headers; // No HTTP header return SimpleGet(result, handler, origin, uri, headers); } static bool SimplePostOrPut(std::string& result, IHttpHandler& handler, RequestOrigin origin, HttpMethod method, const std::string& uri, const char* bodyData, size_t bodySize) { IHttpHandler::Arguments headers; // No HTTP header IHttpHandler::GetArguments getArguments; // No GET argument for POST/PUT UriComponents curi; Toolbox::SplitUriComponents(curi, uri); StringHttpOutput stream; HttpOutput http(stream, false /* no keep alive */); if (handler.Handle(http, origin, LOCALHOST, "", method, curi, headers, getArguments, bodyData, bodySize)) { stream.GetOutput(result); return true; } else { return false; } } bool HttpToolbox::SimplePost(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const char* bodyData, size_t bodySize) { return SimplePostOrPut(result, handler, origin, HttpMethod_Post, uri, bodyData, bodySize); } bool HttpToolbox::SimplePut(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const char* bodyData, size_t bodySize) { return SimplePostOrPut(result, handler, origin, HttpMethod_Put, uri, bodyData, bodySize); } bool HttpToolbox::SimpleDelete(IHttpHandler& handler, RequestOrigin origin, const std::string& uri) { UriComponents curi; Toolbox::SplitUriComponents(curi, uri); IHttpHandler::Arguments headers; // No HTTP header IHttpHandler::GetArguments getArguments; // No GET argument for DELETE StringHttpOutput stream; HttpOutput http(stream, false /* no keep alive */); return handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Delete, curi, headers, getArguments, NULL /* no body for DELETE */, 0); } } Orthanc-1.0.0/Core/HttpServer/HttpToolbox.h0000644000000000000000000000747012634042176016706 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IHttpHandler.h" namespace Orthanc { class HttpToolbox { public: static void ParseGetArguments(IHttpHandler::GetArguments& result, const char* query); static void ParseGetQuery(UriComponents& uri, IHttpHandler::GetArguments& getArguments, const char* query); static std::string GetArgument(const IHttpHandler::Arguments& getArguments, const std::string& name, const std::string& defaultValue); static std::string GetArgument(const IHttpHandler::GetArguments& getArguments, const std::string& name, const std::string& defaultValue); static void ParseCookies(IHttpHandler::Arguments& result, const IHttpHandler::Arguments& httpHeaders); static void CompileGetArguments(IHttpHandler::Arguments& compiled, const IHttpHandler::GetArguments& source); static bool SimpleGet(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri); static bool SimpleGet(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const IHttpHandler::Arguments& httpHeaders); static bool SimplePost(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const char* bodyData, size_t bodySize); static bool SimplePut(std::string& result, IHttpHandler& handler, RequestOrigin origin, const std::string& uri, const char* bodyData, size_t bodySize); static bool SimpleDelete(IHttpHandler& handler, RequestOrigin origin, const std::string& uri); }; } Orthanc-1.0.0/Core/HttpServer/IHttpHandler.h0000644000000000000000000000465612634042176016751 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Enumerations.h" #include "HttpOutput.h" #include #include #include #include namespace Orthanc { class IHttpHandler : public boost::noncopyable { public: typedef std::map Arguments; typedef std::vector< std::pair > GetArguments; virtual ~IHttpHandler() { } virtual bool Handle(HttpOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& getArguments, const char* bodyData, size_t bodySize) = 0; }; } Orthanc-1.0.0/Core/HttpServer/IHttpOutputStream.h0000644000000000000000000000365012634042176020041 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Enumerations.h" #include #include namespace Orthanc { class IHttpOutputStream : public boost::noncopyable { public: virtual ~IHttpOutputStream() { } virtual void OnHttpStatusReceived(HttpStatus status) = 0; virtual void Send(bool isHeader, const void* buffer, size_t length) = 0; }; } Orthanc-1.0.0/Core/HttpServer/IHttpStreamAnswer.h0000644000000000000000000000441012634042176017773 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Enumerations.h" #include #include #include namespace Orthanc { class IHttpStreamAnswer : public boost::noncopyable { public: virtual ~IHttpStreamAnswer() { } // This is the first method to be called virtual HttpCompression SetupHttpCompression(bool gzipAllowed, bool deflateAllowed) = 0; virtual bool HasContentFilename(std::string& filename) = 0; virtual std::string GetContentType() = 0; virtual uint64_t GetContentLength() = 0; virtual bool ReadNextChunk() = 0; virtual const char* GetChunkContent() = 0; virtual size_t GetChunkSize() = 0; }; } Orthanc-1.0.0/Core/HttpServer/MongooseServer.cpp0000644000000000000000000006015512634042176017727 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * In addition, as a special exception, the copyright holders of this * program give permission to link the code of its release with the * OpenSSL project's "OpenSSL" library (or with modified versions of it * that use the same license as the "OpenSSL" library), and distribute * the linked executables. You must obey the GNU General Public License * in all respects for all of the code used other than "OpenSSL". If you * modify file(s) with this exception, you may extend this exception to * your version of the file(s), but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source files * in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ // http://en.highscore.de/cpp/boost/stringhandling.html #include "../PrecompiledHeaders.h" #include "MongooseServer.h" #include "../Logging.h" #include "../ChunkedBuffer.h" #include "HttpToolbox.h" #include "mongoose.h" #include #include #include #include #include #include #include #include #if ORTHANC_SSL_ENABLED == 1 #include #endif #define ORTHANC_REALM "Orthanc Secure Area" static const long LOCALHOST = (127ll << 24) + 1ll; namespace Orthanc { static const char multipart[] = "multipart/form-data; boundary="; static unsigned int multipartLength = sizeof(multipart) / sizeof(char) - 1; namespace { // Anonymous namespace to avoid clashes between compilation modules class MongooseOutputStream : public IHttpOutputStream { private: struct mg_connection* connection_; public: MongooseOutputStream(struct mg_connection* connection) : connection_(connection) { } virtual void Send(bool isHeader, const void* buffer, size_t length) { if (length > 0) { int status = mg_write(connection_, buffer, length); if (status != static_cast(length)) { // status == 0 when the connection has been closed, -1 on error throw OrthancException(ErrorCode_NetworkProtocol); } } } virtual void OnHttpStatusReceived(HttpStatus status) { // Ignore this } }; enum PostDataStatus { PostDataStatus_Success, PostDataStatus_NoLength, PostDataStatus_Pending, PostDataStatus_Failure }; } // TODO Move this to external file class ChunkedFile : public ChunkedBuffer { private: std::string filename_; public: ChunkedFile(const std::string& filename) : filename_(filename) { } const std::string& GetFilename() const { return filename_; } }; class ChunkStore { private: typedef std::list Content; Content content_; unsigned int numPlaces_; boost::mutex mutex_; std::set discardedFiles_; void Clear() { for (Content::iterator it = content_.begin(); it != content_.end(); ++it) { delete *it; } } Content::iterator Find(const std::string& filename) { for (Content::iterator it = content_.begin(); it != content_.end(); ++it) { if ((*it)->GetFilename() == filename) { return it; } } return content_.end(); } void Remove(const std::string& filename) { Content::iterator it = Find(filename); if (it != content_.end()) { delete *it; content_.erase(it); } } public: ChunkStore() { numPlaces_ = 10; } ~ChunkStore() { Clear(); } PostDataStatus Store(std::string& completed, const char* chunkData, size_t chunkSize, const std::string& filename, size_t filesize) { boost::mutex::scoped_lock lock(mutex_); std::set::iterator wasDiscarded = discardedFiles_.find(filename); if (wasDiscarded != discardedFiles_.end()) { discardedFiles_.erase(wasDiscarded); return PostDataStatus_Failure; } ChunkedFile* f; Content::iterator it = Find(filename); if (it == content_.end()) { f = new ChunkedFile(filename); // Make some room if (content_.size() >= numPlaces_) { discardedFiles_.insert(content_.front()->GetFilename()); delete content_.front(); content_.pop_front(); } content_.push_back(f); } else { f = *it; } f->AddChunk(chunkData, chunkSize); if (f->GetNumBytes() > filesize) { Remove(filename); } else if (f->GetNumBytes() == filesize) { f->Flatten(completed); Remove(filename); return PostDataStatus_Success; } return PostDataStatus_Pending; } /*void Print() { boost::mutex::scoped_lock lock(mutex_); printf("ChunkStore status:\n"); for (Content::const_iterator i = content_.begin(); i != content_.end(); i++) { printf(" [%s]: %d\n", (*i)->GetFilename().c_str(), (*i)->GetNumBytes()); } printf("-----\n"); }*/ }; struct MongooseServer::PImpl { struct mg_context *context_; ChunkStore chunkStore_; }; ChunkStore& MongooseServer::GetChunkStore() { return pimpl_->chunkStore_; } static PostDataStatus ReadBody(std::string& postData, struct mg_connection *connection, const IHttpHandler::Arguments& headers) { IHttpHandler::Arguments::const_iterator cs = headers.find("content-length"); if (cs == headers.end()) { return PostDataStatus_NoLength; } int length; try { length = boost::lexical_cast(cs->second); } catch (boost::bad_lexical_cast) { return PostDataStatus_NoLength; } if (length < 0) { length = 0; } postData.resize(length); size_t pos = 0; while (length > 0) { int r = mg_read(connection, &postData[pos], length); if (r <= 0) { return PostDataStatus_Failure; } assert(r <= length); length -= r; pos += r; } return PostDataStatus_Success; } static PostDataStatus ParseMultipartPost(std::string &completedFile, struct mg_connection *connection, const IHttpHandler::Arguments& headers, const std::string& contentType, ChunkStore& chunkStore) { std::string boundary = "--" + contentType.substr(multipartLength); std::string postData; PostDataStatus status = ReadBody(postData, connection, headers); if (status != PostDataStatus_Success) { return status; } /*for (IHttpHandler::Arguments::const_iterator i = headers.begin(); i != headers.end(); i++) { std::cout << "Header [" << i->first << "] = " << i->second << "\n"; } printf("CHUNK\n");*/ typedef IHttpHandler::Arguments::const_iterator ArgumentIterator; ArgumentIterator requestedWith = headers.find("x-requested-with"); ArgumentIterator fileName = headers.find("x-file-name"); ArgumentIterator fileSizeStr = headers.find("x-file-size"); if (requestedWith != headers.end() && requestedWith->second != "XMLHttpRequest") { return PostDataStatus_Failure; } size_t fileSize = 0; if (fileSizeStr != headers.end()) { try { fileSize = boost::lexical_cast(fileSizeStr->second); } catch (boost::bad_lexical_cast) { return PostDataStatus_Failure; } } typedef boost::find_iterator FindIterator; typedef boost::iterator_range Range; //chunkStore.Print(); try { FindIterator last; for (FindIterator it = make_find_iterator(postData, boost::first_finder(boundary)); it!=FindIterator(); ++it) { if (last != FindIterator()) { Range part(&last->back(), &it->front()); Range content = boost::find_first(part, "\r\n\r\n"); if (/*content != Range()*/!content.empty()) { Range c(&content.back() + 1, &it->front() - 2); size_t chunkSize = c.size(); if (chunkSize > 0) { const char* chunkData = &c.front(); if (fileName == headers.end()) { // This file is stored in a single chunk completedFile.resize(chunkSize); if (chunkSize > 0) { memcpy(&completedFile[0], chunkData, chunkSize); } return PostDataStatus_Success; } else { return chunkStore.Store(completedFile, chunkData, chunkSize, fileName->second, fileSize); } } } } last = it; } } catch (std::length_error) { return PostDataStatus_Failure; } return PostDataStatus_Pending; } static bool IsAccessGranted(const MongooseServer& that, const IHttpHandler::Arguments& headers) { bool granted = false; IHttpHandler::Arguments::const_iterator auth = headers.find("authorization"); if (auth != headers.end()) { std::string s = auth->second; if (s.size() > 6 && s.substr(0, 6) == "Basic ") { std::string b64 = s.substr(6); granted = that.IsValidBasicHttpAuthentication(b64); } } return granted; } static std::string GetAuthenticatedUsername(const IHttpHandler::Arguments& headers) { IHttpHandler::Arguments::const_iterator auth = headers.find("authorization"); if (auth == headers.end()) { return ""; } std::string s = auth->second; if (s.size() <= 6 || s.substr(0, 6) != "Basic ") { return ""; } std::string b64 = s.substr(6); std::string decoded; Toolbox::DecodeBase64(decoded, b64); size_t semicolons = decoded.find(':'); if (semicolons == std::string::npos) { // Bad-formatted request return ""; } else { return decoded.substr(0, semicolons); } } static bool ExtractMethod(HttpMethod& method, const struct mg_request_info *request, const IHttpHandler::Arguments& headers, const IHttpHandler::GetArguments& argumentsGET) { std::string overriden; // Check whether some PUT/DELETE faking is done // 1. Faking with Google's approach IHttpHandler::Arguments::const_iterator methodOverride = headers.find("x-http-method-override"); if (methodOverride != headers.end()) { overriden = methodOverride->second; } else if (!strcmp(request->request_method, "GET")) { // 2. Faking with Ruby on Rail's approach // GET /my/resource?_method=delete <=> DELETE /my/resource for (size_t i = 0; i < argumentsGET.size(); i++) { if (argumentsGET[i].first == "_method") { overriden = argumentsGET[i].second; break; } } } if (overriden.size() > 0) { // A faking has been done within this request Toolbox::ToUpperCase(overriden); LOG(INFO) << "HTTP method faking has been detected for " << overriden; if (overriden == "PUT") { method = HttpMethod_Put; return true; } else if (overriden == "DELETE") { method = HttpMethod_Delete; return true; } else { return false; } } // No PUT/DELETE faking was present if (!strcmp(request->request_method, "GET")) { method = HttpMethod_Get; } else if (!strcmp(request->request_method, "POST")) { method = HttpMethod_Post; } else if (!strcmp(request->request_method, "DELETE")) { method = HttpMethod_Delete; } else if (!strcmp(request->request_method, "PUT")) { method = HttpMethod_Put; } else { return false; } return true; } static void ConfigureHttpCompression(HttpOutput& output, const IHttpHandler::Arguments& headers) { // Look if the client wishes HTTP compression // https://en.wikipedia.org/wiki/HTTP_compression IHttpHandler::Arguments::const_iterator it = headers.find("accept-encoding"); if (it != headers.end()) { std::vector encodings; Toolbox::TokenizeString(encodings, it->second, ','); for (size_t i = 0; i < encodings.size(); i++) { std::string s = Toolbox::StripSpaces(encodings[i]); if (s == "deflate") { output.SetDeflateAllowed(true); } else if (s == "gzip") { output.SetGzipAllowed(true); } } } } static void InternalCallback(struct mg_connection *connection, const struct mg_request_info *request) { MongooseServer* that = reinterpret_cast(request->user_data); MongooseOutputStream stream(connection); HttpOutput output(stream, that->IsKeepAliveEnabled()); // Check remote calls if (!that->IsRemoteAccessAllowed() && request->remote_ip != LOCALHOST) { output.SendUnauthorized(ORTHANC_REALM); return; } // Extract the HTTP headers IHttpHandler::Arguments headers; for (int i = 0; i < request->num_headers; i++) { std::string name = request->http_headers[i].name; std::transform(name.begin(), name.end(), name.begin(), ::tolower); headers.insert(std::make_pair(name, request->http_headers[i].value)); } if (that->IsHttpCompressionEnabled()) { ConfigureHttpCompression(output, headers); } // Extract the GET arguments IHttpHandler::GetArguments argumentsGET; if (!strcmp(request->request_method, "GET")) { HttpToolbox::ParseGetArguments(argumentsGET, request->query_string); } // Compute the HTTP method, taking method faking into consideration HttpMethod method = HttpMethod_Get; if (!ExtractMethod(method, request, headers, argumentsGET)) { output.SendStatus(HttpStatus_400_BadRequest); return; } // Authenticate this connection if (that->IsAuthenticationEnabled() && !IsAccessGranted(*that, headers)) { output.SendUnauthorized(ORTHANC_REALM); return; } // Apply the filter, if it is installed char remoteIp[24]; sprintf(remoteIp, "%d.%d.%d.%d", reinterpret_cast(&request->remote_ip) [3], reinterpret_cast(&request->remote_ip) [2], reinterpret_cast(&request->remote_ip) [1], reinterpret_cast(&request->remote_ip) [0]); std::string username = GetAuthenticatedUsername(headers); const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter(); if (filter != NULL) { if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str())) { output.SendUnauthorized(ORTHANC_REALM); return; } } // Extract the body of the request for PUT and POST // TODO Avoid unneccessary memcopy of the body std::string body; if (method == HttpMethod_Post || method == HttpMethod_Put) { PostDataStatus status; IHttpHandler::Arguments::const_iterator ct = headers.find("content-type"); if (ct == headers.end()) { // No content-type specified. Assume no multi-part content occurs at this point. status = ReadBody(body, connection, headers); } else { std::string contentType = ct->second; if (contentType.size() >= multipartLength && !memcmp(contentType.c_str(), multipart, multipartLength)) { status = ParseMultipartPost(body, connection, headers, contentType, that->GetChunkStore()); } else { status = ReadBody(body, connection, headers); } } switch (status) { case PostDataStatus_NoLength: output.SendStatus(HttpStatus_411_LengthRequired); return; case PostDataStatus_Failure: output.SendStatus(HttpStatus_400_BadRequest); return; case PostDataStatus_Pending: output.AnswerEmpty(); return; default: break; } } // Decompose the URI into its components UriComponents uri; try { Toolbox::SplitUriComponents(uri, request->uri); } catch (OrthancException) { output.SendStatus(HttpStatus_400_BadRequest); return; } LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); try { bool found = false; try { if (that->HasHandler()) { found = that->GetHandler().Handle(output, RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers, argumentsGET, body.c_str(), body.size()); } } catch (boost::bad_lexical_cast&) { throw OrthancException(ErrorCode_BadParameterType); } catch (std::runtime_error&) { // Presumably an error while parsing the JSON body throw OrthancException(ErrorCode_BadRequest); } if (!found) { throw OrthancException(ErrorCode_UnknownResource); } } catch (OrthancException& e) { // Using this candidate handler results in an exception try { if (that->GetExceptionFormatter() == NULL) { LOG(ERROR) << "Exception in the HTTP handler: " << e.What(); output.SendStatus(e.GetHttpStatus()); } else { that->GetExceptionFormatter()->Format(output, e, method, request->uri); } } catch (OrthancException&) { // An exception here reflects the fact that the status code // was already set by the HTTP handler. } return; } } #if MONGOOSE_USE_CALLBACKS == 0 static void* Callback(enum mg_event event, struct mg_connection *connection, const struct mg_request_info *request) { if (event == MG_NEW_REQUEST) { InternalCallback(connection, request); // Mark as processed return (void*) ""; } else { return NULL; } } #elif MONGOOSE_USE_CALLBACKS == 1 static int Callback(struct mg_connection *connection) { struct mg_request_info *request = mg_get_request_info(connection); InternalCallback(connection, request); return 1; // Do not let Mongoose handle the request by itself } #else #error Please set MONGOOSE_USE_CALLBACKS #endif bool MongooseServer::IsRunning() const { return (pimpl_->context_ != NULL); } MongooseServer::MongooseServer() : pimpl_(new PImpl) { pimpl_->context_ = NULL; handler_ = NULL; remoteAllowed_ = false; authentication_ = false; ssl_ = false; port_ = 8000; filter_ = NULL; keepAlive_ = false; httpCompression_ = true; exceptionFormatter_ = NULL; #if ORTHANC_SSL_ENABLED == 1 // Check for the Heartbleed exploit // https://en.wikipedia.org/wiki/OpenSSL#Heartbleed_bug if (OPENSSL_VERSION_NUMBER < 0x1000107fL /* openssl-1.0.1g */ && OPENSSL_VERSION_NUMBER >= 0x1000100fL /* openssl-1.0.1 */) { LOG(WARNING) << "This version of OpenSSL is vulnerable to the Heartbleed exploit"; } #endif } MongooseServer::~MongooseServer() { Stop(); } void MongooseServer::SetPortNumber(uint16_t port) { Stop(); port_ = port; } void MongooseServer::Start() { if (!IsRunning()) { std::string port = boost::lexical_cast(port_); if (ssl_) { port += "s"; } const char *options[] = { // Set the TCP port for the HTTP server "listening_ports", port.c_str(), // Optimization reported by Chris Hafey // https://groups.google.com/d/msg/orthanc-users/CKueKX0pJ9E/_UCbl8T-VjIJ "enable_keep_alive", (keepAlive_ ? "yes" : "no"), // Set the SSL certificate, if any. This must be the last option. ssl_ ? "ssl_certificate" : NULL, certificate_.c_str(), NULL }; #if MONGOOSE_USE_CALLBACKS == 0 pimpl_->context_ = mg_start(&Callback, this, options); #elif MONGOOSE_USE_CALLBACKS == 1 struct mg_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.begin_request = Callback; pimpl_->context_ = mg_start(&callbacks, this, options); #else #error Please set MONGOOSE_USE_CALLBACKS #endif if (!pimpl_->context_) { throw OrthancException(ErrorCode_HttpPortInUse); } } } void MongooseServer::Stop() { if (IsRunning()) { mg_stop(pimpl_->context_); pimpl_->context_ = NULL; } } void MongooseServer::ClearUsers() { Stop(); registeredUsers_.clear(); } void MongooseServer::RegisterUser(const char* username, const char* password) { Stop(); std::string tag = std::string(username) + ":" + std::string(password); std::string encoded; Toolbox::EncodeBase64(encoded, tag); registeredUsers_.insert(encoded); } void MongooseServer::SetSslEnabled(bool enabled) { Stop(); #if ORTHANC_SSL_ENABLED == 0 if (enabled) { throw OrthancException(ErrorCode_SslDisabled); } else { ssl_ = false; } #else ssl_ = enabled; #endif } void MongooseServer::SetKeepAliveEnabled(bool enabled) { Stop(); keepAlive_ = enabled; LOG(WARNING) << "HTTP keep alive is " << (enabled ? "enabled" : "disabled"); } void MongooseServer::SetAuthenticationEnabled(bool enabled) { Stop(); authentication_ = enabled; } void MongooseServer::SetSslCertificate(const char* path) { Stop(); certificate_ = path; } void MongooseServer::SetRemoteAccessAllowed(bool allowed) { Stop(); remoteAllowed_ = allowed; } void MongooseServer::SetHttpCompressionEnabled(bool enabled) { Stop(); httpCompression_ = enabled; LOG(WARNING) << "HTTP compression is " << (enabled ? "enabled" : "disabled"); } void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter) { Stop(); filter_ = &filter; } void MongooseServer::SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter) { Stop(); exceptionFormatter_ = &formatter; } bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const { return registeredUsers_.find(basic) != registeredUsers_.end(); } void MongooseServer::Register(IHttpHandler& handler) { Stop(); handler_ = &handler; } IHttpHandler& MongooseServer::GetHandler() const { if (handler_ == NULL) { throw OrthancException(ErrorCode_InternalError); } return *handler_; } } Orthanc-1.0.0/Core/HttpServer/MongooseServer.h0000644000000000000000000001112712634042176017367 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IHttpHandler.h" #include "../OrthancException.h" #include #include #include #include #include namespace Orthanc { class ChunkStore; class IIncomingHttpRequestFilter { public: virtual ~IIncomingHttpRequestFilter() { } virtual bool IsAllowed(HttpMethod method, const char* uri, const char* ip, const char* username) const = 0; }; class IHttpExceptionFormatter { public: virtual ~IHttpExceptionFormatter() { } virtual void Format(HttpOutput& output, const OrthancException& exception, HttpMethod method, const char* uri) = 0; }; class MongooseServer { private: // http://stackoverflow.com/questions/311166/stdauto-ptr-or-boostshared-ptr-for-pimpl-idiom struct PImpl; boost::shared_ptr pimpl_; IHttpHandler *handler_; typedef std::set RegisteredUsers; RegisteredUsers registeredUsers_; bool remoteAllowed_; bool authentication_; bool ssl_; std::string certificate_; uint16_t port_; IIncomingHttpRequestFilter* filter_; bool keepAlive_; bool httpCompression_; IHttpExceptionFormatter* exceptionFormatter_; bool IsRunning() const; public: MongooseServer(); ~MongooseServer(); void SetPortNumber(uint16_t port); uint16_t GetPortNumber() const { return port_; } void Start(); void Stop(); void ClearUsers(); void RegisterUser(const char* username, const char* password); bool IsAuthenticationEnabled() const { return authentication_; } void SetAuthenticationEnabled(bool enabled); bool IsSslEnabled() const { return ssl_; } void SetSslEnabled(bool enabled); bool IsKeepAliveEnabled() const { return keepAlive_; } void SetKeepAliveEnabled(bool enabled); const std::string& GetSslCertificate() const { return certificate_; } void SetSslCertificate(const char* path); bool IsRemoteAccessAllowed() const { return remoteAllowed_; } void SetRemoteAccessAllowed(bool allowed); bool IsHttpCompressionEnabled() const { return httpCompression_;; } void SetHttpCompressionEnabled(bool enabled); const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const { return filter_; } void SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter); ChunkStore& GetChunkStore(); bool IsValidBasicHttpAuthentication(const std::string& basic) const; void Register(IHttpHandler& handler); bool HasHandler() const { return handler_ != NULL; } IHttpHandler& GetHandler() const; void SetHttpExceptionFormatter(IHttpExceptionFormatter& formatter); IHttpExceptionFormatter* GetExceptionFormatter() { return exceptionFormatter_; } }; } Orthanc-1.0.0/Core/HttpServer/StringHttpOutput.cpp0000644000000000000000000000402612634042176020274 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "StringHttpOutput.h" #include "../OrthancException.h" namespace Orthanc { void StringHttpOutput::OnHttpStatusReceived(HttpStatus status) { if (status != HttpStatus_200_Ok) { throw OrthancException(ErrorCode_BadRequest); } } void StringHttpOutput::Send(bool isHeader, const void* buffer, size_t length) { if (!isHeader) { buffer_.AddChunk(reinterpret_cast(buffer), length); } } } Orthanc-1.0.0/Core/HttpServer/StringHttpOutput.h0000644000000000000000000000373012634042176017742 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IHttpOutputStream.h" #include "../ChunkedBuffer.h" namespace Orthanc { class StringHttpOutput : public IHttpOutputStream { private: ChunkedBuffer buffer_; public: virtual void OnHttpStatusReceived(HttpStatus status); virtual void Send(bool isHeader, const void* buffer, size_t length); void GetOutput(std::string& output) { buffer_.Flatten(output); } }; } Orthanc-1.0.0/Core/ICommand.h0000644000000000000000000000351712634042176013777 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IDynamicObject.h" namespace Orthanc { /** * This class is the base class for the "Command" design pattern. * http://en.wikipedia.org/wiki/Command_pattern **/ class ICommand : public IDynamicObject { public: virtual bool Execute() = 0; }; } Orthanc-1.0.0/Core/IDynamicObject.h0000644000000000000000000000373112634042176015132 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 namespace Orthanc { /** * This class should be the ancestor to any class whose type is * determined at the runtime, and that can be dynamically allocated. * Being a child of IDynamicObject only implies the existence of a * virtual destructor. **/ class IDynamicObject : public boost::noncopyable { public: virtual ~IDynamicObject() { } }; } Orthanc-1.0.0/Core/Images/Font.cpp0000644000000000000000000002142712634042176014756 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "Font.h" #include "../Toolbox.h" #include "../OrthancException.h" #include #include #include namespace Orthanc { Font::~Font() { for (Characters::iterator it = characters_.begin(); it != characters_.end(); ++it) { delete it->second; } } void Font::LoadFromMemory(const std::string& font) { Json::Value v; Json::Reader reader; if (!reader.parse(font, v) || v.type() != Json::objectValue || !v.isMember("Name") || !v.isMember("Size") || !v.isMember("Characters") || v["Name"].type() != Json::stringValue || v["Size"].type() != Json::intValue || v["Characters"].type() != Json::objectValue) { throw OrthancException(ErrorCode_BadFont); } name_ = v["Name"].asString(); size_ = v["Size"].asUInt(); maxHeight_ = 0; Json::Value::Members characters = v["Characters"].getMemberNames(); for (size_t i = 0; i < characters.size(); i++) { const Json::Value& info = v["Characters"][characters[i]]; if (info.type() != Json::objectValue || !info.isMember("Advance") || !info.isMember("Bitmap") || !info.isMember("Height") || !info.isMember("Top") || !info.isMember("Width") || info["Advance"].type() != Json::intValue || info["Bitmap"].type() != Json::arrayValue || info["Height"].type() != Json::intValue || info["Top"].type() != Json::intValue || info["Width"].type() != Json::intValue) { throw OrthancException(ErrorCode_BadFont); } std::auto_ptr c(new Character); c->advance_ = info["Advance"].asUInt(); c->height_ = info["Height"].asUInt(); c->top_ = info["Top"].asUInt(); c->width_ = info["Width"].asUInt(); c->bitmap_.resize(info["Bitmap"].size()); if (c->height_ > maxHeight_) { maxHeight_ = c->height_; } for (Json::Value::ArrayIndex j = 0; j < info["Bitmap"].size(); j++) { if (info["Bitmap"][j].type() != Json::intValue) { throw OrthancException(ErrorCode_BadFont); } int value = info["Bitmap"][j].asInt(); if (value < 0 || value > 255) { throw OrthancException(ErrorCode_BadFont); } c->bitmap_[j] = static_cast(value); } int index = boost::lexical_cast(characters[i]); if (index < 0 || index > 255) { throw OrthancException(ErrorCode_BadFont); } characters_[static_cast(index)] = c.release(); } } void Font::LoadFromFile(const std::string& path) { std::string font; Toolbox::ReadFile(font, path); LoadFromMemory(font); } static unsigned int MyMin(unsigned int a, unsigned int b) { return a < b ? a : b; } void Font::DrawCharacter(ImageAccessor& target, const Character& character, int x, int y, const uint8_t color[4]) const { // Compute the bounds of the character if (x >= static_cast(target.GetWidth()) || y >= static_cast(target.GetHeight())) { // The character is out of the image return; } unsigned int left = x < 0 ? -x : 0; unsigned int top = y < 0 ? -y : 0; unsigned int width = MyMin(character.width_, target.GetWidth() - x); unsigned int height = MyMin(character.height_, target.GetHeight() - y); unsigned int bpp = target.GetBytesPerPixel(); // Blit the font bitmap OVER the target image // https://en.wikipedia.org/wiki/Alpha_compositing for (unsigned int cy = top; cy < height; cy++) { uint8_t* p = reinterpret_cast(target.GetRow(y + cy)) + (x + left) * bpp; unsigned int pos = cy * character.width_ + left; switch (target.GetFormat()) { case PixelFormat_Grayscale8: { assert(bpp == 1); for (unsigned int cx = left; cx < width; cx++, pos++, p++) { uint16_t alpha = character.bitmap_[pos]; uint16_t value = alpha * static_cast(color[0]) + (255 - alpha) * static_cast(*p); *p = static_cast(value >> 8); } break; } case PixelFormat_RGB24: { assert(bpp == 3); for (unsigned int cx = left; cx < width; cx++, pos++, p += 3) { uint16_t alpha = character.bitmap_[pos]; for (uint8_t i = 0; i < 3; i++) { uint16_t value = alpha * static_cast(color[i]) + (255 - alpha) * static_cast(p[i]); p[i] = static_cast(value >> 8); } } break; } case PixelFormat_RGBA32: { assert(bpp == 4); for (unsigned int cx = left; cx < width; cx++, pos++, p += 4) { float alpha = static_cast(character.bitmap_[pos]) / 255.0f; float beta = (1.0f - alpha) * static_cast(p[3]) / 255.0f; float denom = 1.0f / (alpha + beta); for (uint8_t i = 0; i < 3; i++) { p[i] = static_cast((alpha * static_cast(color[i]) + beta * static_cast(p[i])) * denom); } p[3] = static_cast(255.0f * (alpha + beta)); } break; } default: throw OrthancException(ErrorCode_NotImplemented); } } } void Font::DrawInternal(ImageAccessor& target, const std::string& utf8, int x, int y, const uint8_t color[4]) const { if (target.GetFormat() != PixelFormat_Grayscale8 && target.GetFormat() != PixelFormat_RGB24 && target.GetFormat() != PixelFormat_RGBA32) { throw OrthancException(ErrorCode_NotImplemented); } int a = x; std::string s = Toolbox::ConvertFromUtf8(utf8, Encoding_Latin1); for (size_t i = 0; i < s.size(); i++) { if (s[i] == '\n') { // Go to the next line a = x; y += maxHeight_ + 1; } else { Characters::const_iterator c = characters_.find(s[i]); if (c != characters_.end()) { DrawCharacter(target, *c->second, a, y + static_cast(c->second->top_), color); a += c->second->advance_; } } } } void Font::Draw(ImageAccessor& target, const std::string& utf8, int x, int y, uint8_t grayscale) const { uint8_t color[4] = { grayscale, grayscale, grayscale, 255 }; DrawInternal(target, utf8, x, y, color); } void Font::Draw(ImageAccessor& target, const std::string& utf8, int x, int y, uint8_t r, uint8_t g, uint8_t b) const { uint8_t color[4] = { r, g, b, 255 }; DrawInternal(target, utf8, x, y, color); } } Orthanc-1.0.0/Core/Images/Font.h0000644000000000000000000000625612634042176014426 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include #include #include #include namespace Orthanc { class Font : public boost::noncopyable { private: struct Character { unsigned int width_; unsigned int height_; unsigned int top_; unsigned int advance_; std::vector bitmap_; }; typedef std::map Characters; std::string name_; unsigned int size_; Characters characters_; unsigned int maxHeight_; void DrawCharacter(ImageAccessor& target, const Character& character, int x, int y, const uint8_t color[4]) const; void DrawInternal(ImageAccessor& target, const std::string& utf8, int x, int y, const uint8_t color[4]) const; public: Font() : size_(0), maxHeight_(0) { } ~Font(); void LoadFromMemory(const std::string& font); void LoadFromFile(const std::string& path); const std::string& GetName() const { return name_; } unsigned int GetSize() const { return size_; } void Draw(ImageAccessor& target, const std::string& utf8, int x, int y, uint8_t grayscale) const; void Draw(ImageAccessor& target, const std::string& utf8, int x, int y, uint8_t r, uint8_t g, uint8_t b) const; }; } Orthanc-1.0.0/Core/Images/FontRegistry.cpp0000644000000000000000000000506412634042176016506 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "FontRegistry.h" #include "../OrthancException.h" #include namespace Orthanc { FontRegistry::~FontRegistry() { for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) { delete *it; } } void FontRegistry::AddFromMemory(const std::string& font) { std::auto_ptr f(new Font); f->LoadFromMemory(font); fonts_.push_back(f.release()); } void FontRegistry::AddFromFile(const std::string& path) { std::auto_ptr f(new Font); f->LoadFromFile(path); fonts_.push_back(f.release()); } void FontRegistry::AddFromResource(EmbeddedResources::FileResourceId resource) { std::string content; EmbeddedResources::GetFileResource(content, resource); AddFromMemory(content); } const Font& FontRegistry::GetFont(size_t i) const { if (i >= fonts_.size()) { throw OrthancException(ErrorCode_ParameterOutOfRange); } else { return *fonts_[i]; } } } Orthanc-1.0.0/Core/Images/FontRegistry.h0000644000000000000000000000412512634042176016150 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "Font.h" #include // Autogenerated file namespace Orthanc { class FontRegistry : public boost::noncopyable { private: typedef std::vector Fonts; Fonts fonts_; public: ~FontRegistry(); void AddFromMemory(const std::string& font); void AddFromFile(const std::string& path); void AddFromResource(EmbeddedResources::FileResourceId resource); size_t GetSize() const { return fonts_.size(); } const Font& GetFont(size_t i) const; }; } Orthanc-1.0.0/Core/Images/Image.h0000644000000000000000000000377712634042176014547 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include "ImageBuffer.h" namespace Orthanc { class Image : public ImageAccessor { private: ImageBuffer image_; public: Image(PixelFormat format, unsigned int width, unsigned int height) : image_(format, width, height) { ImageAccessor accessor = image_.GetAccessor(); AssignWritable(format, width, height, accessor.GetPitch(), accessor.GetBuffer()); } }; } Orthanc-1.0.0/Core/Images/ImageAccessor.cpp0000644000000000000000000001361112634042176016551 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "ImageAccessor.h" #include "../Logging.h" #include "../OrthancException.h" #include "../ChunkedBuffer.h" #include #include #include namespace Orthanc { template static void ToMatlabStringInternal(ChunkedBuffer& target, const ImageAccessor& source) { target.AddChunk("double([ "); for (unsigned int y = 0; y < source.GetHeight(); y++) { const PixelType* p = reinterpret_cast(source.GetConstRow(y)); std::string s; if (y > 0) { s = "; "; } s.reserve(source.GetWidth() * 8); for (unsigned int x = 0; x < source.GetWidth(); x++, p++) { s += boost::lexical_cast(static_cast(*p)) + " "; } target.AddChunk(s); } target.AddChunk("])"); } static void RGB24ToMatlabString(ChunkedBuffer& target, const ImageAccessor& source) { assert(source.GetFormat() == PixelFormat_RGB24); target.AddChunk("double(permute(reshape([ "); for (unsigned int y = 0; y < source.GetHeight(); y++) { const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); std::string s; s.reserve(source.GetWidth() * 3 * 8); for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++) { s += boost::lexical_cast(static_cast(*p)) + " "; } target.AddChunk(s); } target.AddChunk("], [ 3 " + boost::lexical_cast(source.GetHeight()) + " " + boost::lexical_cast(source.GetWidth()) + " ]), [ 3 2 1 ]))"); } void* ImageAccessor::GetBuffer() const { if (readOnly_) { #if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "Trying to write on a read-only image"; #endif throw OrthancException(ErrorCode_ReadOnly); } return buffer_; } const void* ImageAccessor::GetConstRow(unsigned int y) const { if (buffer_ != NULL) { return reinterpret_cast(buffer_) + y * pitch_; } else { return NULL; } } void* ImageAccessor::GetRow(unsigned int y) const { if (readOnly_) { #if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "Trying to write on a read-only image"; #endif throw OrthancException(ErrorCode_ReadOnly); } if (buffer_ != NULL) { return reinterpret_cast(buffer_) + y * pitch_; } else { return NULL; } } void ImageAccessor::AssignEmpty(PixelFormat format) { readOnly_ = false; format_ = format; width_ = 0; height_ = 0; pitch_ = 0; buffer_ = NULL; } void ImageAccessor::AssignReadOnly(PixelFormat format, unsigned int width, unsigned int height, unsigned int pitch, const void *buffer) { readOnly_ = true; format_ = format; width_ = width; height_ = height; pitch_ = pitch; buffer_ = const_cast(buffer); assert(GetBytesPerPixel() * width_ <= pitch_); } void ImageAccessor::AssignWritable(PixelFormat format, unsigned int width, unsigned int height, unsigned int pitch, void *buffer) { readOnly_ = false; format_ = format; width_ = width; height_ = height; pitch_ = pitch; buffer_ = buffer; assert(GetBytesPerPixel() * width_ <= pitch_); } void ImageAccessor::ToMatlabString(std::string& target) const { ChunkedBuffer buffer; switch (GetFormat()) { case PixelFormat_Grayscale8: ToMatlabStringInternal(buffer, *this); break; case PixelFormat_Grayscale16: ToMatlabStringInternal(buffer, *this); break; case PixelFormat_SignedGrayscale16: ToMatlabStringInternal(buffer, *this); break; case PixelFormat_RGB24: RGB24ToMatlabString(buffer, *this); break; default: throw OrthancException(ErrorCode_NotImplemented); } buffer.Flatten(target); } } Orthanc-1.0.0/Core/Images/ImageAccessor.h0000644000000000000000000000625312634042176016222 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Enumerations.h" #include namespace Orthanc { class ImageAccessor { private: bool readOnly_; PixelFormat format_; unsigned int width_; unsigned int height_; unsigned int pitch_; void *buffer_; public: ImageAccessor() { AssignEmpty(PixelFormat_Grayscale8); } virtual ~ImageAccessor() { } bool IsReadOnly() const { return readOnly_; } PixelFormat GetFormat() const { return format_; } unsigned int GetBytesPerPixel() const { return ::Orthanc::GetBytesPerPixel(format_); } unsigned int GetWidth() const { return width_; } unsigned int GetHeight() const { return height_; } unsigned int GetPitch() const { return pitch_; } unsigned int GetSize() const { return GetHeight() * GetPitch(); } const void* GetConstBuffer() const { return buffer_; } void* GetBuffer() const; const void* GetConstRow(unsigned int y) const; void* GetRow(unsigned int y) const; void AssignEmpty(PixelFormat format); void AssignReadOnly(PixelFormat format, unsigned int width, unsigned int height, unsigned int pitch, const void *buffer); void AssignWritable(PixelFormat format, unsigned int width, unsigned int height, unsigned int pitch, void *buffer); void ToMatlabString(std::string& target) const; }; } Orthanc-1.0.0/Core/Images/ImageBuffer.cpp0000644000000000000000000001057012634042176016221 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "ImageBuffer.h" #include "../OrthancException.h" #include #include namespace Orthanc { void ImageBuffer::Allocate() { if (changed_) { Deallocate(); /* if (forceMinimalPitch_) { TODO: Align pitch and memory buffer to optimal size for SIMD. } */ pitch_ = GetBytesPerPixel() * width_; size_t size = pitch_ * height_; if (size == 0) { buffer_ = NULL; } else { buffer_ = malloc(size); if (buffer_ == NULL) { throw OrthancException(ErrorCode_NotEnoughMemory); } } changed_ = false; } } void ImageBuffer::Deallocate() { if (buffer_ != NULL) { free(buffer_); buffer_ = NULL; changed_ = true; } } ImageBuffer::ImageBuffer(PixelFormat format, unsigned int width, unsigned int height) { Initialize(); SetWidth(width); SetHeight(height); SetFormat(format); } void ImageBuffer::Initialize() { changed_ = false; forceMinimalPitch_ = true; format_ = PixelFormat_Grayscale8; width_ = 0; height_ = 0; pitch_ = 0; buffer_ = NULL; } void ImageBuffer::SetFormat(PixelFormat format) { if (format != format_) { changed_ = true; format_ = format; } } void ImageBuffer::SetWidth(unsigned int width) { if (width != width_) { changed_ = true; width_ = width; } } void ImageBuffer::SetHeight(unsigned int height) { if (height != height_) { changed_ = true; height_ = height; } } ImageAccessor ImageBuffer::GetAccessor() { Allocate(); ImageAccessor accessor; accessor.AssignWritable(format_, width_, height_, pitch_, buffer_); return accessor; } ImageAccessor ImageBuffer::GetConstAccessor() { Allocate(); ImageAccessor accessor; accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_); return accessor; } void ImageBuffer::SetMinimalPitchForced(bool force) { if (force != forceMinimalPitch_) { changed_ = true; forceMinimalPitch_ = force; } } void ImageBuffer::AcquireOwnership(ImageBuffer& other) { // Remove the content of the current image Deallocate(); // Force the allocation of the other image (if not already // allocated) other.Allocate(); // Transfer the content of the other image changed_ = false; forceMinimalPitch_ = other.forceMinimalPitch_; format_ = other.format_; width_ = other.width_; height_ = other.height_; pitch_ = other.pitch_; buffer_ = other.buffer_; // Force the reinitialization of the other image other.Initialize(); } } Orthanc-1.0.0/Core/Images/ImageBuffer.h0000644000000000000000000000562112634042176015667 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include #include #include namespace Orthanc { class ImageBuffer : public boost::noncopyable { private: bool changed_; bool forceMinimalPitch_; // Currently unused PixelFormat format_; unsigned int width_; unsigned int height_; unsigned int pitch_; void *buffer_; void Initialize(); void Allocate(); void Deallocate(); public: ImageBuffer(PixelFormat format, unsigned int width, unsigned int height); ImageBuffer() { Initialize(); } ~ImageBuffer() { Deallocate(); } PixelFormat GetFormat() const { return format_; } void SetFormat(PixelFormat format); unsigned int GetWidth() const { return width_; } void SetWidth(unsigned int width); unsigned int GetHeight() const { return height_; } void SetHeight(unsigned int height); unsigned int GetBytesPerPixel() const { return ::Orthanc::GetBytesPerPixel(format_); } ImageAccessor GetAccessor(); ImageAccessor GetConstAccessor(); bool IsMinimalPitchForced() const { return forceMinimalPitch_; } void SetMinimalPitchForced(bool force); void AcquireOwnership(ImageBuffer& other); }; } Orthanc-1.0.0/Core/Images/ImageProcessing.cpp0000644000000000000000000003773612634042176017141 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "ImageProcessing.h" #include "../OrthancException.h" #include #include #include #include #include namespace Orthanc { template static void ConvertInternal(ImageAccessor& target, const ImageAccessor& source) { const TargetType minValue = std::numeric_limits::min(); const TargetType maxValue = std::numeric_limits::max(); for (unsigned int y = 0; y < source.GetHeight(); y++) { TargetType* t = reinterpret_cast(target.GetRow(y)); const SourceType* s = reinterpret_cast(source.GetConstRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++) { if (static_cast(*s) < static_cast(minValue)) { *t = minValue; } else if (static_cast(*s) > static_cast(maxValue)) { *t = maxValue; } else { *t = static_cast(*s); } } } } template static void ConvertColorToGrayscale(ImageAccessor& target, const ImageAccessor& source) { assert(source.GetFormat() == PixelFormat_RGB24); const TargetType minValue = std::numeric_limits::min(); const TargetType maxValue = std::numeric_limits::max(); for (unsigned int y = 0; y < source.GetHeight(); y++) { TargetType* t = reinterpret_cast(target.GetRow(y)); const uint8_t* s = reinterpret_cast(source.GetConstRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3) { // Y = 0.2126 R + 0.7152 G + 0.0722 B int32_t v = (2126 * static_cast(s[0]) + 7152 * static_cast(s[1]) + 0722 * static_cast(s[2])) / 1000; if (static_cast(v) < static_cast(minValue)) { *t = minValue; } else if (static_cast(v) > static_cast(maxValue)) { *t = maxValue; } else { *t = static_cast(v); } } } } template static void SetInternal(ImageAccessor& image, int64_t constant) { for (unsigned int y = 0; y < image.GetHeight(); y++) { PixelType* p = reinterpret_cast(image.GetRow(y)); for (unsigned int x = 0; x < image.GetWidth(); x++, p++) { *p = static_cast(constant); } } } template static void GetMinMaxValueInternal(PixelType& minValue, PixelType& maxValue, const ImageAccessor& source) { // Deal with the special case of empty image if (source.GetWidth() == 0 || source.GetHeight() == 0) { minValue = 0; maxValue = 0; return; } minValue = std::numeric_limits::max(); maxValue = std::numeric_limits::min(); for (unsigned int y = 0; y < source.GetHeight(); y++) { const PixelType* p = reinterpret_cast(source.GetConstRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++, p++) { if (*p < minValue) { minValue = *p; } if (*p > maxValue) { maxValue = *p; } } } } template static void AddConstantInternal(ImageAccessor& image, int64_t constant) { if (constant == 0) { return; } const int64_t minValue = std::numeric_limits::min(); const int64_t maxValue = std::numeric_limits::max(); for (unsigned int y = 0; y < image.GetHeight(); y++) { PixelType* p = reinterpret_cast(image.GetRow(y)); for (unsigned int x = 0; x < image.GetWidth(); x++, p++) { int64_t v = static_cast(*p) + constant; if (v > maxValue) { *p = std::numeric_limits::max(); } else if (v < minValue) { *p = std::numeric_limits::min(); } else { *p = static_cast(v); } } } } template void MultiplyConstantInternal(ImageAccessor& image, float factor) { if (std::abs(factor - 1.0f) <= std::numeric_limits::epsilon()) { return; } const int64_t minValue = std::numeric_limits::min(); const int64_t maxValue = std::numeric_limits::max(); for (unsigned int y = 0; y < image.GetHeight(); y++) { PixelType* p = reinterpret_cast(image.GetRow(y)); for (unsigned int x = 0; x < image.GetWidth(); x++, p++) { int64_t v = boost::math::llround(static_cast(*p) * factor); if (v > maxValue) { *p = std::numeric_limits::max(); } else if (v < minValue) { *p = std::numeric_limits::min(); } else { *p = static_cast(v); } } } } template void ShiftScaleInternal(ImageAccessor& image, float offset, float scaling) { const float minValue = static_cast(std::numeric_limits::min()); const float maxValue = static_cast(std::numeric_limits::max()); for (unsigned int y = 0; y < image.GetHeight(); y++) { PixelType* p = reinterpret_cast(image.GetRow(y)); for (unsigned int x = 0; x < image.GetWidth(); x++, p++) { float v = (static_cast(*p) + offset) * scaling; if (v > maxValue) { *p = std::numeric_limits::max(); } else if (v < minValue) { *p = std::numeric_limits::min(); } else { *p = static_cast(boost::math::iround(v)); } } } } void ImageProcessing::Copy(ImageAccessor& target, const ImageAccessor& source) { if (target.GetWidth() != source.GetWidth() || target.GetHeight() != source.GetHeight()) { throw OrthancException(ErrorCode_IncompatibleImageSize); } if (target.GetFormat() != source.GetFormat()) { throw OrthancException(ErrorCode_IncompatibleImageFormat); } unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth(); assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize); for (unsigned int y = 0; y < source.GetHeight(); y++) { memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); } } void ImageProcessing::Convert(ImageAccessor& target, const ImageAccessor& source) { if (target.GetWidth() != source.GetWidth() || target.GetHeight() != source.GetHeight()) { throw OrthancException(ErrorCode_IncompatibleImageSize); } if (source.GetFormat() == target.GetFormat()) { Copy(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale16 && source.GetFormat() == PixelFormat_Grayscale8) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_SignedGrayscale16 && source.GetFormat() == PixelFormat_Grayscale8) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale8 && source.GetFormat() == PixelFormat_Grayscale16) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_SignedGrayscale16 && source.GetFormat() == PixelFormat_Grayscale16) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale8 && source.GetFormat() == PixelFormat_SignedGrayscale16) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale16 && source.GetFormat() == PixelFormat_SignedGrayscale16) { ConvertInternal(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale8 && source.GetFormat() == PixelFormat_RGB24) { ConvertColorToGrayscale(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale16 && source.GetFormat() == PixelFormat_RGB24) { ConvertColorToGrayscale(target, source); return; } if (target.GetFormat() == PixelFormat_SignedGrayscale16 && source.GetFormat() == PixelFormat_RGB24) { ConvertColorToGrayscale(target, source); return; } if (target.GetFormat() == PixelFormat_Grayscale8 && source.GetFormat() == PixelFormat_RGBA32) { for (unsigned int y = 0; y < source.GetHeight(); y++) { const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); uint8_t* q = reinterpret_cast(target.GetRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++, q++) { *q = static_cast((2126 * static_cast(p[0]) + 7152 * static_cast(p[1]) + 0722 * static_cast(p[2])) / 10000); p += 4; } } return; } if (target.GetFormat() == PixelFormat_RGB24 && source.GetFormat() == PixelFormat_RGBA32) { for (unsigned int y = 0; y < source.GetHeight(); y++) { const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); uint8_t* q = reinterpret_cast(target.GetRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++) { q[0] = p[0]; q[1] = p[1]; q[2] = p[2]; p += 4; q += 3; } } return; } if (target.GetFormat() == PixelFormat_RGBA32 && source.GetFormat() == PixelFormat_RGB24) { for (unsigned int y = 0; y < source.GetHeight(); y++) { const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); uint8_t* q = reinterpret_cast(target.GetRow(y)); for (unsigned int x = 0; x < source.GetWidth(); x++) { q[0] = p[0]; q[1] = p[1]; q[2] = p[2]; q[3] = 255; // Set the alpha channel to full opacity p += 3; q += 4; } } return; } throw OrthancException(ErrorCode_NotImplemented); } void ImageProcessing::Set(ImageAccessor& image, int64_t value) { switch (image.GetFormat()) { case PixelFormat_Grayscale8: SetInternal(image, value); return; case PixelFormat_Grayscale16: SetInternal(image, value); return; case PixelFormat_SignedGrayscale16: SetInternal(image, value); return; default: throw OrthancException(ErrorCode_NotImplemented); } } void ImageProcessing::ShiftRight(ImageAccessor& image, unsigned int shift) { if (image.GetWidth() == 0 || image.GetHeight() == 0 || shift == 0) { // Nothing to do return; } throw OrthancException(ErrorCode_NotImplemented); } void ImageProcessing::GetMinMaxValue(int64_t& minValue, int64_t& maxValue, const ImageAccessor& image) { switch (image.GetFormat()) { case PixelFormat_Grayscale8: { uint8_t a, b; GetMinMaxValueInternal(a, b, image); minValue = a; maxValue = b; break; } case PixelFormat_Grayscale16: { uint16_t a, b; GetMinMaxValueInternal(a, b, image); minValue = a; maxValue = b; break; } case PixelFormat_SignedGrayscale16: { int16_t a, b; GetMinMaxValueInternal(a, b, image); minValue = a; maxValue = b; break; } default: throw OrthancException(ErrorCode_NotImplemented); } } void ImageProcessing::AddConstant(ImageAccessor& image, int64_t value) { switch (image.GetFormat()) { case PixelFormat_Grayscale8: AddConstantInternal(image, value); return; case PixelFormat_Grayscale16: AddConstantInternal(image, value); return; case PixelFormat_SignedGrayscale16: AddConstantInternal(image, value); return; default: throw OrthancException(ErrorCode_NotImplemented); } } void ImageProcessing::MultiplyConstant(ImageAccessor& image, float factor) { switch (image.GetFormat()) { case PixelFormat_Grayscale8: MultiplyConstantInternal(image, factor); return; case PixelFormat_Grayscale16: MultiplyConstantInternal(image, factor); return; case PixelFormat_SignedGrayscale16: MultiplyConstantInternal(image, factor); return; default: throw OrthancException(ErrorCode_NotImplemented); } } void ImageProcessing::ShiftScale(ImageAccessor& image, float offset, float scaling) { switch (image.GetFormat()) { case PixelFormat_Grayscale8: ShiftScaleInternal(image, offset, scaling); return; case PixelFormat_Grayscale16: ShiftScaleInternal(image, offset, scaling); return; case PixelFormat_SignedGrayscale16: ShiftScaleInternal(image, offset, scaling); return; default: throw OrthancException(ErrorCode_NotImplemented); } } } Orthanc-1.0.0/Core/Images/ImageProcessing.h0000644000000000000000000000502412634042176016567 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include namespace Orthanc { class ImageProcessing { public: static void Copy(ImageAccessor& target, const ImageAccessor& source); static void Convert(ImageAccessor& target, const ImageAccessor& source); static void Set(ImageAccessor& image, int64_t value); static void ShiftRight(ImageAccessor& target, unsigned int shift); static void GetMinMaxValue(int64_t& minValue, int64_t& maxValue, const ImageAccessor& image); static void AddConstant(ImageAccessor& image, int64_t value); static void MultiplyConstant(ImageAccessor& image, float factor); static void ShiftScale(ImageAccessor& image, float offset, float scaling); }; } Orthanc-1.0.0/Core/Images/JpegErrorManager.cpp0000644000000000000000000000470012634042176017235 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "JpegErrorManager.h" namespace Orthanc { namespace Internals { void JpegErrorManager::OutputMessage(j_common_ptr cinfo) { char message[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) (cinfo, message); JpegErrorManager* that = reinterpret_cast(cinfo->err); that->message = std::string(message); } void JpegErrorManager::ErrorExit(j_common_ptr cinfo) { (*cinfo->err->output_message) (cinfo); JpegErrorManager* that = reinterpret_cast(cinfo->err); longjmp(that->setjmp_buffer, 1); } JpegErrorManager::JpegErrorManager() { memset(&pub, 0, sizeof(struct jpeg_error_mgr)); memset(&setjmp_buffer, 0, sizeof(jmp_buf)); jpeg_std_error(&pub); pub.error_exit = ErrorExit; pub.output_message = OutputMessage; } } } Orthanc-1.0.0/Core/Images/JpegErrorManager.h0000644000000000000000000000443212634042176016704 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include #include #include namespace Orthanc { namespace Internals { class JpegErrorManager { private: struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ std::string message; static void OutputMessage(j_common_ptr cinfo); static void ErrorExit(j_common_ptr cinfo); public: JpegErrorManager(); struct jpeg_error_mgr* GetPublic() { return &pub; } jmp_buf& GetJumpBuffer() { return setjmp_buffer; } const std::string& GetMessage() const { return message; } }; } } Orthanc-1.0.0/Core/Images/JpegReader.cpp0000644000000000000000000001235512634042176016060 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "JpegReader.h" #include "JpegErrorManager.h" #include "../OrthancException.h" #include "../Logging.h" namespace Orthanc { static void Uncompress(struct jpeg_decompress_struct& cinfo, std::string& content, ImageAccessor& accessor) { jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); PixelFormat format; if (cinfo.output_components == 1 && cinfo.out_color_space == JCS_GRAYSCALE) { format = PixelFormat_Grayscale8; } else if (cinfo.output_components == 3 && cinfo.out_color_space == JCS_RGB) { format = PixelFormat_RGB24; } else { throw OrthancException(ErrorCode_NotImplemented); } unsigned int pitch = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, pitch, 1); try { content.resize(pitch * cinfo.output_height); } catch (...) { throw OrthancException(ErrorCode_NotEnoughMemory); } accessor.AssignWritable(format, cinfo.output_width, cinfo.output_height, pitch, content.empty() ? NULL : &content[0]); uint8_t* target = reinterpret_cast(&content[0]); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(target, buffer[0], pitch); target += pitch; } // Everything went fine, "setjmp()" didn't get called jpeg_finish_decompress(&cinfo); } void JpegReader::ReadFromFile(const char* filename) { FILE* fp = fopen(filename, "rb"); if (!fp) { throw OrthancException(ErrorCode_InexistentFile); } struct jpeg_decompress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); Internals::JpegErrorManager jerr; cinfo.err = jerr.GetPublic(); if (setjmp(jerr.GetJumpBuffer())) { jpeg_destroy_decompress(&cinfo); fclose(fp); LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); throw OrthancException(ErrorCode_InternalError); } // Below this line, we are under the scope of a "setjmp" jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, fp); try { Uncompress(cinfo, content_, *this); } catch (OrthancException&) { jpeg_destroy_decompress(&cinfo); fclose(fp); throw; } jpeg_destroy_decompress(&cinfo); fclose(fp); } void JpegReader::ReadFromMemory(const void* buffer, size_t size) { struct jpeg_decompress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct)); Internals::JpegErrorManager jerr; cinfo.err = jerr.GetPublic(); if (setjmp(jerr.GetJumpBuffer())) { jpeg_destroy_decompress(&cinfo); LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); throw OrthancException(ErrorCode_InternalError); } // Below this line, we are under the scope of a "setjmp" jpeg_create_decompress(&cinfo); jpeg_mem_src(&cinfo, const_cast(reinterpret_cast(buffer)), size); try { Uncompress(cinfo, content_, *this); } catch (OrthancException&) { jpeg_destroy_decompress(&cinfo); throw; } jpeg_destroy_decompress(&cinfo); } void JpegReader::ReadFromMemory(const std::string& buffer) { if (buffer.empty()) { ReadFromMemory(NULL, 0); } else { ReadFromMemory(buffer.c_str(), buffer.size()); } } } Orthanc-1.0.0/Core/Images/JpegReader.h0000644000000000000000000000400112634042176015512 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include namespace Orthanc { class JpegReader : public ImageAccessor { private: std::string content_; public: void ReadFromFile(const char* filename); void ReadFromFile(const std::string& filename) { ReadFromFile(filename.c_str()); } void ReadFromMemory(const void* buffer, size_t size); void ReadFromMemory(const std::string& buffer); }; } Orthanc-1.0.0/Core/Images/JpegWriter.cpp0000644000000000000000000001405612634042176016132 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "JpegWriter.h" #include "../OrthancException.h" #include "../Logging.h" #include "JpegErrorManager.h" #include namespace Orthanc { static void GetLines(std::vector& lines, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { if (format != PixelFormat_Grayscale8 && format != PixelFormat_RGB24) { throw OrthancException(ErrorCode_ParameterOutOfRange); } lines.resize(height); uint8_t* base = const_cast(reinterpret_cast(buffer)); for (unsigned int y = 0; y < height; y++) { lines[y] = base + static_cast(y) * static_cast(pitch); } } static void Compress(struct jpeg_compress_struct& cinfo, std::vector& lines, unsigned int width, unsigned int height, PixelFormat format, uint8_t quality) { cinfo.image_width = width; cinfo.image_height = height; switch (format) { case PixelFormat_Grayscale8: cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; break; case PixelFormat_RGB24: cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; break; default: throw OrthancException(ErrorCode_InternalError); } jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); jpeg_write_scanlines(&cinfo, &lines[0], height); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); } void JpegWriter::SetQuality(uint8_t quality) { if (quality <= 0 || quality > 100) { throw OrthancException(ErrorCode_ParameterOutOfRange); } quality_ = quality; } void JpegWriter::WriteToFile(const char* filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { FILE* fp = fopen(filename, "wb"); if (fp == NULL) { throw OrthancException(ErrorCode_FullStorage); } std::vector lines; GetLines(lines, height, pitch, format, buffer); struct jpeg_compress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); Internals::JpegErrorManager jerr; cinfo.err = jerr.GetPublic(); if (setjmp(jerr.GetJumpBuffer())) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_compress(&cinfo); fclose(fp); LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); throw OrthancException(ErrorCode_InternalError); } // Do not allocate data on the stack below this line! jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, fp); Compress(cinfo, lines, width, height, format, quality_); // Everything went fine, "setjmp()" didn't get called fclose(fp); } void JpegWriter::WriteToMemory(std::string& jpeg, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { std::vector lines; GetLines(lines, height, pitch, format, buffer); struct jpeg_compress_struct cinfo; memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); Internals::JpegErrorManager jerr; unsigned char* data = NULL; unsigned long size; if (setjmp(jerr.GetJumpBuffer())) { jpeg_destroy_compress(&cinfo); if (data != NULL) { free(data); } LOG(ERROR) << "Error during JPEG encoding: " << jerr.GetMessage(); throw OrthancException(ErrorCode_InternalError); } // Do not allocate data on the stack below this line! jpeg_create_compress(&cinfo); cinfo.err = jerr.GetPublic(); jpeg_mem_dest(&cinfo, &data, &size); Compress(cinfo, lines, width, height, format, quality_); // Everything went fine, "setjmp()" didn't get called jpeg.assign(reinterpret_cast(data), size); free(data); } } Orthanc-1.0.0/Core/Images/JpegWriter.h0000644000000000000000000000561512634042176015600 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include #include namespace Orthanc { class JpegWriter { private: uint8_t quality_; public: JpegWriter() : quality_(90) { } void SetQuality(uint8_t quality); uint8_t GetQuality() const { return quality_; } void WriteToFile(const char* filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); void WriteToMemory(std::string& jpeg, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); void WriteToFile(const char* filename, const ImageAccessor& accessor) { WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(), accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); } void WriteToMemory(std::string& jpeg, const ImageAccessor& accessor) { WriteToMemory(jpeg, accessor.GetWidth(), accessor.GetHeight(), accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); } }; } Orthanc-1.0.0/Core/Images/PngReader.cpp0000644000000000000000000001613312634042176015715 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "PngReader.h" #include "../OrthancException.h" #include "../Toolbox.h" #include #include // For memcpy() namespace Orthanc { namespace { struct FileRabi { FILE* fp_; FileRabi(const char* filename) { fp_ = fopen(filename, "rb"); if (!fp_) { throw OrthancException(ErrorCode_InexistentFile); } } ~FileRabi() { if (fp_) fclose(fp_); } }; } struct PngReader::PngRabi { png_structp png_; png_infop info_; png_infop endInfo_; void Destruct() { if (png_) { png_destroy_read_struct(&png_, &info_, &endInfo_); png_ = NULL; info_ = NULL; endInfo_ = NULL; } } PngRabi() { png_ = NULL; info_ = NULL; endInfo_ = NULL; png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_) { throw OrthancException(ErrorCode_NotEnoughMemory); } info_ = png_create_info_struct(png_); if (!info_) { png_destroy_read_struct(&png_, NULL, NULL); throw OrthancException(ErrorCode_NotEnoughMemory); } endInfo_ = png_create_info_struct(png_); if (!info_) { png_destroy_read_struct(&png_, &info_, NULL); throw OrthancException(ErrorCode_NotEnoughMemory); } } ~PngRabi() { Destruct(); } static void MemoryCallback(png_structp png_ptr, png_bytep data, png_size_t size); }; void PngReader::CheckHeader(const void* header) { int is_png = !png_sig_cmp((png_bytep) header, 0, 8); if (!is_png) { throw OrthancException(ErrorCode_BadFileFormat); } } PngReader::PngReader() { } void PngReader::Read(PngRabi& rabi) { png_set_sig_bytes(rabi.png_, 8); png_read_info(rabi.png_, rabi.info_); png_uint_32 width, height; int bit_depth, color_type, interlace_type; int compression_type, filter_method; // get size and bit-depth of the PNG-image png_get_IHDR(rabi.png_, rabi.info_, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); PixelFormat format; unsigned int pitch; if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8) { format = PixelFormat_Grayscale8; pitch = width; } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) { format = PixelFormat_Grayscale16; pitch = 2 * width; if (Toolbox::DetectEndianness() == Endianness_Little) { png_set_swap(rabi.png_); } } else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8) { format = PixelFormat_RGB24; pitch = 3 * width; } else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8) { format = PixelFormat_RGBA32; pitch = 4 * width; } else { throw OrthancException(ErrorCode_NotImplemented); } data_.resize(height * pitch); if (height == 0 || width == 0) { // Empty image, we are done AssignEmpty(format); return; } png_read_update_info(rabi.png_, rabi.info_); std::vector rows(height); for (size_t i = 0; i < height; i++) { rows[i] = &data_[0] + i * pitch; } png_read_image(rabi.png_, &rows[0]); AssignWritable(format, width, height, pitch, &data_[0]); } void PngReader::ReadFromFile(const char* filename) { FileRabi f(filename); char header[8]; if (fread(header, 1, 8, f.fp_) != 8) { throw OrthancException(ErrorCode_BadFileFormat); } CheckHeader(header); PngRabi rabi; if (setjmp(png_jmpbuf(rabi.png_))) { rabi.Destruct(); throw OrthancException(ErrorCode_BadFileFormat); } png_init_io(rabi.png_, f.fp_); Read(rabi); } namespace { struct MemoryBuffer { const uint8_t* buffer_; size_t size_; size_t pos_; bool ok_; }; } void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) { MemoryBuffer* from = reinterpret_cast(png_get_io_ptr(png_ptr)); if (!from->ok_) { return; } if (from->pos_ + byteCountToRead > from->size_) { from->ok_ = false; return; } memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead); from->pos_ += byteCountToRead; } void PngReader::ReadFromMemory(const void* buffer, size_t size) { if (size < 8) { throw OrthancException(ErrorCode_BadFileFormat); } CheckHeader(buffer); PngRabi rabi; if (setjmp(png_jmpbuf(rabi.png_))) { rabi.Destruct(); throw OrthancException(ErrorCode_BadFileFormat); } MemoryBuffer tmp; tmp.buffer_ = reinterpret_cast(buffer) + 8; // We skip the header tmp.size_ = size - 8; tmp.pos_ = 0; tmp.ok_ = true; png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback); Read(rabi); if (!tmp.ok_) { throw OrthancException(ErrorCode_BadFileFormat); } } void PngReader::ReadFromMemory(const std::string& buffer) { if (buffer.size() != 0) { ReadFromMemory(&buffer[0], buffer.size()); } else { ReadFromMemory(NULL, 0); } } } Orthanc-1.0.0/Core/Images/PngReader.h0000644000000000000000000000431012634042176015354 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include "../Enumerations.h" #include #include #include namespace Orthanc { class PngReader : public ImageAccessor { private: struct PngRabi; std::vector data_; void CheckHeader(const void* header); void Read(PngRabi& rabi); public: PngReader(); void ReadFromFile(const char* filename); void ReadFromFile(const std::string& filename) { ReadFromFile(filename.c_str()); } void ReadFromMemory(const void* buffer, size_t size); void ReadFromMemory(const std::string& buffer); }; } Orthanc-1.0.0/Core/Images/PngWriter.cpp0000644000000000000000000001610112634042176015762 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "PngWriter.h" #include #include #include #include "../OrthancException.h" #include "../ChunkedBuffer.h" #include "../Toolbox.h" // http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4 // http://zarb.org/~gc/html/libpng.html /* void write_row_callback(png_ptr, png_uint_32 row, int pass) { }*/ /* bool isError_; // http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2 static void ErrorHandler(png_structp png, png_const_charp message) { printf("** [%s]\n", message); PngWriter* that = (PngWriter*) png_get_error_ptr(png); that->isError_ = true; printf("** %d\n", (int)that); //((PngWriter*) payload)->isError_ = true; } static void WarningHandler(png_structp png, png_const_charp message) { printf("++ %d\n", (int)message); }*/ namespace Orthanc { struct PngWriter::PImpl { png_structp png_; png_infop info_; // Filled by Prepare() std::vector rows_; int bitDepth_; int colorType_; }; PngWriter::PngWriter() : pimpl_(new PImpl) { pimpl_->png_ = NULL; pimpl_->info_ = NULL; pimpl_->png_ = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler); if (!pimpl_->png_) { throw OrthancException(ErrorCode_NotEnoughMemory); } pimpl_->info_ = png_create_info_struct(pimpl_->png_); if (!pimpl_->info_) { png_destroy_write_struct(&pimpl_->png_, NULL); throw OrthancException(ErrorCode_NotEnoughMemory); } } PngWriter::~PngWriter() { if (pimpl_->info_) { png_destroy_info_struct(pimpl_->png_, &pimpl_->info_); } if (pimpl_->png_) { png_destroy_write_struct(&pimpl_->png_, NULL); } } void PngWriter::Prepare(unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { pimpl_->rows_.resize(height); for (unsigned int y = 0; y < height; y++) { pimpl_->rows_[y] = const_cast(reinterpret_cast(buffer)) + y * pitch; } switch (format) { case PixelFormat_RGB24: pimpl_->bitDepth_ = 8; pimpl_->colorType_ = PNG_COLOR_TYPE_RGB; break; case PixelFormat_RGBA32: pimpl_->bitDepth_ = 8; pimpl_->colorType_ = PNG_COLOR_TYPE_RGBA; break; case PixelFormat_Grayscale8: pimpl_->bitDepth_ = 8; pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; break; case PixelFormat_Grayscale16: case PixelFormat_SignedGrayscale16: pimpl_->bitDepth_ = 16; pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; break; default: throw OrthancException(ErrorCode_NotImplemented); } } void PngWriter::Compress(unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format) { png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height, pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(pimpl_->png_, pimpl_->info_); if (height > 0) { switch (format) { case PixelFormat_Grayscale16: case PixelFormat_SignedGrayscale16: { int transforms = 0; if (Toolbox::DetectEndianness() == Endianness_Little) { transforms = PNG_TRANSFORM_SWAP_ENDIAN; } png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]); png_write_png(pimpl_->png_, pimpl_->info_, transforms, NULL); break; } default: png_write_image(pimpl_->png_, &pimpl_->rows_[0]); } } png_write_end(pimpl_->png_, NULL); } void PngWriter::WriteToFile(const char* filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { Prepare(width, height, pitch, format, buffer); FILE* fp = fopen(filename, "wb"); if (!fp) { throw OrthancException(ErrorCode_CannotWriteFile); } png_init_io(pimpl_->png_, fp); if (setjmp(png_jmpbuf(pimpl_->png_))) { // Error during writing PNG throw OrthancException(ErrorCode_CannotWriteFile); } Compress(width, height, pitch, format); fclose(fp); } static void MemoryCallback(png_structp png_ptr, png_bytep data, png_size_t size) { ChunkedBuffer* buffer = reinterpret_cast(png_get_io_ptr(png_ptr)); buffer->AddChunk(reinterpret_cast(data), size); } void PngWriter::WriteToMemory(std::string& png, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer) { ChunkedBuffer chunks; Prepare(width, height, pitch, format, buffer); if (setjmp(png_jmpbuf(pimpl_->png_))) { // Error during writing PNG throw OrthancException(ErrorCode_InternalError); } png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL); Compress(width, height, pitch, format); chunks.Flatten(png); } } Orthanc-1.0.0/Core/Images/PngWriter.h0000644000000000000000000000623212634042176015433 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ImageAccessor.h" #include #include namespace Orthanc { class PngWriter { private: struct PImpl; boost::shared_ptr pimpl_; void Compress(unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format); void Prepare(unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); public: PngWriter(); ~PngWriter(); void WriteToFile(const char* filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); void WriteToMemory(std::string& png, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); void WriteToFile(const char* filename, const ImageAccessor& accessor) { WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(), accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); } void WriteToMemory(std::string& png, const ImageAccessor& accessor) { WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(), accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer()); } }; } Orthanc-1.0.0/Core/Logging.cpp0000644000000000000000000002626112634042176014232 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "Logging.h" #if ORTHANC_ENABLE_LOGGING != 1 namespace Orthanc { namespace Logging { void Initialize() { } void Finalize() { } void EnableInfoLevel(bool enabled) { } void EnableTraceLevel(bool enabled) { } void SetTargetFolder(const std::string& path) { } } } #elif ORTHANC_ENABLE_GOOGLE_LOG == 1 /********************************************************* * Wrapper around Google Log *********************************************************/ namespace Orthanc { namespace Logging { void Initialize() { // Initialize Google's logging library. FLAGS_logtostderr = true; FLAGS_minloglevel = 1; // Do not print LOG(INFO) by default FLAGS_v = 0; // Do not print trace-level VLOG(1) by default google::InitGoogleLogging("Orthanc"); } void Finalize() { google::ShutdownGoogleLogging(); } void EnableInfoLevel(bool enabled) { FLAGS_minloglevel = (enabled ? 0 : 1); } void EnableTraceLevel(bool enabled) { if (enabled) { FLAGS_minloglevel = 0; FLAGS_v = 1; } else { FLAGS_v = 0; } } void SetTargetFolder(const std::string& path) { FLAGS_logtostderr = false; FLAGS_log_dir = path; } } } #else /********************************************************* * Use internal logger, not Google Log *********************************************************/ #include "OrthancException.h" #include "Enumerations.h" #include "Toolbox.h" #include #include #include #if BOOST_HAS_DATE_TIME == 1 # include #else # error Boost::date_time is required #endif namespace { struct LoggingState { bool infoEnabled_; bool traceEnabled_; std::ostream* error_; std::ostream* warning_; std::ostream* info_; std::auto_ptr file_; LoggingState() : infoEnabled_(false), traceEnabled_(false), error_(&std::cerr), warning_(&std::cerr), info_(&std::cerr) { } }; } static std::auto_ptr loggingState_; static boost::mutex loggingMutex_; namespace Orthanc { namespace Logging { static void GetLogPath(boost::filesystem::path& log, boost::filesystem::path& link, const std::string& suffix, const std::string& directory) { /** From Google Log documentation: Unless otherwise specified, logs will be written to the filename "...log.", followed by the date, time, and pid (you can't prevent the date, time, and pid from being in the filename). In this implementation : "hostname" and "username" are not used **/ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); boost::filesystem::path root(directory); boost::filesystem::path exe(Toolbox::GetPathToExecutable()); if (!boost::filesystem::exists(root) || !boost::filesystem::is_directory(root)) { throw OrthancException(ErrorCode_CannotWriteFile); } char date[64]; sprintf(date, "%04d%02d%02d-%02d%02d%02d.%d", static_cast(now.date().year()), now.date().month().as_number(), now.date().day().as_number(), now.time_of_day().hours(), now.time_of_day().minutes(), now.time_of_day().seconds(), Toolbox::GetProcessId()); std::string programName = exe.filename().replace_extension("").string(); log = (root / (programName + ".log" + suffix + "." + std::string(date))); link = (root / (programName + ".log" + suffix)); } static void PrepareLogFile(std::auto_ptr& file, const std::string& suffix, const std::string& directory) { boost::filesystem::path log, link; GetLogPath(log, link, suffix, directory); #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) boost::filesystem::remove(link); boost::filesystem::create_symlink(log.filename(), link); #endif file.reset(new std::ofstream(log.string().c_str())); } void Initialize() { boost::mutex::scoped_lock lock(loggingMutex_); loggingState_.reset(new LoggingState); } void Finalize() { boost::mutex::scoped_lock lock(loggingMutex_); loggingState_.reset(NULL); } void EnableInfoLevel(bool enabled) { boost::mutex::scoped_lock lock(loggingMutex_); assert(loggingState_.get() != NULL); loggingState_->infoEnabled_ = enabled; } void EnableTraceLevel(bool enabled) { boost::mutex::scoped_lock lock(loggingMutex_); assert(loggingState_.get() != NULL); loggingState_->traceEnabled_ = enabled; if (enabled) { // Also enable the "INFO" level when trace-level debugging is enabled loggingState_->infoEnabled_ = true; } } void SetTargetFolder(const std::string& path) { boost::mutex::scoped_lock lock(loggingMutex_); assert(loggingState_.get() != NULL); PrepareLogFile(loggingState_->file_, "" /* no suffix */, path); loggingState_->warning_ = loggingState_->file_.get(); loggingState_->error_ = loggingState_->file_.get(); loggingState_->info_ = loggingState_->file_.get(); } InternalLogger::InternalLogger(const char* level, const char* file, int line) : lock_(loggingMutex_), stream_(&null_) // By default, logging to "/dev/null" is simulated { if (loggingState_.get() == NULL) { fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); return; } LogLevel l = StringToLogLevel(level); if ((l == LogLevel_Info && !loggingState_->infoEnabled_) || (l == LogLevel_Trace && !loggingState_->traceEnabled_)) { // This logging level is disabled, directly exit and unlock // the mutex to speed-up things. The stream is set to "/dev/null" lock_.unlock(); return; } // Compute the header of the line, temporary release the lock as // this is a time-consuming operation lock_.unlock(); std::string header; { boost::filesystem::path path(file); boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_duration duration = now.time_of_day(); /** From Google Log documentation: "Log lines have this form: Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... where the fields are defined as follows: L A single character, representing the log level (eg 'I' for INFO) mm The month (zero padded; ie May is '05') dd The day (zero padded) hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds threadid The space-padded thread ID as returned by GetTID() (this matches the PID on Linux) file The file name line The line number msg The user-supplied message" In this implementation, "threadid" is not printed. **/ char date[32]; sprintf(date, "%c%02d%02d %02d:%02d:%02d.%06d ", level[0], now.date().month().as_number(), now.date().day().as_number(), duration.hours(), duration.minutes(), duration.seconds(), static_cast(duration.fractional_seconds())); header = std::string(date) + path.filename().string() + ":" + boost::lexical_cast(line) + "] "; } // The header is computed, we now re-lock the mutex to access // the stream objects. Pay attention that "loggingState_", // "infoEnabled_" or "traceEnabled_" might have changed while // the mutex was unlocked. lock_.lock(); if (loggingState_.get() == NULL) { fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); return; } switch (l) { case LogLevel_Error: stream_ = loggingState_->error_; break; case LogLevel_Warning: stream_ = loggingState_->warning_; break; case LogLevel_Info: if (loggingState_->infoEnabled_) { stream_ = loggingState_->info_; } break; case LogLevel_Trace: if (loggingState_->traceEnabled_) { stream_ = loggingState_->info_; } break; default: throw OrthancException(ErrorCode_InternalError); } if (stream_ == &null_) { // The logging is disabled for this level. The stream is the // "null_" member of this object, so we can release the global // mutex. lock_.unlock(); } (*stream_) << header; } InternalLogger::~InternalLogger() { if (stream_ != &null_) { #if defined(_WIN32) *stream_ << "\r\n"; #else *stream_ << "\n"; #endif stream_->flush(); } } } } #endif // ORTHANC_ENABLE_LOGGING Orthanc-1.0.0/Core/Logging.h0000644000000000000000000000624112634042176013673 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 namespace Orthanc { namespace Logging { void Initialize(); void Finalize(); void EnableInfoLevel(bool enabled); void EnableTraceLevel(bool enabled); void SetTargetFolder(const std::string& path); struct NullStream : public std::ostream { NullStream() : std::ios(0), std::ostream(0) { } std::ostream& operator<< (const std::string& message) { return *this; } }; } } #if ORTHANC_ENABLE_LOGGING != 1 # define LOG(level) ::Orthanc::Logging::NullStream() # define VLOG(level) ::Orthanc::Logging::NullStream() #else /* ORTHANC_ENABLE_LOGGING == 1 */ #if ORTHANC_ENABLE_GOOGLE_LOG == 1 # include // Including this fixes a problem in glog for recent releases of MinGW # include #else # include # define LOG(level) ::Orthanc::Logging::InternalLogger(#level, __FILE__, __LINE__) # define VLOG(level) ::Orthanc::Logging::InternalLogger("TRACE", __FILE__, __LINE__) #endif #if ORTHANC_ENABLE_GOOGLE_LOG != 1 namespace Orthanc { namespace Logging { class InternalLogger { private: boost::mutex::scoped_lock lock_; NullStream null_; std::ostream* stream_; public: InternalLogger(const char* level, const char* file, int line); ~InternalLogger(); std::ostream& operator<< (const std::string& message) { return (*stream_) << message; } }; } } #endif #endif // ORTHANC_ENABLE_LOGGING Orthanc-1.0.0/Core/Lua/LuaContext.cpp0000644000000000000000000003570712634042176015460 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "LuaContext.h" #include "../Logging.h" #include "../OrthancException.h" #include #include #include extern "C" { #include #include } namespace Orthanc { static bool OnlyContainsDigits(const std::string& s) { for (size_t i = 0; i < s.size(); i++) { if (!isdigit(s[i])) { return false; } } return true; } LuaContext& LuaContext::GetLuaContext(lua_State *state) { const void* value = GetGlobalVariable(state, "_LuaContext"); assert(value != NULL); return *const_cast(reinterpret_cast(value)); } int LuaContext::PrintToLog(lua_State *state) { LuaContext& that = GetLuaContext(state); // http://medek.wordpress.com/2009/02/03/wrapping-lua-errors-and-print-function/ int nArgs = lua_gettop(state); lua_getglobal(state, "tostring"); // Make sure you start at 1 *NOT* 0 for arrays in Lua. std::string result; for (int i = 1; i <= nArgs; i++) { const char *s; lua_pushvalue(state, -1); lua_pushvalue(state, i); lua_call(state, 1, 1); s = lua_tostring(state, -1); if (result.size() > 0) result.append(", "); if (s == NULL) result.append(""); else result.append(s); lua_pop(state, 1); } LOG(WARNING) << "Lua says: " << result; that.log_.append(result); that.log_.append("\n"); return 0; } int LuaContext::ParseJson(lua_State *state) { LuaContext& that = GetLuaContext(state); int nArgs = lua_gettop(state); if (nArgs != 1 || !lua_isstring(state, 1)) // Password { lua_pushnil(state); return 1; } const char* str = lua_tostring(state, 1); Json::Value value; Json::Reader reader; if (reader.parse(str, str + strlen(str), value)) { that.PushJson(value); } else { lua_pushnil(state); } return 1; } int LuaContext::DumpJson(lua_State *state) { LuaContext& that = GetLuaContext(state); int nArgs = lua_gettop(state); if ((nArgs != 1 && nArgs != 2) || (nArgs == 2 && !lua_isboolean(state, 2))) { lua_pushnil(state); return 1; } bool keepStrings = false; if (nArgs == 2) { keepStrings = lua_toboolean(state, 2) ? true : false; } Json::Value json; that.GetJson(json, 1, keepStrings); Json::FastWriter writer; std::string s = writer.write(json); lua_pushlstring(state, s.c_str(), s.size()); return 1; } int LuaContext::SetHttpCredentials(lua_State *state) { LuaContext& that = GetLuaContext(state); // Check the types of the arguments int nArgs = lua_gettop(state); if (nArgs != 2 || !lua_isstring(state, 1) || // Username !lua_isstring(state, 2)) // Password { LOG(ERROR) << "Lua: Bad parameters to SetHttpCredentials()"; } else { // Configure the HTTP client const char* username = lua_tostring(state, 1); const char* password = lua_tostring(state, 2); that.httpClient_.SetCredentials(username, password); } return 0; } bool LuaContext::AnswerHttpQuery(lua_State* state) { std::string str; try { httpClient_.Apply(str); } catch (OrthancException&) { return false; } // Return the result of the HTTP request lua_pushlstring(state, str.c_str(), str.size()); return true; } int LuaContext::CallHttpGet(lua_State *state) { LuaContext& that = GetLuaContext(state); // Check the types of the arguments int nArgs = lua_gettop(state); if (nArgs != 1 || !lua_isstring(state, 1)) // URL { LOG(ERROR) << "Lua: Bad parameters to HttpGet()"; lua_pushnil(state); return 1; } // Configure the HTTP client class const char* url = lua_tostring(state, 1); that.httpClient_.SetMethod(HttpMethod_Get); that.httpClient_.SetUrl(url); // Do the HTTP GET request if (!that.AnswerHttpQuery(state)) { LOG(ERROR) << "Lua: Error in HttpGet() for URL " << url; lua_pushnil(state); } return 1; } int LuaContext::CallHttpPostOrPut(lua_State *state, HttpMethod method) { LuaContext& that = GetLuaContext(state); // Check the types of the arguments int nArgs = lua_gettop(state); if ((nArgs != 1 && nArgs != 2) || !lua_isstring(state, 1) || // URL (nArgs >= 2 && !lua_isstring(state, 2))) // Body data { LOG(ERROR) << "Lua: Bad parameters to HttpPost() or HttpPut()"; lua_pushnil(state); return 1; } // Configure the HTTP client class const char* url = lua_tostring(state, 1); that.httpClient_.SetMethod(method); that.httpClient_.SetUrl(url); if (nArgs >= 2) { that.httpClient_.SetBody(lua_tostring(state, 2)); } else { that.httpClient_.GetBody().clear(); } // Do the HTTP POST/PUT request if (!that.AnswerHttpQuery(state)) { LOG(ERROR) << "Lua: Error in HttpPost() or HttpPut() for URL " << url; lua_pushnil(state); } return 1; } int LuaContext::CallHttpPost(lua_State *state) { return CallHttpPostOrPut(state, HttpMethod_Post); } int LuaContext::CallHttpPut(lua_State *state) { return CallHttpPostOrPut(state, HttpMethod_Put); } int LuaContext::CallHttpDelete(lua_State *state) { LuaContext& that = GetLuaContext(state); // Check the types of the arguments int nArgs = lua_gettop(state); if (nArgs != 1 || !lua_isstring(state, 1)) // URL { LOG(ERROR) << "Lua: Bad parameters to HttpDelete()"; lua_pushnil(state); return 1; } // Configure the HTTP client class const char* url = lua_tostring(state, 1); that.httpClient_.SetMethod(HttpMethod_Delete); that.httpClient_.SetUrl(url); // Do the HTTP DELETE request std::string s; if (!that.httpClient_.Apply(s)) { LOG(ERROR) << "Lua: Error in HttpDelete() for URL " << url; lua_pushnil(state); } else { lua_pushstring(state, "SUCCESS"); } return 1; } void LuaContext::PushJson(const Json::Value& value) { if (value.isString()) { const std::string s = value.asString(); lua_pushlstring(lua_, s.c_str(), s.size()); } else if (value.isDouble()) { lua_pushnumber(lua_, value.asDouble()); } else if (value.isInt()) { lua_pushinteger(lua_, value.asInt()); } else if (value.isUInt()) { lua_pushinteger(lua_, value.asUInt()); } else if (value.isBool()) { lua_pushboolean(lua_, value.asBool()); } else if (value.isNull()) { lua_pushnil(lua_); } else if (value.isArray()) { lua_newtable(lua_); // http://lua-users.org/wiki/SimpleLuaApiExample for (Json::Value::ArrayIndex i = 0; i < value.size(); i++) { // Push the table index (note the "+1" because of Lua conventions) lua_pushnumber(lua_, i + 1); // Push the value of the cell PushJson(value[i]); // Stores the pair in the table lua_rawset(lua_, -3); } } else if (value.isObject()) { lua_newtable(lua_); Json::Value::Members members = value.getMemberNames(); for (Json::Value::Members::const_iterator it = members.begin(); it != members.end(); ++it) { // Push the index of the cell lua_pushlstring(lua_, it->c_str(), it->size()); // Push the value of the cell PushJson(value[*it]); // Stores the pair in the table lua_rawset(lua_, -3); } } else { throw OrthancException(ErrorCode_JsonToLuaTable); } } void LuaContext::GetJson(Json::Value& result, int top, bool keepStrings) { if (lua_istable(lua_, top)) { Json::Value tmp = Json::objectValue; bool isArray = true; size_t size = 0; // Code adapted from: http://stackoverflow.com/a/6142700/881731 // Push another reference to the table on top of the stack (so we know // where it is, and this function can work for negative, positive and // pseudo indices lua_pushvalue(lua_, top); // stack now contains: -1 => table lua_pushnil(lua_); // stack now contains: -1 => nil; -2 => table while (lua_next(lua_, -2)) { // stack now contains: -1 => value; -2 => key; -3 => table // copy the key so that lua_tostring does not modify the original lua_pushvalue(lua_, -2); // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table std::string key(lua_tostring(lua_, -1)); Json::Value v; GetJson(v, -2, keepStrings); tmp[key] = v; size += 1; try { if (!OnlyContainsDigits(key) || boost::lexical_cast(key) != size) { isArray = false; } } catch (boost::bad_lexical_cast&) { isArray = false; } // pop value + copy of key, leaving original key lua_pop(lua_, 2); // stack now contains: -1 => key; -2 => table } // stack now contains: -1 => table (when lua_next returns 0 it pops the key // but does not push anything.) // Pop table lua_pop(lua_, 1); // Stack is now the same as it was on entry to this function if (isArray) { result = Json::arrayValue; for (size_t i = 0; i < size; i++) { result.append(tmp[boost::lexical_cast(i + 1)]); } } else { result = tmp; } } else if (lua_isnil(lua_, top)) { result = Json::nullValue; } else if (!keepStrings && lua_isboolean(lua_, top)) { result = lua_toboolean(lua_, top) ? true : false; } else if (!keepStrings && lua_isnumber(lua_, top)) { // Convert to "int" if truncation does not loose precision double value = static_cast(lua_tonumber(lua_, top)); int truncated = static_cast(value); if (std::abs(value - static_cast(truncated)) <= std::numeric_limits::epsilon()) { result = truncated; } else { result = value; } } else if (lua_isstring(lua_, top)) { // Caution: The "lua_isstring()" case must be the last, since // Lua can convert most types to strings by default. result = std::string(lua_tostring(lua_, top)); } else { LOG(WARNING) << "Unsupported Lua type when returning Json"; result = Json::nullValue; } } LuaContext::LuaContext() { lua_ = luaL_newstate(); if (!lua_) { throw OrthancException(ErrorCode_CannotCreateLua); } luaL_openlibs(lua_); lua_register(lua_, "print", PrintToLog); lua_register(lua_, "ParseJson", ParseJson); lua_register(lua_, "DumpJson", DumpJson); lua_register(lua_, "HttpGet", CallHttpGet); lua_register(lua_, "HttpPost", CallHttpPost); lua_register(lua_, "HttpPut", CallHttpPut); lua_register(lua_, "HttpDelete", CallHttpDelete); lua_register(lua_, "SetHttpCredentials", SetHttpCredentials); SetGlobalVariable("_LuaContext", this); } LuaContext::~LuaContext() { lua_close(lua_); } void LuaContext::ExecuteInternal(std::string* output, const std::string& command) { log_.clear(); int error = (luaL_loadbuffer(lua_, command.c_str(), command.size(), "line") || lua_pcall(lua_, 0, 0, 0)); if (error) { assert(lua_gettop(lua_) >= 1); std::string description(lua_tostring(lua_, -1)); lua_pop(lua_, 1); /* pop error message from the stack */ LOG(ERROR) << "Error while executing Lua script: " << description; throw OrthancException(ErrorCode_CannotExecuteLua); } if (output != NULL) { *output = log_; } } void LuaContext::Execute(EmbeddedResources::FileResourceId resource) { std::string command; EmbeddedResources::GetFileResource(command, resource); ExecuteInternal(NULL, command); } bool LuaContext::IsExistingFunction(const char* name) { lua_settop(lua_, 0); lua_getglobal(lua_, name); return lua_type(lua_, -1) == LUA_TFUNCTION; } void LuaContext::Execute(Json::Value& output, const std::string& command) { std::string s; ExecuteInternal(&s, command); Json::Reader reader; if (!reader.parse(s, output)) { throw OrthancException(ErrorCode_BadJson); } } void LuaContext::RegisterFunction(const char* name, lua_CFunction func) { lua_register(lua_, name, func); } void LuaContext::SetGlobalVariable(const char* name, void* value) { lua_pushlightuserdata(lua_, value); lua_setglobal(lua_, name); } const void* LuaContext::GetGlobalVariable(lua_State* state, const char* name) { lua_getglobal(state, name); assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA); const void* value = lua_topointer(state, -1); lua_pop(state, 1); return value; } } Orthanc-1.0.0/Core/Lua/LuaContext.h0000644000000000000000000000732112634042176015114 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../HttpClient.h" extern "C" { #include } #include #include namespace Orthanc { class LuaContext : public boost::noncopyable { private: friend class LuaFunctionCall; lua_State *lua_; std::string log_; HttpClient httpClient_; static int PrintToLog(lua_State *state); static int ParseJson(lua_State *state); static int DumpJson(lua_State *state); static int SetHttpCredentials(lua_State *state); static int CallHttpPostOrPut(lua_State *state, HttpMethod method); static int CallHttpGet(lua_State *state); static int CallHttpPost(lua_State *state); static int CallHttpPut(lua_State *state); static int CallHttpDelete(lua_State *state); bool AnswerHttpQuery(lua_State* state); void ExecuteInternal(std::string* output, const std::string& command); void GetJson(Json::Value& result, int top, bool keepStrings); public: LuaContext(); ~LuaContext(); void Execute(const std::string& command) { ExecuteInternal(NULL, command); } void Execute(std::string& output, const std::string& command) { ExecuteInternal(&output, command); } void Execute(Json::Value& output, const std::string& command); void Execute(EmbeddedResources::FileResourceId resource); bool IsExistingFunction(const char* name); void SetHttpCredentials(const char* username, const char* password) { httpClient_.SetCredentials(username, password); } void SetHttpProxy(const std::string& proxy) { httpClient_.SetProxy(proxy); } void RegisterFunction(const char* name, lua_CFunction func); void SetGlobalVariable(const char* name, void* value); static LuaContext& GetLuaContext(lua_State *state); static const void* GetGlobalVariable(lua_State* state, const char* name); void PushJson(const Json::Value& value); }; } Orthanc-1.0.0/Core/Lua/LuaFunctionCall.cpp0000644000000000000000000001047712634042176016412 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "LuaFunctionCall.h" #include "../OrthancException.h" #include "../Logging.h" #include #include #include namespace Orthanc { void LuaFunctionCall::CheckAlreadyExecuted() { if (isExecuted_) { throw OrthancException(ErrorCode_LuaAlreadyExecuted); } } LuaFunctionCall::LuaFunctionCall(LuaContext& context, const char* functionName) : context_(context), isExecuted_(false) { // Clear the stack to fulfill the invariant lua_settop(context_.lua_, 0); lua_getglobal(context_.lua_, functionName); } void LuaFunctionCall::PushString(const std::string& value) { CheckAlreadyExecuted(); lua_pushlstring(context_.lua_, value.c_str(), value.size()); } void LuaFunctionCall::PushBoolean(bool value) { CheckAlreadyExecuted(); lua_pushboolean(context_.lua_, value); } void LuaFunctionCall::PushInteger(int value) { CheckAlreadyExecuted(); lua_pushinteger(context_.lua_, value); } void LuaFunctionCall::PushDouble(double value) { CheckAlreadyExecuted(); lua_pushnumber(context_.lua_, value); } void LuaFunctionCall::PushJson(const Json::Value& value) { CheckAlreadyExecuted(); context_.PushJson(value); } void LuaFunctionCall::ExecuteInternal(int numOutputs) { CheckAlreadyExecuted(); assert(lua_gettop(context_.lua_) >= 1); int nargs = lua_gettop(context_.lua_) - 1; int error = lua_pcall(context_.lua_, nargs, numOutputs, 0); if (error) { assert(lua_gettop(context_.lua_) >= 1); std::string description(lua_tostring(context_.lua_, -1)); lua_pop(context_.lua_, 1); /* pop error message from the stack */ LOG(ERROR) << description; throw OrthancException(ErrorCode_CannotExecuteLua); } if (lua_gettop(context_.lua_) < numOutputs) { throw OrthancException(ErrorCode_LuaBadOutput); } isExecuted_ = true; } bool LuaFunctionCall::ExecutePredicate() { ExecuteInternal(1); if (!lua_isboolean(context_.lua_, 1)) { throw OrthancException(ErrorCode_NotLuaPredicate); } return lua_toboolean(context_.lua_, 1) != 0; } void LuaFunctionCall::ExecuteToJson(Json::Value& result, bool keepStrings) { ExecuteInternal(1); context_.GetJson(result, lua_gettop(context_.lua_), keepStrings); } void LuaFunctionCall::ExecuteToString(std::string& result) { ExecuteInternal(1); int top = lua_gettop(context_.lua_); if (lua_isstring(context_.lua_, top)) { result = lua_tostring(context_.lua_, top); } else { throw OrthancException(ErrorCode_LuaReturnsNoString); } } } Orthanc-1.0.0/Core/Lua/LuaFunctionCall.h0000644000000000000000000000452212634042176016051 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "LuaContext.h" #include namespace Orthanc { class LuaFunctionCall : public boost::noncopyable { private: LuaContext& context_; bool isExecuted_; void CheckAlreadyExecuted(); void ExecuteInternal(int numOutputs); public: LuaFunctionCall(LuaContext& context, const char* functionName); void PushString(const std::string& value); void PushBoolean(bool value); void PushInteger(int value); void PushDouble(double value); void PushJson(const Json::Value& value); void Execute() { ExecuteInternal(0); } bool ExecutePredicate(); void ExecuteToJson(Json::Value& result, bool keepStrings); void ExecuteToString(std::string& result); }; } Orthanc-1.0.0/Core/MultiThreading/ILockable.h0000644000000000000000000000350012634042176017045 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 namespace Orthanc { class ILockable : public boost::noncopyable { friend class Locker; protected: virtual void Lock() = 0; virtual void Unlock() = 0; public: virtual ~ILockable() { } }; } Orthanc-1.0.0/Core/MultiThreading/IRunnableBySteps.h0000644000000000000000000000375112634042176020421 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../IDynamicObject.h" namespace Orthanc { class IRunnableBySteps : public IDynamicObject { public: virtual ~IRunnableBySteps() { } // Must return "true" if the runnable wishes to continue. Must // return "false" if the runnable has not finished its job. virtual bool Step() = 0; static void RunUntilDone(IRunnableBySteps& runnable) { while (runnable.Step()); } }; } Orthanc-1.0.0/Core/MultiThreading/Locker.h0000644000000000000000000000354612634042176016451 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ILockable.h" namespace Orthanc { class Locker : public boost::noncopyable { private: ILockable& lockable_; public: Locker(ILockable& lockable) : lockable_(lockable) { lockable_.Lock(); } virtual ~Locker() { lockable_.Unlock(); } }; } Orthanc-1.0.0/Core/MultiThreading/Mutex.cpp0000644000000000000000000000615612634042176016667 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "Mutex.h" #include "../OrthancException.h" #if defined(_WIN32) #include #elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) #include #else #error Support your platform here #endif namespace Orthanc { #if defined (_WIN32) struct Mutex::PImpl { CRITICAL_SECTION criticalSection_; }; Mutex::Mutex() { pimpl_ = new PImpl; ::InitializeCriticalSection(&pimpl_->criticalSection_); } Mutex::~Mutex() { ::DeleteCriticalSection(&pimpl_->criticalSection_); delete pimpl_; } void Mutex::Lock() { ::EnterCriticalSection(&pimpl_->criticalSection_); } void Mutex::Unlock() { ::LeaveCriticalSection(&pimpl_->criticalSection_); } #elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) struct Mutex::PImpl { pthread_mutex_t mutex_; }; Mutex::Mutex() { pimpl_ = new PImpl; if (pthread_mutex_init(&pimpl_->mutex_, NULL) != 0) { delete pimpl_; throw OrthancException(ErrorCode_InternalError); } } Mutex::~Mutex() { pthread_mutex_destroy(&pimpl_->mutex_); delete pimpl_; } void Mutex::Lock() { if (pthread_mutex_lock(&pimpl_->mutex_) != 0) { throw OrthancException(ErrorCode_InternalError); } } void Mutex::Unlock() { if (pthread_mutex_unlock(&pimpl_->mutex_) != 0) { throw OrthancException(ErrorCode_InternalError); } } #else #error Support your plateform here #endif } Orthanc-1.0.0/Core/MultiThreading/Mutex.h0000644000000000000000000000346412634042176016333 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ILockable.h" namespace Orthanc { class Mutex : public ILockable { private: struct PImpl; PImpl *pimpl_; protected: virtual void Lock(); virtual void Unlock(); public: Mutex(); ~Mutex(); }; } Orthanc-1.0.0/Core/MultiThreading/ReaderWriterLock.cpp0000644000000000000000000000572512634042176020776 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "ReaderWriterLock.h" #include namespace Orthanc { namespace { // Anonymous namespace to avoid clashes between compilation // modules. class ReaderLockable : public ILockable { private: boost::shared_mutex& lock_; protected: virtual void Lock() { lock_.lock_shared(); } virtual void Unlock() { lock_.unlock_shared(); } public: ReaderLockable(boost::shared_mutex& lock) : lock_(lock) { } }; class WriterLockable : public ILockable { private: boost::shared_mutex& lock_; protected: virtual void Lock() { lock_.lock(); } virtual void Unlock() { lock_.unlock(); } public: WriterLockable(boost::shared_mutex& lock) : lock_(lock) { } }; } struct ReaderWriterLock::PImpl { boost::shared_mutex lock_; ReaderLockable reader_; WriterLockable writer_; PImpl() : reader_(lock_), writer_(lock_) { } }; ReaderWriterLock::ReaderWriterLock() { pimpl_ = new PImpl; } ReaderWriterLock::~ReaderWriterLock() { delete pimpl_; } ILockable& ReaderWriterLock::ForReader() { return pimpl_->reader_; } ILockable& ReaderWriterLock::ForWriter() { return pimpl_->writer_; } } Orthanc-1.0.0/Core/MultiThreading/ReaderWriterLock.h0000644000000000000000000000357312634042176020442 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "ILockable.h" #include namespace Orthanc { class ReaderWriterLock : public boost::noncopyable { private: struct PImpl; PImpl *pimpl_; public: ReaderWriterLock(); virtual ~ReaderWriterLock(); ILockable& ForReader(); ILockable& ForWriter(); }; } Orthanc-1.0.0/Core/MultiThreading/RunnableWorkersPool.cpp0000644000000000000000000001023412634042176021532 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RunnableWorkersPool.h" #include "SharedMessageQueue.h" #include "../OrthancException.h" #include "../Logging.h" namespace Orthanc { struct RunnableWorkersPool::PImpl { class Worker { private: const bool& continue_; SharedMessageQueue& queue_; boost::thread thread_; static void WorkerThread(Worker* that) { while (that->continue_) { std::auto_ptr obj(that->queue_.Dequeue(100)); if (obj.get() != NULL) { try { IRunnableBySteps& runnable = *dynamic_cast(obj.get()); bool wishToContinue = runnable.Step(); if (wishToContinue) { // The runnable wishes to continue, reinsert it at the beginning of the queue that->queue_.Enqueue(obj.release()); } } catch (OrthancException& e) { LOG(ERROR) << "Exception in a pool of working threads: " << e.What(); } } } } public: Worker(const bool& globalContinue, SharedMessageQueue& queue) : continue_(globalContinue), queue_(queue) { thread_ = boost::thread(WorkerThread, this); } void Join() { if (thread_.joinable()) { thread_.join(); } } }; bool continue_; std::vector workers_; SharedMessageQueue queue_; }; RunnableWorkersPool::RunnableWorkersPool(size_t countWorkers) : pimpl_(new PImpl) { pimpl_->continue_ = true; if (countWorkers == 0) { throw OrthancException(ErrorCode_ParameterOutOfRange); } pimpl_->workers_.resize(countWorkers); for (size_t i = 0; i < countWorkers; i++) { pimpl_->workers_[i] = new PImpl::Worker(pimpl_->continue_, pimpl_->queue_); } } void RunnableWorkersPool::Stop() { if (pimpl_->continue_) { pimpl_->continue_ = false; for (size_t i = 0; i < pimpl_->workers_.size(); i++) { PImpl::Worker* worker = pimpl_->workers_[i]; if (worker != NULL) { worker->Join(); delete worker; } } } } RunnableWorkersPool::~RunnableWorkersPool() { Stop(); } void RunnableWorkersPool::Add(IRunnableBySteps* runnable) { if (!pimpl_->continue_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } pimpl_->queue_.Enqueue(runnable); } } Orthanc-1.0.0/Core/MultiThreading/RunnableWorkersPool.h0000644000000000000000000000370112634042176021200 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "IRunnableBySteps.h" #include namespace Orthanc { class RunnableWorkersPool : public boost::noncopyable { private: struct PImpl; boost::shared_ptr pimpl_; void Stop(); public: RunnableWorkersPool(size_t countWorkers); ~RunnableWorkersPool(); void Add(IRunnableBySteps* runnable); // Takes the ownership }; } Orthanc-1.0.0/Core/MultiThreading/Semaphore.cpp0000644000000000000000000000414312634042176017502 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "Semaphore.h" #include "../OrthancException.h" namespace Orthanc { Semaphore::Semaphore(unsigned int count) : count_(count) { if (count == 0) { throw OrthancException(ErrorCode_ParameterOutOfRange); } } void Semaphore::Release() { boost::mutex::scoped_lock lock(mutex_); count_++; condition_.notify_one(); } void Semaphore::Acquire() { boost::mutex::scoped_lock lock(mutex_); while (count_ == 0) { condition_.wait(lock); } count_++; } } Orthanc-1.0.0/Core/MultiThreading/Semaphore.h0000644000000000000000000000362012634042176017146 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include namespace Orthanc { class Semaphore : public boost::noncopyable { private: unsigned int count_; boost::mutex mutex_; boost::condition_variable condition_; public: explicit Semaphore(unsigned int count); void Release(); void Acquire(); }; } Orthanc-1.0.0/Core/MultiThreading/SharedMessageQueue.cpp0000644000000000000000000001135312634042176021300 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "SharedMessageQueue.h" /** * FIFO (queue): * * back front * +--+--+--+--+--+--+--+--+--+--+--+ * Enqueue -> | | | | | | | | | | | | * | | | | | | | | | | | | -> Dequeue * +--+--+--+--+--+--+--+--+--+--+--+ * ^ * | * Make room here * * * LIFO (stack): * * back front * +--+--+--+--+--+--+--+--+--+--+--+ * | | | | | | | | | | | | <- Enqueue * | | | | | | | | | | | | -> Dequeue * +--+--+--+--+--+--+--+--+--+--+--+ * ^ * | * Make room here **/ namespace Orthanc { SharedMessageQueue::SharedMessageQueue(unsigned int maxSize) : isFifo_(true), maxSize_(maxSize) { } SharedMessageQueue::~SharedMessageQueue() { for (Queue::iterator it = queue_.begin(); it != queue_.end(); ++it) { delete *it; } } void SharedMessageQueue::Enqueue(IDynamicObject* message) { boost::mutex::scoped_lock lock(mutex_); if (maxSize_ != 0 && queue_.size() > maxSize_) { if (isFifo_) { // Too many elements in the queue: Make room delete queue_.front(); queue_.pop_front(); } else { // Too many elements in the stack: Make room delete queue_.back(); queue_.pop_back(); } } if (isFifo_) { // Queue policy (FIFO) queue_.push_back(message); } else { // Stack policy (LIFO) queue_.push_front(message); } elementAvailable_.notify_one(); } IDynamicObject* SharedMessageQueue::Dequeue(int32_t millisecondsTimeout) { boost::mutex::scoped_lock lock(mutex_); // Wait for a message to arrive in the FIFO queue while (queue_.empty()) { if (millisecondsTimeout == 0) { elementAvailable_.wait(lock); } else { bool success = elementAvailable_.timed_wait (lock, boost::posix_time::milliseconds(millisecondsTimeout)); if (!success) { return NULL; } } } std::auto_ptr message(queue_.front()); queue_.pop_front(); if (queue_.empty()) { emptied_.notify_all(); } return message.release(); } bool SharedMessageQueue::WaitEmpty(int32_t millisecondsTimeout) { boost::mutex::scoped_lock lock(mutex_); // Wait for the queue to become empty while (!queue_.empty()) { if (millisecondsTimeout == 0) { emptied_.wait(lock); } else { if (!emptied_.timed_wait (lock, boost::posix_time::milliseconds(millisecondsTimeout))) { return false; } } } return true; } void SharedMessageQueue::SetFifoPolicy() { boost::mutex::scoped_lock lock(mutex_); isFifo_ = true; } void SharedMessageQueue::SetLifoPolicy() { boost::mutex::scoped_lock lock(mutex_); isFifo_ = false; } } Orthanc-1.0.0/Core/MultiThreading/SharedMessageQueue.h0000644000000000000000000000501312634042176020741 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../IDynamicObject.h" #include #include #include namespace Orthanc { class SharedMessageQueue : public boost::noncopyable { private: typedef std::list Queue; bool isFifo_; unsigned int maxSize_; Queue queue_; boost::mutex mutex_; boost::condition_variable elementAvailable_; boost::condition_variable emptied_; public: explicit SharedMessageQueue(unsigned int maxSize = 0); ~SharedMessageQueue(); // This transfers the ownership of the message void Enqueue(IDynamicObject* message); // The caller is responsible to delete the dequeud message! IDynamicObject* Dequeue(int32_t millisecondsTimeout); bool WaitEmpty(int32_t millisecondsTimeout); bool IsFifoPolicy() const { return isFifo_; } bool IsLifoPolicy() const { return !isFifo_; } void SetFifoPolicy(); void SetLifoPolicy(); }; } Orthanc-1.0.0/Core/OrthancException.h0000644000000000000000000000444312634042176015564 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 #include #include "Enumerations.h" namespace Orthanc { class OrthancException { protected: ErrorCode errorCode_; HttpStatus httpStatus_; public: OrthancException(ErrorCode errorCode) : errorCode_(errorCode), httpStatus_(ConvertErrorCodeToHttpStatus(errorCode)) { } OrthancException(ErrorCode errorCode, HttpStatus httpStatus) : errorCode_(errorCode), httpStatus_(httpStatus) { } ErrorCode GetErrorCode() const { return errorCode_; } HttpStatus GetHttpStatus() const { return httpStatus_; } const char* What() const { return EnumerationToString(errorCode_); } }; } Orthanc-1.0.0/Core/PrecompiledHeaders.cpp0000644000000000000000000000311712634042176016376 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" Orthanc-1.0.0/Core/PrecompiledHeaders.h0000644000000000000000000000416312634042176016045 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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(_WIN32) && !defined(NOMINMAX) #define NOMINMAX #endif #if ORTHANC_USE_PRECOMPILED_HEADERS == 1 #include #include #include #include #include #include #include #include #if ORTHANC_PUGIXML_ENABLED == 1 #include #endif #include "Enumerations.h" #include "Logging.h" #include "OrthancException.h" #include "Toolbox.h" #include "Uuid.h" #endif Orthanc-1.0.0/Core/RestApi/RestApi.cpp0000644000000000000000000002013712634042176015556 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApi.h" #include "../Logging.h" #include // To define "_exit()" under Windows #include namespace Orthanc { namespace { // Anonymous namespace to avoid clashes between compilation modules class HttpHandlerVisitor : public RestApiHierarchy::IVisitor { private: RestApi& api_; RestApiOutput& output_; RequestOrigin origin_; const char* remoteIp_; const char* username_; HttpMethod method_; const IHttpHandler::Arguments& headers_; const IHttpHandler::Arguments& getArguments_; const char* bodyData_; size_t bodySize_; public: HttpHandlerVisitor(RestApi& api, RestApiOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const IHttpHandler::Arguments& headers, const IHttpHandler::Arguments& getArguments, const char* bodyData, size_t bodySize) : api_(api), output_(output), origin_(origin), remoteIp_(remoteIp), username_(username), method_(method), headers_(headers), getArguments_(getArguments), bodyData_(bodyData), bodySize_(bodySize) { } virtual bool Visit(const RestApiHierarchy::Resource& resource, const UriComponents& uri, const IHttpHandler::Arguments& components, const UriComponents& trailing) { if (resource.HasHandler(method_)) { switch (method_) { case HttpMethod_Get: { RestApiGetCall call(output_, api_, origin_, remoteIp_, username_, headers_, components, trailing, uri, getArguments_); resource.Handle(call); return true; } case HttpMethod_Post: { RestApiPostCall call(output_, api_, origin_, remoteIp_, username_, headers_, components, trailing, uri, bodyData_, bodySize_); resource.Handle(call); return true; } case HttpMethod_Delete: { RestApiDeleteCall call(output_, api_, origin_, remoteIp_, username_, headers_, components, trailing, uri); resource.Handle(call); return true; } case HttpMethod_Put: { RestApiPutCall call(output_, api_, origin_, remoteIp_, username_, headers_, components, trailing, uri, bodyData_, bodySize_); resource.Handle(call); return true; } default: return false; } } return false; } }; } static void AddMethod(std::string& target, const std::string& method) { if (target.size() > 0) target += "," + method; else target = method; } static std::string MethodsToString(const std::set& methods) { std::string s; if (methods.find(HttpMethod_Get) != methods.end()) { AddMethod(s, "GET"); } if (methods.find(HttpMethod_Post) != methods.end()) { AddMethod(s, "POST"); } if (methods.find(HttpMethod_Put) != methods.end()) { AddMethod(s, "PUT"); } if (methods.find(HttpMethod_Delete) != methods.end()) { AddMethod(s, "DELETE"); } return s; } bool RestApi::Handle(HttpOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& getArguments, const char* bodyData, size_t bodySize) { RestApiOutput wrappedOutput(output, method); #if ORTHANC_PUGIXML_ENABLED == 1 { // Look if the client wishes XML answers instead of JSON // http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html#z3 Arguments::const_iterator it = headers.find("accept"); if (it != headers.end()) { std::vector accepted; Toolbox::TokenizeString(accepted, it->second, ';'); for (size_t i = 0; i < accepted.size(); i++) { if (accepted[i] == "application/xml") { wrappedOutput.SetConvertJsonToXml(true); } if (accepted[i] == "application/json") { wrappedOutput.SetConvertJsonToXml(false); } } } } #endif Arguments compiled; HttpToolbox::CompileGetArguments(compiled, getArguments); HttpHandlerVisitor visitor(*this, wrappedOutput, origin, remoteIp, username, method, headers, compiled, bodyData, bodySize); if (root_.LookupResource(uri, visitor)) { wrappedOutput.Finalize(); return true; } std::set methods; root_.GetAcceptedMethods(methods, uri); if (methods.empty()) { return false; // This URI is not served by this REST API } else { LOG(INFO) << "REST method " << EnumerationToString(method) << " not allowed on: " << Toolbox::FlattenUri(uri); output.SendMethodNotAllowed(MethodsToString(methods)); return true; } } void RestApi::Register(const std::string& path, RestApiGetCall::Handler handler) { root_.Register(path, handler); } void RestApi::Register(const std::string& path, RestApiPutCall::Handler handler) { root_.Register(path, handler); } void RestApi::Register(const std::string& path, RestApiPostCall::Handler handler) { root_.Register(path, handler); } void RestApi::Register(const std::string& path, RestApiDeleteCall::Handler handler) { root_.Register(path, handler); } void RestApi::AutoListChildren(RestApiGetCall& call) { RestApi& context = call.GetContext(); Json::Value directory; if (context.root_.GetDirectory(directory, call.GetFullUri())) { call.GetOutput().AnswerJson(directory); } } } Orthanc-1.0.0/Core/RestApi/RestApi.h0000644000000000000000000000517012634042176015223 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiHierarchy.h" #include namespace Orthanc { class RestApi : public IHttpHandler { private: RestApiHierarchy root_; public: static void AutoListChildren(RestApiGetCall& call); virtual bool Handle(HttpOutput& output, RequestOrigin origin, const char* remoteIp, const char* username, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& getArguments, const char* bodyData, size_t bodySize); void Register(const std::string& path, RestApiGetCall::Handler handler); void Register(const std::string& path, RestApiPutCall::Handler handler); void Register(const std::string& path, RestApiPostCall::Handler handler); void Register(const std::string& path, RestApiDeleteCall::Handler handler); }; } Orthanc-1.0.0/Core/RestApi/RestApiCall.cpp0000644000000000000000000000404112634042176016346 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApiCall.h" namespace Orthanc { bool RestApiCall::ParseJsonRequestInternal(Json::Value& result, const char* request) { result.clear(); Json::Reader reader; return reader.parse(request, result); } std::string RestApiCall::FlattenUri() const { std::string s = "/"; for (size_t i = 0; i < fullUri_.size(); i++) { s += fullUri_[i] + "/"; } return s; } } Orthanc-1.0.0/Core/RestApi/RestApiCall.h0000644000000000000000000001025412634042176016016 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../HttpServer/IHttpHandler.h" #include "../HttpServer/HttpToolbox.h" #include "RestApiPath.h" #include "RestApiOutput.h" #include namespace Orthanc { class RestApi; class RestApiCall : public boost::noncopyable { private: RestApiOutput& output_; RestApi& context_; RequestOrigin origin_; const char* remoteIp_; const char* username_; const IHttpHandler::Arguments& httpHeaders_; const IHttpHandler::Arguments& uriComponents_; const UriComponents& trailing_; const UriComponents& fullUri_; protected: static bool ParseJsonRequestInternal(Json::Value& result, const char* request); public: RestApiCall(RestApiOutput& output, RestApi& context, RequestOrigin origin, const char* remoteIp, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri) : output_(output), context_(context), origin_(origin), remoteIp_(remoteIp), username_(username), httpHeaders_(httpHeaders), uriComponents_(uriComponents), trailing_(trailing), fullUri_(fullUri) { } RestApiOutput& GetOutput() { return output_; } RestApi& GetContext() { return context_; } const UriComponents& GetFullUri() const { return fullUri_; } const UriComponents& GetTrailingUri() const { return trailing_; } std::string GetUriComponent(const std::string& name, const std::string& defaultValue) const { return HttpToolbox::GetArgument(uriComponents_, name, defaultValue); } std::string GetHttpHeader(const std::string& name, const std::string& defaultValue) const { return HttpToolbox::GetArgument(httpHeaders_, name, defaultValue); } const IHttpHandler::Arguments& GetHttpHeaders() const { return httpHeaders_; } void ParseCookies(IHttpHandler::Arguments& result) const { HttpToolbox::ParseCookies(result, httpHeaders_); } std::string FlattenUri() const; RequestOrigin GetRequestOrigin() const { return origin_; } const char* GetRemoteIp() const { return remoteIp_; } const char* GetUsername() const { return username_; } virtual bool ParseJsonRequest(Json::Value& result) const = 0; }; } Orthanc-1.0.0/Core/RestApi/RestApiDeleteCall.h0000644000000000000000000000466412634042176017151 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiCall.h" namespace Orthanc { class RestApiDeleteCall : public RestApiCall { public: typedef void (*Handler) (RestApiDeleteCall& call); RestApiDeleteCall(RestApiOutput& output, RestApi& context, RequestOrigin origin, const char* remoteIp, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri) : RestApiCall(output, context, origin, remoteIp, username, httpHeaders, uriComponents, trailing, fullUri) { } virtual bool ParseJsonRequest(Json::Value& result) const { result.clear(); return true; } }; } Orthanc-1.0.0/Core/RestApi/RestApiGetCall.cpp0000644000000000000000000000364312634042176017015 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApiGetCall.h" namespace Orthanc { bool RestApiGetCall::ParseJsonRequest(Json::Value& result) const { result.clear(); for (IHttpHandler::Arguments::const_iterator it = getArguments_.begin(); it != getArguments_.end(); ++it) { result[it->first] = it->second; } return true; } } Orthanc-1.0.0/Core/RestApi/RestApiGetCall.h0000644000000000000000000000551612634042176016463 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiCall.h" namespace Orthanc { class RestApiGetCall : public RestApiCall { private: const IHttpHandler::Arguments& getArguments_; public: typedef void (*Handler) (RestApiGetCall& call); RestApiGetCall(RestApiOutput& output, RestApi& context, RequestOrigin origin, const char* remoteIp, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, const IHttpHandler::Arguments& getArguments) : RestApiCall(output, context, origin, remoteIp, username, httpHeaders, uriComponents, trailing, fullUri), getArguments_(getArguments) { } std::string GetArgument(const std::string& name, const std::string& defaultValue) const { return HttpToolbox::GetArgument(getArguments_, name, defaultValue); } bool HasArgument(const std::string& name) const { return getArguments_.find(name) != getArguments_.end(); } virtual bool ParseJsonRequest(Json::Value& result) const; }; } Orthanc-1.0.0/Core/RestApi/RestApiHierarchy.cpp0000644000000000000000000002644312634042176017423 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApiHierarchy.h" #include "../OrthancException.h" #include #include namespace Orthanc { RestApiHierarchy::Resource::Resource() : getHandler_(NULL), postHandler_(NULL), putHandler_(NULL), deleteHandler_(NULL) { } bool RestApiHierarchy::Resource::HasHandler(HttpMethod method) const { switch (method) { case HttpMethod_Get: return getHandler_ != NULL; case HttpMethod_Post: return postHandler_ != NULL; case HttpMethod_Put: return putHandler_ != NULL; case HttpMethod_Delete: return deleteHandler_ != NULL; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } bool RestApiHierarchy::Resource::IsEmpty() const { return (getHandler_ == NULL && postHandler_ == NULL && putHandler_ == NULL && deleteHandler_ == NULL); } RestApiHierarchy& RestApiHierarchy::AddChild(Children& children, const std::string& name) { Children::iterator it = children.find(name); if (it == children.end()) { // Create new child RestApiHierarchy *child = new RestApiHierarchy; children[name] = child; return *child; } else { return *it->second; } } bool RestApiHierarchy::Resource::Handle(RestApiGetCall& call) const { if (getHandler_ != NULL) { getHandler_(call); return true; } else { return false; } } bool RestApiHierarchy::Resource::Handle(RestApiPutCall& call) const { if (putHandler_ != NULL) { putHandler_(call); return true; } else { return false; } } bool RestApiHierarchy::Resource::Handle(RestApiPostCall& call) const { if (postHandler_ != NULL) { postHandler_(call); return true; } else { return false; } } bool RestApiHierarchy::Resource::Handle(RestApiDeleteCall& call) const { if (deleteHandler_ != NULL) { deleteHandler_(call); return true; } else { return false; } } void RestApiHierarchy::DeleteChildren(Children& children) { for (Children::iterator it = children.begin(); it != children.end(); ++it) { delete it->second; } } template void RestApiHierarchy::RegisterInternal(const RestApiPath& path, Handler handler, size_t level) { if (path.GetLevelCount() == level) { if (path.IsUniversalTrailing()) { universalHandlers_.Register(handler); } else { handlers_.Register(handler); } } else { RestApiHierarchy* child; if (path.IsWildcardLevel(level)) { child = &AddChild(wildcardChildren_, path.GetWildcardName(level)); } else { child = &AddChild(children_, path.GetLevelName(level)); } child->RegisterInternal(path, handler, level + 1); } } bool RestApiHierarchy::LookupResource(IHttpHandler::Arguments& components, const UriComponents& uri, IVisitor& visitor, size_t level) { if (uri.size() != 0 && level > uri.size()) { return false; } UriComponents trailing; // Look for an exact match on the resource of interest if (uri.size() == 0 || level == uri.size()) { if (!handlers_.IsEmpty() && visitor.Visit(handlers_, uri, components, trailing)) { return true; } } if (level < uri.size()) // A recursive call is possible { // Try and go down in the hierarchy, using an exact match for the child Children::const_iterator child = children_.find(uri[level]); if (child != children_.end()) { if (child->second->LookupResource(components, uri, visitor, level + 1)) { return true; } } // Try and go down in the hierarchy, using wildcard rules for children for (child = wildcardChildren_.begin(); child != wildcardChildren_.end(); ++child) { IHttpHandler::Arguments subComponents = components; subComponents[child->first] = uri[level]; if (child->second->LookupResource(subComponents, uri, visitor, level + 1)) { return true; } } } // As a last resort, call the universal handlers, if any if (!universalHandlers_.IsEmpty()) { trailing.resize(uri.size() - level); size_t pos = 0; for (size_t i = level; i < uri.size(); i++, pos++) { trailing[pos] = uri[i]; } assert(pos == trailing.size()); if (visitor.Visit(universalHandlers_, uri, components, trailing)) { return true; } } return false; } bool RestApiHierarchy::CanGenerateDirectory() const { return (universalHandlers_.IsEmpty() && wildcardChildren_.empty()); } bool RestApiHierarchy::GetDirectory(Json::Value& result, const UriComponents& uri, size_t level) { if (uri.size() == level) { if (CanGenerateDirectory()) { result = Json::arrayValue; for (Children::const_iterator it = children_.begin(); it != children_.end(); ++it) { result.append(it->first); } return true; } else { return false; } } Children::const_iterator child = children_.find(uri[level]); if (child != children_.end()) { if (child->second->GetDirectory(result, uri, level + 1)) { return true; } } for (child = wildcardChildren_.begin(); child != wildcardChildren_.end(); ++child) { if (child->second->GetDirectory(result, uri, level + 1)) { return true; } } return false; } RestApiHierarchy::~RestApiHierarchy() { DeleteChildren(children_); DeleteChildren(wildcardChildren_); } void RestApiHierarchy::Register(const std::string& uri, RestApiGetCall::Handler handler) { RestApiPath path(uri); RegisterInternal(path, handler, 0); } void RestApiHierarchy::Register(const std::string& uri, RestApiPutCall::Handler handler) { RestApiPath path(uri); RegisterInternal(path, handler, 0); } void RestApiHierarchy::Register(const std::string& uri, RestApiPostCall::Handler handler) { RestApiPath path(uri); RegisterInternal(path, handler, 0); } void RestApiHierarchy::Register(const std::string& uri, RestApiDeleteCall::Handler handler) { RestApiPath path(uri); RegisterInternal(path, handler, 0); } void RestApiHierarchy::CreateSiteMap(Json::Value& target) const { target = Json::objectValue; /*std::string s = " "; if (handlers_.HasHandler(HttpMethod_Get)) { s += "GET "; } if (handlers_.HasHandler(HttpMethod_Post)) { s += "POST "; } if (handlers_.HasHandler(HttpMethod_Put)) { s += "PUT "; } if (handlers_.HasHandler(HttpMethod_Delete)) { s += "DELETE "; } target = s;*/ for (Children::const_iterator it = children_.begin(); it != children_.end(); ++it) { it->second->CreateSiteMap(target[it->first]); } for (Children::const_iterator it = wildcardChildren_.begin(); it != wildcardChildren_.end(); ++it) { it->second->CreateSiteMap(target["<" + it->first + ">"]); } } bool RestApiHierarchy::LookupResource(const UriComponents& uri, IVisitor& visitor) { IHttpHandler::Arguments components; return LookupResource(components, uri, visitor, 0); } namespace { // Anonymous namespace to avoid clashes between compilation modules class AcceptedMethodsVisitor : public RestApiHierarchy::IVisitor { private: std::set& methods_; public: AcceptedMethodsVisitor(std::set& methods) : methods_(methods) { } virtual bool Visit(const RestApiHierarchy::Resource& resource, const UriComponents& uri, const IHttpHandler::Arguments& components, const UriComponents& trailing) { if (trailing.size() == 0) // Ignore universal handlers { if (resource.HasHandler(HttpMethod_Get)) { methods_.insert(HttpMethod_Get); } if (resource.HasHandler(HttpMethod_Post)) { methods_.insert(HttpMethod_Post); } if (resource.HasHandler(HttpMethod_Put)) { methods_.insert(HttpMethod_Put); } if (resource.HasHandler(HttpMethod_Delete)) { methods_.insert(HttpMethod_Delete); } } return false; // Continue to check all the possible ways to access this URI } }; } void RestApiHierarchy::GetAcceptedMethods(std::set& methods, const UriComponents& uri) { IHttpHandler::Arguments components; AcceptedMethodsVisitor visitor(methods); if (LookupResource(components, uri, visitor, 0)) { Json::Value d; if (GetDirectory(d, uri)) { methods.insert(HttpMethod_Get); } } } } Orthanc-1.0.0/Core/RestApi/RestApiHierarchy.h0000644000000000000000000001142512634042176017062 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiGetCall.h" #include "RestApiPostCall.h" #include "RestApiPutCall.h" #include "RestApiDeleteCall.h" #include namespace Orthanc { class RestApiHierarchy : public boost::noncopyable { public: class Resource : public boost::noncopyable { private: RestApiGetCall::Handler getHandler_; RestApiPostCall::Handler postHandler_; RestApiPutCall::Handler putHandler_; RestApiDeleteCall::Handler deleteHandler_; public: Resource(); bool HasHandler(HttpMethod method) const; void Register(RestApiGetCall::Handler handler) { getHandler_ = handler; } void Register(RestApiPutCall::Handler handler) { putHandler_ = handler; } void Register(RestApiPostCall::Handler handler) { postHandler_ = handler; } void Register(RestApiDeleteCall::Handler handler) { deleteHandler_ = handler; } bool IsEmpty() const; bool Handle(RestApiGetCall& call) const; bool Handle(RestApiPutCall& call) const; bool Handle(RestApiPostCall& call) const; bool Handle(RestApiDeleteCall& call) const; }; class IVisitor : public boost::noncopyable { public: virtual ~IVisitor() { } virtual bool Visit(const Resource& resource, const UriComponents& uri, const IHttpHandler::Arguments& components, const UriComponents& trailing) = 0; }; private: typedef std::map Children; Resource handlers_; Children children_; Children wildcardChildren_; Resource universalHandlers_; static RestApiHierarchy& AddChild(Children& children, const std::string& name); static void DeleteChildren(Children& children); template void RegisterInternal(const RestApiPath& path, Handler handler, size_t level); bool CanGenerateDirectory() const; bool LookupResource(IHttpHandler::Arguments& components, const UriComponents& uri, IVisitor& visitor, size_t level); bool GetDirectory(Json::Value& result, const UriComponents& uri, size_t level); public: ~RestApiHierarchy(); void Register(const std::string& uri, RestApiGetCall::Handler handler); void Register(const std::string& uri, RestApiPutCall::Handler handler); void Register(const std::string& uri, RestApiPostCall::Handler handler); void Register(const std::string& uri, RestApiDeleteCall::Handler handler); void CreateSiteMap(Json::Value& target) const; bool GetDirectory(Json::Value& result, const UriComponents& uri) { return GetDirectory(result, uri, 0); } bool LookupResource(const UriComponents& uri, IVisitor& visitor); void GetAcceptedMethods(std::set& methods, const UriComponents& uri); }; } Orthanc-1.0.0/Core/RestApi/RestApiOutput.cpp0000644000000000000000000001260212634042176016775 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApiOutput.h" #include "../Logging.h" #include "../OrthancException.h" #include namespace Orthanc { RestApiOutput::RestApiOutput(HttpOutput& output, HttpMethod method) : output_(output), method_(method), convertJsonToXml_(false) { alreadySent_ = false; } RestApiOutput::~RestApiOutput() { } void RestApiOutput::Finalize() { if (!alreadySent_) { if (method_ == HttpMethod_Post) { output_.SendStatus(HttpStatus_400_BadRequest); } else { output_.SendStatus(HttpStatus_404_NotFound); } } } void RestApiOutput::CheckStatus() { if (alreadySent_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } } void RestApiOutput::AnswerStream(IHttpStreamAnswer& stream) { CheckStatus(); output_.Answer(stream); alreadySent_ = true; } void RestApiOutput::AnswerJson(const Json::Value& value) { CheckStatus(); if (convertJsonToXml_) { #if ORTHANC_PUGIXML_ENABLED == 1 std::string s; Toolbox::JsonToXml(s, value); output_.SetContentType("application/xml"); output_.Answer(s); #else LOG(ERROR) << "Orthanc was compiled without XML support"; throw OrthancException(ErrorCode_InternalError); #endif } else { Json::StyledWriter writer; output_.SetContentType("application/json"); output_.Answer(writer.write(value)); } alreadySent_ = true; } void RestApiOutput::AnswerBuffer(const std::string& buffer, const std::string& contentType) { AnswerBuffer(buffer.size() == 0 ? NULL : buffer.c_str(), buffer.size(), contentType); } void RestApiOutput::AnswerBuffer(const void* buffer, size_t length, const std::string& contentType) { CheckStatus(); output_.SetContentType(contentType.c_str()); output_.Answer(buffer, length); alreadySent_ = true; } void RestApiOutput::Redirect(const std::string& path) { CheckStatus(); output_.Redirect(path); alreadySent_ = true; } void RestApiOutput::SignalErrorInternal(HttpStatus status, const char* message, size_t messageSize) { if (status != HttpStatus_400_BadRequest && status != HttpStatus_403_Forbidden && status != HttpStatus_500_InternalServerError && status != HttpStatus_415_UnsupportedMediaType) { throw OrthancException(ErrorCode_BadHttpStatusInRest); } CheckStatus(); output_.SendStatus(status, message, messageSize); alreadySent_ = true; } void RestApiOutput::SignalError(HttpStatus status) { SignalErrorInternal(status, NULL, 0); } void RestApiOutput::SignalError(HttpStatus status, const std::string& message) { SignalErrorInternal(status, message.c_str(), message.size()); } void RestApiOutput::SetCookie(const std::string& name, const std::string& value, unsigned int maxAge) { if (name.find(";") != std::string::npos || name.find(" ") != std::string::npos || value.find(";") != std::string::npos || value.find(" ") != std::string::npos) { throw OrthancException(ErrorCode_NotImplemented); } CheckStatus(); std::string v = value + ";path=/"; if (maxAge != 0) { v += ";max-age=" + boost::lexical_cast(maxAge); } output_.SetCookie(name, v); } void RestApiOutput::ResetCookie(const std::string& name) { // This marks the cookie to be deleted by the browser in 1 second, // and before it actually gets deleted, its value is set to the // empty string SetCookie(name, "", 1); } } Orthanc-1.0.0/Core/RestApi/RestApiOutput.h0000644000000000000000000000567712634042176016460 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../HttpServer/HttpOutput.h" #include "../HttpServer/HttpFileSender.h" #include namespace Orthanc { class RestApiOutput { private: HttpOutput& output_; HttpMethod method_; bool alreadySent_; bool convertJsonToXml_; void CheckStatus(); void SignalErrorInternal(HttpStatus status, const char* message, size_t messageSize); public: RestApiOutput(HttpOutput& output, HttpMethod method); ~RestApiOutput(); void SetConvertJsonToXml(bool convert) { convertJsonToXml_ = convert; } bool IsConvertJsonToXml() const { return convertJsonToXml_; } void AnswerStream(IHttpStreamAnswer& stream); void AnswerJson(const Json::Value& value); void AnswerBuffer(const std::string& buffer, const std::string& contentType); void AnswerBuffer(const void* buffer, size_t length, const std::string& contentType); void SignalError(HttpStatus status); void SignalError(HttpStatus status, const std::string& message); void Redirect(const std::string& path); void SetCookie(const std::string& name, const std::string& value, unsigned int maxAge = 0); void ResetCookie(const std::string& name); void Finalize(); }; } Orthanc-1.0.0/Core/RestApi/RestApiPath.cpp0000644000000000000000000001076112634042176016375 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h" #include "RestApiPath.h" #include "../OrthancException.h" #include namespace Orthanc { RestApiPath::RestApiPath(const std::string& uri) { Toolbox::SplitUriComponents(uri_, uri); if (uri_.size() == 0) { hasTrailing_ = false; return; } if (uri_.back() == "*") { hasTrailing_ = true; uri_.pop_back(); } else { hasTrailing_ = false; } components_.resize(uri_.size()); for (size_t i = 0; i < uri_.size(); i++) { size_t s = uri_[i].size(); assert(s > 0); if (uri_[i][0] == '{' && uri_[i][s - 1] == '}') { components_[i] = uri_[i].substr(1, s - 2); uri_[i] = ""; } else { components_[i] = ""; } } } bool RestApiPath::Match(IHttpHandler::Arguments& components, UriComponents& trailing, const std::string& uriRaw) const { UriComponents uri; Toolbox::SplitUriComponents(uri, uriRaw); return Match(components, trailing, uri); } bool RestApiPath::Match(IHttpHandler::Arguments& components, UriComponents& trailing, const UriComponents& uri) const { assert(uri_.size() == components_.size()); if (uri.size() < uri_.size()) { return false; } if (!hasTrailing_ && uri.size() > uri_.size()) { return false; } components.clear(); trailing.clear(); assert(uri_.size() <= uri.size()); for (size_t i = 0; i < uri_.size(); i++) { if (components_[i].size() == 0) { // This URI component is not a free parameter if (uri_[i] != uri[i]) { return false; } } else { // This URI component is a free parameter components[components_[i]] = uri[i]; } } if (hasTrailing_) { trailing.assign(uri.begin() + uri_.size(), uri.end()); } return true; } bool RestApiPath::Match(const UriComponents& uri) const { IHttpHandler::Arguments components; UriComponents trailing; return Match(components, trailing, uri); } bool RestApiPath::IsWildcardLevel(size_t level) const { assert(uri_.size() == components_.size()); if (level >= uri_.size()) { throw OrthancException(ErrorCode_ParameterOutOfRange); } return uri_[level].length() == 0; } const std::string& RestApiPath::GetWildcardName(size_t level) const { assert(uri_.size() == components_.size()); if (!IsWildcardLevel(level)) { throw OrthancException(ErrorCode_BadParameterType); } return components_[level]; } const std::string& RestApiPath::GetLevelName(size_t level) const { assert(uri_.size() == components_.size()); if (IsWildcardLevel(level)) { throw OrthancException(ErrorCode_BadParameterType); } return uri_[level]; } } Orthanc-1.0.0/Core/RestApi/RestApiPath.h0000644000000000000000000000477712634042176016054 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "../Toolbox.h" #include "../HttpServer/IHttpHandler.h" #include namespace Orthanc { class RestApiPath { private: UriComponents uri_; bool hasTrailing_; std::vector components_; public: RestApiPath(const std::string& uri); // This version is slower bool Match(IHttpHandler::Arguments& components, UriComponents& trailing, const std::string& uriRaw) const; bool Match(IHttpHandler::Arguments& components, UriComponents& trailing, const UriComponents& uri) const; bool Match(const UriComponents& uri) const; size_t GetLevelCount() const { return uri_.size(); } bool IsWildcardLevel(size_t level) const; bool IsUniversalTrailing() const { return hasTrailing_; } const std::string& GetWildcardName(size_t level) const; const std::string& GetLevelName(size_t level) const; }; } Orthanc-1.0.0/Core/RestApi/RestApiPostCall.h0000644000000000000000000000555612634042176016675 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiCall.h" namespace Orthanc { class RestApiPostCall : public RestApiCall { private: const char* bodyData_; size_t bodySize_; public: typedef void (*Handler) (RestApiPostCall& call); RestApiPostCall(RestApiOutput& output, RestApi& context, RequestOrigin origin, const char* remoteIp, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, const char* bodyData, size_t bodySize) : RestApiCall(output, context, origin, remoteIp, username, httpHeaders, uriComponents, trailing, fullUri), bodyData_(bodyData), bodySize_(bodySize) { } const char* GetBodyData() const { return bodyData_; } size_t GetBodySize() const { return bodySize_; } void BodyToString(std::string& result) const { result.assign(bodyData_, bodySize_); } virtual bool ParseJsonRequest(Json::Value& result) const { return ParseJsonRequestInternal(result, bodyData_); } }; } Orthanc-1.0.0/Core/RestApi/RestApiPutCall.h0000644000000000000000000000554012634042176016511 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "RestApiCall.h" namespace Orthanc { class RestApiPutCall : public RestApiCall { private: const char* bodyData_; size_t bodySize_; public: typedef void (*Handler) (RestApiPutCall& call); RestApiPutCall(RestApiOutput& output, RestApi& context, RequestOrigin origin, const char* remoteIp, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::Arguments& uriComponents, const UriComponents& trailing, const UriComponents& fullUri, const char* bodyData, size_t bodySize) : RestApiCall(output, context, origin, remoteIp, username, httpHeaders, uriComponents, trailing, fullUri), bodyData_(bodyData), bodySize_(bodySize) { } const char* GetBodyData() const { return bodyData_; } size_t GetBodySize() const { return bodySize_; } void BodyToString(std::string& result) const { result.assign(bodyData_, bodySize_); } virtual bool ParseJsonRequest(Json::Value& result) const { return ParseJsonRequestInternal(result, bodyData_); } }; } Orthanc-1.0.0/Core/SQLite/Connection.cpp0000644000000000000000000002401612634042176016100 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "Connection.h" #include "OrthancSQLiteException.h" #include #include #include #if ORTHANC_SQLITE_STANDALONE != 1 #include "../Logging.h" #endif #include "sqlite3.h" namespace Orthanc { namespace SQLite { Connection::Connection() : db_(NULL), transactionNesting_(0), needsRollback_(false) { } Connection::~Connection() { Close(); } void Connection::CheckIsOpen() const { if (!db_) { throw OrthancSQLiteException(ErrorCode_SQLiteNotOpened); } } void Connection::Open(const std::string& path) { if (db_) { throw OrthancSQLiteException(ErrorCode_SQLiteAlreadyOpened); } int err = sqlite3_open(path.c_str(), &db_); if (err != SQLITE_OK) { Close(); db_ = NULL; throw OrthancSQLiteException(ErrorCode_SQLiteCannotOpen); } // Execute PRAGMAs at this point // http://www.sqlite.org/pragma.html Execute("PRAGMA FOREIGN_KEYS=ON;"); Execute("PRAGMA RECURSIVE_TRIGGERS=ON;"); } void Connection::OpenInMemory() { Open(":memory:"); } void Connection::Close() { ClearCache(); if (db_) { sqlite3_close(db_); db_ = NULL; } } void Connection::ClearCache() { for (CachedStatements::iterator it = cachedStatements_.begin(); it != cachedStatements_.end(); ++it) { delete it->second; } cachedStatements_.clear(); } StatementReference& Connection::GetCachedStatement(const StatementId& id, const char* sql) { CachedStatements::iterator i = cachedStatements_.find(id); if (i != cachedStatements_.end()) { if (i->second->GetReferenceCount() >= 1) { throw OrthancSQLiteException(ErrorCode_SQLiteStatementAlreadyUsed); } return *i->second; } else { StatementReference* statement = new StatementReference(db_, sql); cachedStatements_[id] = statement; return *statement; } } bool Connection::Execute(const char* sql) { #if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Connection::Execute " << sql; #endif CheckIsOpen(); int error = sqlite3_exec(db_, sql, NULL, NULL, NULL); if (error == SQLITE_ERROR) { #if ORTHANC_SQLITE_STANDALONE != 1 LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_); #endif throw OrthancSQLiteException(ErrorCode_SQLiteExecute); } else { return error == SQLITE_OK; } } int Connection::ExecuteAndReturnErrorCode(const char* sql) { CheckIsOpen(); return sqlite3_exec(db_, sql, NULL, NULL, NULL); } // Info querying ------------------------------------------------------------- bool Connection::IsSQLValid(const char* sql) { sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) return false; sqlite3_finalize(stmt); return true; } bool Connection::DoesTableOrIndexExist(const char* name, const char* type) const { // Our SQL is non-mutating, so this cast is OK. Statement statement(const_cast(*this), "SELECT name FROM sqlite_master WHERE type=? AND name=?"); statement.BindString(0, type); statement.BindString(1, name); return statement.Step(); // Table exists if any row was returned. } bool Connection::DoesTableExist(const char* table_name) const { return DoesTableOrIndexExist(table_name, "table"); } bool Connection::DoesIndexExist(const char* index_name) const { return DoesTableOrIndexExist(index_name, "index"); } bool Connection::DoesColumnExist(const char* table_name, const char* column_name) const { std::string sql("PRAGMA TABLE_INFO("); sql.append(table_name); sql.append(")"); // Our SQL is non-mutating, so this cast is OK. Statement statement(const_cast(*this), sql.c_str()); while (statement.Step()) { if (!statement.ColumnString(1).compare(column_name)) return true; } return false; } int64_t Connection::GetLastInsertRowId() const { return sqlite3_last_insert_rowid(db_); } int Connection::GetLastChangeCount() const { return sqlite3_changes(db_); } int Connection::GetErrorCode() const { return sqlite3_errcode(db_); } int Connection::GetLastErrno() const { int err = 0; if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err)) return -2; return err; } const char* Connection::GetErrorMessage() const { return sqlite3_errmsg(db_); } bool Connection::BeginTransaction() { if (needsRollback_) { assert(transactionNesting_ > 0); // When we're going to rollback, fail on this begin and don't actually // mark us as entering the nested transaction. return false; } bool success = true; if (!transactionNesting_) { needsRollback_ = false; Statement begin(*this, SQLITE_FROM_HERE, "BEGIN TRANSACTION"); if (!begin.Run()) return false; } transactionNesting_++; return success; } void Connection::RollbackTransaction() { if (!transactionNesting_) { throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } transactionNesting_--; if (transactionNesting_ > 0) { // Mark the outermost transaction as needing rollback. needsRollback_ = true; return; } DoRollback(); } bool Connection::CommitTransaction() { if (!transactionNesting_) { throw OrthancSQLiteException(ErrorCode_SQLiteCommitWithoutTransaction); } transactionNesting_--; if (transactionNesting_ > 0) { // Mark any nested transactions as failing after we've already got one. return !needsRollback_; } if (needsRollback_) { DoRollback(); return false; } Statement commit(*this, SQLITE_FROM_HERE, "COMMIT"); return commit.Run(); } void Connection::DoRollback() { Statement rollback(*this, SQLITE_FROM_HERE, "ROLLBACK"); rollback.Run(); needsRollback_ = false; } static void ScalarFunctionCaller(sqlite3_context* rawContext, int argc, sqlite3_value** argv) { FunctionContext context(rawContext, argc, argv); void* payload = sqlite3_user_data(rawContext); assert(payload != NULL); IScalarFunction& func = *reinterpret_cast(payload); func.Compute(context); } static void ScalarFunctionDestroyer(void* payload) { assert(payload != NULL); delete reinterpret_cast(payload); } IScalarFunction* Connection::Register(IScalarFunction* func) { int err = sqlite3_create_function_v2(db_, func->GetName(), func->GetCardinality(), SQLITE_UTF8, func, ScalarFunctionCaller, NULL, NULL, ScalarFunctionDestroyer); if (err != SQLITE_OK) { delete func; throw OrthancSQLiteException(ErrorCode_SQLiteRegisterFunction); } return func; } void Connection::FlushToDisk() { #if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Connection::FlushToDisk"; #endif int err = sqlite3_wal_checkpoint(db_, NULL); if (err != SQLITE_OK) { throw OrthancSQLiteException(ErrorCode_SQLiteFlush); } } } } Orthanc-1.0.0/Core/SQLite/Connection.h0000644000000000000000000001340012634042176015540 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "Statement.h" #include "IScalarFunction.h" #include #include struct sqlite3; struct sqlite3_stmt; #define SQLITE_FROM_HERE ::Orthanc::SQLite::StatementId(__FILE__, __LINE__) namespace Orthanc { namespace SQLite { class Connection : NonCopyable { friend class Statement; friend class Transaction; private: // All cached statements. Keeping a reference to these statements means that // they'll remain active. typedef std::map CachedStatements; CachedStatements cachedStatements_; // The actual sqlite database. Will be NULL before Init has been called or if // Init resulted in an error. sqlite3* db_; // Number of currently-nested transactions. int transactionNesting_; // True if any of the currently nested transactions have been rolled back. // When we get to the outermost transaction, this will determine if we do // a rollback instead of a commit. bool needsRollback_; void ClearCache(); void CheckIsOpen() const; sqlite3* GetWrappedObject() { return db_; } StatementReference& GetCachedStatement(const StatementId& id, const char* sql); bool DoesTableOrIndexExist(const char* name, const char* type) const; void DoRollback(); public: // The database is opened by calling Open[InMemory](). Any uncommitted // transactions will be rolled back when this object is deleted. Connection(); ~Connection(); void Open(const std::string& path); void OpenInMemory(); void Close(); bool Execute(const char* sql); bool Execute(const std::string& sql) { return Execute(sql.c_str()); } void FlushToDisk(); IScalarFunction* Register(IScalarFunction* func); // Takes the ownership of the function // Info querying ------------------------------------------------------------- // Used to check a |sql| statement for syntactic validity. If the // statement is valid SQL, returns true. bool IsSQLValid(const char* sql); // Returns true if the given table exists. bool DoesTableExist(const char* table_name) const; // Returns true if the given index exists. bool DoesIndexExist(const char* index_name) const; // Returns true if a column with the given name exists in the given table. bool DoesColumnExist(const char* table_name, const char* column_name) const; // Returns sqlite's internal ID for the last inserted row. Valid only // immediately after an insert. int64_t GetLastInsertRowId() const; // Returns sqlite's count of the number of rows modified by the last // statement executed. Will be 0 if no statement has executed or the database // is closed. int GetLastChangeCount() const; // Errors -------------------------------------------------------------------- // Returns the error code associated with the last sqlite operation. int GetErrorCode() const; // Returns the errno associated with GetErrorCode(). See // SQLITE_LAST_ERRNO in SQLite documentation. int GetLastErrno() const; // Returns a pointer to a statically allocated string associated with the // last sqlite operation. const char* GetErrorMessage() const; // Diagnostics (for unit tests) ---------------------------------------------- int ExecuteAndReturnErrorCode(const char* sql); bool HasCachedStatement(const StatementId& id) const { return cachedStatements_.find(id) != cachedStatements_.end(); } int GetTransactionNesting() const { return transactionNesting_; } // Transactions -------------------------------------------------------------- bool BeginTransaction(); void RollbackTransaction(); bool CommitTransaction(); }; } } Orthanc-1.0.0/Core/SQLite/FunctionContext.cpp0000644000000000000000000000757112634042176017142 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of the CHU of Liege, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "FunctionContext.h" #include "OrthancSQLiteException.h" #include #include "sqlite3.h" namespace Orthanc { namespace SQLite { FunctionContext::FunctionContext(struct sqlite3_context* context, int argc, struct ::Mem** argv) { assert(context != NULL); assert(argc >= 0); assert(argv != NULL); context_ = context; argc_ = static_cast(argc); argv_ = argv; } void FunctionContext::CheckIndex(unsigned int index) const { if (index >= argc_) { throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange); } } ColumnType FunctionContext::GetColumnType(unsigned int index) const { CheckIndex(index); return static_cast(sqlite3_value_type(argv_[index])); } int FunctionContext::GetIntValue(unsigned int index) const { CheckIndex(index); return sqlite3_value_int(argv_[index]); } int64_t FunctionContext::GetInt64Value(unsigned int index) const { CheckIndex(index); return sqlite3_value_int64(argv_[index]); } double FunctionContext::GetDoubleValue(unsigned int index) const { CheckIndex(index); return sqlite3_value_double(argv_[index]); } std::string FunctionContext::GetStringValue(unsigned int index) const { CheckIndex(index); return std::string(reinterpret_cast(sqlite3_value_text(argv_[index]))); } bool FunctionContext::IsNullValue(unsigned int index) const { CheckIndex(index); return sqlite3_value_type(argv_[index]) == SQLITE_NULL; } void FunctionContext::SetNullResult() { sqlite3_result_null(context_); } void FunctionContext::SetIntResult(int value) { sqlite3_result_int(context_, value); } void FunctionContext::SetDoubleResult(double value) { sqlite3_result_double(context_, value); } void FunctionContext::SetStringResult(const std::string& str) { sqlite3_result_text(context_, str.data(), str.size(), SQLITE_TRANSIENT); } } } Orthanc-1.0.0/Core/SQLite/FunctionContext.h0000644000000000000000000000543312634042176016602 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of the CHU of Liege, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "Statement.h" struct sqlite3_context; struct Mem; // This corresponds to the opaque type "sqlite3_value" namespace Orthanc { namespace SQLite { class FunctionContext : public NonCopyable { friend class Connection; private: struct sqlite3_context* context_; unsigned int argc_; struct ::Mem** argv_; void CheckIndex(unsigned int index) const; public: FunctionContext(struct sqlite3_context* context, int argc, struct ::Mem** argv); ColumnType GetColumnType(unsigned int index) const; unsigned int GetParameterCount() const { return argc_; } int GetIntValue(unsigned int index) const; int64_t GetInt64Value(unsigned int index) const; double GetDoubleValue(unsigned int index) const; std::string GetStringValue(unsigned int index) const; bool IsNullValue(unsigned int index) const; void SetNullResult(); void SetIntResult(int value); void SetDoubleResult(double value); void SetStringResult(const std::string& str); }; } } Orthanc-1.0.0/Core/SQLite/IScalarFunction.h0000644000000000000000000000405112634042176016467 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of the CHU of Liege, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "NonCopyable.h" #include "FunctionContext.h" namespace Orthanc { namespace SQLite { class IScalarFunction : public NonCopyable { public: virtual ~IScalarFunction() { } virtual const char* GetName() const = 0; virtual unsigned int GetCardinality() const = 0; virtual void Compute(FunctionContext& context) = 0; }; } } Orthanc-1.0.0/Core/SQLite/ITransaction.h0000644000000000000000000000473112634042176016046 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "NonCopyable.h" namespace Orthanc { namespace SQLite { class ITransaction : public NonCopyable { public: virtual ~ITransaction() { } // Begins the transaction. This uses the default sqlite "deferred" transaction // type, which means that the DB lock is lazily acquired the next time the // database is accessed, not in the begin transaction command. virtual void Begin() = 0; // Rolls back the transaction. This will happen automatically if you do // nothing when the transaction goes out of scope. virtual void Rollback() = 0; // Commits the transaction, returning true on success. virtual void Commit() = 0; }; } } Orthanc-1.0.0/Core/SQLite/NonCopyable.h0000644000000000000000000000400512634042176015653 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once namespace Orthanc { namespace SQLite { // This class mimics "boost::noncopyable" class NonCopyable { private: NonCopyable(const NonCopyable&); NonCopyable& operator= (const NonCopyable&); protected: NonCopyable() { } ~NonCopyable() { } }; } } Orthanc-1.0.0/Core/SQLite/OrthancSQLiteException.h0000644000000000000000000001221712634042176020005 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #if ORTHANC_SQLITE_STANDALONE == 1 #include namespace Orthanc { namespace SQLite { // Auto-generated by "Resources/GenerateErrorCodes.py" enum ErrorCode { ErrorCode_ParameterOutOfRange, ErrorCode_BadParameterType, ErrorCode_SQLiteNotOpened, ErrorCode_SQLiteAlreadyOpened, ErrorCode_SQLiteCannotOpen, ErrorCode_SQLiteStatementAlreadyUsed, ErrorCode_SQLiteExecute, ErrorCode_SQLiteRollbackWithoutTransaction, ErrorCode_SQLiteCommitWithoutTransaction, ErrorCode_SQLiteRegisterFunction, ErrorCode_SQLiteFlush, ErrorCode_SQLiteCannotRun, ErrorCode_SQLiteCannotStep, ErrorCode_SQLiteBindOutOfRange, ErrorCode_SQLitePrepareStatement, ErrorCode_SQLiteTransactionAlreadyStarted, ErrorCode_SQLiteTransactionCommit, ErrorCode_SQLiteTransactionBegin }; class OrthancSQLiteException : public ::std::runtime_error { public: OrthancSQLiteException(ErrorCode error) : ::std::runtime_error(EnumerationToString(error)) { } // Auto-generated by "Resources/GenerateErrorCodes.py" static const char* EnumerationToString(ErrorCode code) { switch (code) { case ErrorCode_ParameterOutOfRange: return "Parameter out of range"; case ErrorCode_BadParameterType: return "Bad type for a parameter"; case ErrorCode_SQLiteNotOpened: return "SQLite: The database is not opened"; case ErrorCode_SQLiteAlreadyOpened: return "SQLite: Connection is already open"; case ErrorCode_SQLiteCannotOpen: return "SQLite: Unable to open the database"; case ErrorCode_SQLiteStatementAlreadyUsed: return "SQLite: This cached statement is already being referred to"; case ErrorCode_SQLiteExecute: return "SQLite: Cannot execute a command"; case ErrorCode_SQLiteRollbackWithoutTransaction: return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)"; case ErrorCode_SQLiteCommitWithoutTransaction: return "SQLite: Committing a nonexistent transaction"; case ErrorCode_SQLiteRegisterFunction: return "SQLite: Unable to register a function"; case ErrorCode_SQLiteFlush: return "SQLite: Unable to flush the database"; case ErrorCode_SQLiteCannotRun: return "SQLite: Cannot run a cached statement"; case ErrorCode_SQLiteCannotStep: return "SQLite: Cannot step over a cached statement"; case ErrorCode_SQLiteBindOutOfRange: return "SQLite: Bing a value while out of range (serious error)"; case ErrorCode_SQLitePrepareStatement: return "SQLite: Cannot prepare a cached statement"; case ErrorCode_SQLiteTransactionAlreadyStarted: return "SQLite: Beginning the same transaction twice"; case ErrorCode_SQLiteTransactionCommit: return "SQLite: Failure when committing the transaction"; case ErrorCode_SQLiteTransactionBegin: return "SQLite: Cannot start a transaction"; default: return "Unknown error code"; } } }; } } #else # include "../OrthancException.h" # define OrthancSQLiteException ::Orthanc::OrthancException #endif Orthanc-1.0.0/Core/SQLite/README.txt0000644000000000000000000000232012634042176014765 0ustar 00000000000000Introduction ============ The code in this folder is a standalone object-oriented wrapper around SQLite3. It is derived from the code of Chromium: http://src.chromium.org/viewvc/chrome/trunk/src/sql/ http://maxradi.us/documents/sqlite/ Main differences with Chromium ============================== * The reference counting mechanism has been reimplemented to make it simpler. * The OrthancException class is used for the exception mechanisms. * A statement is always valid (is_valid() always return true). * The classes and the methods have been renamed to meet Orthanc's coding conventions. Reuse in another software ========================= To use the Orthanc SQLite wrapper in another project than Orthanc, you just have to define the "ORTHANC_SQLITE_STANDALONE" macro. All the C++ exceptions generated by the wrapper will be objects of the class "::Orthanc::SQLite::OrthancSQLiteException", that derives from the standard exception class "::std::runtime_error". Licensing ========= The code in this folder is licensed under the 3-clause BSD license, in order to respect the original license of the code. It is pretty straightforward to extract the code from this folder and to include it in another project. Orthanc-1.0.0/Core/SQLite/Statement.cpp0000644000000000000000000002411712634042176015747 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "Statement.h" #include "Connection.h" #include #include #include #if ORTHANC_SQLITE_STANDALONE != 1 #include "../Logging.h" #endif #include "sqlite3.h" #if defined(_MSC_VER) #define snprintf _snprintf #endif namespace Orthanc { namespace SQLite { int Statement::CheckError(int err, ErrorCode code) const { bool succeeded = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE); if (!succeeded) { #if ORTHANC_SQLITE_STANDALONE != 1 char buffer[128]; snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); LOG(ERROR) << buffer; #endif throw OrthancSQLiteException(code); } return err; } void Statement::CheckOk(int err, ErrorCode code) const { if (err == SQLITE_RANGE) { // Binding to a non-existent variable is evidence of a serious error. throw OrthancSQLiteException(ErrorCode_SQLiteBindOutOfRange); } else if (err != SQLITE_OK) { #if ORTHANC_SQLITE_STANDALONE != 1 char buffer[128]; snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); LOG(ERROR) << buffer; #endif throw OrthancSQLiteException(code); } } Statement::Statement(Connection& database, const StatementId& id, const std::string& sql) : reference_(database.GetCachedStatement(id, sql.c_str())) { Reset(true); } Statement::Statement(Connection& database, const StatementId& id, const char* sql) : reference_(database.GetCachedStatement(id, sql)) { Reset(true); } Statement::Statement(Connection& database, const std::string& sql) : reference_(database.GetWrappedObject(), sql.c_str()) { } Statement::Statement(Connection& database, const char* sql) : reference_(database.GetWrappedObject(), sql) { } bool Statement::Run() { #if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Statement::Run " << sqlite3_sql(GetStatement()); #endif return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotRun) == SQLITE_DONE; } bool Statement::Step() { #if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Statement::Step " << sqlite3_sql(GetStatement()); #endif return CheckError(sqlite3_step(GetStatement()), ErrorCode_SQLiteCannotStep) == SQLITE_ROW; } void Statement::Reset(bool clear_bound_vars) { // We don't call CheckError() here because sqlite3_reset() returns // the last error that Step() caused thereby generating a second // spurious error callback. if (clear_bound_vars) sqlite3_clear_bindings(GetStatement()); //VLOG(1) << "SQLite::Statement::Reset"; sqlite3_reset(GetStatement()); } std::string Statement::GetOriginalSQLStatement() { return std::string(sqlite3_sql(GetStatement())); } void Statement::BindNull(int col) { CheckOk(sqlite3_bind_null(GetStatement(), col + 1), ErrorCode_BadParameterType); } void Statement::BindBool(int col, bool val) { BindInt(col, val ? 1 : 0); } void Statement::BindInt(int col, int val) { CheckOk(sqlite3_bind_int(GetStatement(), col + 1, val), ErrorCode_BadParameterType); } void Statement::BindInt64(int col, int64_t val) { CheckOk(sqlite3_bind_int64(GetStatement(), col + 1, val), ErrorCode_BadParameterType); } void Statement::BindDouble(int col, double val) { CheckOk(sqlite3_bind_double(GetStatement(), col + 1, val), ErrorCode_BadParameterType); } void Statement::BindCString(int col, const char* val) { CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val, -1, SQLITE_TRANSIENT), ErrorCode_BadParameterType); } void Statement::BindString(int col, const std::string& val) { CheckOk(sqlite3_bind_text(GetStatement(), col + 1, val.data(), val.size(), SQLITE_TRANSIENT), ErrorCode_BadParameterType); } /*void Statement::BindString16(int col, const string16& value) { BindString(col, UTF16ToUTF8(value)); }*/ void Statement::BindBlob(int col, const void* val, int val_len) { CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT), ErrorCode_BadParameterType); } int Statement::ColumnCount() const { return sqlite3_column_count(GetStatement()); } ColumnType Statement::GetColumnType(int col) const { // Verify that our enum matches sqlite's values. assert(COLUMN_TYPE_INTEGER == SQLITE_INTEGER); assert(COLUMN_TYPE_FLOAT == SQLITE_FLOAT); assert(COLUMN_TYPE_TEXT == SQLITE_TEXT); assert(COLUMN_TYPE_BLOB == SQLITE_BLOB); assert(COLUMN_TYPE_NULL == SQLITE_NULL); return static_cast(sqlite3_column_type(GetStatement(), col)); } ColumnType Statement::GetDeclaredColumnType(int col) const { std::string column_type(sqlite3_column_decltype(GetStatement(), col)); std::transform(column_type.begin(), column_type.end(), column_type.begin(), tolower); if (column_type == "integer") return COLUMN_TYPE_INTEGER; else if (column_type == "float") return COLUMN_TYPE_FLOAT; else if (column_type == "text") return COLUMN_TYPE_TEXT; else if (column_type == "blob") return COLUMN_TYPE_BLOB; return COLUMN_TYPE_NULL; } bool Statement::ColumnIsNull(int col) const { return sqlite3_column_type(GetStatement(), col) == SQLITE_NULL; } bool Statement::ColumnBool(int col) const { return !!ColumnInt(col); } int Statement::ColumnInt(int col) const { return sqlite3_column_int(GetStatement(), col); } int64_t Statement::ColumnInt64(int col) const { return sqlite3_column_int64(GetStatement(), col); } double Statement::ColumnDouble(int col) const { return sqlite3_column_double(GetStatement(), col); } std::string Statement::ColumnString(int col) const { const char* str = reinterpret_cast( sqlite3_column_text(GetStatement(), col)); int len = sqlite3_column_bytes(GetStatement(), col); std::string result; if (str && len > 0) result.assign(str, len); return result; } /*string16 Statement::ColumnString16(int col) const { std::string s = ColumnString(col); return !s.empty() ? UTF8ToUTF16(s) : string16(); }*/ int Statement::ColumnByteLength(int col) const { return sqlite3_column_bytes(GetStatement(), col); } const void* Statement::ColumnBlob(int col) const { return sqlite3_column_blob(GetStatement(), col); } bool Statement::ColumnBlobAsString(int col, std::string* blob) { const void* p = ColumnBlob(col); size_t len = ColumnByteLength(col); blob->resize(len); if (blob->size() != len) { return false; } blob->assign(reinterpret_cast(p), len); return true; } /*bool Statement::ColumnBlobAsString16(int col, string16* val) const { const void* data = ColumnBlob(col); size_t len = ColumnByteLength(col) / sizeof(char16); val->resize(len); if (val->size() != len) return false; val->assign(reinterpret_cast(data), len); return true; }*/ /*bool Statement::ColumnBlobAsVector(int col, std::vector* val) const { val->clear(); const void* data = sqlite3_column_blob(GetStatement(), col); int len = sqlite3_column_bytes(GetStatement(), col); if (data && len > 0) { val->resize(len); memcpy(&(*val)[0], data, len); } return true; }*/ /*bool Statement::ColumnBlobAsVector( int col, std::vector* val) const { return ColumnBlobAsVector(col, reinterpret_cast< std::vector* >(val)); }*/ } } Orthanc-1.0.0/Core/SQLite/Statement.h0000644000000000000000000001346112634042176015414 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "NonCopyable.h" #include "OrthancSQLiteException.h" #include "StatementId.h" #include "StatementReference.h" #include #include #if ORTHANC_BUILD_UNIT_TESTS == 1 #include #endif struct sqlite3_stmt; namespace Orthanc { namespace SQLite { class Connection; // Possible return values from ColumnType in a statement. These // should match the values in sqlite3.h. enum ColumnType { COLUMN_TYPE_INTEGER = 1, COLUMN_TYPE_FLOAT = 2, COLUMN_TYPE_TEXT = 3, COLUMN_TYPE_BLOB = 4, COLUMN_TYPE_NULL = 5 }; class Statement : public NonCopyable { friend class Connection; #if ORTHANC_BUILD_UNIT_TESTS == 1 FRIEND_TEST(SQLStatementTest, Run); FRIEND_TEST(SQLStatementTest, Reset); #endif private: StatementReference reference_; int CheckError(int err, ErrorCode code) const; void CheckOk(int err, ErrorCode code) const; struct sqlite3_stmt* GetStatement() const { return reference_.GetWrappedObject(); } public: Statement(Connection& database, const std::string& sql); Statement(Connection& database, const StatementId& id, const std::string& sql); Statement(Connection& database, const char* sql); Statement(Connection& database, const StatementId& id, const char* sql); ~Statement() { Reset(); } bool Run(); bool Step(); // Diagnostics -------------------------------------------------------------- std::string GetOriginalSQLStatement(); // Binding ------------------------------------------------------------------- // These all take a 0-based argument index void BindNull(int col); void BindBool(int col, bool val); void BindInt(int col, int val); void BindInt64(int col, int64_t val); void BindDouble(int col, double val); void BindCString(int col, const char* val); void BindString(int col, const std::string& val); //void BindString16(int col, const string16& value); void BindBlob(int col, const void* value, int value_len); // Retrieving ---------------------------------------------------------------- // Returns the number of output columns in the result. int ColumnCount() const; // Returns the type associated with the given column. // // Watch out: the type may be undefined if you've done something to cause a // "type conversion." This means requesting the value of a column of a type // where that type is not the native type. For safety, call ColumnType only // on a column before getting the value out in any way. ColumnType GetColumnType(int col) const; ColumnType GetDeclaredColumnType(int col) const; // These all take a 0-based argument index. bool ColumnIsNull(int col) const ; bool ColumnBool(int col) const; int ColumnInt(int col) const; int64_t ColumnInt64(int col) const; double ColumnDouble(int col) const; std::string ColumnString(int col) const; //string16 ColumnString16(int col) const; // When reading a blob, you can get a raw pointer to the underlying data, // along with the length, or you can just ask us to copy the blob into a // vector. Danger! ColumnBlob may return NULL if there is no data! int ColumnByteLength(int col) const; const void* ColumnBlob(int col) const; bool ColumnBlobAsString(int col, std::string* blob); //bool ColumnBlobAsString16(int col, string16* val) const; //bool ColumnBlobAsVector(int col, std::vector* val) const; //bool ColumnBlobAsVector(int col, std::vector* val) const; // Resets the statement to its initial condition. This includes any current // result row, and also the bound variables if the |clear_bound_vars| is true. void Reset(bool clear_bound_vars = true); }; } } Orthanc-1.0.0/Core/SQLite/StatementId.cpp0000644000000000000000000000414412634042176016222 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "StatementId.h" #include namespace Orthanc { namespace SQLite { bool StatementId::operator< (const StatementId& other) const { if (line_ != other.line_) return line_ < other.line_; return strcmp(file_, other.file_) < 0; } } } Orthanc-1.0.0/Core/SQLite/StatementId.h0000644000000000000000000000412112634042176015662 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once namespace Orthanc { namespace SQLite { class StatementId { private: const char* file_; int line_; StatementId(); // Forbidden public: StatementId(const char* file, int line) : file_(file), line_(line) { } bool operator< (const StatementId& other) const; }; } } Orthanc-1.0.0/Core/SQLite/StatementReference.cpp0000644000000000000000000001035012634042176017560 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "StatementReference.h" #include "OrthancSQLiteException.h" #if ORTHANC_SQLITE_STANDALONE != 1 #include "../Logging.h" #endif #include #include #include "sqlite3.h" namespace Orthanc { namespace SQLite { bool StatementReference::IsRoot() const { return root_ == NULL; } StatementReference::StatementReference() { root_ = NULL; refCount_ = 0; statement_ = NULL; assert(IsRoot()); } StatementReference::StatementReference(sqlite3* database, const char* sql) { if (database == NULL || sql == NULL) { throw OrthancSQLiteException(ErrorCode_ParameterOutOfRange); } root_ = NULL; refCount_ = 0; int error = sqlite3_prepare_v2(database, sql, -1, &statement_, NULL); if (error != SQLITE_OK) { #if ORTHANC_SQLITE_STANDALONE != 1 LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database); #endif throw OrthancSQLiteException(ErrorCode_SQLitePrepareStatement); } assert(IsRoot()); } StatementReference::StatementReference(StatementReference& other) { refCount_ = 0; if (other.IsRoot()) { root_ = &other; } else { root_ = other.root_; } root_->refCount_++; statement_ = root_->statement_; assert(!IsRoot()); } StatementReference::~StatementReference() { if (IsRoot()) { if (refCount_ != 0) { // There remain references to this object. We cannot throw // an exception because: // http://www.parashift.com/c++-faq/dtors-shouldnt-throw.html #if ORTHANC_SQLITE_STANDALONE != 1 LOG(ERROR) << "Bad value of the reference counter"; #endif } else if (statement_ != NULL) { sqlite3_finalize(statement_); } } else { if (root_->refCount_ == 0) { // There remain references to this object. We cannot throw // an exception because: // http://www.parashift.com/c++-faq/dtors-shouldnt-throw.html #if ORTHANC_SQLITE_STANDALONE != 1 LOG(ERROR) << "Bad value of the reference counter"; #endif } else { root_->refCount_--; } } } uint32_t StatementReference::GetReferenceCount() const { return refCount_; } } } Orthanc-1.0.0/Core/SQLite/StatementReference.h0000644000000000000000000000507612634042176017236 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "NonCopyable.h" #include #include #include struct sqlite3; struct sqlite3_stmt; namespace Orthanc { namespace SQLite { class StatementReference : NonCopyable { private: StatementReference* root_; // Only used for non-root nodes uint32_t refCount_; // Only used for root node struct sqlite3_stmt* statement_; bool IsRoot() const; public: StatementReference(); StatementReference(sqlite3* database, const char* sql); StatementReference(StatementReference& other); ~StatementReference(); uint32_t GetReferenceCount() const; struct sqlite3_stmt* GetWrappedObject() const { assert(statement_ != NULL); return statement_; } }; } } Orthanc-1.0.0/Core/SQLite/Transaction.cpp0000644000000000000000000000601512634042176016265 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" #endif #include "Transaction.h" #include "OrthancSQLiteException.h" namespace Orthanc { namespace SQLite { Transaction::Transaction(Connection& connection) : connection_(connection), isOpen_(false) { } Transaction::~Transaction() { if (isOpen_) { connection_.RollbackTransaction(); } } void Transaction::Begin() { if (isOpen_) { throw OrthancSQLiteException(ErrorCode_SQLiteTransactionAlreadyStarted); } isOpen_ = connection_.BeginTransaction(); if (!isOpen_) { throw OrthancSQLiteException(ErrorCode_SQLiteTransactionBegin); } } void Transaction::Rollback() { if (!isOpen_) { throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } isOpen_ = false; connection_.RollbackTransaction(); } void Transaction::Commit() { if (!isOpen_) { throw OrthancSQLiteException(ErrorCode_SQLiteRollbackWithoutTransaction); } isOpen_ = false; if (!connection_.CommitTransaction()) { throw OrthancSQLiteException(ErrorCode_SQLiteTransactionCommit); } } } } Orthanc-1.0.0/Core/SQLite/Transaction.h0000644000000000000000000000461512634042176015736 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * * Copyright (C) 2012-2015 Sebastien Jodogne , * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc., the name of the CHU of Liege, * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ #pragma once #include "Connection.h" #include "ITransaction.h" namespace Orthanc { namespace SQLite { class Transaction : public ITransaction { private: Connection& connection_; // True when the transaction is open, false when it's already been committed // or rolled back. bool isOpen_; public: explicit Transaction(Connection& connection); virtual ~Transaction(); // Returns true when there is a transaction that has been successfully begun. bool IsOpen() const { return isOpen_; } virtual void Begin(); virtual void Rollback(); virtual void Commit(); }; } } Orthanc-1.0.0/Core/Toolbox.cpp0000644000000000000000000007540412634042176014275 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "Toolbox.h" #include "OrthancException.h" #include "Logging.h" #include #include #include #include #include #include #include #include #include #if BOOST_HAS_DATE_TIME == 1 #include #endif #if BOOST_HAS_REGEX == 1 #include #endif #if defined(_WIN32) #include #include // For "_spawnvp()" and "_getpid()" #else #include // For "execvp()" #include // For "waitpid()" #endif #if defined(__APPLE__) && defined(__MACH__) #include /* _NSGetExecutablePath */ #include /* PATH_MAX */ #endif #if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) #include /* PATH_MAX */ #include #include #endif #if BOOST_HAS_LOCALE != 1 #error Since version 0.7.6, Orthanc entirely relies on boost::locale #endif #include #if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 #include "../Resources/ThirdParty/md5/md5.h" #endif #if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 #include "../Resources/ThirdParty/base64/base64.h" #endif #if defined(_MSC_VER) && (_MSC_VER < 1800) // Patch for the missing "_strtoll" symbol when compiling with Visual Studio < 2013 extern "C" { int64_t _strtoi64(const char *nptr, char **endptr, int base); int64_t strtoll(const char *nptr, char **endptr, int base) { return _strtoi64(nptr, endptr, base); } } #endif #if ORTHANC_PUGIXML_ENABLED == 1 #include "ChunkedBuffer.h" #include #endif namespace Orthanc { static bool finish; #if defined(_WIN32) static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType) { // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx finish = true; return true; } #else static void SignalHandler(int) { finish = true; } #endif void Toolbox::USleep(uint64_t microSeconds) { #if defined(_WIN32) ::Sleep(static_cast(microSeconds / static_cast(1000))); #elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) usleep(microSeconds); #else #error Support your platform here #endif } static void ServerBarrierInternal(const bool* stopFlag) { #if defined(_WIN32) SetConsoleCtrlHandler(ConsoleControlHandler, true); #else signal(SIGINT, SignalHandler); signal(SIGQUIT, SignalHandler); signal(SIGTERM, SignalHandler); #endif // Active loop that awakens every 100ms finish = false; while (!(*stopFlag || finish)) { Toolbox::USleep(100 * 1000); } #if defined(_WIN32) SetConsoleCtrlHandler(ConsoleControlHandler, false); #else signal(SIGINT, NULL); signal(SIGQUIT, NULL); signal(SIGTERM, NULL); #endif } void Toolbox::ServerBarrier(const bool& stopFlag) { ServerBarrierInternal(&stopFlag); } void Toolbox::ServerBarrier() { const bool stopFlag = false; ServerBarrierInternal(&stopFlag); } void Toolbox::ToUpperCase(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), toupper); } void Toolbox::ToLowerCase(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), tolower); } void Toolbox::ToUpperCase(std::string& result, const std::string& source) { result = source; ToUpperCase(result); } void Toolbox::ToLowerCase(std::string& result, const std::string& source) { result = source; ToLowerCase(result); } void Toolbox::ReadFile(std::string& content, const std::string& path) { if (!boost::filesystem::is_regular_file(path)) { LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; throw OrthancException(ErrorCode_RegularFileExpected); } boost::filesystem::ifstream f; f.open(path, std::ifstream::in | std::ifstream::binary); if (!f.good()) { throw OrthancException(ErrorCode_InexistentFile); } // http://www.cplusplus.com/reference/iostream/istream/tellg/ f.seekg(0, std::ios::end); std::streamsize size = f.tellg(); f.seekg(0, std::ios::beg); content.resize(size); if (size != 0) { f.read(reinterpret_cast(&content[0]), size); } f.close(); } void Toolbox::WriteFile(const void* content, size_t size, const std::string& path) { boost::filesystem::ofstream f; f.open(path, std::ofstream::binary); if (!f.good()) { throw OrthancException(ErrorCode_CannotWriteFile); } if (size != 0) { f.write(reinterpret_cast(content), size); } f.close(); } void Toolbox::WriteFile(const std::string& content, const std::string& path) { WriteFile(content.size() > 0 ? content.c_str() : NULL, content.size(), path); } void Toolbox::RemoveFile(const std::string& path) { if (boost::filesystem::exists(path)) { if (boost::filesystem::is_regular_file(path)) { boost::filesystem::remove(path); } else { throw OrthancException(ErrorCode_RegularFileExpected); } } } void Toolbox::SplitUriComponents(UriComponents& components, const std::string& uri) { static const char URI_SEPARATOR = '/'; components.clear(); if (uri.size() == 0 || uri[0] != URI_SEPARATOR) { throw OrthancException(ErrorCode_UriSyntax); } // Count the number of slashes in the URI to make an assumption // about the number of components in the URI unsigned int estimatedSize = 0; for (unsigned int i = 0; i < uri.size(); i++) { if (uri[i] == URI_SEPARATOR) estimatedSize++; } components.reserve(estimatedSize - 1); unsigned int start = 1; unsigned int end = 1; while (end < uri.size()) { // This is the loop invariant assert(uri[start - 1] == '/' && (end >= start)); if (uri[end] == '/') { components.push_back(std::string(&uri[start], end - start)); end++; start = end; } else { end++; } } if (start < uri.size()) { components.push_back(std::string(&uri[start], end - start)); } for (size_t i = 0; i < components.size(); i++) { if (components[i].size() == 0) { // Empty component, as in: "/coucou//e" throw OrthancException(ErrorCode_UriSyntax); } } } void Toolbox::TruncateUri(UriComponents& target, const UriComponents& source, size_t fromLevel) { target.clear(); if (source.size() > fromLevel) { target.resize(source.size() - fromLevel); size_t j = 0; for (size_t i = fromLevel; i < source.size(); i++, j++) { target[j] = source[i]; } assert(j == target.size()); } } bool Toolbox::IsChildUri(const UriComponents& baseUri, const UriComponents& testedUri) { if (testedUri.size() < baseUri.size()) { return false; } for (size_t i = 0; i < baseUri.size(); i++) { if (baseUri[i] != testedUri[i]) return false; } return true; } std::string Toolbox::AutodetectMimeType(const std::string& path) { std::string contentType; size_t lastDot = path.rfind('.'); size_t lastSlash = path.rfind('/'); if (lastDot == std::string::npos || (lastSlash != std::string::npos && lastDot < lastSlash)) { // No trailing dot, unable to detect the content type } else { const char* extension = &path[lastDot + 1]; // http://en.wikipedia.org/wiki/Mime_types // Text types if (!strcmp(extension, "txt")) contentType = "text/plain"; else if (!strcmp(extension, "html")) contentType = "text/html"; else if (!strcmp(extension, "xml")) contentType = "text/xml"; else if (!strcmp(extension, "css")) contentType = "text/css"; // Application types else if (!strcmp(extension, "js")) contentType = "application/javascript"; else if (!strcmp(extension, "json")) contentType = "application/json"; else if (!strcmp(extension, "pdf")) contentType = "application/pdf"; // Images types else if (!strcmp(extension, "jpg") || !strcmp(extension, "jpeg")) contentType = "image/jpeg"; else if (!strcmp(extension, "gif")) contentType = "image/gif"; else if (!strcmp(extension, "png")) contentType = "image/png"; } return contentType; } std::string Toolbox::FlattenUri(const UriComponents& components, size_t fromLevel) { if (components.size() <= fromLevel) { return "/"; } else { std::string r; for (size_t i = fromLevel; i < components.size(); i++) { r += "/" + components[i]; } return r; } } uint64_t Toolbox::GetFileSize(const std::string& path) { try { return static_cast(boost::filesystem::file_size(path)); } catch (boost::filesystem::filesystem_error&) { throw OrthancException(ErrorCode_InexistentFile); } } #if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 static char GetHexadecimalCharacter(uint8_t value) { assert(value < 16); if (value < 10) { return value + '0'; } else { return (value - 10) + 'a'; } } void Toolbox::ComputeMD5(std::string& result, const std::string& data) { if (data.size() > 0) { ComputeMD5(result, &data[0], data.size()); } else { ComputeMD5(result, NULL, 0); } } void Toolbox::ComputeMD5(std::string& result, const void* data, size_t size) { md5_state_s state; md5_init(&state); if (size > 0) { md5_append(&state, reinterpret_cast(data), static_cast(size)); } md5_byte_t actualHash[16]; md5_finish(&state, actualHash); result.resize(32); for (unsigned int i = 0; i < 16; i++) { result[2 * i] = GetHexadecimalCharacter(static_cast(actualHash[i] / 16)); result[2 * i + 1] = GetHexadecimalCharacter(static_cast(actualHash[i] % 16)); } } #endif #if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void Toolbox::EncodeBase64(std::string& result, const std::string& data) { result = base64_encode(data); } void Toolbox::DecodeBase64(std::string& result, const std::string& data) { result = base64_decode(data); } # if BOOST_HAS_REGEX == 1 void Toolbox::DecodeDataUriScheme(std::string& mime, std::string& content, const std::string& source) { boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)", boost::regex::icase /* case insensitive search */); boost::cmatch what; if (regex_match(source.c_str(), what, pattern)) { mime = what[1]; DecodeBase64(content, what[2]); } else { throw OrthancException(ErrorCode_BadFileFormat); } } # endif void Toolbox::EncodeDataUriScheme(std::string& result, const std::string& mime, const std::string& content) { result = "data:" + mime + ";base64," + base64_encode(content); } #endif #if defined(_WIN32) static std::string GetPathToExecutableInternal() { // Yes, this is ugly, but there is no simple way to get the // required buffer size, so we use a big constant std::vector buffer(32768); /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast(buffer.size() - 1)); return std::string(&buffer[0]); } #elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) static std::string GetPathToExecutableInternal() { std::vector buffer(PATH_MAX + 1); ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1); if (bytes == 0) { throw OrthancException(ErrorCode_PathToExecutable); } return std::string(&buffer[0]); } #elif defined(__APPLE__) && defined(__MACH__) static std::string GetPathToExecutableInternal() { char pathbuf[PATH_MAX + 1]; unsigned int bufsize = static_cast(sizeof(pathbuf)); _NSGetExecutablePath( pathbuf, &bufsize); return std::string(pathbuf); } #else #error Support your platform here #endif std::string Toolbox::GetPathToExecutable() { boost::filesystem::path p(GetPathToExecutableInternal()); return boost::filesystem::absolute(p).string(); } std::string Toolbox::GetDirectoryOfExecutable() { boost::filesystem::path p(GetPathToExecutableInternal()); return boost::filesystem::absolute(p.parent_path()).string(); } static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding) { switch (sourceEncoding) { case Encoding_Utf8: return "UTF-8"; case Encoding_Ascii: return "ASCII"; case Encoding_Latin1: return "ISO-8859-1"; break; case Encoding_Latin2: return "ISO-8859-2"; break; case Encoding_Latin3: return "ISO-8859-3"; break; case Encoding_Latin4: return "ISO-8859-4"; break; case Encoding_Latin5: return "ISO-8859-9"; break; case Encoding_Cyrillic: return "ISO-8859-5"; break; case Encoding_Windows1251: return "WINDOWS-1251"; break; case Encoding_Arabic: return "ISO-8859-6"; break; case Encoding_Greek: return "ISO-8859-7"; break; case Encoding_Hebrew: return "ISO-8859-8"; break; case Encoding_Japanese: return "SHIFT-JIS"; break; case Encoding_Chinese: return "GB18030"; break; case Encoding_Thai: return "TIS620.2533-0"; break; default: throw OrthancException(ErrorCode_NotImplemented); } } std::string Toolbox::ConvertToUtf8(const std::string& source, Encoding sourceEncoding) { if (sourceEncoding == Encoding_Utf8) { // Already in UTF-8: No conversion is required return source; } if (sourceEncoding == Encoding_Ascii) { return ConvertToAscii(source); } const char* encoding = GetBoostLocaleEncoding(sourceEncoding); try { return boost::locale::conv::to_utf(source, encoding); } catch (std::runtime_error&) { // Bad input string or bad encoding return ConvertToAscii(source); } } std::string Toolbox::ConvertFromUtf8(const std::string& source, Encoding targetEncoding) { if (targetEncoding == Encoding_Utf8) { // Already in UTF-8: No conversion is required return source; } if (targetEncoding == Encoding_Ascii) { return ConvertToAscii(source); } const char* encoding = GetBoostLocaleEncoding(targetEncoding); try { return boost::locale::conv::from_utf(source, encoding); } catch (std::runtime_error&) { // Bad input string or bad encoding return ConvertToAscii(source); } } std::string Toolbox::ConvertToAscii(const std::string& source) { std::string result; result.reserve(source.size() + 1); for (size_t i = 0; i < source.size(); i++) { if (source[i] <= 127 && source[i] >= 0 && !iscntrl(source[i])) { result.push_back(source[i]); } } return result; } void Toolbox::ComputeSHA1(std::string& result, const void* data, size_t size) { boost::uuids::detail::sha1 sha1; if (size > 0) { sha1.process_bytes(data, size); } unsigned int digest[5]; // Sanity check for the memory layout: A SHA-1 digest is 160 bits wide assert(sizeof(unsigned int) == 4 && sizeof(digest) == (160 / 8)); sha1.get_digest(digest); result.resize(8 * 5 + 4); sprintf(&result[0], "%08x-%08x-%08x-%08x-%08x", digest[0], digest[1], digest[2], digest[3], digest[4]); } void Toolbox::ComputeSHA1(std::string& result, const std::string& data) { if (data.size() > 0) { ComputeSHA1(result, data.c_str(), data.size()); } else { ComputeSHA1(result, NULL, 0); } } bool Toolbox::IsSHA1(const char* str, size_t size) { if (size == 0) { return false; } const char* start = str; const char* end = str + size; // Trim the beginning of the string while (start < end) { if (*start == '\0' || isspace(*start)) { start++; } else { break; } } // Trim the trailing of the string while (start < end) { if (*(end - 1) == '\0' || isspace(*(end - 1))) { end--; } else { break; } } if (end - start != 44) { return false; } for (unsigned int i = 0; i < 44; i++) { if (i == 8 || i == 17 || i == 26 || i == 35) { if (start[i] != '-') return false; } else { if (!isalnum(start[i])) return false; } } return true; } bool Toolbox::IsSHA1(const std::string& s) { if (s.size() == 0) { return false; } else { return IsSHA1(s.c_str(), s.size()); } } #if BOOST_HAS_DATE_TIME == 1 std::string Toolbox::GetNowIsoString() { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); return boost::posix_time::to_iso_string(now); } void Toolbox::GetNowDicom(std::string& date, std::string& time) { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); tm tm = boost::posix_time::to_tm(now); char s[32]; sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); date.assign(s); // TODO milliseconds sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0); time.assign(s); } #endif std::string Toolbox::StripSpaces(const std::string& source) { size_t first = 0; while (first < source.length() && isspace(source[first])) { first++; } if (first == source.length()) { // String containing only spaces return ""; } size_t last = source.length(); while (last > first && isspace(source[last - 1])) { last--; } assert(first <= last); return source.substr(first, last - first); } static char Hex2Dec(char c) { return ((c >= '0' && c <= '9') ? c - '0' : ((c >= 'a' && c <= 'f') ? c - 'a' + 10 : c - 'A' + 10)); } void Toolbox::UrlDecode(std::string& s) { // http://en.wikipedia.org/wiki/Percent-encoding // http://www.w3schools.com/tags/ref_urlencode.asp // http://stackoverflow.com/questions/154536/encode-decode-urls-in-c if (s.size() == 0) { return; } size_t source = 0; size_t target = 0; while (source < s.size()) { if (s[source] == '%' && source + 2 < s.size() && isalnum(s[source + 1]) && isalnum(s[source + 2])) { s[target] = (Hex2Dec(s[source + 1]) << 4) | Hex2Dec(s[source + 2]); source += 3; target += 1; } else { if (s[source] == '+') s[target] = ' '; else s[target] = s[source]; source++; target++; } } s.resize(target); } Endianness Toolbox::DetectEndianness() { // http://sourceforge.net/p/predef/wiki/Endianness/ uint8_t buffer[4]; buffer[0] = 0x00; buffer[1] = 0x01; buffer[2] = 0x02; buffer[3] = 0x03; switch (*((uint32_t *)buffer)) { case 0x00010203: return Endianness_Big; case 0x03020100: return Endianness_Little; default: throw OrthancException(ErrorCode_NotImplemented); } } #if BOOST_HAS_REGEX == 1 std::string Toolbox::WildcardToRegularExpression(const std::string& source) { // TODO - Speed up this with a regular expression std::string result = source; // Escape all special characters boost::replace_all(result, "\\", "\\\\"); boost::replace_all(result, "^", "\\^"); boost::replace_all(result, ".", "\\."); boost::replace_all(result, "$", "\\$"); boost::replace_all(result, "|", "\\|"); boost::replace_all(result, "(", "\\("); boost::replace_all(result, ")", "\\)"); boost::replace_all(result, "[", "\\["); boost::replace_all(result, "]", "\\]"); boost::replace_all(result, "+", "\\+"); boost::replace_all(result, "/", "\\/"); boost::replace_all(result, "{", "\\{"); boost::replace_all(result, "}", "\\}"); // Convert wildcards '*' and '?' to their regex equivalents boost::replace_all(result, "?", "."); boost::replace_all(result, "*", ".*"); return result; } #endif void Toolbox::TokenizeString(std::vector& result, const std::string& value, char separator) { result.clear(); std::string currentItem; for (size_t i = 0; i < value.size(); i++) { if (value[i] == separator) { result.push_back(currentItem); currentItem.clear(); } else { currentItem.push_back(value[i]); } } result.push_back(currentItem); } void Toolbox::MakeDirectory(const std::string& path) { if (boost::filesystem::exists(path)) { if (!boost::filesystem::is_directory(path)) { throw OrthancException(ErrorCode_DirectoryOverFile); } } else { if (!boost::filesystem::create_directories(path)) { throw OrthancException(ErrorCode_MakeDirectory); } } } bool Toolbox::IsExistingFile(const std::string& path) { return boost::filesystem::exists(path); } #if ORTHANC_PUGIXML_ENABLED == 1 class ChunkedBufferWriter : public pugi::xml_writer { private: ChunkedBuffer buffer_; public: virtual void write(const void *data, size_t size) { if (size > 0) { buffer_.AddChunk(reinterpret_cast(data), size); } } void Flatten(std::string& s) { buffer_.Flatten(s); } }; static void JsonToXmlInternal(pugi::xml_node& target, const Json::Value& source, const std::string& arrayElement) { // http://jsoncpp.sourceforge.net/value_8h_source.html#l00030 switch (source.type()) { case Json::nullValue: { target.append_child(pugi::node_pcdata).set_value("null"); break; } case Json::intValue: { std::string s = boost::lexical_cast(source.asInt()); target.append_child(pugi::node_pcdata).set_value(s.c_str()); break; } case Json::uintValue: { std::string s = boost::lexical_cast(source.asUInt()); target.append_child(pugi::node_pcdata).set_value(s.c_str()); break; } case Json::realValue: { std::string s = boost::lexical_cast(source.asFloat()); target.append_child(pugi::node_pcdata).set_value(s.c_str()); break; } case Json::stringValue: { target.append_child(pugi::node_pcdata).set_value(source.asString().c_str()); break; } case Json::booleanValue: { target.append_child(pugi::node_pcdata).set_value(source.asBool() ? "true" : "false"); break; } case Json::arrayValue: { for (Json::Value::ArrayIndex i = 0; i < source.size(); i++) { pugi::xml_node node = target.append_child(); node.set_name(arrayElement.c_str()); JsonToXmlInternal(node, source[i], arrayElement); } break; } case Json::objectValue: { Json::Value::Members members = source.getMemberNames(); for (size_t i = 0; i < members.size(); i++) { pugi::xml_node node = target.append_child(); node.set_name(members[i].c_str()); JsonToXmlInternal(node, source[members[i]], arrayElement); } break; } default: throw OrthancException(ErrorCode_NotImplemented); } } void Toolbox::JsonToXml(std::string& target, const Json::Value& source, const std::string& rootElement, const std::string& arrayElement) { pugi::xml_document doc; pugi::xml_node n = doc.append_child(rootElement.c_str()); JsonToXmlInternal(n, source, arrayElement); pugi::xml_node decl = doc.prepend_child(pugi::node_declaration); decl.append_attribute("version").set_value("1.0"); decl.append_attribute("encoding").set_value("utf-8"); ChunkedBufferWriter writer; doc.save(writer, " ", pugi::format_default, pugi::encoding_utf8); writer.Flatten(target); } #endif void Toolbox::ExecuteSystemCommand(const std::string& command, const std::vector& arguments) { // Convert the arguments as a C array std::vector args(arguments.size() + 2); args.front() = const_cast(command.c_str()); for (size_t i = 0; i < arguments.size(); i++) { args[i + 1] = const_cast(arguments[i].c_str()); } args.back() = NULL; int status; #if defined(_WIN32) // http://msdn.microsoft.com/en-us/library/275khfab.aspx status = static_cast(_spawnvp(_P_OVERLAY, command.c_str(), &args[0])); #else int pid = fork(); if (pid == -1) { // Error in fork() #if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "Cannot fork a child process"; #endif throw OrthancException(ErrorCode_SystemCommand); } else if (pid == 0) { // Execute the system command in the child process execvp(command.c_str(), &args[0]); // We should never get here _exit(1); } else { // Wait for the system command to exit waitpid(pid, &status, 0); } #endif if (status != 0) { #if ORTHANC_ENABLE_LOGGING == 1 LOG(ERROR) << "System command failed with status code " << status; #endif throw OrthancException(ErrorCode_SystemCommand); } } bool Toolbox::IsInteger(const std::string& str) { std::string s = StripSpaces(str); if (s.size() == 0) { return false; } size_t pos = 0; if (s[0] == '-') { if (s.size() == 1) { return false; } pos = 1; } while (pos < s.size()) { if (!isdigit(s[pos])) { return false; } pos++; } return true; } void Toolbox::CopyJsonWithoutComments(Json::Value& target, const Json::Value& source) { switch (source.type()) { case Json::nullValue: target = Json::nullValue; break; case Json::intValue: target = source.asInt64(); break; case Json::uintValue: target = source.asUInt64(); break; case Json::realValue: target = source.asDouble(); break; case Json::stringValue: target = source.asString(); break; case Json::booleanValue: target = source.asBool(); break; case Json::arrayValue: { target = Json::arrayValue; for (Json::Value::ArrayIndex i = 0; i < source.size(); i++) { Json::Value& item = target.append(Json::nullValue); CopyJsonWithoutComments(item, source[i]); } break; } case Json::objectValue: { target = Json::objectValue; Json::Value::Members members = source.getMemberNames(); for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) { const std::string item = members[i]; CopyJsonWithoutComments(target[item], source[item]); } break; } default: break; } } bool Toolbox::StartsWith(const std::string& str, const std::string& prefix) { if (str.size() < prefix.size()) { return false; } else { return str.compare(0, prefix.size(), prefix) == 0; } } int Toolbox::GetProcessId() { #if defined(_WIN32) return static_cast(_getpid()); #else return static_cast(getpid()); #endif } } Orthanc-1.0.0/Core/Toolbox.h0000644000000000000000000001367612634042176013745 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "Enumerations.h" #include #include #include #include namespace Orthanc { typedef std::vector UriComponents; class NullType { }; namespace Toolbox { void ServerBarrier(const bool& stopFlag); void ServerBarrier(); void ToUpperCase(std::string& s); // Inplace version void ToLowerCase(std::string& s); // Inplace version void ToUpperCase(std::string& result, const std::string& source); void ToLowerCase(std::string& result, const std::string& source); void ReadFile(std::string& content, const std::string& path); void WriteFile(const std::string& content, const std::string& path); void WriteFile(const void* content, size_t size, const std::string& path); void USleep(uint64_t microSeconds); void RemoveFile(const std::string& path); void SplitUriComponents(UriComponents& components, const std::string& uri); void TruncateUri(UriComponents& target, const UriComponents& source, size_t fromLevel); bool IsChildUri(const UriComponents& baseUri, const UriComponents& testedUri); std::string AutodetectMimeType(const std::string& path); std::string FlattenUri(const UriComponents& components, size_t fromLevel = 0); uint64_t GetFileSize(const std::string& path); #if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1 void ComputeMD5(std::string& result, const std::string& data); void ComputeMD5(std::string& result, const void* data, size_t size); #endif void ComputeSHA1(std::string& result, const std::string& data); void ComputeSHA1(std::string& result, const void* data, size_t size); bool IsSHA1(const char* str, size_t size); bool IsSHA1(const std::string& s); #if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1 void DecodeBase64(std::string& result, const std::string& data); void EncodeBase64(std::string& result, const std::string& data); # if BOOST_HAS_REGEX == 1 void DecodeDataUriScheme(std::string& mime, std::string& content, const std::string& source); # endif void EncodeDataUriScheme(std::string& result, const std::string& mime, const std::string& content); #endif std::string GetPathToExecutable(); std::string GetDirectoryOfExecutable(); std::string ConvertToUtf8(const std::string& source, Encoding sourceEncoding); std::string ConvertFromUtf8(const std::string& source, Encoding targetEncoding); std::string ConvertToAscii(const std::string& source); std::string StripSpaces(const std::string& source); #if BOOST_HAS_DATE_TIME == 1 std::string GetNowIsoString(); void GetNowDicom(std::string& date, std::string& time); #endif // In-place percent-decoding for URL void UrlDecode(std::string& s); Endianness DetectEndianness(); #if BOOST_HAS_REGEX == 1 std::string WildcardToRegularExpression(const std::string& s); #endif void TokenizeString(std::vector& result, const std::string& source, char separator); void MakeDirectory(const std::string& path); bool IsExistingFile(const std::string& path); #if ORTHANC_PUGIXML_ENABLED == 1 void JsonToXml(std::string& target, const Json::Value& source, const std::string& rootElement = "root", const std::string& arrayElement = "item"); #endif void ExecuteSystemCommand(const std::string& command, const std::vector& arguments); bool IsInteger(const std::string& str); void CopyJsonWithoutComments(Json::Value& target, const Json::Value& source); bool StartsWith(const std::string& str, const std::string& prefix); int GetProcessId(); } } Orthanc-1.0.0/Core/Uuid.cpp0000644000000000000000000000742712634042176013555 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h" #include "Uuid.h" // http://stackoverflow.com/a/1626302 extern "C" { #ifdef WIN32 #include #else #include #endif } #include namespace Orthanc { namespace Toolbox { std::string GenerateUuid() { #ifdef WIN32 UUID uuid; UuidCreate ( &uuid ); unsigned char * str; UuidToStringA ( &uuid, &str ); std::string s( ( char* ) str ); RpcStringFreeA ( &str ); #else uuid_t uuid; uuid_generate_random ( uuid ); char s[37]; uuid_unparse ( uuid, s ); #endif return s; } bool IsUuid(const std::string& str) { if (str.size() != 36) { return false; } for (size_t i = 0; i < str.length(); i++) { if (i == 8 || i == 13 || i == 18 || i == 23) { if (str[i] != '-') return false; } else { if (!isalnum(str[i])) return false; } } return true; } bool StartsWithUuid(const std::string& str) { if (str.size() < 36) { return false; } if (str.size() == 36) { return IsUuid(str); } assert(str.size() > 36); if (!isspace(str[36])) { return false; } return IsUuid(str.substr(0, 36)); } static std::string CreateTemporaryPath(const char* extension) { #if BOOST_HAS_FILESYSTEM_V3 == 1 boost::filesystem::path tmpDir = boost::filesystem::temp_directory_path(); #elif defined(__linux__) boost::filesystem::path tmpDir("/tmp"); #else #error Support your platform here #endif // We use UUID to create unique path to temporary files std::string filename = "Orthanc-" + Orthanc::Toolbox::GenerateUuid(); if (extension != NULL) { filename.append(extension); } tmpDir /= filename; return tmpDir.string(); } TemporaryFile::TemporaryFile() : path_(CreateTemporaryPath(NULL)) { } TemporaryFile::TemporaryFile(const char* extension) : path_(CreateTemporaryPath(extension)) { } TemporaryFile::~TemporaryFile() { boost::filesystem::remove(path_); } } } Orthanc-1.0.0/Core/Uuid.h0000644000000000000000000000512712634042176013215 0ustar 00000000000000/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, 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 /** * GUID vs. UUID * The simple answer is: no difference, they are the same thing. Treat * them as a 16 byte (128 bits) value that is used as a unique * value. In Microsoft-speak they are called GUIDs, but call them * UUIDs when not using Microsoft-speak. * http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid **/ #include "Toolbox.h" namespace Orthanc { namespace Toolbox { std::string GenerateUuid(); bool IsUuid(const std::string& str); bool StartsWithUuid(const std::string& str); class TemporaryFile { private: std::string path_; public: TemporaryFile(); TemporaryFile(const char* extension); ~TemporaryFile(); const std::string& GetPath() const { return path_; } void Write(const std::string& content) { Toolbox::WriteFile(content, path_); } void Read(std::string& content) const { Toolbox::ReadFile(content, path_); } }; } } Orthanc-1.0.0/DarwinCompilation.txt0000644000000000000000000000262612634042176015433 0ustar 00000000000000This file is a complement to "INSTALL", which contains instructions that are specific to Mac OS X (Darwin). Static linking for OS X using XCode =================================== The most simple way of building Orthanc under OS X consists in statically linking against all the third-party dependencies. In this case, no package manager such as Homebrew or MacPorts is required. The build tool (CMake) will download the sources of all the required packages and automatically compile them. Prerequisites ------------- 1) XCode must be installed. 2) CMake must be installed (http://www.cmake.org/). 3) It is assumed that Orthanc source code is placed in the folder "~/Orthanc" and that the binaries will be compiled to "~/OrthancBuild". Prepare the build with CMake ---------------------------- # cd ~/OrthancBuild # cmake -GXcode -DCMAKE_OSX_DEPLOYMENT_TARGET=10.8 -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DALLOW_DOWNLOADS=ON ~/Orthanc NB: Adapt the value of "CMAKE_OSX_DEPLOYMENT_TARGET" with respect to your version of XCode. Build the Debug version of Orthanc ---------------------------------- # xcodebuild # ./Debug/UnitTests The binaries of Orthanc are located at "~/OrthancBuild/Debug/Orthanc". Build the Release version of Orthanc ------------------------------------ # xcodebuild -configuration Release # ./Debug/UnitTests The binaries of Orthanc are located at "~/OrthancBuild/Release/Orthanc". Orthanc-1.0.0/INSTALL0000644000000000000000000000640312634042176012275 0ustar 00000000000000Orthanc - A Lightweight, RESTful DICOM Server ============================================= Dependencies ------------ 1) CMake: Orthanc uses CMake (http://www.cmake.org/) to automate its building process. 2) Python: Some code is autogenerated through Python (http://www.python.org/). 3) Mercurial: To use the cutting edge code, a Mercurial client must be installed (http://mercurial.selenic.com/). We recommand TortoiseHg. W) 7-Zip: For the native build under Windows, the 7-Zip tool is used to uncompress the third-party packages (http://www.7-zip.org/). You thus have to download and install CMake, Python, Mercurial and possibly 7-Zip first. The path to their executable must be in the "PATH" environment variable. The other third party dependencies are automatically downloaded by the CMake scripts. The downloaded packages are stored in the "ThirdPartyDownloads" directory. Building Orthanc at a glance ---------------------------- To build Orthanc, you must: 1) Download the source code (either using Mercurial, or through the official releases). For the examples below, we assume the source directory is "~/Orthanc". 2) Create a build directory. For the examples below, we assume the build directory is "~/OrthancBuild". 3) Depending on your platform, follow the build instructions below. WARNING 1: If you do not create a fresh "~/OrthancBuild" directory after upgrading the source code (i.e. if you reuse the build directory that was used to build a different version of Orthanc), the build might fail because of changes in the compilation/linking flags. Always prefer to force a re-build in a new directory. WARNING 2: If cmake complains about not being able to uncompress third-party dependencies, delete the "~/Orthanc/ThirdPartyDownloads/" folder, then restart cmake. Native Linux Compilation ------------------------ See the file "LinuxCompilation.txt". Native OS X Compilation ----------------------- See the file "DarwinCompilation.txt". Native Windows build with Microsoft Visual Studio ------------------------------------------------- # cd [...]\OrthancBuild # cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON -G "Visual Studio 8 2005" [...]\Orthanc Then open the "[...]/OrthancBuild/Orthanc.sln" with Visual Studio. NOTES: * More recent versions of Visual Studio than 2005 should also work. Type "cmake" without arguments to have the list of generators that are available on your computer. * You will have to install the Platform SDK (version 6 or above) for Visual Studio 2005: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK. Read the CMake FAQ: http://goo.gl/By90B Cross-Compilation for Windows under Linux ----------------------------------------- To cross-compile Windows binaries under Linux using MinGW, please use the following command: # cd ~/OrthancBuild # cmake -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DCMAKE_BUILD_TYPE=Debug ~/Orthanc # make Native Windows build with MinGW (VERY SLOW) ------------------------------------------- # cd [...]\OrthancBuild # cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug [...]\Orthanc # mingw32-make Orthanc-1.0.0/LinuxCompilation.txt0000644000000000000000000001530712634042176015306 0ustar 00000000000000This file is a complement to "INSTALL", which contains instructions that are specific to Linux. Static linking for Linux ======================== The most simple way of building Orthanc under Linux consists in statically linking against all the third-party dependencies. In this case, the system-wide libraries will not be used. The build tool (CMake) will download the sources of all the required packages and automatically compile them. This process should work on any Linux distribution, provided that a C/C++ compiler ("build-essential" in Debian-based systems), the Python interpreter, CMake, the "unzip" system tool, and the development package for libuuid ("uuid-dev" in Debian) are installed. We now make the assumption that Orthanc source code is placed in the folder "~/Orthanc" and that the binaries will be compiled to "~/OrthancBuild". To build binaries with debug information: # cd ~/OrthancBuild # cmake -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Debug ~/Orthanc # make # make doc To build a release version: # cd ~/OrthancBuild # cmake -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release ~/Orthanc # make # make doc Note 1- When the "STATIC_BUILD" option is set to "ON", the build tool will not ask you the permission to download packages from the Internet. Note 2- If the development package of libuuid was not installed when first invoking cmake, you will have to manually remove the build directory ("rm -rf ~/OrthancBuild") after installing this package, then run cmake again. Note 3- To build the documentation, you will have to install doxyen. Use system-wide libraries under Linux ===================================== Under Linux, by default, Orthanc links against the shared libraries of your system (the "STATIC_BUILD" option is set to "OFF"). This greatly speeds up the compilation. This is also required when building packages for Linux distributions. Because using system libraries is the default behavior, you just have to use: # cd ~/OrthancBuild # cmake -DCMAKE_BUILD_TYPE=Debug ~/Orthanc # make Note that to build the documentation, you will have to install doxyen. However, on some Linux distributions, it is still required to download and static link against some third-party dependencies, e.g. when the system-wide library is not shipped or is outdated. Because of difference in the packaging of the various Linux distribution, it is also sometimes required to fine-tune some options. You will find below build instructions for specific Linux distributions. Distributions tagged by "SUPPORTED" are tested by Sébastien Jodogne. Distributions tagged by "CONTRIBUTED" come from Orthanc users. SUPPORTED - Debian Jessie/Sid ----------------------------- # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ libgoogle-glog-dev libgtest-dev libpng-dev libjpeg-dev \ libsqlite3-dev libssl-dev zlib1g-dev libdcmtk2-dev \ libboost-all-dev libwrap0-dev libjsoncpp-dev libpugixml-dev # cmake -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ -DDCMTK_LIBRARIES=dcmjpls \ ~/Orthanc Note: Have also a look at the official package: http://anonscm.debian.org/viewvc/debian-med/trunk/packages/orthanc/trunk/debian/ SUPPORTED - Ubuntu 12.04.5 LTS ------------------------------ # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \ zlib1g-dev libdcmtk2-dev libboost1.48-all-dev libwrap0-dev \ libcharls-dev # cmake "-DDCMTK_LIBRARIES=boost_locale;CharLS;dcmjpls;wrap;oflog" \ -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_SYSTEM_JSONCPP=OFF \ -DUSE_SYSTEM_GOOGLE_LOG=OFF \ -DUSE_SYSTEM_PUGIXML=OFF \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ ~/Orthanc SUPPORTED - Ubuntu 14.04 LTS ---------------------------- # sudo apt-get install build-essential unzip cmake mercurial \ uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \ libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \ zlib1g-dev libdcmtk2-dev libboost-all-dev libwrap0-dev \ libcharls-dev libjsoncpp-dev libpugixml-dev # cmake -DALLOW_DOWNLOADS=ON \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DDCMTK_LIBRARIES=dcmjpls \ ~/Orthanc SUPPORTED - Fedora 20-22 ------------------------ # sudo yum install unzip make automake gcc gcc-c++ python cmake \ boost-devel curl-devel dcmtk-devel glog-devel \ gtest-devel libpng-devel libsqlite3x-devel libuuid-devel jpeg-devel \ mongoose-devel openssl-devel jsoncpp-devel lua-devel pugixml-devel You will also have to install "gflags-devel" on Fedora 21&22: # sudo yum install gflags-devel # cmake "-DDCMTK_LIBRARIES=CharLS" \ -DSYSTEM_MONGOOSE_USE_CALLBACKS=OFF \ ~/Orthanc Note: Have also a look at the official package: http://pkgs.fedoraproject.org/cgit/orthanc.git/tree/?h=f18 SUPPORTED - FreeBSD 10.1 ------------------------ # pkg install jsoncpp pugixml lua51 curl googletest dcmtk cmake jpeg \ e2fsprogs-libuuid glog boost-libs sqlite3 python libiconv # cmake -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DDCMTK_LIBRARIES="dcmdsig;charls;dcmjpls" \ ~/Orthanc SUPPORTED - CentOS 6 -------------------- # yum install unzip make automake gcc gcc-c++ python cmake curl-devel \ libpng-devel sqlite-devel libuuid-devel openssl-devel \ lua-devel mercurial patch tar # cmake -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_JSONCPP=OFF \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_SYSTEM_PUGIXML=OFF \ -DUSE_SYSTEM_SQLITE=OFF \ -DUSE_SYSTEM_BOOST=OFF \ -DUSE_SYSTEM_DCMTK=OFF \ -DUSE_SYSTEM_GOOGLE_TEST=OFF \ -DUSE_SYSTEM_LIBJPEG=OFF \ ~/Orthanc Other Linux distributions? -------------------------- Please send us your build instructions (by a mail to s.jodogne@gmail.com)! You can find build instructions for Orthanc up to 0.7.0 on the following Wiki page: https://code.google.com/p/orthanc/wiki/LinuxCompilationUpTo070 These instructions will not work as such beyond Orthanc 0.7.0, but they might give indications. Using ccache ============ Under Linux, you also have the opportunity to use "ccache" to dramatically decrease the compilation time when rebuilding Orthanc. This is especially useful for developers. To this end, you would use: # CC="ccache gcc" CXX="ccache g++" cmake ~/Orthanc [Other Options] Orthanc-1.0.0/NEWS0000644000000000000000000005361412634042176011751 0ustar 00000000000000Pending changes in the mainline =============================== Version 1.0.0 (2015/12/15) ========================== * Lua: "IncomingFindRequestFilter()" to apply filters to incoming C-Find requests * New function in plugin SDK: "OrthancPluginSendMultipartItem2()" * Fix of DICOMDIR generation with DCMTK 3.6.1, support of encodings * Fix range search if the lower or upper limit is absent * Fix modality worklists lookups if tags with UN (unknown) VR are present * Warn about badly formatted modality/peer definitions in configuration file at startup Version 0.9.6 (2015/12/08) ========================== * Promiscuous mode (accept unknown SOP class UID) is now turned off by default * Fix serialization of DICOM buffers that might contain garbage trailing * Fix modality worklists server if some fields are null * More tolerant "/series/.../ordered-slices" with broken series * Improved logging information if upgrade fails * Fix formatting of multipart HTTP answers (bis) Version 0.9.5 (2015/12/02) ========================== Major ----- * Experimental support of DICOM C-FIND SCP for modality worklists through plugins * Support of DICOM C-FIND SCU for modality worklists ("/modalities/{dicom}/find-worklist") REST API -------- * New URIs: - "/series/.../ordered-slices" to order the slices of a 2D+t or 3D series - "/tools/shutdown" to stop Orthanc from the REST API - ".../compress", ".../uncompress" and ".../is-compressed" for attachments - "/tools/create-archive" to create ZIP from a set of resources - "/tools/create-media" to create ZIP+DICOMDIR from a set of resources - "/instances/.../header" to get the meta information (header) of the DICOM instance * "/tools/create-dicom": - Support of binary tags encoded using data URI scheme - Support of hierarchical structures (creation of sequences) - Create tags with unknown VR * "/modify" can insert/modify sequences * ".../preview" and ".../image-uint8" can return JPEG images if the HTTP Accept Header asks so * "Origin" metadata for the instances Minor ----- * New configuration options: - "UnknownSopClassAccepted" to disable promiscuous mode (accept unknown SOP class UID) - New configuration option: "Dictionary" to declare custom DICOM tags * Add ".dcm" suffix to files in ZIP archives (cf. URI ".../archive") * MIME content type can be associated to custom attachments (cf. "UserContentType") Plugins ------- * New functions: - "OrthancPluginRegisterDecodeImageCallback()" to replace the built-in image decoder - "OrthancPluginDicomInstanceToJson()" to convert DICOM to JSON - "OrthancPluginDicomBufferToJson()" to convert DICOM to JSON - "OrthancPluginRegisterErrorCode()" to declare custom error codes - "OrthancPluginRegisterDictionaryTag()" to declare custom DICOM tags - "OrthancPluginLookupDictionary()" to get information about some DICOM tag - "OrthancPluginRestApiGet2()" to provide HTTP headers when calling Orthanc API - "OrthancPluginGetInstanceOrigin()" to know through which mechanism an instance was received - "OrthancPluginCreateImage()" and "OrthancPluginCreateImageAccessor()" to create images - "OrthancPluginDecodeDicomImage()" to decode DICOM images - "OrthancPluginComputeMd5()" and "OrthancPluginComputeSha1()" to compute MD5/SHA-1 hash * New events in change callbacks: - "OrthancStarted" - "OrthancStopped" - "UpdatedAttachment" - "UpdatedMetadata" * "/system" URI gives information about the plugins used for storage area and DB back-end * Plugin callbacks must now return explicit "OrthancPluginErrorCode" (instead of integers) Lua --- * Optional argument "keepStrings" in "DumpJson()" Maintenance ----------- * Full indexation of the patient/study tags to speed up searches and C-FIND * Many refactorings, notably of the searching features and of the image decoding * C-MOVE SCP for studies using AccessionNumber tag * Fix issue 4 (C-STORE Association not renegotiated on Specific-to-specific transfer syntax change) * Fix formatting of multipart HTTP answers * "--logdir" flag creates a single log file instead of 3 separate files for errors/warnings/infos * "--errors" flag lists the error codes that could be returned by Orthanc * Under Windows, the exit status of Orthanc corresponds to the encountered error code * New "AgfaImpax", "EFilm2" and "Vitrea" modality manufacturers * C-FIND SCP will return tags with sequence value representation * Upgrade to Boost 1.59.0 for static builds Version 0.9.4 (2015/09/16) ========================== * Preview of PDF files encapsulated in DICOM from Orthanc Explorer * Creation of DICOM files with encapsulated PDF through "/tools/create-dicom" * "limit" and "since" arguments while retrieving DICOM resources in the REST API * Support of "deflate" and "gzip" content-types in HTTP requests * Options to validate peers against CA certificates in HTTPS requests * New configuration option: "HttpTimeout" to set the default timeout for HTTP requests Lua --- * More information about the origin request in the "OnStoredInstance()" and "ReceivedInstanceFilter()" callbacks. WARNING: This can result in incompatibilities wrt. previous versions of Orthanc. * New function "GetOrthancConfiguration()" to get the Orthanc configuration Plugins ------- * New functions to compress/uncompress images using PNG and JPEG * New functions to issue HTTP requests from plugins * New function "OrthancPluginBufferCompression()" to (un)compress memory buffers * New function "OrthancPluginReadFile()" to read files from the filesystem * New function "OrthancPluginWriteFile()" to write files to the filesystem * New function "OrthancPluginGetErrorDescription()" to convert error codes to strings * New function "OrthancPluginSendHttpStatus()" to send HTTP status with a body * New function "OrthancPluginRegisterRestCallbackNoLock()" for high-performance plugins * Plugins have access to explicit error codes * Improvements to the sample "ServeFolders" plugin * Primitives to upgrade the database version in plugins Maintenance ----------- * Many code refactorings * Improved error codes (no more custom descriptions in exceptions) * If error while calling the REST API, the answer body contains description of the error (this feature can be disabled with the "HttpDescribeErrors" option) * Upgrade to curl 7.44.0 for static and Windows builds * Upgrade to libcurl 1.0.2d for static and Windows builds * Depends on libjpeg 9a * Bypass zlib uncompression if "StorageCompression" is enabled and HTTP client supports deflate Version 0.9.3 (2015/08/07) ========================== * C-Echo testing can be triggered from Orthanc Explorer (in the query/retrieve page) * Removal of the dependency upon Google Log, Orthanc now uses its internal logger (use -DENABLE_GOOGLE_LOG=ON to re-enable Google Log) * Upgrade to JsonCpp 0.10.5 for static and Windows builds Version 0.9.2 (2015/08/02) ========================== * Upgrade to Boost 1.58.0 for static and Windows builds * Source code repository moved from Google Code to BitBucket * Inject version information into Windows binaries * Fix access to binary data in HTTP/REST requests by Lua scripts * Fix potential deadlock in the callbacks of plugins Version 0.9.1 (2015/07/02) ========================== General ------- * The configuration can be splitted into several files stored inside the same folder * Custom setting of the local AET during C-STORE SCU (both in Lua and in the REST API) * Many code refactorings Lua --- * Access to the REST API of Orthanc (RestApiGet, RestApiPost, RestApiPut, RestApiDelete) * Functions to convert between Lua values and JSON strings: "ParseJson" and "DumpJson" * New events: "OnStablePatient", "OnStableStudy", "OnStableSeries", "Initialize", "Finalize" Plugins ------- * Plugins can retrieve the configuration file directly as a JSON string * Plugins can send answers as multipart messages Fixes ----- * Fix compatibility issues for C-FIND SCU to Siemens Syngo.Via modalities SCP * Fix issue 15 (Lua scripts making HTTP requests) * Fix issue 35 (Characters in PatientID string are not protected for C-FIND) * Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges) Version 0.9.0 (2015/06/03) ========================== Major ----- * DICOM Query/Retrieve available from Orthanc Explorer * C-MOVE SCU and C-FIND SCU are accessible through the REST API * "?expand" flag for URIs "/patients", "/studies" and "/series" * "/tools/find" URI to search for DICOM resources from REST * Support of FreeBSD * The "Orthanc Client" SDK is now a separate project Minor ----- * Speed-up in Orthanc Explorer for large amount of images * Speed-up of the C-FIND SCP server of Orthanc * Allow replacing PatientID/StudyInstanceUID/SeriesInstanceUID from Lua scripts * Option "CaseSensitivePN" to enable case-insensitive C-FIND SCP Fixes ----- * Prevent freeze on C-FIND if no DICOM tag is to be returned * Fix slow C-STORE SCP on recent versions of Linux, if USE_SYSTEM_DCMTK is set to OFF (http://forum.dcmtk.org/viewtopic.php?f=1&t=4009) * Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052)) * Fix issue 32 (Cyrillic symbols): Introduction of the "Windows1251" encoding * Plugins now receive duplicated GET arguments in their REST callbacks Version 0.8.6 (2015/02/12) ========================== Major ----- * URIs to get all the parents of a given resource in a single REST call * Instances without PatientID are now allowed * Support of HTTP proxy to access Orthanc peers Minor ----- * Support of Tudor DICOM in Query/Retrieve * More flexible "/modify" and "/anonymize" for single instance * Access to called AET and remote AET from Lua scripts ("OnStoredInstance") * Option "DicomAssociationCloseDelay" to set delay before closing DICOM association * ZIP archives now display the accession number of the studies Plugins ------- * Introspection of plugins (cf. the "/plugins" URI) * Plugins can access the command-line arguments used to launch Orthanc * Plugins can extend Orthanc Explorer with custom JavaScript * Plugins can get/set global properties to save their configuration * Plugins can do REST calls to other plugins (cf. "xxxAfterPlugins()") * Scan of folders for plugins Fixes ----- * Code refactorings * Fix issue 25 (AET with underscore not allowed) * Fix replacement and insertion of private DICOM tags * Fix anonymization generating non-portable DICOM files Version 0.8.5 (2014/11/04) ========================== General ------- * Major speed-up thanks to a new database schema * Plugins can monitor changes through callbacks * Download ZIP + DICOMDIR from Orthanc Explorer * Sample plugin framework to serve static resources (./Plugins/Samples/WebSkeleton/) Fixes ----- * Fix issue 19 (YBR_FULL are decoded incorrectly) * Fix issue 21 (Microsoft Visual Studio precompiled headers) * Fix issue 22 (Error decoding multi-frame instances) * Fix issue 24 (Build fails on OSX when directory has .DS_Store files) * Fix crash when bad HTTP credentials are provided Version 0.8.4 (2014/09/19) ========================== * "/instances-tags" to get the tags of all the child instances of a patient/study/series with a single REST call (bulk tags retrieval) * Configuration/Lua to select the accepted C-STORE SCP transfer syntaxes * Fix reporting of errors in Orthanc Explorer when sending images to peers/modalities * Installation of plugin SDK in CMake Version 0.8.3 (2014/09/11) ========================== Major ----- * Creation of ZIP archives for media storage, with DICOMDIR * URIs to get all the children of a given resource in a single REST call * "/tools/lookup" URI to map DICOM UIDs to Orthanc identifiers * Support of index-only mode (using the "StoreDicom" option) * Plugins can implement a custom storage area Minor ----- * Configuration option to enable HTTP Keep-Alive * Configuration option to disable the logging of exported resources in "/exports" * Plugins can retrieve the path to Orthanc and to its configuration file * "/tools/create-dicom" now accepts the "PatientID" DICOM tag (+ updated sample) * Possibility to set HTTP headers from plugins * "LastUpdate" metadata is now always returned for patients, studies and series Maintenance ----------- * Refactoring of HttpOutput ("Content-Length" header is now always sent) * Upgrade to Mongoose 3.8 * Fixes for Visual Studio 2013 and Windows 64bit * Fix issue 16: Handling of "AT" value representations in JSON * Fix issue 17 Version 0.8.2 (2014/08/07) ========================== * Support of the standard text encodings * Hot restart of Orthanc by posting to "/tools/reset" * More fault-tolerant commands in Lua scripts * Parameter to set the default encoding for DICOM files without SpecificCharacterSet * Fix of issue #14 (support of XCode 5.1) * Upgrade to Google Test 1.7.0 Version 0.8.1 (2014/07/29) ========================== General ------- * Access patient module at the study level to cope with PatientID collisions * On-the-fly conversion of JSON to XML according to the HTTP Accept header * C-Echo SCU in the REST API * DICOM conformance statement available at URI "/tools/dicom-conformance" Lua scripts ----------- * Lua scripts can do HTTP requests, and thus can call Web services * Lua scripts can invoke system commands, with CallSystem() Plugins ------- * Lookup for DICOM UIDs in the plugin SDK * Plugins have access to the HTTP headers and can answer with HTTP status codes * Callback to react to the incoming of DICOM instances Fixes ----- * Fix build of Google Log with Visual Studio >= 11.0 * Fix automated generation of the list of resource children in the REST API Version 0.8.0 (2014/07/10) ========================== Major changes ------------- * Routing images with Lua scripts * Introduction of the Orthanc Plugin SDK * Official support of OS X (Darwin) 10.8 Minor changes ------------- * Extraction of tags for the patient/study/series/instance DICOM modules * Extraction of the tags shared by all the instances of a patient/study/series * Options to limit the number of results for an incoming C-FIND query * Support of kFreeBSD * Several code refactorings * Fix OrthancCppClient::GetVoxelSizeZ() Version 0.7.6 (2014/06/11) ========================== * Support of JPEG and JPEG-LS decompression * Download DICOM images as Matlab/Octave arrays * Precompiled headers for Microsoft Visual Studio Version 0.7.5 (2014/05/08) ========================== * Dynamic negotiation of SOP classes for C-STORE SCU * Creation of DICOM instances using the REST API * Embedding of images within DICOM instances * Adding/removal/modification of remote modalities/peers through REST * Reuse of the previous SCU connection to avoid unnecessary handshakes * Fix problems with anonymization and modification * Fix missing licensing terms about reuse of some code from DCMTK * Various code refactorings Version 0.7.4 (2014/04/16) ========================== * Switch to openssl-1.0.1g in static builds (cf. Heartbleed exploit) * Switch to boost 1.55.0 in static builds (to solve compiling errors) * Better logging about nonexistent tags * Dcm4Chee manufacturer * Automatic discovering of the path to the DICOM dictionaries * In the "DicomModalities" config, the port number can be a string Version 0.7.3 (2014/02/14) ========================== Major changes ------------- * Fixes in the implementation of the C-FIND handler for Query/Retrieve * Custom attachment of files to patients, studies, series or instances * Access to lowlevel info about the attached files through the REST API * Recover pixel data for more transfer syntaxes (notably JPEG) Minor changes ------------- * AET comparison is now case-insensitive by default * Possibility to disable the HTTP server or the DICOM server * Automatic computation of MD5 hashes for the stored DICOM files * Maintenance tool to recover DICOM files compressed by Orthanc * The newline characters in the configuration file are fixed for Linux * Capture of the SIGTERM signal in Linux Version 0.7.2 (2013/11/08) ========================== * Support of Query/Retrieve from medInria * Accept more transfer syntaxes for C-STORE SCP and SCU (notably JPEG) * Create the meta-header when receiving files through C-STORE SCP * Fixes and improvements thanks to the static analyzer cppcheck Version 0.7.1 (2013/10/30) ========================== * Use ZIP64 only when required to improve compatibility (cf. issue #7) * Refactoring of the CMake options * Fix for big-endian architectures (RedHat bug #985748) * Use filenames with 8 characters in ZIP files for maximum compatibility * Possibility to build Orthanc inplace (in the source directory) Version 0.7.0 (2013/10/25) ========================== Major changes ------------- * DICOM Query/Retrieve is supported Minor changes ------------- * Possibility to keep the PatientID during an anonymization * Check whether "unzip", "tar" and/or "7-zip" are installed from CMake Version 0.6.2 (2013/10/04) ========================== * Build of the C++ client as a shared library * Improvements and documentation of the C++ client API * Fix of Debian bug #724947 (licensing issue with the SHA-1 library) * Switch to Boost 1.54.0 (cf. issue #9) * "make uninstall" is now possible Version 0.6.1 (2013/09/16) ========================== * Detection of stable patients/studies/series * C-FIND SCU at the instance level * Link from modified to original resource in Orthanc Explorer * Fix of issue #8 * Anonymization of the medical alerts tag (0010,2000) Version 0.6.0 (2013/07/16) ========================== Major changes ------------- * Introduction of the C++ client * Send DICOM resources to other Orthanc instances through HTTP * Access to signed images (instances/.../image-int16) (Closes: Debian #716958) Minor changes ------------- * Export of DICOM files to the host filesystem (instances/.../export) * Statistics about patients, studies, series and instances * Link from anonymized to original resource in Orthanc Explorer * Fixes for Red Hat and Debian packaging * Fixes for history in Orthanc Explorer * Fixes for boost::thread, as reported by Cyril Paulus * Fix licensing (Closes: Debian #712038) Metadata -------- * Access to the metadata through the REST API (.../metadata) * Support of user-defined metadata * "LastUpdate" metadata for patients, studies and series * "/tools/now" to be used in combination with "LastUpdate" * Improved support of series with temporal positions Version 0.5.2 (2013/05/07) ========================== * "Bulk" Store-SCU (send several DICOM instances with the same DICOM connection) * Store-SCU for patients and studies in Orthanc Explorer * Filtering of incoming DICOM instances (through Lua scripting) * Filtering of incoming HTTP requests (through Lua scripting) * Clearing of "/exports" and "/changes" * Check MD5 of third party downloads * Faking of the HTTP methods PUT and DELETE Version 0.5.1 (2013/04/17) ========================== * Support of RGB images * Fix of store SCU in release builds * Possibility to store the SQLite index at another place than the DICOM instances (for performance) Version 0.5.0 (2013/01/31) ========================== Major changes ------------- * Download of modified or anonymized DICOM instances * Inplace modification and anonymization of DICOM series, studies and patients Minor changes ------------- * Support of private tags * Implementation of the PMSCT_RLE1 image decoding for Philips modalities * Generation of random DICOM UID through the REST API (/tools/generate-uid) Version 0.4.0 (2012/12/14) ========================== Major changes ------------- * Recycling of disk space * Raw access to the value of the DICOM tags in the REST API Minor changes ------------- * Protection of patients against recycling (also in Orthanc Explorer) * The DICOM dictionaries are embedded in Windows builds Version 0.3.1 (2012/12/05) ========================== * Download archives of patients, studies and series as ZIP files * Orthanc now checks the version of its database schema before starting Version 0.3.0 (2012/11/30) ========================== Major changes ------------- * Transparent compression of the DICOM instances on the disk * The patient/study/series/instances are now indexed by SHA-1 digests of their DICOM Instance IDs (and not by UUIDs anymore): The same DICOM objects are thus always identified by the same Orthanc IDs * Log of exported instances through DICOM C-STORE SCU ("/exported" URI) * Full refactoring of the DB schema and of the REST API * Introduction of generic classes for REST APIs (in Core/RestApi) Minor changes ------------- * "/statistics" URI * "last" flag to retrieve the last change from the "/changes" URI * Generate a sample configuration file from command line * "CompletedSeries" event in the changes API * Thread to continuously flush DB to disk (SQLite checkpoints for improved robustness) Version 0.2.3 (2012/10/26) ========================== * Use HTTP Content-Disposition to set a filename when downloading JSON/DCM * URI "/system" for general information about Orthanc * Versioning info and help on the command line * Improved logging * Possibility of dynamic linking against jsoncpp, sqlite, boost and dmctk for Debian packaging * Fix some bugs * Switch to default 8042 port for HTTP Version 0.2.2 (2012/10/04) ========================== * Switch to Google Logging * Fixes to Debian packaging Version 0.2.1 (2012/09/28) ========================== * Status of series * Continuous Integration Server is up and running * Ready for Debian packaging Version 0.2.0 (2012/09/16) ========================== Major changes ------------- * Renaming to "Orthanc" * Focus on security: Support of SSL, HTTP Basic Authentication and interdiction of remote access * Access to multi-frame images (for nuclear medicine) * Access to the raw PNG images (in 8bpp and 16bpp) Minor changes ------------- * Change of the licensing of the "Core/SQLite" folder to BSD (to reflect the original licensing terms of Chromium, from which the code derives) * Standalone build for cross-compilation Version 0.1.1 (2012/07/20) ========================== * Fix Windows version * Native Windows build with Microsoft Visual Studio 2005 * Add path to storage in Configuration.json Version 0.1.0 (2012/07/19) ========================== * Initial release Orthanc-1.0.0/OrthancExplorer/explorer.css0000644000000000000000000000124012634042176016727 0ustar 00000000000000ul.tree ul { margin-left: 36px; } #progress { position: relative; /*height: 2em; */ width: 100%; background-color: grey; height: 2.5em; } #progress .label { z-index: 10; position: absolute; left:0; top: 0; width: 100%; font-weight: bold; text-align: center; text-shadow: none; padding: .5em; color: white; } #progress .bar { z-index: 0; position: absolute; left:0; top: 0; height: 100%; width: 0%; background-color: green; } .ui-title a { text-decoration: none; color: white !important; } .switch-container .ui-slider-switch { width: 100%; }Orthanc-1.0.0/OrthancExplorer/explorer.html0000644000000000000000000004520612634042176017115 0ustar 00000000000000 Orthanc Explorer

Find a patient

Plugins

Upload DICOM files

Patients

  • Drag and drop DICOM files here

Plugins

Patients

DICOM Query/Retrieve (1/3)

Patients
Field of interest:
Modalities:

DICOM Query/Retrieve (2/3)

Patients Query/Retrieve

DICOM Query/Retrieve (3/3)

Patients Query/Retrieve
Orthanc-1.0.0/OrthancExplorer/explorer.js0000644000000000000000000006576012634042176016574 0ustar 00000000000000// http://stackoverflow.com/questions/1663741/is-there-a-good-jquery-drag-and-drop-file-upload-plugin // Forbid the access to IE if ($.browser.msie) { alert("Please use Mozilla Firefox or Google Chrome. Microsoft Internet Explorer is not supported."); } // http://jquerymobile.com/demos/1.1.0/docs/api/globalconfig.html //$.mobile.ajaxEnabled = false; //$.mobile.page.prototype.options.addBackBtn = true; //$.mobile.defaultPageTransition = 'slide'; var currentPage = ''; var currentUuid = ''; // http://stackoverflow.com/a/4673436 String.prototype.format = function() { var args = arguments; return this.replace(/{(\d+)}/g, function(match, number) { /*return typeof args[number] != 'undefined' ? args[number] : match;*/ return args[number]; }); }; function Refresh() { if (currentPage == 'patient') RefreshPatient(); else if (currentPage == 'study') RefreshStudy(); else if (currentPage == 'series') RefreshSeries(); else if (currentPage == 'instance') RefreshInstance(); } $(document).ready(function() { var $tree = $('#dicom-tree'); $tree.tree({ autoEscape: false }); $('#dicom-tree').bind( 'tree.click', function(event) { if (event.node.is_open) $tree.tree('closeNode', event.node, true); else $tree.tree('openNode', event.node, true); } ); currentPage = $.mobile.pageData.active; currentUuid = $.mobile.pageData.uuid; if (!(typeof currentPage === 'undefined') && !(typeof currentUuid === 'undefined') && currentPage.length > 0 && currentUuid.length > 0) { Refresh(); } }); function SplitLongUid(s) { return '' + s.substr(0, s.length / 2) + ' ' + s.substr(s.length / 2, s.length - s.length / 2) + ''; } function ParseDicomDate(s) { y = parseInt(s.substr(0, 4), 10); m = parseInt(s.substr(4, 2), 10) - 1; d = parseInt(s.substr(6, 2), 10); if (y == null || m == null || d == null || !isFinite(y) || !isFinite(m) || !isFinite(d)) { return null; } if (y < 1900 || y > 2100 || m < 0 || m >= 12 || d <= 0 || d >= 32) { return null; } return new Date(y, m, d); } function FormatDicomDate(s) { if (s == undefined) return "No date"; var d = ParseDicomDate(s); if (d == null) return '?'; else return d.toString('dddd, MMMM d, yyyy'); } function Sort(arr, fieldExtractor, isInteger, reverse) { var defaultValue; if (isInteger) defaultValue = 0; else defaultValue = ''; arr.sort(function(a, b) { var ta = fieldExtractor(a); var tb = fieldExtractor(b); var order; if (ta == undefined) ta = defaultValue; if (tb == undefined) tb = defaultValue; if (isInteger) { ta = parseInt(ta, 10); tb = parseInt(tb, 10); order = ta - tb; } else { if (ta < tb) order = -1; else if (ta > tb) order = 1; else order = 0; } if (reverse) return -order; else return order; }); } function SortOnDicomTag(arr, tag, isInteger, reverse) { return Sort(arr, function(a) { return a.MainDicomTags[tag]; }, isInteger, reverse); } function GetResource(uri, callback) { $.ajax({ url: '..' + uri, dataType: 'json', async: false, cache: false, success: function(s) { callback(s); } }); } function CompleteFormatting(s, link, isReverse) { if (link != null) { s = 'href="' + link + '">' + s + ''; if (isReverse) s = 'data-direction="reverse" '+ s; s = '' + s + ''; else return '
  • ' + s + '
  • '; } function FormatMainDicomTags(tags, tagsToIgnore) { var s = ''; for (var i in tags) { if (tagsToIgnore.indexOf(i) == -1) { var v = tags[i]; if (i == "PatientBirthDate" || i == "StudyDate" || i == "SeriesDate") { v = FormatDicomDate(v); } else if (i == "DicomStudyInstanceUID" || i == "DicomSeriesInstanceUID") { v = SplitLongUid(v); } s += ('

    {0}: {1}

    ').format(i, v); } } return s; } function FormatPatient(patient, link, isReverse) { var s = ('

    {0}

    {1}' + '{2}' ).format (patient.MainDicomTags.PatientName, FormatMainDicomTags(patient.MainDicomTags, [ "PatientName" /*"OtherPatientIDs" */ ]), patient.Studies.length ); return CompleteFormatting(s, link, isReverse); } function FormatStudy(study, link, isReverse) { var s = ('

    {0}

    {1}' + '{2}' ).format (study.MainDicomTags.StudyDescription, FormatMainDicomTags(study.MainDicomTags, [ "StudyDescription", "StudyTime" ]), study.Series.length ); return CompleteFormatting(s, link, isReverse); } function FormatSeries(series, link, isReverse) { var c; if (series.ExpectedNumberOfInstances == null || series.Instances.length == series.ExpectedNumberOfInstances) { c = series.Instances.length; } else { c = series.Instances.length + '/' + series.ExpectedNumberOfInstances; } var s = ('

    {0}

    ' + '

    Status: {1}

    {2}' + '{3}').format (series.MainDicomTags.SeriesDescription, series.Status, FormatMainDicomTags(series.MainDicomTags, [ "SeriesDescription", "SeriesTime", "Manufacturer", "ImagesInAcquisition", "SeriesDate", "ImageOrientationPatient" ]), c ); return CompleteFormatting(s, link, isReverse); } function FormatInstance(instance, link, isReverse) { var s = ('

    Instance {0}

    {1}').format (instance.IndexInSeries, FormatMainDicomTags(instance.MainDicomTags, [ "AcquisitionNumber", "InstanceNumber", "InstanceCreationDate", "InstanceCreationTime", "ImagePositionPatient" ]) ); return CompleteFormatting(s, link, isReverse); } $('[data-role="page"]').live('pagebeforeshow', function() { $.ajax({ url: '../system', dataType: 'json', async: false, cache: false, success: function(s) { if (s.Name != "") { $('.orthanc-name').html('
    ' + s.Name + ' » '); } } }); }); $('#find-patients').live('pagebeforeshow', function() { GetResource('/patients?expand', function(patients) { var target = $('#all-patients'); $('li', target).remove(); SortOnDicomTag(patients, 'PatientName', false, false); for (var i = 0; i < patients.length; i++) { var p = FormatPatient(patients[i], '#patient?uuid=' + patients[i].ID); target.append(p); } target.listview('refresh'); }); }); function SetupAnonymizedOrModifiedFrom(buttonSelector, resource, resourceType, field) { if (field in resource) { $(buttonSelector).closest('li').show(); $(buttonSelector).click(function(e) { window.location.assign('explorer.html#' + resourceType + '?uuid=' + resource[field]); }); } else { $(buttonSelector).closest('li').hide(); } } function RefreshPatient() { if ($.mobile.pageData) { GetResource('/patients/' + $.mobile.pageData.uuid, function(patient) { GetResource('/patients/' + $.mobile.pageData.uuid + '/studies', function(studies) { SortOnDicomTag(studies, 'StudyDate', false, true); $('#patient-info li').remove(); $('#patient-info') .append('
  • Patient
  • ') .append(FormatPatient(patient)) .listview('refresh'); var target = $('#list-studies'); $('li', target).remove(); for (var i = 0; i < studies.length; i++) { if (i == 0 || studies[i].MainDicomTags.StudyDate != studies[i - 1].MainDicomTags.StudyDate) { target.append('
  • {0}
  • '.format (FormatDicomDate(studies[i].MainDicomTags.StudyDate))); } target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID)); } SetupAnonymizedOrModifiedFrom('#patient-anonymized-from', patient, 'patient', 'AnonymizedFrom'); SetupAnonymizedOrModifiedFrom('#patient-modified-from', patient, 'patient', 'ModifiedFrom'); target.listview('refresh'); // Check whether this patient is protected $.ajax({ url: '../patients/' + $.mobile.pageData.uuid + '/protected', type: 'GET', dataType: 'text', async: false, cache: false, success: function (s) { var v = (s == '1') ? 'on' : 'off'; $('#protection').val(v).slider('refresh'); } }); currentPage = 'patient'; currentUuid = $.mobile.pageData.uuid; }); }); } } function RefreshStudy() { if ($.mobile.pageData) { GetResource('/studies/' + $.mobile.pageData.uuid, function(study) { GetResource('/patients/' + study.ParentPatient, function(patient) { GetResource('/studies/' + $.mobile.pageData.uuid + '/series', function(series) { SortOnDicomTag(series, 'SeriesDate', false, true); $('#study .patient-link').attr('href', '#patient?uuid=' + patient.ID); $('#study-info li').remove(); $('#study-info') .append('
  • Patient
  • ') .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true)) .append('
  • Study
  • ') .append(FormatStudy(study)) .listview('refresh'); SetupAnonymizedOrModifiedFrom('#study-anonymized-from', study, 'study', 'AnonymizedFrom'); SetupAnonymizedOrModifiedFrom('#study-modified-from', study, 'study', 'ModifiedFrom'); var target = $('#list-series'); $('li', target).remove(); for (var i = 0; i < series.length; i++) { if (i == 0 || series[i].MainDicomTags.SeriesDate != series[i - 1].MainDicomTags.SeriesDate) { target.append('
  • {0}
  • '.format (FormatDicomDate(series[i].MainDicomTags.SeriesDate))); } target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID)); } target.listview('refresh'); currentPage = 'study'; currentUuid = $.mobile.pageData.uuid; }); }); }); } } function RefreshSeries() { if ($.mobile.pageData) { GetResource('/series/' + $.mobile.pageData.uuid, function(series) { GetResource('/studies/' + series.ParentStudy, function(study) { GetResource('/patients/' + study.ParentPatient, function(patient) { GetResource('/series/' + $.mobile.pageData.uuid + '/instances', function(instances) { Sort(instances, function(x) { return x.IndexInSeries; }, true, false); $('#series .patient-link').attr('href', '#patient?uuid=' + patient.ID); $('#series .study-link').attr('href', '#study?uuid=' + study.ID); $('#series-info li').remove(); $('#series-info') .append('
  • Patient
  • ') .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true)) .append('
  • Study
  • ') .append(FormatStudy(study, '#study?uuid=' + study.ID, true)) .append('
  • Series
  • ') .append(FormatSeries(series)) .listview('refresh'); SetupAnonymizedOrModifiedFrom('#series-anonymized-from', series, 'series', 'AnonymizedFrom'); SetupAnonymizedOrModifiedFrom('#series-modified-from', series, 'series', 'ModifiedFrom'); var target = $('#list-instances'); $('li', target).remove(); for (var i = 0; i < instances.length; i++) { target.append(FormatInstance(instances[i], '#instance?uuid=' + instances[i].ID)); } target.listview('refresh'); currentPage = 'series'; currentUuid = $.mobile.pageData.uuid; }); }); }); }); } } function ConvertForTree(dicom) { var result = []; for (var i in dicom) { if (dicom[i] != null) { var label = i + ' (' + dicom[i]["Name"] + '): '; if (dicom[i]["Type"] == 'String') { result.push({ label: label + '' + dicom[i]["Value"] + '', children: [] }); } else if (dicom[i]["Type"] == 'TooLong') { result.push({ label: label + 'Too long', children: [] }); } else if (dicom[i]["Type"] == 'Null') { result.push({ label: label + 'Null', children: [] }); } else if (dicom[i]["Type"] == 'Sequence') { var c = []; for (var j = 0; j < dicom[i]["Value"].length; j++) { c.push({ label: 'Item ' + j, children: ConvertForTree(dicom[i]["Value"][j]) }); } result.push({ label: label + '[]', children: c }); } } } return result; } function RefreshInstance() { if ($.mobile.pageData) { GetResource('/instances/' + $.mobile.pageData.uuid, function(instance) { GetResource('/series/' + instance.ParentSeries, function(series) { GetResource('/studies/' + series.ParentStudy, function(study) { GetResource('/patients/' + study.ParentPatient, function(patient) { $('#instance .patient-link').attr('href', '#patient?uuid=' + patient.ID); $('#instance .study-link').attr('href', '#study?uuid=' + study.ID); $('#instance .series-link').attr('href', '#series?uuid=' + series.ID); $('#instance-info li').remove(); $('#instance-info') .append('
  • Patient
  • ') .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true)) .append('
  • Study
  • ') .append(FormatStudy(study, '#study?uuid=' + study.ID, true)) .append('
  • Series
  • ') .append(FormatSeries(series, '#series?uuid=' + series.ID, true)) .append('
  • Instance
  • ') .append(FormatInstance(instance)) .listview('refresh'); GetResource('/instances/' + instance.ID + '/tags', function(s) { $('#dicom-tree').tree('loadData', ConvertForTree(s)); }); SetupAnonymizedOrModifiedFrom('#instance-anonymized-from', instance, 'instance', 'AnonymizedFrom'); SetupAnonymizedOrModifiedFrom('#instance-modified-from', instance, 'instance', 'ModifiedFrom'); currentPage = 'instance'; currentUuid = $.mobile.pageData.uuid; }); }); }); }); } } $(document).live('pagebeforehide', function() { currentPage = ''; currentUuid = ''; }); $('#patient').live('pagebeforeshow', RefreshPatient); $('#study').live('pagebeforeshow', RefreshStudy); $('#series').live('pagebeforeshow', RefreshSeries); $('#instance').live('pagebeforeshow', RefreshInstance); $(function() { $(window).hashchange(function(e, data) { // This fixes the navigation with the back button and with the anonymization if ('uuid' in $.mobile.pageData && currentPage == $.mobile.pageData.active && currentUuid != $.mobile.pageData.uuid) { Refresh(); } }); }); function DeleteResource(path) { $.ajax({ url: path, type: 'DELETE', dataType: 'json', async: false, success: function(s) { var ancestor = s.RemainingAncestor; if (ancestor == null) $.mobile.changePage('#find-patients'); else $.mobile.changePage('#' + ancestor.Type.toLowerCase() + '?uuid=' + ancestor.ID); } }); } function OpenDeleteResourceDialog(path, title) { $(document).simpledialog2({ // http://dev.jtsage.com/jQM-SimpleDialog/demos2/ // http://dev.jtsage.com/jQM-SimpleDialog/demos2/options.html mode: 'button', animate: false, headerText: title, headerClose: true, width: '500px', buttons : { 'OK': { click: function () { DeleteResource(path); }, icon: "delete", theme: "c" }, 'Cancel': { click: function () { } } } }); } $('#instance-delete').live('click', function() { OpenDeleteResourceDialog('../instances/' + $.mobile.pageData.uuid, 'Delete this instance?'); }); $('#study-delete').live('click', function() { OpenDeleteResourceDialog('../studies/' + $.mobile.pageData.uuid, 'Delete this study?'); }); $('#series-delete').live('click', function() { OpenDeleteResourceDialog('../series/' + $.mobile.pageData.uuid, 'Delete this series?'); }); $('#patient-delete').live('click', function() { OpenDeleteResourceDialog('../patients/' + $.mobile.pageData.uuid, 'Delete this patient?'); }); $('#instance-download-dicom').live('click', function(e) { // http://stackoverflow.com/a/1296101 e.preventDefault(); //stop the browser from following window.location.href = '../instances/' + $.mobile.pageData.uuid + '/file'; }); $('#instance-download-json').live('click', function(e) { // http://stackoverflow.com/a/1296101 e.preventDefault(); //stop the browser from following window.location.href = '../instances/' + $.mobile.pageData.uuid + '/tags'; }); $('#instance-preview').live('click', function(e) { if ($.mobile.pageData) { var pdf = '../instances/' + $.mobile.pageData.uuid + '/pdf'; $.ajax({ url: pdf, cache: false, success: function(s) { window.location.assign(pdf); }, error: function() { GetResource('/instances/' + $.mobile.pageData.uuid + '/frames', function(frames) { if (frames.length == 1) { // Viewing a single-frame image jQuery.slimbox('../instances/' + $.mobile.pageData.uuid + '/preview', '', { overlayFadeDuration : 1, resizeDuration : 1, imageFadeDuration : 1 }); } else { // Viewing a multi-frame image var images = []; for (var i = 0; i < frames.length; i++) { images.push([ '../instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]); } jQuery.slimbox(images, 0, { overlayFadeDuration : 1, resizeDuration : 1, imageFadeDuration : 1, loop : true }); } }); } }); } }); $('#series-preview').live('click', function(e) { if ($.mobile.pageData) { GetResource('/series/' + $.mobile.pageData.uuid, function(series) { GetResource('/series/' + $.mobile.pageData.uuid + '/instances', function(instances) { Sort(instances, function(x) { return x.IndexInSeries; }, true, false); var images = []; for (var i = 0; i < instances.length; i++) { images.push([ '../instances/' + instances[i].ID + '/preview', '{0}/{1}'.format(i + 1, instances.length) ]) } jQuery.slimbox(images, 0, { overlayFadeDuration : 1, resizeDuration : 1, imageFadeDuration : 1, loop : true }); }); }); } }); function ChooseDicomModality(callback) { var clickedModality = ''; var clickedPeer = ''; var items = $('"); }; createLi = function(node) { var $li; if (node.hasChildren()) { $li = createFolderLi(node); } else { $li = createNodeLi(node); } if (_this.options.onCreateLi) { _this.options.onCreateLi(node, $li); } return $li; }; createNodeLi = function(node) { var escaped_name; escaped_name = escapeIfNecessary(node.name); return $("
  • " + escaped_name + "
  • "); }; createFolderLi = function(node) { var button_class, escaped_name, folder_class, getButtonClass, getFolderClass; getButtonClass = function() { var classes; classes = ['toggler']; if (!node.is_open) { classes.push('closed'); } return classes.join(' '); }; getFolderClass = function() { var classes; classes = ['folder']; if (!node.is_open) { classes.push('closed'); } return classes.join(' '); }; button_class = getButtonClass(); folder_class = getFolderClass(); escaped_name = escapeIfNecessary(node.name); return $("
  • »" + escaped_name + "
  • "); }; doCreateDomElements = function($element, children, is_root_node, is_open) { var $li, $ul, child, _i, _len; $ul = createUl(is_root_node); $element.append($ul); for (_i = 0, _len = children.length; _i < _len; _i++) { child = children[_i]; $li = createLi(child); $ul.append($li); child.element = $li[0]; $li.data('node', child); if (child.hasChildren()) { doCreateDomElements($li, child.children, false, child.is_open); } } return null; }; if (from_node && from_node.parent) { is_root_node = false; node_element = this._getNodeElementForNode(from_node); node_element.getUl().remove(); $element = node_element.$element; } else { from_node = this.tree; $element = this.element; $element.empty(); is_root_node = true; } return doCreateDomElements($element, from_node.children, is_root_node, is_root_node); }; JqTreeWidget.prototype._click = function(e) { var $target, event, node, node_element; if (e.ctrlKey) { return; } $target = $(e.target); if ($target.is('.toggler')) { node_element = this._getNodeElement($target); if (node_element && node_element.node.hasChildren()) { node_element.toggle(); this._saveState(); e.preventDefault(); return e.stopPropagation(); } } else if ($target.is('div') || $target.is('span')) { node = this._getNode($target); if (node) { if ((!this.options.onCanSelectNode) || this.options.onCanSelectNode(node)) { this.selectNode(node); event = $.Event('tree.click'); event.node = node; return this.element.trigger(event); } } } }; JqTreeWidget.prototype._getNode = function($element) { var $li; $li = $element.closest('li'); if ($li.length === 0) { return null; } else { return $li.data('node'); } }; JqTreeWidget.prototype._getNodeElementForNode = function(node) { if (node.hasChildren()) { return new FolderElement(node, this.element); } else { return new NodeElement(node, this.element); } }; JqTreeWidget.prototype._getNodeElement = function($element) { var node; node = this._getNode($element); if (node) { return this._getNodeElementForNode(node); } else { return null; } }; JqTreeWidget.prototype._contextmenu = function(e) { var $div, event, node; $div = $(e.target).closest('ul.tree div'); if ($div.length) { node = this._getNode($div); if (node) { e.preventDefault(); e.stopPropagation(); event = $.Event('tree.contextmenu'); event.node = node; event.click_event = e; this.element.trigger(event); return false; } } }; JqTreeWidget.prototype._saveState = function() { if (this.options.saveState) { return this.save_state_handler.saveState(); } }; JqTreeWidget.prototype._mouseCapture = function(event) { if (this.options.dragAndDrop) { return this.dnd_handler.mouseCapture(event); } else { return false; } }; JqTreeWidget.prototype._mouseStart = function(event) { if (this.options.dragAndDrop) { return this.dnd_handler.mouseStart(event); } else { return false; } }; JqTreeWidget.prototype._mouseDrag = function(event) { if (this.options.dragAndDrop) { return this.dnd_handler.mouseDrag(event); } else { return false; } }; JqTreeWidget.prototype._mouseStop = function() { if (this.options.dragAndDrop) { return this.dnd_handler.mouseStop(); } else { return false; } }; JqTreeWidget.prototype.testGenerateHitAreas = function(moving_node) { this.dnd_handler.current_item = this._getNodeElementForNode(moving_node); this.dnd_handler.generateHitAreas(); return this.dnd_handler.hit_areas; }; return JqTreeWidget; })(MouseWidget); SimpleWidget.register(JqTreeWidget, 'tree'); GhostDropHint = (function() { function GhostDropHint(node, $element, position) { this.$element = $element; this.node = node; this.$ghost = $('
  • '); if (position === Position.AFTER) { this.moveAfter(); } else if (position === Position.BEFORE) { this.moveBefore(); } else if (position === Position.INSIDE) { if (node.hasChildren() && node.is_open) { this.moveInsideOpenFolder(); } else { this.moveInside(); } } } GhostDropHint.prototype.remove = function() { return this.$ghost.remove(); }; GhostDropHint.prototype.moveAfter = function() { return this.$element.after(this.$ghost); }; GhostDropHint.prototype.moveBefore = function() { return this.$element.before(this.$ghost); }; GhostDropHint.prototype.moveInsideOpenFolder = function() { return $(this.node.children[0].element).before(this.$ghost); }; GhostDropHint.prototype.moveInside = function() { this.$element.after(this.$ghost); return this.$ghost.addClass('inside'); }; return GhostDropHint; })(); BorderDropHint = (function() { function BorderDropHint($element) { var $div, width; $div = $element.children('div'); width = $element.width() - 4; this.$hint = $(''); $div.append(this.$hint); this.$hint.css({ width: width, height: $div.height() - 4 }); } BorderDropHint.prototype.remove = function() { return this.$hint.remove(); }; return BorderDropHint; })(); NodeElement = (function() { function NodeElement(node, tree_element) { this.init(node, tree_element); } NodeElement.prototype.init = function(node, tree_element) { this.node = node; this.tree_element = tree_element; return this.$element = $(node.element); }; NodeElement.prototype.getUl = function() { return this.$element.children('ul:first'); }; NodeElement.prototype.getSpan = function() { return this.$element.children('div').find('span.title'); }; NodeElement.prototype.getLi = function() { return this.$element; }; NodeElement.prototype.addDropHint = function(position) { if (position === Position.INSIDE) { return new BorderDropHint(this.$element); } else { return new GhostDropHint(this.node, this.$element, position); } }; NodeElement.prototype.select = function() { return this.getLi().addClass('selected'); }; NodeElement.prototype.deselect = function() { return this.getLi().removeClass('selected'); }; return NodeElement; })(); FolderElement = (function(_super) { __extends(FolderElement, _super); function FolderElement() { return FolderElement.__super__.constructor.apply(this, arguments); } FolderElement.prototype.toggle = function() { if (this.node.is_open) { return this.close(); } else { return this.open(); } }; FolderElement.prototype.open = function(on_finished, skip_slide) { var doOpen, _this = this; if (!this.node.is_open) { this.node.is_open = true; this.getButton().removeClass('closed'); doOpen = function() { var event; _this.getLi().removeClass('closed'); if (on_finished) { on_finished(); } event = $.Event('tree.open'); event.node = _this.node; return _this.tree_element.trigger(event); }; if (skip_slide) { return doOpen(); } else { return this.getUl().slideDown('fast', doOpen); } } }; FolderElement.prototype.close = function(skip_slide) { var doClose, _this = this; if (this.node.is_open) { this.node.is_open = false; this.getButton().addClass('closed'); doClose = function() { var event; _this.getLi().addClass('closed'); event = $.Event('tree.close'); event.node = _this.node; return _this.tree_element.trigger(event); }; if (skip_slide) { return doClose(); } else { return this.getUl().slideUp('fast', doClose); } } }; FolderElement.prototype.getButton = function() { return this.$element.children('div').find('a.toggler'); }; FolderElement.prototype.addDropHint = function(position) { if (!this.node.is_open && position === Position.INSIDE) { return new BorderDropHint(this.$element); } else { return new GhostDropHint(this.node, this.$element, position); } }; return FolderElement; })(NodeElement); DragElement = (function() { function DragElement(node, offset_x, offset_y, $tree) { this.offset_x = offset_x; this.offset_y = offset_y; this.$element = $("" + node.name + ""); this.$element.css("position", "absolute"); $tree.append(this.$element); } DragElement.prototype.move = function(page_x, page_y) { return this.$element.offset({ left: page_x - this.offset_x, top: page_y - this.offset_y }); }; DragElement.prototype.remove = function() { return this.$element.remove(); }; return DragElement; })(); SaveStateHandler = (function() { function SaveStateHandler(tree_widget) { this.tree_widget = tree_widget; } SaveStateHandler.prototype.saveState = function() { if (this.tree_widget.options.onSetStateFromStorage) { return this.tree_widget.options.onSetStateFromStorage(this.getState()); } else if (typeof localStorage !== "undefined" && localStorage !== null) { return localStorage.setItem(this.getCookieName(), this.getState()); } else if ($.cookie) { return $.cookie(this.getCookieName(), this.getState(), { path: '/' }); } }; SaveStateHandler.prototype.restoreState = function() { var state; if (this.tree_widget.options.onGetStateFromStorage) { state = this.tree_widget.options.onGetStateFromStorage(); } else if (typeof localStorage !== "undefined" && localStorage !== null) { state = localStorage.getItem(this.getCookieName()); } else if ($.cookie) { state = $.cookie(this.getCookieName(), { path: '/' }); } else { state = null; } if (!state) { return false; } else { this.setState(state); return true; } }; SaveStateHandler.prototype.getState = function() { var open_nodes, selected_node, _this = this; open_nodes = []; this.tree_widget.tree.iterate(function(node) { if (node.is_open && node.id && node.hasChildren()) { open_nodes.push(node.id); } return true; }); selected_node = ''; if (this.tree_widget.selected_node) { selected_node = this.tree_widget.selected_node.id; } return toJson({ open_nodes: open_nodes, selected_node: selected_node }); }; SaveStateHandler.prototype.setState = function(state) { var data, open_nodes, selected_node_id, _this = this; data = $.parseJSON(state); if (data) { open_nodes = data.open_nodes; selected_node_id = data.selected_node; return this.tree_widget.tree.iterate(function(node) { if (node.id && node.hasChildren() && (indexOf(open_nodes, node.id) >= 0)) { node.is_open = true; } if (selected_node_id && (node.id === selected_node_id)) { _this.tree_widget.selected_node = node; } return true; }); } }; SaveStateHandler.prototype.getCookieName = function() { if (typeof this.tree_widget.options.saveState === 'string') { return this.tree_widget.options.saveState; } else { return 'tree'; } }; return SaveStateHandler; })(); SelectNodeHandler = (function() { function SelectNodeHandler(tree_widget) { this.tree_widget = tree_widget; } SelectNodeHandler.prototype.selectNode = function(node, must_open_parents) { var parent; if (this.tree_widget.options.selectable) { if (this.tree_widget.selected_node) { this.tree_widget._getNodeElementForNode(this.tree_widget.selected_node).deselect(); this.tree_widget.selected_node = null; } if (node) { this.tree_widget._getNodeElementForNode(node).select(); this.tree_widget.selected_node = node; if (must_open_parents) { parent = this.tree_widget.selected_node.parent; while (parent) { if (!parent.is_open) { this.tree_widget.openNode(parent, true); } parent = parent.parent; } } } if (this.tree_widget.options.saveState) { return this.tree_widget.save_state_handler.saveState(); } } }; SelectNodeHandler.prototype.selectCurrentNode = function() { var node_element; if (this.tree_widget.selected_node) { node_element = this.tree_widget._getNodeElementForNode(this.tree_widget.selected_node); if (node_element) { return node_element.select(); } } }; return SelectNodeHandler; })(); DragAndDropHandler = (function() { function DragAndDropHandler(tree_widget) { this.tree_widget = tree_widget; this.hovered_area = null; this.$ghost = null; this.hit_areas = []; this.is_dragging = false; } DragAndDropHandler.prototype.mouseCapture = function(event) { var $element, node_element; $element = $(event.target); if (this.tree_widget.options.onIsMoveHandle && !this.tree_widget.options.onIsMoveHandle($element)) { return null; } node_element = this.tree_widget._getNodeElement($element); if (node_element && this.tree_widget.options.onCanMove) { if (!this.tree_widget.options.onCanMove(node_element.node)) { node_element = null; } } this.current_item = node_element; return this.current_item !== null; }; DragAndDropHandler.prototype.mouseStart = function(event) { var offsetX, offsetY, _ref; this.refreshHitAreas(); _ref = this.getOffsetFromEvent(event), offsetX = _ref[0], offsetY = _ref[1]; this.drag_element = new DragElement(this.current_item.node, offsetX, offsetY, this.tree_widget.element); this.is_dragging = true; this.current_item.$element.addClass('moving'); return true; }; DragAndDropHandler.prototype.mouseDrag = function(event) { var area, position_name; this.drag_element.move(event.pageX, event.pageY); area = this.findHoveredArea(event.pageX, event.pageY); if (area && this.tree_widget.options.onCanMoveTo) { position_name = Position.getName(area.position); if (!this.tree_widget.options.onCanMoveTo(this.current_item.node, area.node, position_name)) { area = null; } } if (!area) { this.removeDropHint(); this.removeHover(); this.stopOpenFolderTimer(); } else { if (this.hovered_area !== area) { this.hovered_area = area; this.updateDropHint(); } } return true; }; DragAndDropHandler.prototype.mouseStop = function() { this.moveItem(); this.clear(); this.removeHover(); this.removeDropHint(); this.removeHitAreas(); this.current_item.$element.removeClass('moving'); this.is_dragging = false; return false; }; DragAndDropHandler.prototype.getOffsetFromEvent = function(event) { var element_offset; element_offset = $(event.target).offset(); return [event.pageX - element_offset.left, event.pageY - element_offset.top]; }; DragAndDropHandler.prototype.refreshHitAreas = function() { this.removeHitAreas(); return this.generateHitAreas(); }; DragAndDropHandler.prototype.removeHitAreas = function() { return this.hit_areas = []; }; DragAndDropHandler.prototype.clear = function() { this.drag_element.remove(); return this.drag_element = null; }; DragAndDropHandler.prototype.removeDropHint = function() { if (this.previous_ghost) { return this.previous_ghost.remove(); } }; DragAndDropHandler.prototype.removeHover = function() { return this.hovered_area = null; }; DragAndDropHandler.prototype.generateHitAreas = function() { var addPosition, getTop, groupPositions, handleAfterOpenFolder, handleClosedFolder, handleFirstNode, handleNode, handleOpenFolder, hit_areas, last_top, positions, _this = this; positions = []; last_top = 0; getTop = function($element) { return $element.offset().top; }; addPosition = function(node, position, top) { positions.push({ top: top, node: node, position: position }); return last_top = top; }; groupPositions = function(handle_group) { var group, position, previous_top, _i, _len; previous_top = -1; group = []; for (_i = 0, _len = positions.length; _i < _len; _i++) { position = positions[_i]; if (position.top !== previous_top) { if (group.length) { handle_group(group, previous_top, position.top); } previous_top = position.top; group = []; } group.push(position); } return handle_group(group, previous_top, _this.tree_widget.element.offset().top + _this.tree_widget.element.height()); }; handleNode = function(node, next_node, $element) { var top; top = getTop($element); if (node === _this.current_item.node) { addPosition(node, Position.NONE, top); } else { addPosition(node, Position.INSIDE, top); } if (next_node === _this.current_item.node || node === _this.current_item.node) { return addPosition(node, Position.NONE, top); } else { return addPosition(node, Position.AFTER, top); } }; handleOpenFolder = function(node, $element) { if (node === _this.current_item.node) { return false; } if (node.children[0] !== _this.current_item.node) { addPosition(node, Position.INSIDE, getTop($element)); } return true; }; handleAfterOpenFolder = function(node, next_node, $element) { if (node === _this.current_item.node || next_node === _this.current_item.node) { return addPosition(node, Position.NONE, last_top); } else { return addPosition(node, Position.AFTER, last_top); } }; handleClosedFolder = function(node, next_node, $element) { var top; top = getTop($element); if (node === _this.current_item.node) { return addPosition(node, Position.NONE, top); } else { addPosition(node, Position.INSIDE, top); if (next_node !== _this.current_item.node) { return addPosition(node, Position.AFTER, top); } } }; handleFirstNode = function(node, $element) { if (node !== _this.current_item.node) { return addPosition(node, Position.BEFORE, getTop($(node.element))); } }; this.iterateVisibleNodes(handleNode, handleOpenFolder, handleClosedFolder, handleAfterOpenFolder, handleFirstNode); hit_areas = []; groupPositions(function(positions_in_group, top, bottom) { var area_height, area_top, position, _i, _len; area_height = (bottom - top) / positions_in_group.length; area_top = top; for (_i = 0, _len = positions_in_group.length; _i < _len; _i++) { position = positions_in_group[_i]; hit_areas.push({ top: area_top, bottom: area_top + area_height, node: position.node, position: position.position }); area_top += area_height; } return null; }); return this.hit_areas = hit_areas; }; DragAndDropHandler.prototype.iterateVisibleNodes = function(handle_node, handle_open_folder, handle_closed_folder, handle_after_open_folder, handle_first_node) { var is_first_node, iterate, _this = this; is_first_node = true; iterate = function(node, next_node) { var $element, child, children_length, i, must_iterate_inside, _i, _len, _ref; must_iterate_inside = (node.is_open || !node.element) && node.hasChildren(); if (node.element) { $element = $(node.element); if (!$element.is(':visible')) { return; } if (is_first_node) { handle_first_node(node, $element); is_first_node = false; } if (!node.hasChildren()) { handle_node(node, next_node, $element); } else if (node.is_open) { if (!handle_open_folder(node, $element)) { must_iterate_inside = false; } } else { handle_closed_folder(node, next_node, $element); } } if (must_iterate_inside) { children_length = node.children.length; _ref = node.children; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { child = _ref[i]; if (i === (children_length - 1)) { iterate(node.children[i], null); } else { iterate(node.children[i], node.children[i + 1]); } } if (node.is_open) { return handle_after_open_folder(node, next_node, $element); } } }; return iterate(this.tree_widget.tree); }; DragAndDropHandler.prototype.findHoveredArea = function(x, y) { var area, high, low, mid, tree_offset; tree_offset = this.tree_widget.element.offset(); if (x < tree_offset.left || y < tree_offset.top || x > (tree_offset.left + this.tree_widget.element.width()) || y > (tree_offset.top + this.tree_widget.element.height())) { return null; } low = 0; high = this.hit_areas.length; while (low < high) { mid = (low + high) >> 1; area = this.hit_areas[mid]; if (y < area.top) { high = mid; } else if (y > area.bottom) { low = mid + 1; } else { return area; } } return null; }; DragAndDropHandler.prototype.updateDropHint = function() { var node, node_element; this.stopOpenFolderTimer(); if (!this.hovered_area) { return; } node = this.hovered_area.node; if (node.hasChildren() && !node.is_open && this.hovered_area.position === Position.INSIDE) { this.startOpenFolderTimer(node); } this.removeDropHint(); node_element = this.tree_widget._getNodeElementForNode(this.hovered_area.node); return this.previous_ghost = node_element.addDropHint(this.hovered_area.position); }; DragAndDropHandler.prototype.startOpenFolderTimer = function(folder) { var openFolder, _this = this; openFolder = function() { return _this.tree_widget._getNodeElementForNode(folder).open(function() { _this.refreshHitAreas(); return _this.updateDropHint(); }); }; return this.open_folder_timer = setTimeout(openFolder, 500); }; DragAndDropHandler.prototype.stopOpenFolderTimer = function() { if (this.open_folder_timer) { clearTimeout(this.open_folder_timer); return this.open_folder_timer = null; } }; DragAndDropHandler.prototype.moveItem = function() { var doMove, event, moved_node, position, previous_parent, target_node, _this = this; if (this.hovered_area && this.hovered_area.position !== Position.NONE) { moved_node = this.current_item.node; target_node = this.hovered_area.node; position = this.hovered_area.position; previous_parent = moved_node.parent; if (position === Position.INSIDE) { this.hovered_area.node.is_open = true; } doMove = function() { _this.tree_widget.tree.moveNode(moved_node, target_node, position); _this.tree_widget.element.empty(); return _this.tree_widget._refreshElements(); }; event = $.Event('tree.move'); event.move_info = { moved_node: moved_node, target_node: target_node, position: Position.getName(position), previous_parent: previous_parent, do_move: doMove }; this.tree_widget.element.trigger(event); if (!event.isDefaultPrevented()) { return doMove(); } } }; return DragAndDropHandler; })(); this.Tree.Node = Node; }).call(this); Orthanc-1.0.0/OrthancExplorer/query-retrieve.js0000644000000000000000000002005512634042176017710 0ustar 00000000000000function JavascriptDateToDicom(date) { var s = date.toISOString(); return s.substring(0, 4) + s.substring(5, 7) + s.substring(8, 10); } function GenerateDicomDate(days) { var today = new Date(); var other = new Date(today); other.setDate(today.getDate() + days); return JavascriptDateToDicom(other); } $('#query-retrieve').live('pagebeforeshow', function() { $.ajax({ url: '../modalities', dataType: 'json', async: false, cache: false, success: function(modalities) { var target = $('#qr-server'); $('option', target).remove(); for (var i = 0; i < modalities.length; i++) { var option = $('