Mercurial > hg > orthanc
changeset 1308:f7966e9950e4 db-changes
integration mainline->db-changes
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 11 Feb 2015 10:32:14 +0100 |
parents | 178de5edc0a8 (current diff) f796207e3df1 (diff) |
children | 8f4487d8f79e |
files | Plugins/OrthancCPlugin/OrthancCPlugin.h Resources/CMake/PlustacheConfiguration.cmake Resources/CMake/PlustacheConfiguration.patch UnitTestsSources/PlustacheTests.cpp |
diffstat | 309 files changed, 5934 insertions(+), 3677 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.travis.yml Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,68 @@ +language: 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
--- a/AUTHORS Tue Nov 04 13:56:17 2014 +0100 +++ b/AUTHORS Wed Feb 11 10:32:14 2015 +0100 @@ -6,7 +6,9 @@ ------------------ * Sebastien Jodogne <s.jodogne@gmail.com> - Department of Medical Physics, CHU of Liege, Belgium + Department of Medical Physics + University Hospital of Liege + Belgium Overall design and lead developer.
--- a/CMakeLists.txt Tue Nov 04 13:56:17 2014 +0100 +++ b/CMakeLists.txt Wed Feb 11 10:32:14 2015 +0100 @@ -37,9 +37,7 @@ SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml)") # Experimental options -SET(USE_PLUSTACHE OFF CACHE BOOL "Use the Plustache templating engine (experimental)") SET(USE_PUGIXML ON CACHE BOOL "Use the Pugixml parser (turn off only for debug)") -SET(USE_SYSTEM_PLUSTACHE OFF CACHE BOOL "Use the system version of Plustache (experimental)") # Distribution-specific settings SET(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") @@ -166,6 +164,7 @@ OrthancServer/ServerToolbox.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancMoveRequestHandler.cpp + OrthancServer/ExportedResource.cpp # From "lua-scripting" branch OrthancServer/DicomInstanceToStore.cpp @@ -198,7 +197,16 @@ UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/JpegLosslessTests.cpp UnitTestsSources/PluginsTests.cpp - UnitTestsSources/PlustacheTests.cpp + ) + + +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 ) @@ -229,7 +237,6 @@ include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/Resources/CMake/PlustacheConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake) @@ -260,21 +267,11 @@ ## Autogeneration of files ##################################################################### -# Prepare the embedded files -set(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 - ) - if (${STANDALONE_BUILD}) # We embed all the resources in the binaries for standalone builds add_definitions(-DORTHANC_STANDALONE=1) EmbedResources( - ${EMBEDDED_FILES} + ${ORTHANC_EMBEDDED_FILES} ORTHANC_EXPLORER ${CMAKE_CURRENT_SOURCE_DIR}/OrthancExplorer ${DCMTK_DICTIONARIES} ) @@ -284,7 +281,7 @@ -DORTHANC_PATH=\"${CMAKE_SOURCE_DIR}\" ) EmbedResources( - ${EMBEDDED_FILES} + ${ORTHANC_EMBEDDED_FILES} ) endif() @@ -479,7 +476,7 @@ install( FILES ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h - ${ORTHANC_ROOT}/Plugins/OrthancCPlugin/OrthancCPlugin.h + ${ORTHANC_ROOT}/Plugins/Include/OrthancCPlugin.h DESTINATION include/orthanc ) endif()
--- a/Core/Cache/ICachePageProvider.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Cache/ICachePageProvider.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Cache/LeastRecentlyUsedIndex.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Cache/LeastRecentlyUsedIndex.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Cache/MemoryCache.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Cache/MemoryCache.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Cache/MemoryCache.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Cache/MemoryCache.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ChunkedBuffer.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ChunkedBuffer.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ChunkedBuffer.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ChunkedBuffer.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Compression/BufferCompressor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/BufferCompressor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Compression/BufferCompressor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/BufferCompressor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Compression/HierarchicalZipWriter.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/HierarchicalZipWriter.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Compression/HierarchicalZipWriter.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/HierarchicalZipWriter.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -114,6 +114,16 @@ 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);
--- a/Core/Compression/ZipWriter.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/ZipWriter.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -38,10 +38,11 @@ #include "ZipWriter.h" -#include "../../Resources/ThirdParty/minizip/zip.h" +#include <limits> +#include <boost/filesystem.hpp> #include <boost/date_time/posix_time/posix_time.hpp> -#include <limits> +#include "../../Resources/ThirdParty/minizip/zip.h" #include "../OrthancException.h" @@ -77,15 +78,19 @@ struct ZipWriter::PImpl { zipFile file_; + + PImpl() : file_(NULL) + { + } }; - ZipWriter::ZipWriter() : pimpl_(new PImpl) + ZipWriter::ZipWriter() : + pimpl_(new PImpl), + isZip64_(false), + hasFileInZip_(false), + append_(false), + compressionLevel_(6) { - compressionLevel_ = 6; - hasFileInZip_ = false; - isZip64_ = false; - - pimpl_->file_ = NULL; } ZipWriter::~ZipWriter() @@ -122,13 +127,20 @@ 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(), APPEND_STATUS_CREATE); + pimpl_->file_ = zipOpen64(path_.c_str(), mode); } else { - pimpl_->file_ = zipOpen(path_.c_str(), APPEND_STATUS_CREATE); + pimpl_->file_ = zipOpen(path_.c_str(), mode); } if (!pimpl_->file_) @@ -230,4 +242,13 @@ length -= bytes; } } + + + void ZipWriter::SetAppendToExisting(bool append) + { + Close(); + append_ = append; + } + + }
--- a/Core/Compression/ZipWriter.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/ZipWriter.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -50,6 +50,7 @@ bool isZip64_; bool hasFileInZip_; + bool append_; uint8_t compressionLevel_; std::string path_; @@ -71,6 +72,13 @@ { return compressionLevel_; } + + void SetAppendToExisting(bool append); + + bool IsAppendToExisting() const + { + return append_; + } void Open();
--- a/Core/Compression/ZlibCompressor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/ZlibCompressor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Compression/ZlibCompressor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Compression/ZlibCompressor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomArray.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomArray.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomArray.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomArray.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomElement.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomElement.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomImageInformation.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomImageInformation.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomImageInformation.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomImageInformation.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomInstanceHasher.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomInstanceHasher.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -48,8 +48,7 @@ seriesUid_ = seriesUid; instanceUid_ = instanceUid; - if (patientId_.size() == 0 || - studyUid_.size() == 0 || + if (studyUid_.size() == 0 || seriesUid_.size() == 0 || instanceUid_.size() == 0) { @@ -59,7 +58,9 @@ DicomInstanceHasher::DicomInstanceHasher(const DicomMap& instance) { - Setup(instance.GetValue(DICOM_TAG_PATIENT_ID).AsString(), + const DicomValue* patientId = instance.TestAndGetValue(DICOM_TAG_PATIENT_ID); + + Setup(patientId == NULL ? "" : patientId->AsString(), instance.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(), instance.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(), instance.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString());
--- a/Core/DicomFormat/DicomInstanceHasher.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomInstanceHasher.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomMap.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomMap.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomMap.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomMap.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -125,11 +125,13 @@ 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);
--- a/Core/DicomFormat/DicomNullValue.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomNullValue.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomString.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomString.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/DicomFormat/DicomTag.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomTag.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -119,14 +119,14 @@ void DicomTag::GetTagsForModule(std::set<DicomTag>& target, - ResourceType module) + DicomModule module) { // REFERENCE: 11_03pu.pdf, DICOM PS 3.3 2011 - Information Object Definitions target.clear(); switch (module) { - case ResourceType_Patient: + 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 @@ -156,7 +156,7 @@ target.insert(DicomTag(0x0010, 0x0024)); // Issuer of Patient ID qualifiers sequence break; - case ResourceType_Study: + 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 @@ -177,7 +177,7 @@ target.insert(DicomTag(0x0040, 0x1012)); // Reason for performed procedure code sequence break; - case ResourceType_Series: + 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 @@ -210,7 +210,7 @@ target.insert(DicomTag(0x0040, 0x0280)); // Comments on the Performed Procedure Step break; - case ResourceType_Instance: + 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
--- a/Core/DicomFormat/DicomTag.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomTag.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -85,7 +85,7 @@ friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag); static void GetTagsForModule(std::set<DicomTag>& target, - ResourceType module); + DicomModule module); bool IsIdentifier() const; }; @@ -109,10 +109,11 @@ static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010); - // The following is used for "modify" operations + // 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);
--- a/Core/DicomFormat/DicomValue.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/DicomFormat/DicomValue.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/EnumerationDictionary.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/EnumerationDictionary.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Enumerations.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Enumerations.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Enumerations.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Enumerations.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -58,6 +58,7 @@ ErrorCode_BadRequest, ErrorCode_NetworkProtocol, ErrorCode_SystemCommand, + ErrorCode_Database, // Specific error codes ErrorCode_UriSyntax, @@ -273,6 +274,15 @@ PhotometricInterpretation_Unknown }; + enum DicomModule + { + DicomModule_Patient, + DicomModule_Study, + DicomModule_Series, + DicomModule_Instance, + DicomModule_Image + }; + /** * WARNING: Do not change the explicit values in the enumerations
--- a/Core/FileStorage/CompressedFileStorageAccessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/CompressedFileStorageAccessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/CompressedFileStorageAccessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/CompressedFileStorageAccessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/FileInfo.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/FileInfo.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/FileStorageAccessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/FileStorageAccessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/FileStorageAccessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/FileStorageAccessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/FilesystemStorage.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/FilesystemStorage.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -139,7 +139,7 @@ void FilesystemStorage::Read(std::string& content, const std::string& uuid, - FileContentType /*type*/) const + FileContentType /*type*/) { content.clear(); Toolbox::ReadFile(content, GetPath(uuid).string());
--- a/Core/FileStorage/FilesystemStorage.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/FilesystemStorage.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -60,7 +60,7 @@ virtual void Read(std::string& content, const std::string& uuid, - FileContentType type) const; + FileContentType type); virtual void Remove(const std::string& uuid, FileContentType type);
--- a/Core/FileStorage/IStorageArea.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/IStorageArea.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -52,7 +52,7 @@ virtual void Read(std::string& content, const std::string& uuid, - FileContentType type) const = 0; + FileContentType type) = 0; virtual void Remove(const std::string& uuid, FileContentType type) = 0;
--- a/Core/FileStorage/StorageAccessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/StorageAccessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/FileStorage/StorageAccessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/FileStorage/StorageAccessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpClient.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpClient.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -171,6 +171,7 @@ 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) @@ -189,6 +190,11 @@ 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:
--- a/Core/HttpClient.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpClient.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -53,6 +53,7 @@ std::string postData_; bool isVerbose_; long timeout_; + std::string proxy_; void Setup(); @@ -134,6 +135,11 @@ void SetCredentials(const char* username, const char* password); + void SetProxy(const std::string& proxy) + { + proxy_ = proxy; + } + static void GlobalInitialize(); static void GlobalFinalize();
--- a/Core/HttpServer/BufferHttpSender.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/BufferHttpSender.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/FilesystemHttpHandler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/FilesystemHttpHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/FilesystemHttpHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/FilesystemHttpSender.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/FilesystemHttpSender.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/FilesystemHttpSender.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpFileSender.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpFileSender.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpFileSender.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpFileSender.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpHandler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpHandler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpOutput.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpOutput.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/HttpOutput.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/HttpOutput.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/IHttpOutputStream.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/IHttpOutputStream.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/HttpServer/MongooseServer.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/MongooseServer.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -418,7 +418,8 @@ if (auth != headers.end()) { std::string s = auth->second; - if (s.substr(0, 6) == "Basic ") + if (s.size() > 6 && + s.substr(0, 6) == "Basic ") { std::string b64 = s.substr(6); granted = that.IsValidBasicHttpAuthentication(b64); @@ -439,7 +440,8 @@ } std::string s = auth->second; - if (s.substr(0, 6) != "Basic ") + if (s.size() <= 6 || + s.substr(0, 6) != "Basic ") { return ""; }
--- a/Core/HttpServer/MongooseServer.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/HttpServer/MongooseServer.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ICommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ICommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/IDynamicObject.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/IDynamicObject.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageAccessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageAccessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageAccessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageAccessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageBuffer.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageBuffer.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageBuffer.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageBuffer.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageProcessing.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageProcessing.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/ImageProcessing.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/ImageProcessing.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/PngReader.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/PngReader.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/PngReader.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/PngReader.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/PngWriter.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/PngWriter.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/ImageFormats/PngWriter.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/ImageFormats/PngWriter.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Lua/LuaContext.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Lua/LuaContext.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Lua/LuaContext.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Lua/LuaContext.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -102,5 +102,10 @@ { httpClient_.SetCredentials(username, password); } + + void SetHttpProxy(const std::string& proxy) + { + httpClient_.SetProxy(proxy); + } }; }
--- a/Core/Lua/LuaException.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Lua/LuaException.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Lua/LuaFunctionCall.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Lua/LuaFunctionCall.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Lua/LuaFunctionCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Lua/LuaFunctionCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/ArrayFilledByThreads.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ArrayFilledByThreads.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/BagOfRunnablesBySteps.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/BagOfRunnablesBySteps.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/ILockable.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ILockable.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/IRunnableBySteps.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/IRunnableBySteps.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/Locker.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/Locker.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/Mutex.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/Mutex.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/Mutex.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/Mutex.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/ReaderWriterLock.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ReaderWriterLock.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/ReaderWriterLock.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ReaderWriterLock.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/Semaphore.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/Semaphore.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/Semaphore.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/Semaphore.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/SharedMessageQueue.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/SharedMessageQueue.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -33,11 +33,40 @@ #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) + SharedMessageQueue::SharedMessageQueue(unsigned int maxSize) : + isFifo_(true), + maxSize_(maxSize) { - maxSize_ = maxSize; } @@ -56,12 +85,31 @@ if (maxSize_ != 0 && queue_.size() > maxSize_) { - // Too many elements in the queue: First remove the oldest - delete queue_.front(); - queue_.pop_front(); + 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(); + } } - queue_.push_back(message); + if (isFifo_) + { + // Queue policy (FIFO) + queue_.push_back(message); + } + else + { + // Stack policy (LIFO) + queue_.push_front(message); + } + elementAvailable_.notify_one(); } @@ -124,4 +172,17 @@ return true; } + + + void SharedMessageQueue::SetFifoPolicy() + { + boost::mutex::scoped_lock lock(mutex_); + isFifo_ = true; + } + + void SharedMessageQueue::SetLifoPolicy() + { + boost::mutex::scoped_lock lock(mutex_); + isFifo_ = false; + } }
--- a/Core/MultiThreading/SharedMessageQueue.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/SharedMessageQueue.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -45,6 +45,7 @@ private: typedef std::list<IDynamicObject*> Queue; + bool isFifo_; unsigned int maxSize_; Queue queue_; boost::mutex mutex_; @@ -63,5 +64,19 @@ IDynamicObject* Dequeue(int32_t millisecondsTimeout); bool WaitEmpty(int32_t millisecondsTimeout); + + bool IsFifoPolicy() const + { + return isFifo_; + } + + bool IsLifoPolicy() const + { + return !isFifo_; + } + + void SetFifoPolicy(); + + void SetLifoPolicy(); }; }
--- a/Core/MultiThreading/ThreadedCommandProcessor.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ThreadedCommandProcessor.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/MultiThreading/ThreadedCommandProcessor.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/MultiThreading/ThreadedCommandProcessor.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/OrthancException.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/OrthancException.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -130,6 +130,9 @@ case ErrorCode_Plugin: return "Error encountered inside a plugin"; + case ErrorCode_Database: + return "Error with the database engine"; + case ErrorCode_Custom: default: return "???";
--- a/Core/OrthancException.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/OrthancException.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/PrecompiledHeaders.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/PrecompiledHeaders.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/PrecompiledHeaders.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/PrecompiledHeaders.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApi.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApi.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApi.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApi.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiCall.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiCall.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiDeleteCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiDeleteCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiGetCall.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiGetCall.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiGetCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiGetCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiHierarchy.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiHierarchy.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -159,7 +159,7 @@ void RestApiHierarchy::DeleteChildren(Children& children) { for (Children::iterator it = children.begin(); - it != children.end(); it++) + it != children.end(); ++it) { delete it->second; } @@ -238,7 +238,7 @@ // Try and go down in the hierarchy, using wildcard rules for children for (child = wildcardChildren_.begin(); - child != wildcardChildren_.end(); child++) + child != wildcardChildren_.end(); ++child) { HttpHandler::Arguments subComponents = components; subComponents[child->first] = uri[level]; @@ -276,7 +276,7 @@ bool RestApiHierarchy::CanGenerateDirectory() const { return (universalHandlers_.IsEmpty() && - wildcardChildren_.size() == 0); + wildcardChildren_.empty()); } @@ -291,7 +291,7 @@ result = Json::arrayValue; for (Children::const_iterator it = children_.begin(); - it != children_.end(); it++) + it != children_.end(); ++it) { result.append(it->first); } @@ -314,7 +314,7 @@ } for (child = wildcardChildren_.begin(); - child != wildcardChildren_.end(); child++) + child != wildcardChildren_.end(); ++child) { if (child->second->GetDirectory(result, uri, level + 1)) { @@ -388,13 +388,13 @@ target = s;*/ for (Children::const_iterator it = children_.begin(); - it != children_.end(); it++) + it != children_.end(); ++it) { it->second->CreateSiteMap(target[it->first]); } for (Children::const_iterator it = wildcardChildren_.begin(); - it != wildcardChildren_.end(); it++) + it != wildcardChildren_.end(); ++it) { it->second->CreateSiteMap(target["<" + it->first + ">"]); }
--- a/Core/RestApi/RestApiHierarchy.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiHierarchy.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiOutput.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiOutput.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiOutput.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiOutput.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiPath.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiPath.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiPath.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiPath.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiPostCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiPostCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/RestApi/RestApiPutCall.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/RestApi/RestApiPutCall.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/SQLite/Connection.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Connection.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -34,15 +35,21 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "Connection.h" +#include "OrthancSQLiteException.h" #include <memory> #include <cassert> #include <sqlite3.h> #include <string.h> +#if ORTHANC_SQLITE_STANDALONE != 1 #include <glog/logging.h> +#endif namespace Orthanc @@ -67,7 +74,7 @@ { if (!db_) { - throw OrthancException("SQLite: The database is not opened"); + throw OrthancSQLiteException("SQLite: The database is not opened"); } } @@ -75,7 +82,7 @@ { if (db_) { - throw OrthancException("SQLite: Connection is already open"); + throw OrthancSQLiteException("SQLite: Connection is already open"); } int err = sqlite3_open(path.c_str(), &db_); @@ -83,7 +90,7 @@ { Close(); db_ = NULL; - throw OrthancException("SQLite: Unable to open the database"); + throw OrthancSQLiteException("SQLite: Unable to open the database"); } // Execute PRAGMAs at this point @@ -129,7 +136,7 @@ { if (i->second->GetReferenceCount() >= 1) { - throw OrthancException("SQLite: This cached statement is already being referred to"); + throw OrthancSQLiteException("SQLite: This cached statement is already being referred to"); } return *i->second; @@ -145,13 +152,16 @@ 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) { - throw OrthancException("SQLite Execute error: " + std::string(sqlite3_errmsg(db_))); + throw OrthancSQLiteException("SQLite Execute error: " + std::string(sqlite3_errmsg(db_))); } else { @@ -272,7 +282,7 @@ { if (!transactionNesting_) { - throw OrthancException("Rolling back a nonexistent transaction"); + throw OrthancSQLiteException("Rolling back a nonexistent transaction"); } transactionNesting_--; @@ -291,7 +301,7 @@ { if (!transactionNesting_) { - throw OrthancException("Committing a nonexistent transaction"); + throw OrthancSQLiteException("Committing a nonexistent transaction"); } transactionNesting_--; @@ -359,7 +369,7 @@ if (err != SQLITE_OK) { delete func; - throw OrthancException("SQLite: Unable to register a function"); + throw OrthancSQLiteException("SQLite: Unable to register a function"); } return func; @@ -368,12 +378,15 @@ 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 OrthancException("SQLite: Unable to flush the database"); + throw OrthancSQLiteException("SQLite: Unable to flush the database"); } } }
--- a/Core/SQLite/Connection.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Connection.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -40,7 +41,6 @@ #include "IScalarFunction.h" #include <string> -#include <boost/noncopyable.hpp> #include <map> struct sqlite3; @@ -52,7 +52,7 @@ { namespace SQLite { - class Connection : boost::noncopyable + class Connection : NonCopyable { friend class Statement; friend class Transaction;
--- a/Core/SQLite/FunctionContext.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/FunctionContext.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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 @@ -31,8 +32,12 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "FunctionContext.h" +#include "OrthancSQLiteException.h" #include <sqlite3.h> @@ -57,7 +62,7 @@ { if (index >= argc_) { - throw OrthancException(ErrorCode_ParameterOutOfRange); + throw OrthancSQLiteException("Parameter out of range"); } }
--- a/Core/SQLite/FunctionContext.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/FunctionContext.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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 @@ -33,8 +34,6 @@ #pragma once -#include <boost/noncopyable.hpp> - #include "Statement.h" struct sqlite3_context; @@ -44,7 +43,7 @@ { namespace SQLite { - class FunctionContext : public boost::noncopyable + class FunctionContext : public NonCopyable { friend class Connection;
--- a/Core/SQLite/IScalarFunction.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/IScalarFunction.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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 @@ -33,13 +34,14 @@ #pragma once +#include "NonCopyable.h" #include "FunctionContext.h" namespace Orthanc { namespace SQLite { - class IScalarFunction : public boost::noncopyable + class IScalarFunction : public NonCopyable { public: virtual ~IScalarFunction()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/SQLite/ITransaction.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,66 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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; + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/SQLite/NonCopyable.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,60 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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() + { + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/SQLite/OrthancSQLiteException.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,67 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * 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 <stdexcept> + +namespace Orthanc +{ + namespace SQLite + { + class OrthancSQLiteException : public ::std::runtime_error + { + public: + OrthancSQLiteException(const std::string& what) : + ::std::runtime_error(what) + { + } + + OrthancSQLiteException(const char* what) : + ::std::runtime_error(what) + { + } + }; + } +} + +#else +# include "../OrthancException.h" +# define OrthancSQLiteException ::Orthanc::OrthancException +#endif
--- a/Core/SQLite/README.txt Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/README.txt Wed Feb 11 10:32:14 2015 +0100 @@ -19,6 +19,17 @@ 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 ========= @@ -26,4 +37,4 @@ 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. +to include it in another project.
--- a/Core/SQLite/Statement.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Statement.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -34,16 +35,25 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "Statement.h" - #include "Connection.h" -#include "../Toolbox.h" -#include <boost/lexical_cast.hpp> #include <sqlite3.h> #include <string.h> +#include <stdio.h> +#include <algorithm> + +#if ORTHANC_SQLITE_STANDALONE != 1 #include <glog/logging.h> +#endif + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif namespace Orthanc { @@ -54,7 +64,9 @@ bool succeeded = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE); if (!succeeded) { - throw OrthancException("SQLite error code " + boost::lexical_cast<std::string>(err)); + char buffer[128]; + snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); + throw OrthancSQLiteException(buffer); } return err; @@ -65,11 +77,13 @@ if (err == SQLITE_RANGE) { // Binding to a non-existent variable is evidence of a serious error. - throw OrthancException("Bind value out of range"); + throw OrthancSQLiteException("Bind value out of range"); } else if (err != SQLITE_OK) { - throw OrthancException("SQLite error code " + boost::lexical_cast<std::string>(err)); + char buffer[128]; + snprintf(buffer, sizeof(buffer) - 1, "SQLite error code %d", err); + throw OrthancSQLiteException(buffer); } } @@ -108,13 +122,19 @@ bool Statement::Run() { +#if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Statement::Run " << sqlite3_sql(GetStatement()); +#endif + return CheckError(sqlite3_step(GetStatement())) == SQLITE_DONE; } bool Statement::Step() { +#if ORTHANC_SQLITE_STANDALONE != 1 VLOG(1) << "SQLite::Statement::Step " << sqlite3_sql(GetStatement()); +#endif + return CheckError(sqlite3_step(GetStatement())) == SQLITE_ROW; } @@ -206,7 +226,7 @@ ColumnType Statement::GetDeclaredColumnType(int col) const { std::string column_type(sqlite3_column_decltype(GetStatement(), col)); - Toolbox::ToLowerCase(column_type); + std::transform(column_type.begin(), column_type.end(), column_type.begin(), tolower); if (column_type == "integer") return COLUMN_TYPE_INTEGER;
--- a/Core/SQLite/Statement.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Statement.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -36,13 +37,13 @@ #pragma once -#include "../OrthancException.h" +#include "NonCopyable.h" +#include "OrthancSQLiteException.h" #include "StatementId.h" #include "StatementReference.h" #include <vector> #include <stdint.h> -#include <boost/noncopyable.hpp> #if ORTHANC_BUILD_UNIT_TESTS == 1 #include <gtest/gtest_prod.h> @@ -68,7 +69,7 @@ COLUMN_TYPE_NULL = 5 }; - class Statement : public boost::noncopyable + class Statement : public NonCopyable { friend class Connection;
--- a/Core/SQLite/StatementId.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/StatementId.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -34,7 +35,10 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "StatementId.h" #include <string.h>
--- a/Core/SQLite/StatementId.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/StatementId.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. *
--- a/Core/SQLite/StatementReference.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/StatementReference.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -34,13 +35,18 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "StatementReference.h" +#include "OrthancSQLiteException.h" -#include "../OrthancException.h" +#if ORTHANC_SQLITE_STANDALONE != 1 +#include <glog/logging.h> +#endif #include <cassert> -#include <glog/logging.h> #include "sqlite3.h" namespace Orthanc @@ -65,7 +71,7 @@ { if (database == NULL || sql == NULL) { - throw OrthancException(ErrorCode_ParameterOutOfRange); + throw OrthancSQLiteException("Parameter out of range"); } root_ = NULL; @@ -74,7 +80,7 @@ int error = sqlite3_prepare_v2(database, sql, -1, &statement_, NULL); if (error != SQLITE_OK) { - throw OrthancException("SQLite: " + std::string(sqlite3_errmsg(database))); + throw OrthancSQLiteException("SQLite: " + std::string(sqlite3_errmsg(database))); } assert(IsRoot()); @@ -109,7 +115,9 @@ // 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) { @@ -124,7 +132,9 @@ // 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 {
--- a/Core/SQLite/StatementReference.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/StatementReference.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -36,7 +37,8 @@ #pragma once -#include <boost/noncopyable.hpp> +#include "NonCopyable.h" + #include <stdint.h> #include <cassert> #include <stdlib.h> @@ -48,7 +50,7 @@ { namespace SQLite { - class StatementReference : boost::noncopyable + class StatementReference : NonCopyable { private: StatementReference* root_; // Only used for non-root nodes
--- a/Core/SQLite/Transaction.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Transaction.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -34,8 +35,12 @@ **/ +#if ORTHANC_SQLITE_STANDALONE != 1 #include "../PrecompiledHeaders.h" +#endif + #include "Transaction.h" +#include "OrthancSQLiteException.h" namespace Orthanc { @@ -59,13 +64,13 @@ { if (isOpen_) { - throw OrthancException("SQLite: Beginning a transaction twice!"); + throw OrthancSQLiteException("SQLite: Beginning a transaction twice!"); } isOpen_ = connection_.BeginTransaction(); if (!isOpen_) { - throw OrthancException("SQLite: Unable to create a transaction"); + throw OrthancSQLiteException("SQLite: Unable to create a transaction"); } } @@ -73,8 +78,8 @@ { if (!isOpen_) { - throw OrthancException("SQLite: Attempting to roll back a nonexistent transaction. " - "Did you remember to call Begin()?"); + throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. " + "Did you remember to call Begin()?"); } isOpen_ = false; @@ -86,15 +91,15 @@ { if (!isOpen_) { - throw OrthancException("SQLite: Attempting to roll back a nonexistent transaction. " - "Did you remember to call Begin()?"); + throw OrthancSQLiteException("SQLite: Attempting to roll back a nonexistent transaction. " + "Did you remember to call Begin()?"); } isOpen_ = false; if (!connection_.CommitTransaction()) { - throw OrthancException("SQLite: Failure when committing the transaction"); + throw OrthancSQLiteException("SQLite: Failure when committing the transaction"); } } }
--- a/Core/SQLite/Transaction.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/SQLite/Transaction.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,8 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * + * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne@gmail.com>, + * Medical Physics Department, CHU of Liege, Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. * @@ -37,12 +38,13 @@ #pragma once #include "Connection.h" +#include "ITransaction.h" namespace Orthanc { namespace SQLite { - class Transaction : public boost::noncopyable + class Transaction : public ITransaction { private: Connection& connection_; @@ -53,22 +55,17 @@ public: explicit Transaction(Connection& connection); - ~Transaction(); + + virtual ~Transaction(); // Returns true when there is a transaction that has been successfully begun. bool IsOpen() const { return isOpen_; } - // 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. - void Begin(); + virtual void Begin(); - // Rolls back the transaction. This will happen automatically if you do - // nothing when the transaction goes out of scope. - void Rollback(); + virtual void Rollback(); - // Commits the transaction, returning true on success. - void Commit(); + virtual void Commit(); }; } }
--- a/Core/Toolbox.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Toolbox.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Toolbox.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Toolbox.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Uuid.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Uuid.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Core/Uuid.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Core/Uuid.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/LinuxCompilation.txt Tue Nov 04 13:56:17 2014 +0100 +++ b/LinuxCompilation.txt Wed Feb 11 10:32:14 2015 +0100 @@ -44,6 +44,8 @@ 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 ===================================== @@ -58,6 +60,7 @@ # 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
--- a/NEWS Tue Nov 04 13:56:17 2014 +0100 +++ b/NEWS Wed Feb 11 10:32:14 2015 +0100 @@ -1,10 +1,53 @@ Pending changes in the mainline =============================== +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 + +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 + + +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 -* Plugins can monitor changes through callbacks -* Sample plugin framework to serve static resources +* 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)
--- a/OrthancCppClient/Instance.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Instance.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Instance.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Instance.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/OrthancClientException.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/OrthancClientException.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/OrthancConnection.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/OrthancConnection.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/OrthancConnection.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/OrthancConnection.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/OrthancCppClient.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/OrthancCppClient.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Patient.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Patient.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Patient.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Patient.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Series.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Series.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Series.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Series.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1489,7 +1489,7 @@ LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCompany() { - return "CHU of Liege"; + return "University Hospital of Liege"; } LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetProduct() @@ -1499,7 +1499,7 @@ LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetCopyright() { - return "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege"; + return "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege"; } LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetVersion() @@ -1509,12 +1509,12 @@ LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFileVersion() { - return "0.8.0.4"; + return "0.8.0.5"; } LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFullVersion() { - return "0.8.4"; + return "0.8.5"; } LAAW_EXPORT_DLL_API void LAAW_CALL_CONVENTION LAAW_EXTERNC_FreeString(char* str)
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ #include <winver.h> VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,8,0,4 + FILEVERSION 0,8,0,5 PRODUCTVERSION 0,8,0,0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL @@ -10,13 +10,13 @@ BEGIN BLOCK "040904E4" BEGIN - VALUE "Comments", "Release 0.8.4" - VALUE "CompanyName", "CHU of Liege" + VALUE "Comments", "Release 0.8.5" + VALUE "CompanyName", "University Hospital of Liege" VALUE "FileDescription", "Native client to the REST API of Orthanc" - VALUE "FileVersion", "0.8.0.4" + VALUE "FileVersion", "0.8.0.5" VALUE "InternalName", "OrthancClient" - VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege" - VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/" + VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege" + VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/" VALUE "OriginalFilename", "OrthancClient_Windows32.dll" VALUE "ProductName", "OrthancClient" VALUE "ProductVersion", "0.8"
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ #include <winver.h> VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,8,0,4 + FILEVERSION 0,8,0,5 PRODUCTVERSION 0,8,0,0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL @@ -10,13 +10,13 @@ BEGIN BLOCK "040904E4" BEGIN - VALUE "Comments", "Release 0.8.4" - VALUE "CompanyName", "CHU of Liege" + VALUE "Comments", "Release 0.8.5" + VALUE "CompanyName", "University Hospital of Liege" VALUE "FileDescription", "Native client to the REST API of Orthanc" - VALUE "FileVersion", "0.8.0.4" + VALUE "FileVersion", "0.8.0.5" VALUE "InternalName", "OrthancClient" - VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege" - VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/" + VALUE "LegalCopyright", "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege" + VALUE "LegalTrademarks", "Licensing information is available on http://www.orthanc-server.com/" VALUE "OriginalFilename", "OrthancClient_Windows64.dll" VALUE "ProductName", "OrthancClient" VALUE "ProductVersion", "0.8"
--- a/OrthancCppClient/SharedLibrary/Product.json Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/SharedLibrary/Product.json Wed Feb 11 10:32:14 2015 +0100 @@ -1,8 +1,8 @@ { "Product" : "OrthancClient", "Description" : "Native client to the REST API of Orthanc", - "Company" : "CHU of Liege", - "Copyright" : "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege", - "Legal" : "Licensing information is available on https://code.google.com/p/orthanc/", - "Version" : "0.8.4" + "Company" : "University Hospital of Liege", + "Copyright" : "(c) 2012-2015, Sebastien Jodogne, University Hospital of Liege", + "Legal" : "Licensing information is available on http://www.orthanc-server.com/", + "Version" : "0.8.5" }
--- a/OrthancCppClient/SharedLibrary/SharedLibrary.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/SharedLibrary/SharedLibrary.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Study.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Study.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancCppClient/Study.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancCppClient/Study.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancExplorer/explorer.html Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancExplorer/explorer.html Wed Feb 11 10:32:14 2015 +0100 @@ -30,11 +30,13 @@ <link rel="stylesheet" href="explorer.css" /> <script src="file-upload.js"></script> <script src="explorer.js"></script> + <script src="../plugins/explorer.js"></script> </head> <body> <div data-role="page" id="find-patients" > <div data-role="header" > <h1><span class="orthanc-name"></span>Find a patient</h1> + <a href="#plugins" data-icon="grid" class="ui-btn-left" data-direction="reverse">Plugins</a> <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a> </div> <div data-role="content"> @@ -46,7 +48,7 @@ <div data-role="page" id="upload" > <div data-role="header" > <h1><span class="orthanc-name"></span>Upload DICOM files</h1> - <a href="#find-patients" data-icon="search" class="ui-btn-left" data-direction="reverse">Find patient</a> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> </div> <div data-role="content"> <div style="display:none"> @@ -72,7 +74,7 @@ <div data-role="page" id="patient" > <div data-role="header" > <h1><span class="orthanc-name"></span>Patient</h1> - <a href="#find-patients" data-icon="search" class="ui-btn-left" data-direction="reverse">Find patient</a> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a> </div> <div data-role="content"> @@ -126,7 +128,7 @@ <a href="#" class="patient-link">Patient</a> » Study </h1> - <a href="#find-patients" data-icon="search" class="ui-btn-left" data-direction="reverse">Find patient</a> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a> </div> <div data-role="content"> @@ -175,7 +177,7 @@ Series </h1> - <a href="#find-patients" data-icon="search" class="ui-btn-left" data-direction="reverse">Find patient</a> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a> </div> <div data-role="content"> @@ -225,7 +227,7 @@ <a href="#" class="series-link">Series</a> » Instance </h1> - <a href="#find-patients" data-icon="search" class="ui-btn-left" data-direction="reverse">Find patient</a> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> <a href="#upload" data-icon="gear" class="ui-btn-right">Upload DICOM</a> </div> <div data-role="content"> @@ -271,6 +273,18 @@ </div> </div> + <div data-role="page" id="plugins" > + <div data-role="header" > + <h1><span class="orthanc-name"></span>Plugins</h1> + <a href="#find-patients" data-icon="home" class="ui-btn-left" data-direction="reverse">Patients</a> + </div> + <div data-role="content"> + <ul id="all-plugins" data-role="listview" data-inset="true" data-filter="true"> + </ul> + </div> + </div> + + <div id="peer-store" style="display:none;" class="ui-body-c"> <p align="center"><b>Sending to Orthanc peer...</b></p> <p><img src="libs/images/ajax-loader2.gif" alt="" /></p>
--- a/OrthancExplorer/explorer.js Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancExplorer/explorer.js Wed Feb 11 10:32:14 2015 +0100 @@ -1023,3 +1023,46 @@ OpenAnonymizeResourceDialog('../patients/' + $.mobile.pageData.uuid, 'Anonymize this patient?'); }); + + +$('#plugins').live('pagebeforeshow', function() { + $.ajax({ + url: '../plugins', + dataType: 'json', + async: false, + cache: false, + success: function(plugins) { + var target = $('#all-plugins'); + $('li', target).remove(); + + plugins.map(function(id) { + return $.ajax({ + url: '../plugins/' + id, + dataType: 'json', + async: false, + cache: false, + success: function(plugin) { + var li = $('<li>'); + var item = li; + + if ('RootUri' in plugin) + { + item = $('<a>'); + li.append(item); + item.click(function() { + window.open(plugin.RootUri); + }); + } + + item.append($('<h1>').text(plugin.ID)); + item.append($('<p>').text(plugin.Description)); + item.append($('<span>').addClass('ui-li-count').text(plugin.Version)); + target.append(li); + } + }); + }); + + target.listview('refresh'); + } + }); +});
--- a/OrthancServer/DatabaseWrapper.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DatabaseWrapper.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -39,6 +39,7 @@ #include <glog/logging.h> #include <stdio.h> +#include <boost/lexical_cast.hpp> namespace Orthanc { @@ -213,20 +214,6 @@ } } - std::string DatabaseWrapper::GetGlobalProperty(GlobalProperty property, - const std::string& defaultValue) - { - std::string s; - if (LookupGlobalProperty(s, property)) - { - return s; - } - else - { - return defaultValue; - } - } - int64_t DatabaseWrapper::CreateResource(const std::string& publicId, ResourceType type) { @@ -234,38 +221,12 @@ s.BindInt(0, type); s.BindString(1, publicId); s.Run(); - int64_t id = db_.GetLastInsertRowId(); - - ChangeType changeType; - switch (type) - { - case ResourceType_Patient: - changeType = ChangeType_NewPatient; - break; - - case ResourceType_Study: - changeType = ChangeType_NewStudy; - break; - - case ResourceType_Series: - changeType = ChangeType_NewSeries; - break; - - case ResourceType_Instance: - changeType = ChangeType_NewInstance; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - LogChange(id, changeType, type, publicId); - return id; + return db_.GetLastInsertRowId(); } - bool DatabaseWrapper::LookupResource(const std::string& publicId, - int64_t& id, - ResourceType& type) + bool DatabaseWrapper::LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); @@ -349,16 +310,17 @@ s.Run(); } - void DatabaseWrapper::GetChildren(Json::Value& childrenPublicIds, + + void DatabaseWrapper::GetChildren(std::list<std::string>& childrenPublicIds, int64_t id) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); s.BindInt64(0, id); - childrenPublicIds = Json::arrayValue; + childrenPublicIds.clear(); while (s.Step()) { - childrenPublicIds.append(s.ColumnString(0)); + childrenPublicIds.push_back(s.ColumnString(0)); } } @@ -371,10 +333,11 @@ s.BindInt64(0, id); s.Run(); - if (signalRemainingAncestor_->HasRemainingAncestor()) + if (signalRemainingAncestor_->HasRemainingAncestor() && + listener_ != NULL) { - listener_.SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), - signalRemainingAncestor_->GetRemainingAncestorId()); + listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), + signalRemainingAncestor_->GetRemainingAncestorId()); } } @@ -433,45 +396,6 @@ } - std::string DatabaseWrapper::GetMetadata(int64_t id, - MetadataType type, - const std::string& defaultValue) - { - std::string s; - if (LookupMetadata(s, id, type)) - { - return s; - } - else - { - return defaultValue; - } - } - - - bool DatabaseWrapper::GetMetadataAsInteger(int& result, - int64_t id, - MetadataType type) - { - std::string s = GetMetadata(id, type, ""); - if (s.size() == 0) - { - return false; - } - - try - { - result = boost::lexical_cast<int>(s); - return true; - } - catch (boost::bad_lexical_cast&) - { - return false; - } - } - - - void DatabaseWrapper::AddAttachment(int64_t id, const FileInfo& attachment) { @@ -499,10 +423,10 @@ - void DatabaseWrapper::ListAvailableAttachments(std::list<FileContentType>& result, + void DatabaseWrapper::ListAvailableAttachments(std::list<FileContentType>& target, int64_t id) { - result.clear(); + target.clear(); SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT fileType FROM AttachedFiles WHERE id=?"); @@ -510,7 +434,7 @@ while (s.Step()) { - result.push_back(static_cast<FileContentType>(s.ColumnInt(0))); + target.push_back(static_cast<FileContentType>(s.ColumnInt(0))); } } @@ -543,32 +467,30 @@ static void SetMainDicomTagsInternal(SQLite::Statement& s, int64_t id, - const DicomElement& element) + const DicomTag& tag, + const std::string& value) { s.BindInt64(0, id); - s.BindInt(1, element.GetTag().GetGroup()); - s.BindInt(2, element.GetTag().GetElement()); - s.BindString(3, element.GetValue().AsString()); + s.BindInt(1, tag.GetGroup()); + s.BindInt(2, tag.GetElement()); + s.BindString(3, value); s.Run(); } - void DatabaseWrapper::SetMainDicomTags(int64_t id, - const DicomMap& tags) + void DatabaseWrapper::SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) { - DicomArray flattened(tags); - for (size_t i = 0; i < flattened.GetSize(); i++) + if (tag.IsIdentifier()) { - if (flattened.GetElement(i).GetTag().IsIdentifier()) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); - SetMainDicomTagsInternal(s, id, flattened.GetElement(i)); - } - else - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); - SetMainDicomTagsInternal(s, id, flattened.GetElement(i)); - } + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); + SetMainDicomTagsInternal(s, id, tag, value); + } + else + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); + SetMainDicomTagsInternal(s, id, tag, value); } } @@ -597,7 +519,7 @@ } - bool DatabaseWrapper::GetParentPublicId(std::string& result, + bool DatabaseWrapper::GetParentPublicId(std::string& target, int64_t id) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " @@ -606,7 +528,7 @@ if (s.Step()) { - result = s.ColumnString(0); + target = s.ColumnString(0); return true; } else @@ -616,34 +538,34 @@ } - void DatabaseWrapper::GetChildrenPublicId(std::list<std::string>& result, + void DatabaseWrapper::GetChildrenPublicId(std::list<std::string>& target, int64_t id) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " "WHERE a.parentId = b.internalId AND b.internalId = ?"); s.BindInt64(0, id); - result.clear(); + target.clear(); while (s.Step()) { - result.push_back(s.ColumnString(0)); + target.push_back(s.ColumnString(0)); } } - void DatabaseWrapper::GetChildrenInternalId(std::list<int64_t>& result, + void DatabaseWrapper::GetChildrenInternalId(std::list<int64_t>& target, int64_t id) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " "WHERE a.parentId = b.internalId AND b.internalId = ?"); s.BindInt64(0, id); - result.clear(); + target.clear(); while (s.Step()) { - result.push_back(s.ColumnInt64(0)); + target.push_back(s.ColumnInt64(0)); } } @@ -651,171 +573,124 @@ void DatabaseWrapper::LogChange(int64_t internalId, const ServerIndexChange& change) { - if (change.GetChangeType() <= ChangeType_INTERNAL_LastLogged) - { - const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); - s.BindInt(0, change.GetChangeType()); - s.BindInt64(1, internalId); - s.BindInt(2, change.GetResourceType()); - s.BindString(3, boost::posix_time::to_iso_string(now)); - s.Run(); - } - - listener_.SignalChange(change); + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); + s.BindInt(0, change.GetChangeType()); + s.BindInt64(1, internalId); + s.BindInt(2, change.GetResourceType()); + s.BindString(3, change.GetDate()); + s.Run(); } - void DatabaseWrapper::GetChangesInternal(Json::Value& target, + void DatabaseWrapper::GetChangesInternal(std::list<ServerIndexChange>& target, + bool& done, SQLite::Statement& s, - int64_t since, - unsigned int maxResults) + uint32_t maxResults) { - Json::Value changes = Json::arrayValue; - int64_t last = since; + target.clear(); - while (changes.size() < maxResults && s.Step()) + while (target.size() < maxResults && s.Step()) { int64_t seq = s.ColumnInt64(0); ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); - int64_t internalId = s.ColumnInt64(2); ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); const std::string& date = s.ColumnString(4); + + int64_t internalId = s.ColumnInt64(2); std::string publicId = GetPublicId(internalId); - Json::Value item = Json::objectValue; - item["Seq"] = static_cast<int>(seq); - item["ChangeType"] = EnumerationToString(changeType); - item["ResourceType"] = EnumerationToString(resourceType); - item["ID"] = publicId; - item["Path"] = GetBasePath(resourceType, publicId); - item["Date"] = date; - last = seq; - - changes.append(item); + target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); } - target = Json::objectValue; - target["Changes"] = changes; - target["Done"] = !(changes.size() == maxResults && s.Step()); - target["Last"] = static_cast<int>(last); + done = !(target.size() == maxResults && s.Step()); } - void DatabaseWrapper::GetChanges(Json::Value& target, + void DatabaseWrapper::GetChanges(std::list<ServerIndexChange>& target, + bool& done, int64_t since, - unsigned int maxResults) + uint32_t maxResults) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); s.BindInt64(0, since); s.BindInt(1, maxResults + 1); - GetChangesInternal(target, s, since, maxResults); + GetChangesInternal(target, done, s, maxResults); } - void DatabaseWrapper::GetLastChange(Json::Value& target) + void DatabaseWrapper::GetLastChange(std::list<ServerIndexChange>& target) { + bool done; // Ignored SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); - GetChangesInternal(target, s, 0, 1); + GetChangesInternal(target, done, s, 1); } - void DatabaseWrapper::LogExportedResource(ResourceType resourceType, - const std::string& publicId, - const std::string& remoteModality, - const std::string& patientId, - const std::string& studyInstanceUid, - const std::string& seriesInstanceUid, - const std::string& sopInstanceUid, - const boost::posix_time::ptime& date) + void DatabaseWrapper::LogExportedResource(const ExportedResource& resource) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); - s.BindInt(0, resourceType); - s.BindString(1, publicId); - s.BindString(2, remoteModality); - s.BindString(3, patientId); - s.BindString(4, studyInstanceUid); - s.BindString(5, seriesInstanceUid); - s.BindString(6, sopInstanceUid); - s.BindString(7, boost::posix_time::to_iso_string(date)); - + s.BindInt(0, resource.GetResourceType()); + s.BindString(1, resource.GetPublicId()); + s.BindString(2, resource.GetModality()); + s.BindString(3, resource.GetPatientId()); + s.BindString(4, resource.GetStudyInstanceUid()); + s.BindString(5, resource.GetSeriesInstanceUid()); + s.BindString(6, resource.GetSopInstanceUid()); + s.BindString(7, resource.GetDate()); s.Run(); } - void DatabaseWrapper::GetExportedResourcesInternal(Json::Value& target, + void DatabaseWrapper::GetExportedResourcesInternal(std::list<ExportedResource>& target, + bool& done, SQLite::Statement& s, - int64_t since, - unsigned int maxResults) + uint32_t maxResults) { - Json::Value changes = Json::arrayValue; - int64_t last = since; + target.clear(); - while (changes.size() < maxResults && s.Step()) + while (target.size() < maxResults && s.Step()) { int64_t seq = s.ColumnInt64(0); ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); std::string publicId = s.ColumnString(2); - Json::Value item = Json::objectValue; - item["Seq"] = static_cast<int>(seq); - item["ResourceType"] = EnumerationToString(resourceType); - item["ID"] = publicId; - item["Path"] = GetBasePath(resourceType, publicId); - item["RemoteModality"] = s.ColumnString(3); - item["Date"] = s.ColumnString(8); - - // WARNING: Do not add "break" below and do not reorder the case items! - switch (resourceType) - { - case ResourceType_Instance: - item["SopInstanceUid"] = s.ColumnString(7); + ExportedResource resource(seq, + resourceType, + publicId, + s.ColumnString(3), // modality + s.ColumnString(8), // date + s.ColumnString(4), // patient ID + s.ColumnString(5), // study instance UID + s.ColumnString(6), // series instance UID + s.ColumnString(7)); // sop instance UID - case ResourceType_Series: - item["SeriesInstanceUid"] = s.ColumnString(6); - - case ResourceType_Study: - item["StudyInstanceUid"] = s.ColumnString(5); - - case ResourceType_Patient: - item["PatientId"] = s.ColumnString(4); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - last = seq; - - changes.append(item); + target.push_back(resource); } - target = Json::objectValue; - target["Exports"] = changes; - target["Done"] = !(changes.size() == maxResults && s.Step()); - target["Last"] = static_cast<int>(last); + done = !(target.size() == maxResults && s.Step()); } - void DatabaseWrapper::GetExportedResources(Json::Value& target, + void DatabaseWrapper::GetExportedResources(std::list<ExportedResource>& target, + bool& done, int64_t since, - unsigned int maxResults) + uint32_t maxResults) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); s.BindInt64(0, since); s.BindInt(1, maxResults + 1); - GetExportedResourcesInternal(target, s, since, maxResults); + GetExportedResourcesInternal(target, done, s, maxResults); } - void DatabaseWrapper::GetLastExportedResource(Json::Value& target) + void DatabaseWrapper::GetLastExportedResource(std::list<ExportedResource>& target) { + bool done; // Ignored SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); - GetExportedResourcesInternal(target, s, 0, 1); + GetExportedResourcesInternal(target, done, s, 1); } @@ -854,16 +729,16 @@ return static_cast<uint64_t>(s.ColumnInt64(0)); } - void DatabaseWrapper::GetAllPublicIds(Json::Value& target, + void DatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); s.BindInt(0, resourceType); - target = Json::arrayValue; + target.clear(); while (s.Step()) { - target.append(s.ColumnString(0)); + target.push_back(s.ColumnString(0)); } } @@ -878,16 +753,13 @@ } - DatabaseWrapper::DatabaseWrapper(const std::string& path, - IServerIndexListener& listener) : - listener_(listener) + DatabaseWrapper::DatabaseWrapper(const std::string& path) : listener_(NULL) { db_.Open(path); Open(); } - DatabaseWrapper::DatabaseWrapper(IServerIndexListener& listener) : - listener_(listener) + DatabaseWrapper::DatabaseWrapper() : listener_(NULL) { db_.OpenInMemory(); Open(); @@ -912,7 +784,12 @@ } // Check the version of the database - std::string version = GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "Unknown"); + std::string version; + if (!LookupGlobalProperty(version, GlobalProperty_DatabaseSchemaVersion)) + { + version = "Unknown"; + } + bool ok = false; try { @@ -958,8 +835,13 @@ signalRemainingAncestor_ = new Internals::SignalRemainingAncestor; db_.Register(signalRemainingAncestor_); - db_.Register(new Internals::SignalFileDeleted(listener_)); - db_.Register(new Internals::SignalResourceDeleted(listener_)); + } + + void DatabaseWrapper::SetListener(IServerIndexListener& listener) + { + listener_ = &listener; + db_.Register(new Internals::SignalFileDeleted(listener)); + db_.Register(new Internals::SignalResourceDeleted(listener)); } uint64_t DatabaseWrapper::GetResourceCount(ResourceType resourceType) @@ -1046,33 +928,6 @@ } - uint64_t DatabaseWrapper::IncrementGlobalSequence(GlobalProperty property) - { - std::string oldValue; - - if (LookupGlobalProperty(oldValue, property)) - { - uint64_t oldNumber; - - try - { - oldNumber = boost::lexical_cast<uint64_t>(oldValue); - SetGlobalProperty(property, boost::lexical_cast<std::string>(oldNumber + 1)); - return oldNumber + 1; - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_InternalError); - } - } - else - { - // Initialize the sequence at "1" - SetGlobalProperty(property, "1"); - return 1; - } - } - void DatabaseWrapper::ClearTable(const std::string& tableName) { @@ -1089,7 +944,7 @@ } - void DatabaseWrapper::LookupIdentifier(std::list<int64_t>& result, + void DatabaseWrapper::LookupIdentifier(std::list<int64_t>& target, const DicomTag& tag, const std::string& value) { @@ -1105,16 +960,16 @@ s.BindInt(1, tag.GetElement()); s.BindString(2, value); - result.clear(); + target.clear(); while (s.Step()) { - result.push_back(s.ColumnInt64(0)); + target.push_back(s.ColumnInt64(0)); } } - void DatabaseWrapper::LookupIdentifier(std::list<int64_t>& result, + void DatabaseWrapper::LookupIdentifier(std::list<int64_t>& target, const std::string& value) { SQLite::Statement s(db_, SQLITE_FROM_HERE, @@ -1122,19 +977,19 @@ s.BindString(0, value); - result.clear(); + target.clear(); while (s.Step()) { - result.push_back(s.ColumnInt64(0)); + target.push_back(s.ColumnInt64(0)); } } - void DatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& result, + void DatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& target, int64_t id) { - result.clear(); + target.clear(); SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); s.BindInt64(0, id); @@ -1142,7 +997,7 @@ while (s.Step()) { MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); - result[key] = s.ColumnString(1); + target[key] = s.ColumnString(1); } }
--- a/OrthancServer/DatabaseWrapper.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DatabaseWrapper.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -32,14 +32,10 @@ #pragma once +#include "IDatabaseWrapper.h" + #include "../Core/SQLite/Connection.h" #include "../Core/SQLite/Transaction.h" -#include "../Core/DicomFormat/DicomInstanceHasher.h" -#include "../Core/FileStorage/FileInfo.h" -#include "IServerIndexListener.h" - -#include <list> -#include <boost/date_time/posix_time/posix_time.hpp> namespace Orthanc { @@ -53,197 +49,193 @@ * translates low-level requests into SQL statements. Mutual * exclusion MUST be implemented at a higher level. **/ - class DatabaseWrapper + class DatabaseWrapper : public IDatabaseWrapper { private: - IServerIndexListener& listener_; + IServerIndexListener* listener_; SQLite::Connection db_; Internals::SignalRemainingAncestor* signalRemainingAncestor_; void Open(); - void GetChangesInternal(Json::Value& target, + void GetChangesInternal(std::list<ServerIndexChange>& target, + bool& done, SQLite::Statement& s, - int64_t since, - unsigned int maxResults); + uint32_t maxResults); - void GetExportedResourcesInternal(Json::Value& target, + void GetExportedResourcesInternal(std::list<ExportedResource>& target, + bool& done, SQLite::Statement& s, - int64_t since, - unsigned int maxResults); + uint32_t maxResults); + + void ClearTable(const std::string& tableName); public: - void SetGlobalProperty(GlobalProperty property, - const std::string& value); + DatabaseWrapper(const std::string& path); - bool LookupGlobalProperty(std::string& target, - GlobalProperty property); + DatabaseWrapper(); + + virtual void SetListener(IServerIndexListener& listener); - std::string GetGlobalProperty(GlobalProperty property, - const std::string& defaultValue = ""); + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value); - int64_t CreateResource(const std::string& publicId, - ResourceType type); + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property); + + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type); - bool LookupResource(const std::string& publicId, - int64_t& id, - ResourceType& type); + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId); - bool LookupParent(int64_t& parentId, - int64_t resourceId); + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId); - std::string GetPublicId(int64_t resourceId); + virtual std::string GetPublicId(int64_t resourceId); - ResourceType GetResourceType(int64_t resourceId); + virtual ResourceType GetResourceType(int64_t resourceId); - void AttachChild(int64_t parent, - int64_t child); + virtual void AttachChild(int64_t parent, + int64_t child); - void GetChildren(Json::Value& childrenPublicIds, - int64_t id); + virtual void DeleteResource(int64_t id); - void DeleteResource(int64_t id); + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value); - void SetMetadata(int64_t id, - MetadataType type, - const std::string& value); + virtual void DeleteMetadata(int64_t id, + MetadataType type); - void DeleteMetadata(int64_t id, - MetadataType type); + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type); - bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type); - - void ListAvailableMetadata(std::list<MetadataType>& target, - int64_t id); + virtual void ListAvailableMetadata(std::list<MetadataType>& target, + int64_t id); - std::string GetMetadata(int64_t id, - MetadataType type, - const std::string& defaultValue = ""); + virtual void AddAttachment(int64_t id, + const FileInfo& attachment); - bool GetMetadataAsInteger(int& result, - int64_t id, - MetadataType type); + virtual void DeleteAttachment(int64_t id, + FileContentType attachment); + + virtual void ListAvailableAttachments(std::list<FileContentType>& target, + int64_t id); - void AddAttachment(int64_t id, - const FileInfo& attachment); + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType); - void DeleteAttachment(int64_t id, - FileContentType attachment); + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value); - void ListAvailableAttachments(std::list<FileContentType>& result, + virtual void GetMainDicomTags(DicomMap& map, int64_t id); - bool LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType); - - void SetMainDicomTags(int64_t id, - const DicomMap& tags); + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id); - void GetMainDicomTags(DicomMap& map, - int64_t id); + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id); - bool GetParentPublicId(std::string& result, - int64_t id); - - void GetChildrenPublicId(std::list<std::string>& result, - int64_t id); + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change); - void GetChildrenInternalId(std::list<int64_t>& result, - int64_t id); + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults); + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/); - void LogChange(int64_t internalId, - ChangeType changeType, - ResourceType resourceType, - const std::string& publicId) - { - ServerIndexChange change(changeType, resourceType, publicId); - LogChange(internalId, change); - } - - void LogChange(int64_t internalId, - const ServerIndexChange& change); - - void GetChanges(Json::Value& target, - int64_t since, - unsigned int maxResults); - - void GetLastChange(Json::Value& target); + virtual void LogExportedResource(const ExportedResource& resource); + + virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults); - void LogExportedResource(ResourceType resourceType, - const std::string& publicId, - const std::string& remoteModality, - const std::string& patientId, - const std::string& studyInstanceUid, - const std::string& seriesInstanceUid, - const std::string& sopInstanceUid, - const boost::posix_time::ptime& date = - boost::posix_time::second_clock::local_time()); - - void GetExportedResources(Json::Value& target, - int64_t since, - unsigned int maxResults); + virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/); - void GetLastExportedResource(Json::Value& target); - - // For unit testing only! - int64_t GetTableRecordCount(const std::string& table); - - uint64_t GetTotalCompressedSize(); + virtual uint64_t GetTotalCompressedSize(); - uint64_t GetTotalUncompressedSize(); + virtual uint64_t GetTotalUncompressedSize(); - uint64_t GetResourceCount(ResourceType resourceType); + virtual uint64_t GetResourceCount(ResourceType resourceType); - void GetAllPublicIds(Json::Value& target, - ResourceType resourceType); + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType); - bool SelectPatientToRecycle(int64_t& internalId); - - bool SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid); + virtual bool SelectPatientToRecycle(int64_t& internalId); - bool IsProtectedPatient(int64_t internalId); + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid); - void SetProtectedPatient(int64_t internalId, - bool isProtected); + virtual bool IsProtectedPatient(int64_t internalId); - DatabaseWrapper(const std::string& path, - IServerIndexListener& listener); + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected); - DatabaseWrapper(IServerIndexListener& listener); - - SQLite::Transaction* StartTransaction() + virtual SQLite::ITransaction* StartTransaction() { return new SQLite::Transaction(db_); } + virtual void FlushToDisk() + { + db_.FlushToDisk(); + } + + virtual bool HasFlushToDisk() const + { + return true; + } + + virtual void ClearChanges() + { + ClearTable("Changes"); + } + + virtual void ClearExportedResources() + { + ClearTable("ExportedResources"); + } + + virtual bool IsExistingResource(int64_t internalId); + + virtual void LookupIdentifier(std::list<int64_t>& target, + const DicomTag& tag, + const std::string& value); + + virtual void LookupIdentifier(std::list<int64_t>& target, + const std::string& value); + + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id); + + + + + /** + * The methods declared below are for unit testing only! + **/ + const char* GetErrorMessage() const { return db_.GetErrorMessage(); } - void FlushToDisk() - { - db_.FlushToDisk(); - } - - uint64_t IncrementGlobalSequence(GlobalProperty property); - - void ClearTable(const std::string& tableName); + void GetChildren(std::list<std::string>& childrenPublicIds, + int64_t id); - bool IsExistingResource(int64_t internalId); - - void LookupIdentifier(std::list<int64_t>& result, - const DicomTag& tag, - const std::string& value); + int64_t GetTableRecordCount(const std::string& table); + + bool GetParentPublicId(std::string& target, + int64_t id); - void LookupIdentifier(std::list<int64_t>& result, - const std::string& value); - - void GetAllMetadata(std::map<MetadataType, std::string>& result, - int64_t id); }; }
--- a/OrthancServer/DicomDirWriter.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomDirWriter.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomDirWriter.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomDirWriter.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -34,9 +34,11 @@ #include "ParsedDicomFile.h" +#include <boost/noncopyable.hpp> + namespace Orthanc { - class DicomDirWriter + class DicomDirWriter : public boost::noncopyable { private: class PImpl;
--- a/OrthancServer/DicomInstanceToStore.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomInstanceToStore.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomInstanceToStore.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomInstanceToStore.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -60,7 +60,7 @@ } public: - SmartContainer() : content_(NULL), toDelete_(false) + SmartContainer() : content_(NULL), toDelete_(false), isReadOnly_(true) { } @@ -144,6 +144,7 @@ SmartContainer<Json::Value> json_; std::string remoteAet_; + std::string calledAet_; ServerIndex::MetadataMap metadata_; void ComputeMissingInformation(); @@ -169,7 +170,7 @@ json_.SetConstReference(json); } - const std::string GetRemoteAet() const + const std::string& GetRemoteAet() const { return remoteAet_; } @@ -179,6 +180,16 @@ remoteAet_ = aet; } + const std::string& GetCalledAet() const + { + return calledAet_; + } + + void SetCalledAet(const std::string& aet) + { + calledAet_ = aet; + } + void AddMetadata(ResourceType level, MetadataType metadata, const std::string& value);
--- a/OrthancServer/DicomModification.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomModification.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -39,8 +39,23 @@ #include <memory> // For std::auto_ptr #include <glog/logging.h> + +static const std::string ORTHANC_DEIDENTIFICATION_METHOD = "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"; + namespace Orthanc { + void DicomModification::MarkNotOrthancAnonymization() + { + Replacements::iterator it = replacements_.find(DICOM_TAG_DEIDENTIFICATION_METHOD); + + if (it != replacements_.end() && + it->second == ORTHANC_DEIDENTIFICATION_METHOD) + { + replacements_.erase(it); + } + } + + void DicomModification::MapDicomIdentifier(ParsedDicomFile& dicom, ResourceType level) { @@ -90,6 +105,7 @@ { removePrivateTags_ = false; level_ = ResourceType_Instance; + allowManualIdentifiers_ = true; } void DicomModification::Keep(const DicomTag& tag) @@ -101,6 +117,8 @@ { privateTagsToKeep_.insert(tag); } + + MarkNotOrthancAnonymization(); } void DicomModification::Remove(const DicomTag& tag) @@ -108,6 +126,8 @@ removals_.insert(tag); replacements_.erase(tag); privateTagsToKeep_.erase(tag); + + MarkNotOrthancAnonymization(); } bool DicomModification::IsRemoved(const DicomTag& tag) const @@ -116,11 +136,17 @@ } void DicomModification::Replace(const DicomTag& tag, - const std::string& value) + const std::string& value, + bool safeForAnonymization) { removals_.erase(tag); privateTagsToKeep_.erase(tag); replacements_[tag] = value; + + if (!safeForAnonymization) + { + MarkNotOrthancAnonymization(); + } } bool DicomModification::IsReplaced(const DicomTag& tag) const @@ -145,12 +171,22 @@ void DicomModification::SetRemovePrivateTags(bool removed) { removePrivateTags_ = removed; + + if (!removed) + { + MarkNotOrthancAnonymization(); + } } void DicomModification::SetLevel(ResourceType level) { uidMap_.clear(); level_ = level; + + if (level != ResourceType_Patient) + { + MarkNotOrthancAnonymization(); + } } void DicomModification::SetupAnonymization() @@ -219,7 +255,7 @@ removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts // Set the DeidentificationMethod tag - replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1")); + replacements_.insert(std::make_pair(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD)); // Set the PatientIdentityRemoved tag replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES")); @@ -246,49 +282,59 @@ } - // Sanity checks + // Sanity checks at the patient level if (level_ == ResourceType_Patient && !IsReplaced(DICOM_TAG_PATIENT_ID)) { LOG(ERROR) << "When modifying a patient, her PatientID is required to be modified"; throw OrthancException(ErrorCode_BadRequest); } - if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID)) + if (!allowManualIdentifiers_) { - LOG(ERROR) << "When modifying a patient, the StudyInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); + if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a patient, the StudyInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } + + if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a patient, the SeriesInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } + + if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a patient, the SopInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } } - if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) - { - LOG(ERROR) << "When modifying a patient, the SeriesInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); - } - if (level_ == ResourceType_Patient && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) - { - LOG(ERROR) << "When modifying a patient, the SopInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); - } - + // Sanity checks at the study level if (level_ == ResourceType_Study && IsReplaced(DICOM_TAG_PATIENT_ID)) { LOG(ERROR) << "When modifying a study, the parent PatientID cannot be manually modified"; throw OrthancException(ErrorCode_BadRequest); } - if (level_ == ResourceType_Study && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) + if (!allowManualIdentifiers_) { - LOG(ERROR) << "When modifying a study, the SeriesInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); + if (level_ == ResourceType_Study && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a study, the SeriesInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } + + if (level_ == ResourceType_Study && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a study, the SopInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } } - if (level_ == ResourceType_Study && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) - { - LOG(ERROR) << "When modifying a study, the SopInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); - } + // Sanity checks at the series level if (level_ == ResourceType_Series && IsReplaced(DICOM_TAG_PATIENT_ID)) { LOG(ERROR) << "When modifying a series, the parent PatientID cannot be manually modified"; @@ -301,12 +347,17 @@ throw OrthancException(ErrorCode_BadRequest); } - if (level_ == ResourceType_Series && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) + if (!allowManualIdentifiers_) { - LOG(ERROR) << "When modifying a series, the SopInstanceUID cannot be manually modified"; - throw OrthancException(ErrorCode_BadRequest); + if (level_ == ResourceType_Series && IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) + { + LOG(ERROR) << "When modifying a series, the SopInstanceUID cannot be manually modified"; + throw OrthancException(ErrorCode_BadRequest); + } } + + // Sanity checks at the instance level if (level_ == ResourceType_Instance && IsReplaced(DICOM_TAG_PATIENT_ID)) { LOG(ERROR) << "When modifying an instance, the parent PatientID cannot be manually modified"; @@ -347,17 +398,20 @@ } // (4) Update the DICOM identifiers - if (level_ <= ResourceType_Study) + if (level_ <= ResourceType_Study && + !IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID)) { MapDicomIdentifier(toModify, ResourceType_Study); } - if (level_ <= ResourceType_Series) + if (level_ <= ResourceType_Series && + !IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) { MapDicomIdentifier(toModify, ResourceType_Series); } - if (level_ <= ResourceType_Instance) // Always true + if (level_ <= ResourceType_Instance && // Always true + !IsReplaced(DICOM_TAG_SOP_INSTANCE_UID)) { MapDicomIdentifier(toModify, ResourceType_Instance); }
--- a/OrthancServer/DicomModification.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomModification.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -56,10 +56,13 @@ ResourceType level_; UidMap uidMap_; SetOfTags privateTagsToKeep_; + bool allowManualIdentifiers_; void MapDicomIdentifier(ParsedDicomFile& dicom, ResourceType level); + void MarkNotOrthancAnonymization(); + public: DicomModification(); @@ -70,7 +73,8 @@ bool IsRemoved(const DicomTag& tag) const; void Replace(const DicomTag& tag, - const std::string& value); + const std::string& value, + bool safeForAnonymization = false); bool IsReplaced(const DicomTag& tag) const; @@ -93,5 +97,15 @@ void SetupAnonymization(); void Apply(ParsedDicomFile& toModify); + + void SetAllowManualIdentifiers(bool check) + { + allowManualIdentifiers_ = check; + } + + bool AreAllowManualIdentifiers() const + { + return allowManualIdentifiers_; + } }; }
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomFindAnswers.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomFindAnswers.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/DicomServer.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomServer.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -278,11 +278,20 @@ throw OrthancException("Too short AET"); } + if (aet.size() > 16) + { + throw OrthancException("AET must be shorter than 16 characters"); + } + for (size_t i = 0; i < aet.size(); i++) { - if (!isalnum(aet[i]) && aet[i] != '-') + if (!(aet[i] == '-' || + aet[i] == '_' || + isdigit(aet[i]) || + (aet[i] >= 'A' && aet[i] <= 'Z'))) { - throw OrthancException("Only alphanumeric characters are allowed in AET"); + LOG(WARNING) << "For best interoperability, only upper case, alphanumeric characters should be present in AET: \"" << aet << "\""; + break; } }
--- a/OrthancServer/DicomProtocol/DicomServer.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomServer.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -454,7 +454,7 @@ int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); if (presID == 0) { - throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the distant AET"); + throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the remote AET"); } T_DIMSE_C_FindRQ request; @@ -546,7 +546,7 @@ int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); if (presID == 0) { - throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the distant AET"); + throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the remote AET"); } T_DIMSE_C_MoveRQ request; @@ -614,10 +614,10 @@ pimpl_(new PImpl), preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), localAet_("STORESCU"), - distantAet_("ANY-SCP"), - distantHost_("127.0.0.1") + remoteAet_("ANY-SCP"), + remoteHost_("127.0.0.1") { - distantPort_ = 104; + remotePort_ = 104; manufacturer_ = ModalityManufacturer_Generic; SetTimeout(10); @@ -642,10 +642,10 @@ void DicomUserConnection::Connect(const RemoteModalityParameters& parameters) { - SetDistantApplicationEntityTitle(parameters.GetApplicationEntityTitle()); - SetDistantHost(parameters.GetHost()); - SetDistantPort(parameters.GetPort()); - SetDistantManufacturer(parameters.GetManufacturer()); + SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle()); + SetRemoteHost(parameters.GetHost()); + SetRemotePort(parameters.GetPort()); + SetRemoteManufacturer(parameters.GetManufacturer()); } @@ -658,16 +658,16 @@ } } - void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) + void DicomUserConnection::SetRemoteApplicationEntityTitle(const std::string& aet) { - if (distantAet_ != aet) + if (remoteAet_ != aet) { Close(); - distantAet_ = aet; + remoteAet_ = aet; } } - void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) + void DicomUserConnection::SetRemoteManufacturer(ModalityManufacturer manufacturer) { if (manufacturer_ != manufacturer) { @@ -691,26 +691,26 @@ } - void DicomUserConnection::SetDistantHost(const std::string& host) + void DicomUserConnection::SetRemoteHost(const std::string& host) { - if (distantHost_ != host) + if (remoteHost_ != host) { if (host.size() > HOST_NAME_MAX - 10) { - throw OrthancException("Distant host name is too long"); + throw OrthancException("Remote host name is too long"); } Close(); - distantHost_ = host; + remoteHost_ = host; } } - void DicomUserConnection::SetDistantPort(uint16_t port) + void DicomUserConnection::SetRemotePort(uint16_t port) { - if (distantPort_ != port) + if (remotePort_ != port) { Close(); - distantPort_ = port; + remotePort_ = port; } } @@ -723,30 +723,30 @@ } LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle() - << "\" to AET \"" << GetDistantApplicationEntityTitle() << "\" on host " - << GetDistantHost() << ":" << GetDistantPort() - << " (manufacturer: " << EnumerationToString(GetDistantManufacturer()) << ")"; + << "\" to AET \"" << GetRemoteApplicationEntityTitle() << "\" on host " + << GetRemoteHost() << ":" << GetRemotePort() + << " (manufacturer: " << EnumerationToString(GetRemoteManufacturer()) << ")"; Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ pimpl_->acseTimeout_, &pimpl_->net_)); Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); // Set this application's title and the called application's title in the params - Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), distantAet_.c_str(), NULL)); + Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), remoteAet_.c_str(), NULL)); - // Set the network addresses of the local and distant entities + // Set the network addresses of the local and remote entities char localHost[HOST_NAME_MAX]; gethostname(localHost, HOST_NAME_MAX - 1); - char distantHostAndPort[HOST_NAME_MAX]; + char remoteHostAndPort[HOST_NAME_MAX]; #ifdef _MSC_VER _snprintf #else snprintf #endif - (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); + (remoteHostAndPort, HOST_NAME_MAX - 1, "%s:%d", remoteHost_.c_str(), remotePort_); - Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); + Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, remoteHostAndPort)); // Set various options Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); @@ -942,7 +942,7 @@ defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) { // Make room in the default storage syntaxes - assert(defaultStorageSOPClasses_.size() > 0); // Necessarily true because condition (*) is false + assert(!defaultStorageSOPClasses_.empty()); // Necessarily true because condition (*) is false defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin()); }
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -60,9 +60,9 @@ // Connection parameters std::string preferredTransferSyntax_; std::string localAet_; - std::string distantAet_; - std::string distantHost_; - uint16_t distantPort_; + std::string remoteAet_; + std::string remoteHost_; + uint16_t remotePort_; ModalityManufacturer manufacturer_; std::set<std::string> storageSOPClasses_; std::list<std::string> reservedStorageSOPClasses_; @@ -97,30 +97,30 @@ return localAet_; } - void SetDistantApplicationEntityTitle(const std::string& aet); + void SetRemoteApplicationEntityTitle(const std::string& aet); - const std::string& GetDistantApplicationEntityTitle() const + const std::string& GetRemoteApplicationEntityTitle() const { - return distantAet_; + return remoteAet_; } - void SetDistantHost(const std::string& host); + void SetRemoteHost(const std::string& host); - const std::string& GetDistantHost() const + const std::string& GetRemoteHost() const { - return distantHost_; + return remoteHost_; } - void SetDistantPort(uint16_t port); + void SetRemotePort(uint16_t port); - uint16_t GetDistantPort() const + uint16_t GetRemotePort() const { - return distantPort_; + return remotePort_; } - void SetDistantManufacturer(ModalityManufacturer manufacturer); + void SetRemoteManufacturer(ModalityManufacturer manufacturer); - ModalityManufacturer GetDistantManufacturer() const + ModalityManufacturer GetRemoteManufacturer() const { return manufacturer_; }
--- a/OrthancServer/DicomProtocol/IApplicationEntityFilter.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IApplicationEntityFilter.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/IMoveRequestHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IMoveRequestHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/IStoreRequestHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IStoreRequestHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -50,6 +50,7 @@ virtual void Handle(const std::string& dicomFile, const DicomMap& dicomSummary, const Json::Value& dicomJson, - const std::string& distantAet) = 0; + const std::string& remoteAet, + const std::string& calledAet) = 0; }; }
--- a/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -50,10 +50,10 @@ ModalityManufacturer manufacturer) { if (connection_ != NULL && - connection_->GetDistantApplicationEntityTitle() == remoteAet && - connection_->GetDistantHost() == address && - connection_->GetDistantPort() == port && - connection_->GetDistantManufacturer() == manufacturer) + connection_->GetRemoteApplicationEntityTitle() == remoteAet && + connection_->GetRemoteHost() == address && + connection_->GetRemotePort() == port && + connection_->GetRemoteManufacturer() == manufacturer) { // The current connection can be reused LOG(INFO) << "Reusing the previous SCU connection"; @@ -64,10 +64,10 @@ connection_ = new DicomUserConnection(); connection_->SetLocalApplicationEntityTitle(localAet_); - connection_->SetDistantApplicationEntityTitle(remoteAet); - connection_->SetDistantHost(address); - connection_->SetDistantPort(port); - connection_->SetDistantManufacturer(manufacturer); + connection_->SetRemoteApplicationEntityTitle(remoteAet); + connection_->SetRemoteHost(address); + connection_->SetRemotePort(port); + connection_->SetRemoteManufacturer(manufacturer); connection_->Open(); } @@ -152,9 +152,15 @@ Close(); } - void ReusableDicomUserConnection::SetMillisecondsBeforeClose(unsigned int ms) + void ReusableDicomUserConnection::SetMillisecondsBeforeClose(uint64_t ms) { boost::mutex::scoped_lock lock(mutex_); + + if (ms == 0) + { + ms = 1; + } + timeBeforeClose_ = boost::posix_time::milliseconds(ms); } @@ -173,7 +179,7 @@ void ReusableDicomUserConnection::Unlock() { if (connection_ != NULL && - connection_->GetDistantManufacturer() == ModalityManufacturer_StoreScp) + connection_->GetRemoteManufacturer() == ModalityManufacturer_StoreScp) { // "storescp" from DCMTK has problems when reusing a // connection. Always close.
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -88,12 +88,12 @@ virtual ~ReusableDicomUserConnection(); - unsigned int GetMillisecondsBeforeClose() const + uint64_t GetMillisecondsBeforeClose() const { - return static_cast<unsigned int>(timeBeforeClose_.total_milliseconds()); + return static_cast<uint64_t>(timeBeforeClose_.total_milliseconds()); } - void SetMillisecondsBeforeClose(unsigned int ms); + void SetMillisecondsBeforeClose(uint64_t ms); const std::string& GetLocalApplicationEntityTitle() const;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/ExportedResource.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,69 @@ +/** + * 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://www.gnu.org/licenses/>. + **/ + + +#include "ExportedResource.h" + +#include "../Core/OrthancException.h" + +namespace Orthanc +{ + void ExportedResource::Format(Json::Value& item) const + { + item = Json::objectValue; + item["Seq"] = static_cast<int>(seq_); + item["ResourceType"] = EnumerationToString(resourceType_); + item["ID"] = publicId_; + item["Path"] = GetBasePath(resourceType_, publicId_); + item["RemoteModality"] = modality_; + item["Date"] = date_; + + // WARNING: Do not add "break" below and do not reorder the case items! + switch (resourceType_) + { + case ResourceType_Instance: + item["SOPInstanceUID"] = sopInstanceUid_; + + case ResourceType_Series: + item["SeriesInstanceUID"] = seriesInstanceUid_; + + case ResourceType_Study: + item["StudyInstanceUID"] = studyInstanceUid_; + + case ResourceType_Patient: + item["PatientID"] = patientId_; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/ExportedResource.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,125 @@ +/** + * 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://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ServerEnumerations.h" +#include "../Core/Toolbox.h" + +#include <string> +#include <json/value.h> + +namespace Orthanc +{ + class ExportedResource + { + private: + int64_t seq_; + ResourceType resourceType_; + std::string publicId_; + std::string modality_; + std::string date_; + std::string patientId_; + std::string studyInstanceUid_; + std::string seriesInstanceUid_; + std::string sopInstanceUid_; + + public: + ExportedResource(int64_t seq, + ResourceType resourceType, + const std::string& publicId, + const std::string& modality, + const std::string& date, + const std::string& patientId, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid) : + seq_(seq), + resourceType_(resourceType), + publicId_(publicId), + modality_(modality), + date_(date), + patientId_(patientId), + studyInstanceUid_(studyInstanceUid), + seriesInstanceUid_(seriesInstanceUid), + sopInstanceUid_(sopInstanceUid) + { + } + + int64_t GetSeq() const + { + return seq_; + } + + ResourceType GetResourceType() const + { + return resourceType_; + } + + const std::string& GetPublicId() const + { + return publicId_; + } + + const std::string& GetModality() const + { + return modality_; + } + + const std::string& GetDate() const + { + return date_; + } + + const std::string& GetPatientId() const + { + return patientId_; + } + + const std::string& GetStudyInstanceUid() const + { + return studyInstanceUid_; + } + + const std::string& GetSeriesInstanceUid() const + { + return seriesInstanceUid_; + } + + const std::string& GetSopInstanceUid() const + { + return sopInstanceUid_; + } + + void Format(Json::Value& item) const; + }; +}
--- a/OrthancServer/FromDcmtkBridge.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/FromDcmtkBridge.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -186,15 +186,27 @@ bool FromDcmtkBridge::IsPrivateTag(DcmTag& tag) { +#if 1 + DcmTagKey tmp(tag.getGTag(), tag.getETag()); + return tmp.isPrivate(); +#else + // Implementation for Orthanc versions <= 0.8.5 return (tag.getPrivateCreator() != NULL || !strcmp("PrivateCreator", tag.getTagName())); // TODO - This may change with future versions of DCMTK +#endif } bool FromDcmtkBridge::IsPrivateTag(const DicomTag& tag) { +#if 1 + DcmTagKey tmp(tag.GetGroup(), tag.GetElement()); + return tmp.isPrivate(); +#else + // Implementation for Orthanc versions <= 0.8.5 DcmTag tmp(tag.GetGroup(), tag.GetElement()); return IsPrivateTag(tmp); +#endif }
--- a/OrthancServer/FromDcmtkBridge.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/FromDcmtkBridge.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/IDatabaseWrapper.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,181 @@ +/** + * 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://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Core/DicomFormat/DicomMap.h" +#include "../Core/SQLite/ITransaction.h" +#include "../Core/FileStorage/FileInfo.h" +#include "IServerIndexListener.h" +#include "ExportedResource.h" + +#include <list> +#include <boost/noncopyable.hpp> + +namespace Orthanc +{ + class IDatabaseWrapper : public boost::noncopyable + { + public: + virtual ~IDatabaseWrapper() + { + } + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) = 0; + + virtual void AttachChild(int64_t parent, + int64_t child) = 0; + + virtual void ClearChanges() = 0; + + virtual void ClearExportedResources() = 0; + + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type) = 0; + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) = 0; + + virtual void DeleteMetadata(int64_t id, + MetadataType type) = 0; + + virtual void DeleteResource(int64_t id) = 0; + + virtual void FlushToDisk() = 0; + + virtual bool HasFlushToDisk() const = 0; + + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id) = 0; + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType) = 0; + + + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) = 0; + + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id) = 0; + + virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) = 0; + + virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) = 0; + + virtual void GetMainDicomTags(DicomMap& map, + int64_t id) = 0; + + virtual std::string GetPublicId(int64_t resourceId) = 0; + + virtual uint64_t GetResourceCount(ResourceType resourceType) = 0; + + virtual ResourceType GetResourceType(int64_t resourceId) = 0; + + virtual uint64_t GetTotalCompressedSize() = 0; + + virtual uint64_t GetTotalUncompressedSize() = 0; + + virtual bool IsExistingResource(int64_t internalId) = 0; + + virtual bool IsProtectedPatient(int64_t internalId) = 0; + + virtual void ListAvailableMetadata(std::list<MetadataType>& target, + int64_t id) = 0; + + virtual void ListAvailableAttachments(std::list<FileContentType>& target, + int64_t id) = 0; + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change) = 0; + + virtual void LogExportedResource(const ExportedResource& resource) = 0; + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) = 0; + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property) = 0; + + virtual void LookupIdentifier(std::list<int64_t>& target, + const DicomTag& tag, + const std::string& value) = 0; + + virtual void LookupIdentifier(std::list<int64_t>& target, + const std::string& value) = 0; + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) = 0; + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) = 0; + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) = 0; + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value) = 0; + + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) = 0; + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) = 0; + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) = 0; + + virtual SQLite::ITransaction* StartTransaction() = 0; + + virtual void SetListener(IServerIndexListener& listener) = 0; + }; +}
--- a/OrthancServer/IServerIndexListener.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/IServerIndexListener.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/CommandDispatcher.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/CommandDispatcher.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/CommandDispatcher.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/CommandDispatcher.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/DicomImageDecoder.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/DicomImageDecoder.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/FindScp.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/FindScp.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/FindScp.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/FindScp.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/MoveScp.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/MoveScp.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/MoveScp.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/MoveScp.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Internals/StoreScp.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/StoreScp.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -102,7 +102,8 @@ struct StoreCallbackData { IStoreRequestHandler* handler; - const char* distantAET; + const char* remoteAET; + const char* calledAET; const char* modality; const char* affectedSOPInstanceUID; uint32_t messageID; @@ -201,7 +202,7 @@ { try { - cbdata->handler->Handle(buffer, summary, dicomJson, cbdata->distantAET); + cbdata->handler->Handle(buffer, summary, dicomJson, cbdata->remoteAET, cbdata->calledAET); } catch (OrthancException& e) { @@ -255,11 +256,13 @@ callbackData.messageID = req->MessageID; if (assoc && assoc->params) { - callbackData.distantAET = assoc->params->DULparams.callingAPTitle; + callbackData.remoteAET = assoc->params->DULparams.callingAPTitle; + callbackData.calledAET = assoc->params->DULparams.calledAPTitle; } else { - callbackData.distantAET = ""; + callbackData.remoteAET = ""; + callbackData.calledAET = ""; } DcmFileFormat dcmff;
--- a/OrthancServer/Internals/StoreScp.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Internals/StoreScp.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancFindRequestHandler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancFindRequestHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancInitialization.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancInitialization.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -45,6 +45,9 @@ #include <boost/thread.hpp> #include <glog/logging.h> +#include "DatabaseWrapper.h" +#include "../Core/FileStorage/FilesystemStorage.h" + #if ORTHANC_JPEG_ENABLED == 1 #include <dcmtk/dcmjpeg/djdecode.h> @@ -183,6 +186,52 @@ } + static std::string GetStringValue(const Json::Value& configuration, + const std::string& key, + const std::string& defaultValue) + { + if (configuration.type() != Json::objectValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + if (!configuration.isMember(key)) + { + return defaultValue; + } + + if (configuration[key].type() != Json::stringValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + return configuration[key].asString(); + } + + + static int GetIntegerValue(const Json::Value& configuration, + const std::string& key, + int defaultValue) + { + if (configuration.type() != Json::objectValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + if (!configuration.isMember(key)) + { + return defaultValue; + } + + if (configuration[key].type() != Json::intValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + return configuration[key].asInt(); + } + + void OrthancInitialize(const char* configurationFile) { boost::mutex::scoped_lock lock(globalMutex_); @@ -654,4 +703,108 @@ { return configurationAbsolutePath_; } + + + static IDatabaseWrapper* CreateSQLiteWrapper() + { + std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); + + // Open the database + boost::filesystem::path indexDirectory = Configuration::InterpretStringParameterAsPath( + Configuration::GetGlobalStringParameter("IndexDirectory", storageDirectoryStr)); + + LOG(WARNING) << "SQLite index directory: " << indexDirectory; + + try + { + boost::filesystem::create_directories(indexDirectory); + } + catch (boost::filesystem::filesystem_error) + { + } + + return new DatabaseWrapper(indexDirectory.string() + "/index"); + } + + + namespace + { + // Anonymous namespace to avoid clashes between compilation modules + + class FilesystemStorageWithoutDicom : public IStorageArea + { + private: + FilesystemStorage storage_; + + public: + FilesystemStorageWithoutDicom(const std::string& path) : storage_(path) + { + } + + virtual void Create(const std::string& uuid, + const void* content, + size_t size, + FileContentType type) + { + if (type != FileContentType_Dicom) + { + storage_.Create(uuid, content, size, type); + } + } + + virtual void Read(std::string& content, + const std::string& uuid, + FileContentType type) + { + if (type != FileContentType_Dicom) + { + storage_.Read(content, uuid, type); + } + else + { + throw OrthancException(ErrorCode_UnknownResource); + } + } + + virtual void Remove(const std::string& uuid, + FileContentType type) + { + if (type != FileContentType_Dicom) + { + storage_.Remove(uuid, type); + } + } + }; + } + + + static IStorageArea* CreateFilesystemStorage() + { + std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); + + boost::filesystem::path storageDirectory = Configuration::InterpretStringParameterAsPath(storageDirectoryStr); + LOG(WARNING) << "Storage directory: " << storageDirectory; + + if (Configuration::GetGlobalBoolParameter("StoreDicom", true)) + { + return new FilesystemStorage(storageDirectory.string()); + } + else + { + LOG(WARNING) << "The DICOM files will not be stored, Orthanc running in index-only mode"; + return new FilesystemStorageWithoutDicom(storageDirectory.string()); + } + } + + + IDatabaseWrapper* Configuration::CreateDatabaseWrapper() + { + return CreateSQLiteWrapper(); + } + + + IStorageArea* Configuration::CreateStorageArea() + { + return CreateFilesystemStorage(); + } }
--- a/OrthancServer/OrthancInitialization.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancInitialization.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -40,6 +40,8 @@ #include "DicomProtocol/RemoteModalityParameters.h" #include "ServerEnumerations.h" #include "OrthancPeerParameters.h" +#include "IDatabaseWrapper.h" +#include "../Core/FileStorage/IStorageArea.h" namespace Orthanc { @@ -102,5 +104,9 @@ static void RemovePeer(const std::string& symbolicName); static const std::string& GetConfigurationAbsolutePath(); + + static IDatabaseWrapper* CreateDatabaseWrapper(); + + static IStorageArea* CreateStorageArea(); }; }
--- a/OrthancServer/OrthancMoveRequestHandler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancMoveRequestHandler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -102,8 +102,8 @@ bool OrthancMoveRequestHandler::LookupIdentifier(std::string& publicId, - DicomTag tag, - const DicomMap& input) + DicomTag tag, + const DicomMap& input) { if (!input.HasTag(tag)) { @@ -132,18 +132,42 @@ { LOG(WARNING) << "Move-SCU request received for AET \"" << aet << "\""; - /** * Retrieve the query level. **/ + ResourceType level; const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); - if (levelTmp == NULL) + + if (levelTmp != NULL) + { + level = StringToResourceType(levelTmp->AsString().c_str()); + } + else { - throw OrthancException(ErrorCode_BadRequest); + // The query level is not present in the C-Move request, which + // does not follow the DICOM standard. This is for instance the + // behavior of Tudor DICOM. Try and automatically deduce the + // query level: Start from the instance level, going up to the + // patient level until a valid DICOM identifier is found. + + std::string publicId; + + if (LookupIdentifier(publicId, DICOM_TAG_SOP_INSTANCE_UID, input) || + LookupIdentifier(publicId, DICOM_TAG_SERIES_INSTANCE_UID, input) || + LookupIdentifier(publicId, DICOM_TAG_STUDY_INSTANCE_UID, input) || + LookupIdentifier(publicId, DICOM_TAG_PATIENT_ID, input)) + { + return new OrthancMoveRequestIterator(context_, aet, publicId); + } + else + { + // No identifier is present in the request. + throw OrthancException(ErrorCode_BadRequest); + } } - ResourceType level = StringToResourceType(levelTmp->AsString().c_str()); + /** * Lookup for the resource to be sent.
--- a/OrthancServer/OrthancMoveRequestHandler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancMoveRequestHandler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancPeerParameters.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancPeerParameters.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancPeerParameters.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancPeerParameters.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -194,7 +194,7 @@ { // Overwrite the random Patient's Name by one that is more // user-friendly (provided none was specified by the user) - target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call))); + target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true); } return true; @@ -361,6 +361,7 @@ static void ModifyInstance(RestApiPostCall& call) { DicomModification modification; + modification.SetAllowManualIdentifiers(true); if (ParseModifyRequest(modification, call)) { @@ -389,6 +390,7 @@ static void AnonymizeInstance(RestApiPostCall& call) { DicomModification modification; + modification.SetAllowManualIdentifiers(true); if (ParseAnonymizationRequest(modification, call)) {
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -324,7 +324,7 @@ size_t pos = 0; for (std::list<std::string>::const_iterator - it = instances.begin(); it != instances.end(); it++, pos++) + it = instances.begin(); it != instances.end(); ++it, ++pos) { // "DICOM restricts the filenames on DICOM media to 8 // characters (some systems wrongly use 8.3, but this does not
--- a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -81,11 +81,16 @@ GetSinceAndLimit(since, limit, last, call); Json::Value result; - if ((!last && context.GetIndex().GetChanges(result, since, limit)) || - ( last && context.GetIndex().GetLastChange(result))) + if (last) { - call.GetOutput().AnswerJson(result); + context.GetIndex().GetLastChange(result); } + else + { + context.GetIndex().GetChanges(result, since, limit); + } + + call.GetOutput().AnswerJson(result); } @@ -108,11 +113,16 @@ GetSinceAndLimit(since, limit, last, call); Json::Value result; - if ((!last && context.GetIndex().GetExportedResources(result, since, limit)) || - ( last && context.GetIndex().GetLastExportedResource(result))) + if (last) { - call.GetOutput().AnswerJson(result); + context.GetIndex().GetLastExportedResource(result); } + else + { + context.GetIndex().GetExportedResources(result, since, limit); + } + + call.GetOutput().AnswerJson(result); }
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -626,7 +626,7 @@ shared = Json::objectValue; for (Instances::const_iterator it = instances.begin(); - it != instances.end(); it++) + it != instances.end(); ++it) { // Get the tags of the current instance, in the simplified format Json::Value tags; @@ -713,10 +713,14 @@ static void GetModuleInternal(RestApiGetCall& call, ResourceType resourceType, - ResourceType queryLevel) + DicomModule module) { - if (resourceType != queryLevel && - !(resourceType == ResourceType_Study && queryLevel == ResourceType_Patient)) + if (!((resourceType == ResourceType_Patient && module == DicomModule_Patient) || + (resourceType == ResourceType_Study && module == DicomModule_Patient) || + (resourceType == ResourceType_Study && module == DicomModule_Study) || + (resourceType == ResourceType_Series && module == DicomModule_Series) || + (resourceType == ResourceType_Instance && module == DicomModule_Instance) || + (resourceType == ResourceType_Instance && module == DicomModule_Image))) { throw OrthancException(ErrorCode_NotImplemented); } @@ -725,9 +729,9 @@ std::string publicId = call.GetUriComponent("id", ""); bool simplify = call.HasArgument("simplify"); - typedef std::set<DicomTag> Module; - Module module; - DicomTag::GetTagsForModule(module, queryLevel); + typedef std::set<DicomTag> ModuleTags; + ModuleTags moduleTags; + DicomTag::GetTagsForModule(moduleTags, module); Json::Value tags; @@ -751,9 +755,9 @@ // Filter the tags of the instance according to the module Json::Value result = Json::objectValue; - for (Module::const_iterator it = module.begin(); it != module.end(); it++) + for (ModuleTags::const_iterator tag = moduleTags.begin(); tag != moduleTags.end(); ++tag) { - std::string s = it->Format(); + std::string s = tag->Format(); if (tags.isMember(s)) { result[s] = tags[s]; @@ -774,16 +778,11 @@ - template <enum ResourceType resourceType> + template <enum ResourceType resourceType, + enum DicomModule module> static void GetModule(RestApiGetCall& call) { - GetModuleInternal(call, resourceType, resourceType); - } - - - static void GetPatientModuleForStudy(RestApiGetCall& call) - { - GetModuleInternal(call, ResourceType_Study, ResourceType_Patient); + GetModuleInternal(call, resourceType, module); } @@ -799,7 +798,7 @@ Json::Value result = Json::arrayValue; for (Resources::const_iterator it = resources.begin(); - it != resources.end(); it++) + it != resources.end(); ++it) { ResourceType type = it->first; const std::string& id = it->second; @@ -831,7 +830,7 @@ b.clear(); for (std::list<std::string>::const_iterator - it = a.begin(); it != a.end(); it++) + it = a.begin(); it != a.end(); ++it) { index.GetChildren(c, *it); b.splice(b.begin(), c); @@ -862,7 +861,7 @@ Json::Value result = Json::arrayValue; for (std::list<std::string>::const_iterator - it = a.begin(); it != a.end(); it++) + it = a.begin(); it != a.end(); ++it) { Json::Value item; @@ -891,7 +890,7 @@ Json::Value result = Json::objectValue; for (Instances::const_iterator it = instances.begin(); - it != instances.end(); it++) + it != instances.end(); ++it) { Json::Value full; context.ReadJson(full, *it); @@ -913,6 +912,47 @@ + template <enum ResourceType start, + enum ResourceType end> + static void GetParentResource(RestApiGetCall& call) + { + assert(start > end); + + ServerIndex& index = OrthancRestApi::GetIndex(call); + + std::string current = call.GetUriComponent("id", ""); + ResourceType currentType = start; + while (currentType > end) + { + std::string parent; + if (!index.LookupParent(parent, current)) + { + // Error that could happen if the resource gets deleted by + // another concurrent call + return; + } + + current = parent; + switch (currentType) + { + case ResourceType_Instance: currentType = ResourceType_Series; break; + case ResourceType_Series: currentType = ResourceType_Study; break; + case ResourceType_Study: currentType = ResourceType_Patient; break; + default: throw OrthancException(ErrorCode_InternalError); + } + } + + assert(currentType == end); + + Json::Value result; + if (index.LookupResource(result, current, end)) + { + call.GetOutput().AnswerJson(result); + } + } + + + void OrthancRestApi::RegisterResources() { Register("/instances", ListResources<ResourceType_Instance>); @@ -938,11 +978,11 @@ Register("/series/{id}/shared-tags", GetSharedTags); Register("/studies/{id}/shared-tags", GetSharedTags); - Register("/instances/{id}/module", GetModule<ResourceType_Instance>); - Register("/patients/{id}/module", GetModule<ResourceType_Patient>); - Register("/series/{id}/module", GetModule<ResourceType_Series>); - Register("/studies/{id}/module", GetModule<ResourceType_Study>); - Register("/studies/{id}/module-patient", GetPatientModuleForStudy); + Register("/instances/{id}/module", GetModule<ResourceType_Instance, DicomModule_Instance>); + Register("/patients/{id}/module", GetModule<ResourceType_Patient, DicomModule_Patient>); + Register("/series/{id}/module", GetModule<ResourceType_Series, DicomModule_Series>); + Register("/studies/{id}/module", GetModule<ResourceType_Study, DicomModule_Study>); + Register("/studies/{id}/module-patient", GetModule<ResourceType_Study, DicomModule_Patient>); Register("/instances/{id}/file", GetInstanceFile); Register("/instances/{id}/export", ExportInstanceFile); @@ -990,6 +1030,13 @@ Register("/studies/{id}/instances", GetChildResources<ResourceType_Study, ResourceType_Instance>); Register("/series/{id}/instances", GetChildResources<ResourceType_Series, ResourceType_Instance>); + Register("/studies/{id}/patient", GetParentResource<ResourceType_Study, ResourceType_Patient>); + Register("/series/{id}/patient", GetParentResource<ResourceType_Series, ResourceType_Patient>); + Register("/series/{id}/study", GetParentResource<ResourceType_Series, ResourceType_Study>); + Register("/instances/{id}/patient", GetParentResource<ResourceType_Instance, ResourceType_Patient>); + Register("/instances/{id}/study", GetParentResource<ResourceType_Instance, ResourceType_Study>); + Register("/instances/{id}/series", GetParentResource<ResourceType_Instance, ResourceType_Series>); + Register("/patients/{id}/instances-tags", GetChildInstancesTags); Register("/studies/{id}/instances-tags", GetChildInstancesTags); Register("/series/{id}/instances-tags", GetChildInstancesTags);
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -35,6 +35,8 @@ #include "../OrthancInitialization.h" #include "../FromDcmtkBridge.h" +#include "../../Plugins/Engine/PluginsManager.h" +#include "../../Plugins/Engine/OrthancPlugins.h" #include <glog/logging.h> @@ -54,6 +56,7 @@ result["Version"] = ORTHANC_VERSION; result["Name"] = Configuration::GetGlobalStringParameter("Name", ""); + result["DatabaseVersion"] = boost::lexical_cast<int>(OrthancRestApi::GetIndex(call).GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "0")); call.GetOutput().AnswerJson(result); } @@ -113,6 +116,95 @@ } + // Plugins information ------------------------------------------------------ + + static void ListPlugins(RestApiGetCall& call) + { + Json::Value v = Json::arrayValue; + + v.append("explorer.js"); + + if (OrthancRestApi::GetContext(call).HasPlugins()) + { + std::list<std::string> plugins; + OrthancRestApi::GetContext(call).GetPluginsManager().ListPlugins(plugins); + + for (std::list<std::string>::const_iterator + it = plugins.begin(); it != plugins.end(); ++it) + { + v.append(*it); + } + } + + call.GetOutput().AnswerJson(v); + } + + + static void GetPlugin(RestApiGetCall& call) + { + if (!OrthancRestApi::GetContext(call).HasPlugins()) + { + return; + } + + const PluginsManager& manager = OrthancRestApi::GetContext(call).GetPluginsManager(); + std::string id = call.GetUriComponent("id", ""); + + if (manager.HasPlugin(id)) + { + Json::Value v = Json::objectValue; + v["ID"] = id; + v["Version"] = manager.GetPluginVersion(id); + + const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetOrthancPlugins(); + const char *c = plugins.GetProperty(id.c_str(), _OrthancPluginProperty_RootUri); + if (c != NULL) + { + v["RootUri"] = c; + } + + c = plugins.GetProperty(id.c_str(), _OrthancPluginProperty_Description); + if (c != NULL) + { + v["Description"] = c; + } + + c = plugins.GetProperty(id.c_str(), _OrthancPluginProperty_OrthancExplorer); + v["ExtendsOrthancExplorer"] = (c != NULL); + + call.GetOutput().AnswerJson(v); + } + } + + + static void GetOrthancExplorerPlugins(RestApiGetCall& call) + { + std::string s = "// Extensions to Orthanc Explorer by the registered plugins\n\n"; + + if (OrthancRestApi::GetContext(call).HasPlugins()) + { + const PluginsManager& manager = OrthancRestApi::GetContext(call).GetPluginsManager(); + const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetOrthancPlugins(); + + std::list<std::string> lst; + OrthancRestApi::GetContext(call).GetPluginsManager().ListPlugins(lst); + + for (std::list<std::string>::const_iterator + it = lst.begin(); it != lst.end(); ++it) + { + const char* tmp = plugins.GetProperty(it->c_str(), _OrthancPluginProperty_OrthancExplorer); + if (tmp != NULL) + { + s += "/**\n * From plugin: " + *it + " (version " + manager.GetPluginVersion(*it) + ")\n **/\n\n"; + s += std::string(tmp) + "\n\n"; + } + } + } + + call.GetOutput().AnswerBuffer(s, "application/javascript"); + } + + void OrthancRestApi::RegisterSystem() { Register("/", ServeRoot); @@ -122,5 +214,9 @@ Register("/tools/execute-script", ExecuteScript); Register("/tools/now", GetNowIsoString); Register("/tools/dicom-conformance", GetDicomConformanceStatement); + + Register("/plugins", ListPlugins); + Register("/plugins/{id}", GetPlugin); + Register("/plugins/explorer.js", GetOrthancExplorerPlugins); } }
--- a/OrthancServer/ParsedDicomFile.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ParsedDicomFile.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -807,13 +807,30 @@ + void ParsedDicomFile::Insert(const DicomTag& tag, const std::string& value) { - std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); - FillElementWithString(*element, tag, value); + OFCondition cond; + + if (FromDcmtkBridge::IsPrivateTag(tag)) + { + // This is a private tag + // http://support.dcmtk.org/redmine/projects/dcmtk/wiki/howto_addprivatedata - if (!pimpl_->file_->getDataset()->insert(element.release(), false, false).good()) + DcmTag key(tag.GetGroup(), tag.GetElement(), EVR_OB); + cond = pimpl_->file_->getDataset()->putAndInsertUint8Array + (key, (const Uint8*) value.c_str(), value.size(), false); + } + else + { + std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); + FillElementWithString(*element, tag, value); + + cond = pimpl_->file_->getDataset()->insert(element.release(), false, false); + } + + if (!cond.good()) { // This field already exists throw OrthancException(ErrorCode_InternalError); @@ -847,7 +864,17 @@ } else { - FillElementWithString(*element, tag, value); + if (FromDcmtkBridge::IsPrivateTag(tag)) + { + if (!element->putUint8Array((const Uint8*) value.c_str(), value.size()).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + } + else + { + FillElementWithString(*element, tag, value); + } } @@ -888,25 +915,53 @@ { DcmTagKey k(tag.GetGroup(), tag.GetElement()); DcmDataset& dataset = *pimpl_->file_->getDataset(); - DcmElement* element = NULL; - if (!dataset.findAndGetElement(k, element).good() || - element == NULL) + + if (FromDcmtkBridge::IsPrivateTag(tag)) { - return false; - } + const Uint8* data = NULL; // This is freed in the destructor of the dataset + long unsigned int count = 0; - std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_)); + if (dataset.findAndGetUint8Array(k, data, &count).good()) + { + if (count > 0) + { + assert(data != NULL); + value.assign(reinterpret_cast<const char*>(data), count); + } + else + { + value.clear(); + } - if (v.get() == NULL) - { - value = ""; + return true; + } + else + { + return false; + } } else { - value = v->AsString(); + DcmElement* element = NULL; + if (!dataset.findAndGetElement(k, element).good() || + element == NULL) + { + return false; + } + + std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_)); + + if (v.get() == NULL) + { + value = ""; + } + else + { + value = v->AsString(); + } + + return true; } - - return true; }
--- a/OrthancServer/ParsedDicomFile.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ParsedDicomFile.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/PrecompiledHeadersServer.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/PrecompiledHeadersServer.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/PrecompiledHeadersServer.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/PrecompiledHeadersServer.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/CallSystemCommand.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/CallSystemCommand.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/CallSystemCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/CallSystemCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/DeleteInstanceCommand.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/DeleteInstanceCommand.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/DeleteInstanceCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/DeleteInstanceCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/IServerCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/IServerCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ModifyInstanceCommand.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ModifyInstanceCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/ServerCommandInstance.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerCommandInstance.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -60,10 +60,10 @@ } for (std::list<ServerCommandInstance*>::iterator - it = next_.begin(); it != next_.end(); it++) + it = next_.begin(); it != next_.end(); ++it) { for (ListOfStrings::const_iterator - output = outputs.begin(); output != outputs.end(); output++) + output = outputs.begin(); output != outputs.end(); ++output) { (*it)->AddInput(*output); }
--- a/OrthancServer/Scheduler/ServerCommandInstance.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerCommandInstance.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/ServerJob.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerJob.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -44,18 +44,18 @@ unsigned int count = 0; for (std::list<ServerCommandInstance*>::const_iterator - it = filters_.begin(); it != filters_.end(); it++) + it = filters_.begin(); it != filters_.end(); ++it) { index[*it] = count++; } for (std::list<ServerCommandInstance*>::const_iterator - it = filters_.begin(); it != filters_.end(); it++) + it = filters_.begin(); it != filters_.end(); ++it) { const std::list<ServerCommandInstance*>& nextCommands = (*it)->GetNextCommands(); for (std::list<ServerCommandInstance*>::const_iterator - next = nextCommands.begin(); next != nextCommands.end(); next++) + next = nextCommands.begin(); next != nextCommands.end(); ++next) { if (index.find(*next) == index.end() || index[*next] <= index[*it]) @@ -82,7 +82,7 @@ size_t size = filters_.size(); for (std::list<ServerCommandInstance*>::iterator - it = filters_.begin(); it != filters_.end(); it++) + it = filters_.begin(); it != filters_.end(); ++it) { target.Enqueue(*it); } @@ -94,24 +94,24 @@ } - ServerJob::ServerJob() + ServerJob::ServerJob() : + jobId_(Toolbox::GenerateUuid()), + submitted_(false), + description_("no description") { - jobId_ = Toolbox::GenerateUuid(); - submitted_ = false; - description_ = "no description"; } ServerJob::~ServerJob() { for (std::list<ServerCommandInstance*>::iterator - it = filters_.begin(); it != filters_.end(); it++) + it = filters_.begin(); it != filters_.end(); ++it) { delete *it; } for (std::list<IDynamicObject*>::iterator - it = payloads_.begin(); it != payloads_.end(); it++) + it = payloads_.begin(); it != payloads_.end(); ++it) { delete *it; }
--- a/OrthancServer/Scheduler/ServerJob.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerJob.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/ServerScheduler.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerScheduler.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -55,7 +55,7 @@ const ListOfStrings& inputs) { for (ListOfStrings::const_iterator - it = inputs.begin(); it != inputs.end(); it++) + it = inputs.begin(); it != inputs.end(); ++it) { target_.push_back(*it); } @@ -232,7 +232,7 @@ ServerCommandInstance& sink = job.AddCommand(new Sink(outputs)); for (std::list<ServerCommandInstance*>::iterator - it = job.filters_.begin(); it != job.filters_.end(); it++) + it = job.filters_.begin(); it != job.filters_.end(); ++it) { if ((*it) != &sink && (*it)->IsConnectedToSink()) @@ -328,7 +328,7 @@ jobs.clear(); for (Jobs::const_iterator - it = jobs_.begin(); it != jobs_.end(); it++) + it = jobs_.begin(); it != jobs_.end(); ++it) { jobs.push_back(it->first); }
--- a/OrthancServer/Scheduler/ServerScheduler.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/ServerScheduler.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/StorePeerCommand.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/StorePeerCommand.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -52,6 +52,7 @@ { // Configure the HTTP client HttpClient client; + client.SetProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); if (peer_.GetUsername().size() != 0 && peer_.GetPassword().size() != 0) {
--- a/OrthancServer/Scheduler/StorePeerCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/StorePeerCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/StoreScuCommand.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/Scheduler/StoreScuCommand.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/Scheduler/StoreScuCommand.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ServerContext.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerContext.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -71,18 +71,22 @@ namespace Orthanc { - ServerContext::ServerContext(const boost::filesystem::path& indexPath) : - index_(*this, indexPath.string()), + ServerContext::ServerContext(IDatabaseWrapper& database) : + index_(*this, database), compressionEnabled_(false), provider_(*this), dicomCache_(provider_, DICOM_CACHE_SIZE), scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), - plugins_(NULL) + plugins_(NULL), + pluginsManager_(NULL) { scu_.SetLocalApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); - //scu_.SetMillisecondsBeforeClose(1); // The connection is always released + + uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5); // In seconds + scu_.SetMillisecondsBeforeClose(s * 1000); // Milliseconds are expected here lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); + lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); } void ServerContext::SetCompressionEnabled(bool enabled) @@ -207,7 +211,9 @@ void ServerContext::ApplyLuaOnStoredInstance(const std::string& instanceId, const Json::Value& simplifiedDicom, - const Json::Value& metadata) + const Json::Value& metadata, + const std::string& remoteAet, + const std::string& calledAet) { LuaContextLocker locker(*this); @@ -219,6 +225,8 @@ call.PushString(instanceId); call.PushJson(simplifiedDicom); call.PushJson(metadata); + call.PushJson(remoteAet); + call.PushJson(calledAet); call.Execute(); Json::Value operations; @@ -312,7 +320,7 @@ dicom.GetMetadata().clear(); for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); - it != instanceMetadata.end(); it++) + it != instanceMetadata.end(); ++it) { dicom.GetMetadata().insert(std::make_pair(std::make_pair(ResourceType_Instance, it->first), it->second)); @@ -356,11 +364,12 @@ try { - ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata); + ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata, + dicom.GetRemoteAet(), dicom.GetCalledAet()); } catch (OrthancException& e) { - LOG(ERROR) << "Error in OnStoredInstance callback (Lua): " << e.What(); + LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (Lua): " << e.What(); } if (plugins_ != NULL) @@ -371,7 +380,7 @@ } catch (OrthancException& e) { - LOG(ERROR) << "Error in OnStoredInstance callback (plugins): " << e.What(); + LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (plugins): " << e.What(); } } } @@ -536,4 +545,36 @@ } } } + + + bool ServerContext::HasPlugins() const + { + return (pluginsManager_ && plugins_); + } + + + const PluginsManager& ServerContext::GetPluginsManager() const + { + if (HasPlugins()) + { + return *pluginsManager_; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + + + const OrthancPlugins& ServerContext::GetOrthancPlugins() const + { + if (HasPlugins()) + { + return *plugins_; + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } }
--- a/OrthancServer/ServerContext.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerContext.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -49,6 +49,7 @@ namespace Orthanc { class OrthancPlugins; + class PluginsManager; /** * This class is responsible for maintaining the storage area on the @@ -76,7 +77,9 @@ void ApplyLuaOnStoredInstance(const std::string& instanceId, const Json::Value& simplifiedDicom, - const Json::Value& metadata); + const Json::Value& metadata, + const std::string& remoteAet, + const std::string& calledAet); ServerIndex index_; CompressedFileStorageAccessor accessor_; @@ -91,6 +94,7 @@ boost::mutex luaMutex_; LuaContext lua_; OrthancPlugins* plugins_; // TODO Turn it into a listener pattern (idem for Lua callbacks) + const PluginsManager* pluginsManager_; public: class DicomCacheLocker : public boost::noncopyable @@ -135,7 +139,7 @@ }; - ServerContext(const boost::filesystem::path& indexPath); + ServerContext(IDatabaseWrapper& database); void SetStorageArea(IStorageArea& storage) { @@ -195,15 +199,29 @@ return scheduler_; } - void SetOrthancPlugins(OrthancPlugins& plugins) + void SetOrthancPlugins(const PluginsManager& manager, + OrthancPlugins& plugins) { + pluginsManager_ = &manager; plugins_ = &plugins; } + void ResetOrthancPlugins() + { + pluginsManager_ = NULL; + plugins_ = NULL; + } + bool DeleteResource(Json::Value& target, const std::string& uuid, ResourceType expectedType); void SignalChange(const ServerIndexChange& change); + + bool HasPlugins() const; + + const PluginsManager& GetPluginsManager() const; + + const OrthancPlugins& GetOrthancPlugins() const; }; }
--- a/OrthancServer/ServerEnumerations.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerEnumerations.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ServerEnumerations.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerEnumerations.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ServerIndex.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerIndex.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -43,7 +43,6 @@ #include "../Core/Toolbox.h" #include "../Core/Uuid.h" #include "../Core/DicomFormat/DicomArray.h" -#include "../Core/SQLite/Transaction.h" #include "FromDcmtkBridge.h" #include "ServerContext.h" @@ -140,7 +139,7 @@ { for (std::list<ServerIndexChange>::const_iterator it = pendingChanges_.begin(); - it != pendingChanges_.end(); it++) + it != pendingChanges_.end(); ++it) { context_.SignalChange(*it); } @@ -214,7 +213,7 @@ { private: ServerIndex& index_; - std::auto_ptr<SQLite::Transaction> transaction_; + std::auto_ptr<SQLite::ITransaction> transaction_; bool isCommitted_; public: @@ -222,10 +221,10 @@ index_(index), isCommitted_(false) { - assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize()); + transaction_.reset(index_.db_.StartTransaction()); + transaction_->Begin(); - transaction_.reset(index_.db_->StartTransaction()); - transaction_->Begin(); + assert(index_.currentStorageSize_ == index_.db_.GetTotalCompressedSize()); index_.listener_->StartTransaction(); } @@ -251,8 +250,6 @@ assert(index_.currentStorageSize_ >= index_.listener_->GetSizeOfFilesToRemove()); index_.currentStorageSize_ -= index_.listener_->GetSizeOfFilesToRemove(); - assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize()); - // Send all the pending changes to the Orthanc plugins index_.listener_->CommitChanges(); @@ -309,13 +306,13 @@ int64_t id; ResourceType type; - if (!db_->LookupResource(uuid, id, type) || + if (!db_.LookupResource(id, type, uuid) || expectedType != type) { return false; } - db_->DeleteResource(id); + db_.DeleteResource(id); if (listener_->HasRemainingLevel()) { @@ -346,9 +343,10 @@ try { boost::mutex::scoped_lock lock(that->mutex_); - std::string sleepString = that->db_->GetGlobalProperty(GlobalProperty_FlushSleep); + std::string sleepString; - if (Toolbox::IsInteger(sleepString)) + if (that->db_.LookupGlobalProperty(sleepString, GlobalProperty_FlushSleep) && + Toolbox::IsInteger(sleepString)) { sleep = boost::lexical_cast<unsigned int>(sleepString); } @@ -371,7 +369,7 @@ } boost::mutex::scoped_lock lock(that->mutex_); - that->db_->FlushToDisk(); + that->db_.FlushToDisk(); count = 0; } @@ -379,7 +377,7 @@ } - static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db, + static void ComputeExpectedNumberOfInstances(IDatabaseWrapper& db, int64_t series, const DicomMap& dicomSummary) { @@ -419,40 +417,147 @@ } + + + bool ServerIndex::GetMetadataAsInteger(int64_t& result, + int64_t id, + MetadataType type) + { + std::string s; + if (!db_.LookupMetadata(s, id, type)) + { + return false; + } + + try + { + result = boost::lexical_cast<int64_t>(s); + return true; + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + + + void ServerIndex::LogChange(int64_t internalId, + ChangeType changeType, + ResourceType resourceType, + const std::string& publicId) + { + ServerIndexChange change(changeType, resourceType, publicId); + + if (changeType <= ChangeType_INTERNAL_LastLogged) + { + db_.LogChange(internalId, change); + } + + assert(listener_.get() != NULL); + listener_->SignalChange(change); + } + + + uint64_t ServerIndex::IncrementGlobalSequenceInternal(GlobalProperty property) + { + std::string oldValue; + + if (db_.LookupGlobalProperty(oldValue, property)) + { + uint64_t oldNumber; + + try + { + oldNumber = boost::lexical_cast<uint64_t>(oldValue); + db_.SetGlobalProperty(property, boost::lexical_cast<std::string>(oldNumber + 1)); + return oldNumber + 1; + } + catch (boost::bad_lexical_cast&) + { + throw OrthancException(ErrorCode_InternalError); + } + } + else + { + // Initialize the sequence at "1" + db_.SetGlobalProperty(property, "1"); + return 1; + } + } + + + + void ServerIndex::SetMainDicomTags(int64_t resource, + const DicomMap& tags) + { + DicomArray flattened(tags); + for (size_t i = 0; i < flattened.GetSize(); i++) + { + const DicomElement& element = flattened.GetElement(i); + db_.SetMainDicomTag(resource, element.GetTag(), element.GetValue().AsString()); + } + } + + + int64_t ServerIndex::CreateResource(const std::string& publicId, + ResourceType type) + { + int64_t id = db_.CreateResource(publicId, type); + + ChangeType changeType; + switch (type) + { + case ResourceType_Patient: + changeType = ChangeType_NewPatient; + break; + + case ResourceType_Study: + changeType = ChangeType_NewStudy; + break; + + case ResourceType_Series: + changeType = ChangeType_NewSeries; + break; + + case ResourceType_Instance: + changeType = ChangeType_NewInstance; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + ServerIndexChange change(changeType, type, publicId); + db_.LogChange(id, change); + + assert(listener_.get() != NULL); + listener_->SignalChange(change); + + return id; + } + + ServerIndex::ServerIndex(ServerContext& context, - const std::string& dbPath) : + IDatabaseWrapper& db) : done_(false), + db_(db), maximumStorageSize_(0), maximumPatients_(0) { listener_.reset(new Internals::ServerIndexListener(context)); - - if (dbPath == ":memory:") - { - db_.reset(new DatabaseWrapper(*listener_)); - } - else - { - boost::filesystem::path p = dbPath; + db_.SetListener(*listener_); - try - { - boost::filesystem::create_directories(p); - } - catch (boost::filesystem::filesystem_error) - { - } - - db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); - } - - currentStorageSize_ = db_->GetTotalCompressedSize(); + currentStorageSize_ = db_.GetTotalCompressedSize(); // Initial recycling if the parameters have changed since the last // execution of Orthanc StandaloneRecycling(); - flushThread_ = boost::thread(FlushThread, this); + if (db.HasFlushToDisk()) + { + flushThread_ = boost::thread(FlushThread, this); + } + unstableResourcesMonitorThread_ = boost::thread(UnstableResourcesMonitorThread, this); } @@ -461,7 +566,8 @@ { done_ = true; - if (flushThread_.joinable()) + if (db_.HasFlushToDisk() && + flushThread_.joinable()) { flushThread_.join(); } @@ -493,10 +599,10 @@ { ResourceType type; int64_t tmp; - if (db_->LookupResource(hasher.HashInstance(), tmp, type)) + if (db_.LookupResource(tmp, type, hasher.HashInstance())) { assert(type == ResourceType_Instance); - db_->GetAllMetadata(instanceMetadata, tmp); + db_.GetAllMetadata(instanceMetadata, tmp); return StoreStatus_AlreadyStored; } } @@ -512,11 +618,11 @@ Recycle(instanceSize, hasher.HashPatient()); // Create the instance - int64_t instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance); + int64_t instance = CreateResource(hasher.HashInstance(), ResourceType_Instance); DicomMap dicom; dicomSummary.ExtractInstanceInformation(dicom); - db_->SetMainDicomTags(instance, dicom); + SetMainDicomTags(instance, dicom); // Detect up to which level the patient/study/series/instance // hierarchy must be created @@ -528,26 +634,26 @@ { ResourceType dummy; - if (db_->LookupResource(hasher.HashSeries(), series, dummy)) + if (db_.LookupResource(series, dummy, hasher.HashSeries())) { assert(dummy == ResourceType_Series); // The patient, the study and the series already exist - bool ok = (db_->LookupResource(hasher.HashPatient(), patient, dummy) && - db_->LookupResource(hasher.HashStudy(), study, dummy)); + bool ok = (db_.LookupResource(patient, dummy, hasher.HashPatient()) && + db_.LookupResource(study, dummy, hasher.HashStudy())); assert(ok); } - else if (db_->LookupResource(hasher.HashStudy(), study, dummy)) + else if (db_.LookupResource(study, dummy, hasher.HashStudy())) { assert(dummy == ResourceType_Study); // New series: The patient and the study already exist isNewSeries = true; - bool ok = db_->LookupResource(hasher.HashPatient(), patient, dummy); + bool ok = db_.LookupResource(patient, dummy, hasher.HashPatient()); assert(ok); } - else if (db_->LookupResource(hasher.HashPatient(), patient, dummy)) + else if (db_.LookupResource(patient, dummy, hasher.HashPatient())) { assert(dummy == ResourceType_Patient); @@ -567,38 +673,38 @@ // Create the series if needed if (isNewSeries) { - series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series); + series = CreateResource(hasher.HashSeries(), ResourceType_Series); dicomSummary.ExtractSeriesInformation(dicom); - db_->SetMainDicomTags(series, dicom); + SetMainDicomTags(series, dicom); } // Create the study if needed if (isNewStudy) { - study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study); + study = CreateResource(hasher.HashStudy(), ResourceType_Study); dicomSummary.ExtractStudyInformation(dicom); - db_->SetMainDicomTags(study, dicom); + SetMainDicomTags(study, dicom); } // Create the patient if needed if (isNewPatient) { - patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient); + patient = CreateResource(hasher.HashPatient(), ResourceType_Patient); dicomSummary.ExtractPatientInformation(dicom); - db_->SetMainDicomTags(patient, dicom); + SetMainDicomTags(patient, dicom); } // Create the parent-to-child links - db_->AttachChild(series, instance); + db_.AttachChild(series, instance); if (isNewSeries) { - db_->AttachChild(study, series); + db_.AttachChild(study, series); } if (isNewStudy) { - db_->AttachChild(patient, study); + db_.AttachChild(patient, study); } // Sanity checks @@ -611,7 +717,7 @@ for (Attachments::const_iterator it = attachments.begin(); it != attachments.end(); ++it) { - db_->AddAttachment(instance, *it); + db_.AddAttachment(instance, *it); } // Attach the user-specified metadata @@ -621,19 +727,19 @@ switch (it->first.first) { case ResourceType_Patient: - db_->SetMetadata(patient, it->first.second, it->second); + db_.SetMetadata(patient, it->first.second, it->second); break; case ResourceType_Study: - db_->SetMetadata(study, it->first.second, it->second); + db_.SetMetadata(study, it->first.second, it->second); break; case ResourceType_Series: - db_->SetMetadata(series, it->first.second, it->second); + db_.SetMetadata(series, it->first.second, it->second); break; case ResourceType_Instance: - db_->SetMetadata(instance, it->first.second, it->second); + db_.SetMetadata(instance, it->first.second, it->second); instanceMetadata[it->first.second] = it->second; break; @@ -644,36 +750,36 @@ // Attach the auto-computed metadata for the patient/study/series levels std::string now = Toolbox::GetNowIsoString(); - db_->SetMetadata(series, MetadataType_LastUpdate, now); - db_->SetMetadata(study, MetadataType_LastUpdate, now); - db_->SetMetadata(patient, MetadataType_LastUpdate, now); + db_.SetMetadata(series, MetadataType_LastUpdate, now); + db_.SetMetadata(study, MetadataType_LastUpdate, now); + db_.SetMetadata(patient, MetadataType_LastUpdate, now); // Attach the auto-computed metadata for the instance level, // reflecting these additions into the input metadata map - db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); + db_.SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); instanceMetadata[MetadataType_Instance_ReceptionDate] = now; - db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); + db_.SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); instanceMetadata[MetadataType_Instance_RemoteAet] = remoteAet; const DicomValue* value; if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) { - db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); + db_.SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); instanceMetadata[MetadataType_Instance_IndexInSeries] = value->AsString(); } // Check whether the series of this new instance is now completed if (isNewSeries) { - ComputeExpectedNumberOfInstances(*db_, series, dicomSummary); + ComputeExpectedNumberOfInstances(db_, series, dicomSummary); } SeriesStatus seriesStatus = GetSeriesStatus(series); if (seriesStatus == SeriesStatus_Complete) { - db_->LogChange(series, ChangeType_CompletedSeries, ResourceType_Series, hasher.HashSeries()); + LogChange(series, ChangeType_CompletedSeries, ResourceType_Series, hasher.HashSeries()); } // Mark the parent resources of this instance as unstable @@ -687,8 +793,7 @@ } catch (OrthancException& e) { - LOG(ERROR) << "EXCEPTION [" << e.What() << "]" - << " (SQLite status: " << db_->GetErrorMessage() << ")"; + LOG(ERROR) << "EXCEPTION [" << e.What() << "]"; } return StoreStatus_Failure; @@ -701,17 +806,17 @@ target = Json::objectValue; uint64_t cs = currentStorageSize_; - assert(cs == db_->GetTotalCompressedSize()); - uint64_t us = db_->GetTotalUncompressedSize(); + assert(cs == db_.GetTotalCompressedSize()); + uint64_t us = db_.GetTotalUncompressedSize(); target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs); target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us); target["TotalDiskSizeMB"] = boost::lexical_cast<unsigned int>(cs / MEGA_BYTES); target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MEGA_BYTES); - target["CountPatients"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Patient)); - target["CountStudies"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Study)); - target["CountSeries"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Series)); - target["CountInstances"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Instance)); + target["CountPatients"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Patient)); + target["CountStudies"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Study)); + target["CountSeries"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Series)); + target["CountInstances"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Instance)); } @@ -719,34 +824,23 @@ SeriesStatus ServerIndex::GetSeriesStatus(int64_t id) { // Get the expected number of instances in this series (from the metadata) - std::string s = db_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances); - - size_t expected; - try - { - expected = boost::lexical_cast<size_t>(s); - } - catch (boost::bad_lexical_cast&) + int64_t expected; + if (!GetMetadataAsInteger(expected, id, MetadataType_Series_ExpectedNumberOfInstances)) { return SeriesStatus_Unknown; } // Loop over the instances of this series std::list<int64_t> children; - db_->GetChildrenInternalId(children, id); + db_.GetChildrenInternalId(children, id); - std::set<size_t> instances; + std::set<int64_t> instances; for (std::list<int64_t>::const_iterator it = children.begin(); it != children.end(); ++it) { // Get the index of this instance in the series - s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); - size_t index; - try - { - index = boost::lexical_cast<size_t>(s); - } - catch (boost::bad_lexical_cast&) + int64_t index; + if (!GetMetadataAsInteger(index, *it, MetadataType_Instance_IndexInSeries)) { return SeriesStatus_Unknown; } @@ -766,7 +860,7 @@ instances.insert(index); } - if (instances.size() == expected) + if (static_cast<int64_t>(instances.size()) == expected) { return SeriesStatus_Complete; } @@ -782,7 +876,7 @@ int64_t resourceId) { DicomMap tags; - db_->GetMainDicomTags(tags, resourceId); + db_.GetMainDicomTags(tags, resourceId); target["MainDicomTags"] = Json::objectValue; FromDcmtkBridge::ToJson(target["MainDicomTags"], tags); } @@ -798,7 +892,7 @@ // Lookup for the requested resource int64_t id; ResourceType type; - if (!db_->LookupResource(publicId, id, type) || + if (!db_.LookupResource(id, type, publicId) || type != expectedType) { return false; @@ -808,12 +902,12 @@ if (type != ResourceType_Patient) { int64_t parentId; - if (!db_->LookupParent(parentId, id)) + if (!db_.LookupParent(parentId, id)) { throw OrthancException(ErrorCode_InternalError); } - std::string parent = db_->GetPublicId(parentId); + std::string parent = db_.GetPublicId(parentId); switch (type) { @@ -836,7 +930,7 @@ // List the children resources std::list<std::string> children; - db_->GetChildrenPublicId(children, id); + db_.GetChildrenPublicId(children, id); if (type != ResourceType_Instance) { @@ -883,9 +977,9 @@ result["Type"] = "Series"; result["Status"] = EnumerationToString(GetSeriesStatus(id)); - int i; - if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) - result["ExpectedNumberOfInstances"] = i; + int64_t i; + if (GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) + result["ExpectedNumberOfInstances"] = static_cast<int>(i); else result["ExpectedNumberOfInstances"] = Json::nullValue; @@ -897,7 +991,7 @@ result["Type"] = "Instance"; FileInfo attachment; - if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom)) + if (!db_.LookupAttachment(attachment, id, FileContentType_Dicom)) { throw OrthancException(ErrorCode_InternalError); } @@ -905,9 +999,9 @@ result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); result["FileUuid"] = attachment.GetUuid(); - int i; - if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) - result["IndexInSeries"] = i; + int64_t i; + if (GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) + result["IndexInSeries"] = static_cast<int>(i); else result["IndexInSeries"] = Json::nullValue; @@ -924,13 +1018,15 @@ std::string tmp; - tmp = db_->GetMetadata(id, MetadataType_AnonymizedFrom); - if (tmp.size() != 0) + if (db_.LookupMetadata(tmp, id, MetadataType_AnonymizedFrom)) + { result["AnonymizedFrom"] = tmp; + } - tmp = db_->GetMetadata(id, MetadataType_ModifiedFrom); - if (tmp.size() != 0) + if (db_.LookupMetadata(tmp, id, MetadataType_ModifiedFrom)) + { result["ModifiedFrom"] = tmp; + } if (type == ResourceType_Patient || type == ResourceType_Study || @@ -938,9 +1034,10 @@ { result["IsStable"] = !unstableResources_.Contains(id); - tmp = db_->GetMetadata(id, MetadataType_LastUpdate); - if (tmp.size() != 0) + if (db_.LookupMetadata(tmp, id, MetadataType_LastUpdate)) + { result["LastUpdate"] = tmp; + } } return true; @@ -955,12 +1052,12 @@ int64_t id; ResourceType type; - if (!db_->LookupResource(instanceUuid, id, type)) + if (!db_.LookupResource(id, type, instanceUuid)) { throw OrthancException(ErrorCode_UnknownResource); } - if (db_->LookupAttachment(attachment, id, contentType)) + if (db_.LookupAttachment(attachment, id, contentType)) { assert(attachment.GetContentType() == contentType); return true; @@ -976,27 +1073,76 @@ void ServerIndex::GetAllUuids(Json::Value& target, ResourceType resourceType) { - boost::mutex::scoped_lock lock(mutex_); - db_->GetAllPublicIds(target, resourceType); + std::list<std::string> lst; + + { + boost::mutex::scoped_lock lock(mutex_); + db_.GetAllPublicIds(lst, resourceType); + } + + target = Json::arrayValue; + for (std::list<std::string>::const_iterator + it = lst.begin(); it != lst.end(); ++it) + { + target.append(*it); + } } - bool ServerIndex::GetChanges(Json::Value& target, + template <typename T> + static void FormatLog(Json::Value& target, + const std::list<T>& log, + const std::string& name, + bool done, + int64_t since) + { + Json::Value items = Json::arrayValue; + for (typename std::list<T>::const_iterator + it = log.begin(); it != log.end(); ++it) + { + Json::Value item; + it->Format(item); + items.append(item); + } + + target = Json::objectValue; + target[name] = items; + target["Done"] = done; + + int64_t last = (log.empty() ? since : log.back().GetSeq()); + target["Last"] = static_cast<int>(last); + } + + + void ServerIndex::GetChanges(Json::Value& target, int64_t since, unsigned int maxResults) { - boost::mutex::scoped_lock lock(mutex_); - db_->GetChanges(target, since, maxResults); - return true; + std::list<ServerIndexChange> changes; + bool done; + + { + boost::mutex::scoped_lock lock(mutex_); + db_.GetChanges(changes, done, since, maxResults); + } + + FormatLog(target, changes, "Changes", done, since); } - bool ServerIndex::GetLastChange(Json::Value& target) + + void ServerIndex::GetLastChange(Json::Value& target) { - boost::mutex::scoped_lock lock(mutex_); - db_->GetLastChange(target); - return true; + std::list<ServerIndexChange> changes; + + { + boost::mutex::scoped_lock lock(mutex_); + db_.GetLastChange(changes); + } + + FormatLog(target, changes, "Changes", true, 0); } + void ServerIndex::LogExportedResource(const std::string& publicId, const std::string& remoteModality) { @@ -1004,7 +1150,7 @@ int64_t id; ResourceType type; - if (!db_->LookupResource(publicId, id, type)) + if (!db_.LookupResource(id, type, publicId)) { throw OrthancException(ErrorCode_InternalError); } @@ -1022,7 +1168,7 @@ while (!done) { DicomMap map; - db_->GetMainDicomTags(map, currentId); + db_.GetMainDicomTags(map, currentId); switch (currentType) { @@ -1054,36 +1200,51 @@ // the current resource if (!done) { - bool ok = db_->LookupParent(currentId, currentId); + bool ok = db_.LookupParent(currentId, currentId); assert(ok); } } - // No need for a SQLite::Transaction here, as we only insert 1 record - db_->LogExportedResource(type, - publicId, - remoteModality, - patientId, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid); + // No need for a SQLite::ITransaction here, as we only insert 1 record + ExportedResource resource(-1, + type, + publicId, + remoteModality, + Toolbox::GetNowIsoString(), + patientId, + studyInstanceUid, + seriesInstanceUid, + sopInstanceUid); + db_.LogExportedResource(resource); } - bool ServerIndex::GetExportedResources(Json::Value& target, + void ServerIndex::GetExportedResources(Json::Value& target, int64_t since, unsigned int maxResults) { - boost::mutex::scoped_lock lock(mutex_); - db_->GetExportedResources(target, since, maxResults); - return true; + std::list<ExportedResource> exported; + bool done; + + { + boost::mutex::scoped_lock lock(mutex_); + db_.GetExportedResources(exported, done, since, maxResults); + } + + FormatLog(target, exported, "Exports", done, since); } - bool ServerIndex::GetLastExportedResource(Json::Value& target) + + void ServerIndex::GetLastExportedResource(Json::Value& target) { - boost::mutex::scoped_lock lock(mutex_); - db_->GetLastExportedResource(target); - return true; + std::list<ExportedResource> exported; + + { + boost::mutex::scoped_lock lock(mutex_); + db_.GetLastExportedResource(exported); + } + + FormatLog(target, exported, "Exports", true, 0); } @@ -1092,7 +1253,7 @@ if (maximumStorageSize_ != 0) { uint64_t currentSize = currentStorageSize_ - listener_->GetSizeOfFilesToRemove(); - assert(db_->GetTotalCompressedSize() == currentSize); + assert(db_.GetTotalCompressedSize() == currentSize); if (currentSize + instanceSize > maximumStorageSize_) { @@ -1102,7 +1263,7 @@ if (maximumPatients_ != 0) { - uint64_t patientCount = db_->GetResourceCount(ResourceType_Patient); + uint64_t patientCount = db_.GetResourceCount(ResourceType_Patient); if (patientCount > maximumPatients_) { return true; @@ -1125,7 +1286,7 @@ // already stored int64_t patientToAvoid; ResourceType type; - bool hasPatientToAvoid = db_->LookupResource(newPatientId, patientToAvoid, type); + bool hasPatientToAvoid = db_.LookupResource(patientToAvoid, type, newPatientId); if (hasPatientToAvoid && type != ResourceType_Patient) { @@ -1140,8 +1301,8 @@ // If other instances of this patient are already in the store, // we must avoid to recycle them bool ok = hasPatientToAvoid ? - db_->SelectPatientToRecycle(patientToRecycle, patientToAvoid) : - db_->SelectPatientToRecycle(patientToRecycle); + db_.SelectPatientToRecycle(patientToRecycle, patientToAvoid) : + db_.SelectPatientToRecycle(patientToRecycle); if (!ok) { @@ -1149,7 +1310,7 @@ } LOG(INFO) << "Recycling one patient"; - db_->DeleteResource(patientToRecycle); + db_.DeleteResource(patientToRecycle); if (!IsRecyclingNeeded(instanceSize)) { @@ -1209,13 +1370,13 @@ // Lookup for the requested resource int64_t id; ResourceType type; - if (!db_->LookupResource(publicId, id, type) || + if (!db_.LookupResource(id, type, publicId) || type != ResourceType_Patient) { throw OrthancException(ErrorCode_ParameterOutOfRange); } - return db_->IsProtectedPatient(id); + return db_.IsProtectedPatient(id); } @@ -1227,14 +1388,14 @@ // Lookup for the requested resource int64_t id; ResourceType type; - if (!db_->LookupResource(publicId, id, type) || + if (!db_.LookupResource(id, type, publicId) || type != ResourceType_Patient) { throw OrthancException(ErrorCode_ParameterOutOfRange); } - // No need for a SQLite::Transaction here, as we only make 1 write to the DB - db_->SetProtectedPatient(id, isProtected); + // No need for a SQLite::ITransaction here, as we only make 1 write to the DB + db_.SetProtectedPatient(id, isProtected); if (isProtected) LOG(INFO) << "Patient " << publicId << " has been protected"; @@ -1252,7 +1413,7 @@ ResourceType type; int64_t resource; - if (!db_->LookupResource(publicId, resource, type)) + if (!db_.LookupResource(resource, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } @@ -1264,12 +1425,12 @@ } std::list<int64_t> tmp; - db_->GetChildrenInternalId(tmp, resource); + db_.GetChildrenInternalId(tmp, resource); for (std::list<int64_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) { - result.push_back(db_->GetPublicId(*it)); + result.push_back(db_.GetPublicId(*it)); } } @@ -1283,7 +1444,7 @@ ResourceType type; int64_t top; - if (!db_->LookupResource(publicId, top, type)) + if (!db_.LookupResource(top, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } @@ -1306,14 +1467,14 @@ int64_t resource = toExplore.top(); toExplore.pop(); - if (db_->GetResourceType(resource) == ResourceType_Instance) + if (db_.GetResourceType(resource) == ResourceType_Instance) { - result.push_back(db_->GetPublicId(resource)); + result.push_back(db_.GetPublicId(resource)); } else { // Tag all the children of this resource as to be explored - db_->GetChildrenInternalId(tmp, resource); + db_.GetChildrenInternalId(tmp, resource); for (std::list<int64_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) { @@ -1332,12 +1493,12 @@ ResourceType rtype; int64_t id; - if (!db_->LookupResource(publicId, id, rtype)) + if (!db_.LookupResource(id, rtype, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - db_->SetMetadata(id, type, value); + db_.SetMetadata(id, type, value); } @@ -1348,12 +1509,12 @@ ResourceType rtype; int64_t id; - if (!db_->LookupResource(publicId, id, rtype)) + if (!db_.LookupResource(id, rtype, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - db_->DeleteMetadata(id, type); + db_.DeleteMetadata(id, type); } @@ -1365,12 +1526,12 @@ ResourceType rtype; int64_t id; - if (!db_->LookupResource(publicId, id, rtype)) + if (!db_.LookupResource(id, rtype, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - return db_->LookupMetadata(target, id, type); + return db_.LookupMetadata(target, id, type); } @@ -1381,12 +1542,12 @@ ResourceType rtype; int64_t id; - if (!db_->LookupResource(publicId, id, rtype)) + if (!db_.LookupResource(id, rtype, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - db_->ListAvailableMetadata(target, id); + db_.ListAvailableMetadata(target, id); } @@ -1398,13 +1559,13 @@ ResourceType type; int64_t id; - if (!db_->LookupResource(publicId, id, type) || + if (!db_.LookupResource(id, type, publicId) || expectedType != type) { throw OrthancException(ErrorCode_UnknownResource); } - db_->ListAvailableAttachments(target, id); + db_.ListAvailableAttachments(target, id); } @@ -1415,15 +1576,15 @@ ResourceType type; int64_t id; - if (!db_->LookupResource(publicId, id, type)) + if (!db_.LookupResource(id, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } int64_t parentId; - if (db_->LookupParent(parentId, id)) + if (db_.LookupParent(parentId, id)) { - target = db_->GetPublicId(parentId); + target = db_.GetPublicId(parentId); return true; } else @@ -1437,10 +1598,10 @@ { boost::mutex::scoped_lock lock(mutex_); - std::auto_ptr<SQLite::Transaction> transaction(db_->StartTransaction()); + std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction()); transaction->Begin(); - uint64_t seq = db_->IncrementGlobalSequence(sequence); + uint64_t seq = IncrementGlobalSequenceInternal(sequence); transaction->Commit(); return seq; @@ -1452,17 +1613,17 @@ const std::string& publicId) { boost::mutex::scoped_lock lock(mutex_); - std::auto_ptr<SQLite::Transaction> transaction(db_->StartTransaction()); + std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction()); transaction->Begin(); int64_t id; ResourceType type; - if (!db_->LookupResource(publicId, id, type)) + if (!db_.LookupResource(id, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - db_->LogChange(id, changeType, type, publicId); + LogChange(id, changeType, type, publicId); transaction->Commit(); } @@ -1471,13 +1632,13 @@ void ServerIndex::DeleteChanges() { boost::mutex::scoped_lock lock(mutex_); - db_->ClearTable("Changes"); + db_.ClearChanges(); } void ServerIndex::DeleteExportedResources() { boost::mutex::scoped_lock lock(mutex_); - db_->ClearTable("ExportedResources"); + db_.ClearExportedResources(); } @@ -1504,16 +1665,16 @@ int64_t resource = toExplore.top(); toExplore.pop(); - ResourceType thisType = db_->GetResourceType(resource); + ResourceType thisType = db_.GetResourceType(resource); std::list<FileContentType> f; - db_->ListAvailableAttachments(f, resource); + db_.ListAvailableAttachments(f, resource); for (std::list<FileContentType>::const_iterator it = f.begin(); it != f.end(); ++it) { FileInfo attachment; - if (db_->LookupAttachment(attachment, resource, *it)) + if (db_.LookupAttachment(attachment, resource, *it)) { compressedSize += attachment.GetCompressedSize(); uncompressedSize += attachment.GetUncompressedSize(); @@ -1542,7 +1703,7 @@ // Tag all the children of this resource as to be explored std::list<int64_t> tmp; - db_->GetChildrenInternalId(tmp, resource); + db_.GetChildrenInternalId(tmp, resource); for (std::list<int64_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) { @@ -1571,7 +1732,7 @@ ResourceType type; int64_t top; - if (!db_->LookupResource(publicId, top, type)) + if (!db_.LookupResource(top, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } @@ -1620,7 +1781,7 @@ ResourceType type; int64_t top; - if (!db_->LookupResource(publicId, top, type)) + if (!db_.LookupResource(top, type, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } @@ -1657,20 +1818,20 @@ int64_t id = that->unstableResources_.RemoveOldest(payload); // Ensure that the resource is still existing before logging the change - if (that->db_->IsExistingResource(id)) + if (that->db_.IsExistingResource(id)) { switch (payload.GetResourceType()) { case ResourceType_Patient: - that->db_->LogChange(id, ChangeType_StablePatient, ResourceType_Patient, payload.GetPublicId()); + that->LogChange(id, ChangeType_StablePatient, ResourceType_Patient, payload.GetPublicId()); break; case ResourceType_Study: - that->db_->LogChange(id, ChangeType_StableStudy, ResourceType_Study, payload.GetPublicId()); + that->LogChange(id, ChangeType_StableStudy, ResourceType_Study, payload.GetPublicId()); break; case ResourceType_Series: - that->db_->LogChange(id, ChangeType_StableSeries, ResourceType_Series, payload.GetPublicId()); + that->LogChange(id, ChangeType_StableSeries, ResourceType_Series, payload.GetPublicId()); break; default: @@ -1700,7 +1861,7 @@ unstableResources_.AddOrMakeMostRecent(id, payload); //LOG(INFO) << "Unstable resource: " << EnumerationToString(type) << " " << id; - db_->LogChange(id, ChangeType_NewChildInstance, type, publicId); + LogChange(id, ChangeType_NewChildInstance, type, publicId); } @@ -1715,14 +1876,14 @@ boost::mutex::scoped_lock lock(mutex_); std::list<int64_t> id; - db_->LookupIdentifier(id, tag, value); + db_.LookupIdentifier(id, tag, value); for (std::list<int64_t>::const_iterator it = id.begin(); it != id.end(); ++it) { - if (db_->GetResourceType(*it) == type) + if (db_.GetResourceType(*it) == type) { - result.push_back(db_->GetPublicId(*it)); + result.push_back(db_.GetPublicId(*it)); } } } @@ -1737,12 +1898,12 @@ boost::mutex::scoped_lock lock(mutex_); std::list<int64_t> id; - db_->LookupIdentifier(id, tag, value); + db_.LookupIdentifier(id, tag, value); for (std::list<int64_t>::const_iterator it = id.begin(); it != id.end(); ++it) { - result.push_back(db_->GetPublicId(*it)); + result.push_back(db_.GetPublicId(*it)); } } @@ -1755,13 +1916,13 @@ boost::mutex::scoped_lock lock(mutex_); std::list<int64_t> id; - db_->LookupIdentifier(id, value); + db_.LookupIdentifier(id, value); for (std::list<int64_t>::const_iterator it = id.begin(); it != id.end(); ++it) { - result.push_back(std::make_pair(db_->GetResourceType(*it), - db_->GetPublicId(*it))); + result.push_back(std::make_pair(db_.GetResourceType(*it), + db_.GetPublicId(*it))); } } @@ -1775,20 +1936,20 @@ ResourceType resourceType; int64_t resourceId; - if (!db_->LookupResource(publicId, resourceId, resourceType)) + if (!db_.LookupResource(resourceId, resourceType, publicId)) { return StoreStatus_Failure; // Inexistent resource } // Remove possible previous attachment - db_->DeleteAttachment(resourceId, attachment.GetContentType()); + db_.DeleteAttachment(resourceId, attachment.GetContentType()); // Locate the patient of the target resource int64_t patientId = resourceId; for (;;) { int64_t parent; - if (db_->LookupParent(parent, patientId)) + if (db_.LookupParent(parent, patientId)) { // We have not reached the patient level yet patientId = parent; @@ -1801,10 +1962,10 @@ } // Possibly apply the recycling mechanism while preserving this patient - assert(db_->GetResourceType(patientId) == ResourceType_Patient); - Recycle(attachment.GetCompressedSize(), db_->GetPublicId(patientId)); + assert(db_.GetResourceType(patientId) == ResourceType_Patient); + Recycle(attachment.GetCompressedSize(), db_.GetPublicId(patientId)); - db_->AddAttachment(resourceId, attachment); + db_.AddAttachment(resourceId, attachment); t.Commit(attachment.GetCompressedSize()); @@ -1821,12 +1982,12 @@ ResourceType rtype; int64_t id; - if (!db_->LookupResource(publicId, id, rtype)) + if (!db_.LookupResource(id, rtype, publicId)) { throw OrthancException(ErrorCode_UnknownResource); } - db_->DeleteAttachment(id, type); + db_.DeleteAttachment(id, type); t.Commit(0); } @@ -1841,22 +2002,54 @@ ResourceType type; int64_t id; - if (!db_->LookupResource(publicId, id, type)) + if (!db_.LookupResource(id, type, publicId)) { return false; } std::list<MetadataType> metadata; - db_->ListAvailableMetadata(metadata, id); + db_.ListAvailableMetadata(metadata, id); for (std::list<MetadataType>::const_iterator - it = metadata.begin(); it != metadata.end(); it++) + it = metadata.begin(); it != metadata.end(); ++it) { std::string key = EnumerationToString(*it); - std::string value = db_->GetMetadata(id, *it); + + std::string value; + if (!db_.LookupMetadata(value, id, *it)) + { + value.clear(); + } + target[key] = value; } return true; } + + + void ServerIndex::SetGlobalProperty(GlobalProperty property, + const std::string& value) + { + boost::mutex::scoped_lock lock(mutex_); + db_.SetGlobalProperty(property, value); + } + + + std::string ServerIndex::GetGlobalProperty(GlobalProperty property, + const std::string& defaultValue) + { + boost::mutex::scoped_lock lock(mutex_); + + std::string value; + if (db_.LookupGlobalProperty(value, property)) + { + return value; + } + else + { + return defaultValue; + } + } + }
--- a/OrthancServer/ServerIndex.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerIndex.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -40,7 +40,7 @@ #include "../Core/DicomFormat/DicomInstanceHasher.h" #include "ServerEnumerations.h" -#include "DatabaseWrapper.h" +#include "IDatabaseWrapper.h" namespace Orthanc @@ -68,7 +68,7 @@ boost::thread unstableResourcesMonitorThread_; std::auto_ptr<Internals::ServerIndexListener> listener_; - std::auto_ptr<DatabaseWrapper> db_; + IDatabaseWrapper& db_; LeastRecentlyUsedIndex<int64_t, UnstableResourcePayload> unstableResources_; uint64_t currentStorageSize_; @@ -103,9 +103,26 @@ /* in */ int64_t id, /* in */ ResourceType type); + bool GetMetadataAsInteger(int64_t& result, + int64_t id, + MetadataType type); + + void LogChange(int64_t internalId, + ChangeType changeType, + ResourceType resourceType, + const std::string& publicId); + + uint64_t IncrementGlobalSequenceInternal(GlobalProperty property); + + void SetMainDicomTags(int64_t resource, + const DicomMap& tags); + + int64_t CreateResource(const std::string& publicId, + ResourceType type); + public: ServerIndex(ServerContext& context, - const std::string& dbPath); + IDatabaseWrapper& database); ~ServerIndex(); @@ -148,20 +165,20 @@ const std::string& uuid, ResourceType expectedType); - bool GetChanges(Json::Value& target, + void GetChanges(Json::Value& target, int64_t since, unsigned int maxResults); - bool GetLastChange(Json::Value& target); + void GetLastChange(Json::Value& target); void LogExportedResource(const std::string& publicId, const std::string& remoteModality); - bool GetExportedResources(Json::Value& target, + void GetExportedResources(Json::Value& target, int64_t since, unsigned int maxResults); - bool GetLastExportedResource(Json::Value& target); + void GetLastExportedResource(Json::Value& target); bool IsProtectedPatient(const std::string& publicId); @@ -234,5 +251,11 @@ void DeleteAttachment(const std::string& publicId, FileContentType type); + + void SetGlobalProperty(GlobalProperty property, + const std::string& value); + + std::string GetGlobalProperty(GlobalProperty property, + const std::string& defaultValue); }; }
--- a/OrthancServer/ServerIndexChange.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerIndexChange.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -33,28 +33,52 @@ #pragma once #include "ServerEnumerations.h" +#include "../Core/Toolbox.h" #include <string> +#include <json/value.h> namespace Orthanc { struct ServerIndexChange { private: + int64_t seq_; ChangeType changeType_; ResourceType resourceType_; std::string publicId_; + std::string date_; public: ServerIndexChange(ChangeType changeType, ResourceType resourceType, - const std::string& publicId) : + const std::string& publicId) : + seq_(-1), changeType_(changeType), resourceType_(resourceType), - publicId_(publicId) + publicId_(publicId), + date_(Toolbox::GetNowIsoString()) { } + ServerIndexChange(int64_t seq, + ChangeType changeType, + ResourceType resourceType, + const std::string& publicId, + const std::string& date) : + seq_(seq), + changeType_(changeType), + resourceType_(resourceType), + publicId_(publicId), + date_(date) + { + } + + int64_t GetSeq() const + { + return seq_; + } + ChangeType GetChangeType() const { return changeType_; @@ -69,5 +93,21 @@ { return publicId_; } + + const std::string& GetDate() const + { + return date_; + } + + void Format(Json::Value& item) const + { + item = Json::objectValue; + item["Seq"] = static_cast<int>(seq_); + item["ChangeType"] = EnumerationToString(changeType_); + item["ResourceType"] = EnumerationToString(resourceType_); + item["ID"] = publicId_; + item["Path"] = GetBasePath(resourceType_, publicId_); + item["Date"] = date_; + } }; }
--- a/OrthancServer/ServerToolbox.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerToolbox.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ServerToolbox.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ServerToolbox.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ToDcmtkBridge.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ToDcmtkBridge.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/ToDcmtkBridge.h Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/ToDcmtkBridge.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/OrthancServer/main.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/OrthancServer/main.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -38,7 +38,6 @@ #include <boost/algorithm/string/predicate.hpp> #include "../Core/Uuid.h" -#include "../Core/FileStorage/FilesystemStorage.h" #include "../Core/HttpServer/EmbeddedResourceHttpHandler.h" #include "../Core/HttpServer/FilesystemHttpHandler.h" #include "../Core/Lua/LuaFunctionCall.h" @@ -73,7 +72,8 @@ virtual void Handle(const std::string& dicomFile, const DicomMap& dicomSummary, const Json::Value& dicomJson, - const std::string& remoteAet) + const std::string& remoteAet, + const std::string& calledAet) { if (dicomFile.size() > 0) { @@ -82,6 +82,7 @@ toStore.SetSummary(dicomSummary); toStore.SetJson(dicomJson); toStore.SetRemoteAet(remoteAet); + toStore.SetCalledAet(calledAet); std::string id; server_.Store(id, toStore); @@ -339,7 +340,7 @@ { std::cout << path << " " << ORTHANC_VERSION << std::endl - << "Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege (Belgium) " << std::endl + << "Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics Department, University Hospital of Liege (Belgium)" << std::endl << "Licensing GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>, with OpenSSL exception." << std::endl << "This is free software: you are free to change and redistribute it." << std::endl << "There is NO WARRANTY, to the extent permitted by law." << std::endl @@ -382,64 +383,17 @@ -class FilesystemStorageWithoutDicom : public IStorageArea +static bool StartOrthanc(int argc, char *argv[]) { -private: - FilesystemStorage storage_; - -public: - FilesystemStorageWithoutDicom(const std::string& path) : storage_(path) - { - } - - virtual void Create(const std::string& uuid, - const void* content, - size_t size, - FileContentType type) - { - if (type != FileContentType_Dicom) - { - storage_.Create(uuid, content, size, type); - } - } + std::auto_ptr<IDatabaseWrapper> database; + database.reset(Configuration::CreateDatabaseWrapper()); - virtual void Read(std::string& content, - const std::string& uuid, - FileContentType type) const - { - if (type != FileContentType_Dicom) - { - storage_.Read(content, uuid, type); - } - else - { - throw OrthancException(ErrorCode_UnknownResource); - } - } - - virtual void Remove(const std::string& uuid, - FileContentType type) - { - if (type != FileContentType_Dicom) - { - storage_.Remove(uuid, type); - } - } -}; - - -static bool StartOrthanc() -{ - std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); - boost::filesystem::path indexDirectory = Configuration::InterpretStringParameterAsPath( - Configuration::GetGlobalStringParameter("IndexDirectory", storageDirectoryStr)); // "storage" must be declared BEFORE "ServerContext context", to // avoid mess in the invokation order of the destructors. std::auto_ptr<IStorageArea> storage; - ServerContext context(indexDirectory); - LOG(WARNING) << "Index directory: " << indexDirectory; + ServerContext context(*database); context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false)); context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true)); @@ -513,13 +467,14 @@ #if ENABLE_PLUGINS == 1 OrthancPlugins orthancPlugins(context); + orthancPlugins.SetCommandLineArguments(argc, argv); orthancPlugins.SetOrthancRestApi(restApi); PluginsManager pluginsManager; pluginsManager.RegisterServiceProvider(orthancPlugins); LoadPlugins(pluginsManager); httpServer.RegisterHandler(orthancPlugins); - context.SetOrthancPlugins(orthancPlugins); + context.SetOrthancPlugins(pluginsManager, orthancPlugins); #endif httpServer.RegisterHandler(staticResources); @@ -536,17 +491,7 @@ else #endif { - boost::filesystem::path storageDirectory = Configuration::InterpretStringParameterAsPath(storageDirectoryStr); - LOG(WARNING) << "Storage directory: " << storageDirectory; - if (Configuration::GetGlobalBoolParameter("StoreDicom", true)) - { - storage.reset(new FilesystemStorage(storageDirectory.string())); - } - else - { - LOG(WARNING) << "The DICOM files will not be stored, Orthanc running in index-only mode"; - storage.reset(new FilesystemStorageWithoutDicom(storageDirectory.string())); - } + storage.reset(Configuration::CreateStorageArea()); } context.SetStorageArea(*storage); @@ -586,6 +531,7 @@ LOG(WARNING) << "Orthanc is stopping"; #if ENABLE_PLUGINS == 1 + context.ResetOrthancPlugins(); orthancPlugins.Stop(); LOG(WARNING) << " Plugins have stopped"; #endif @@ -683,7 +629,7 @@ { OrthancInitialize(configurationFile); - bool reset = StartOrthanc(); + bool reset = StartOrthanc(argc, argv); if (reset) { OrthancFinalize(); @@ -694,14 +640,24 @@ } } } - catch (OrthancException& e) + catch (const OrthancException& e) { LOG(ERROR) << "Uncaught exception, stopping now: [" << e.What() << "]"; status = -1; } + catch (const std::exception& e) + { + LOG(ERROR) << "Uncaught exception, stopping now: [" << e.what() << "]"; + status = -1; + } + catch (const std::string& s) + { + LOG(ERROR) << "Uncaught exception, stopping now: [" << s << "]"; + status = -1; + } catch (...) { - LOG(ERROR) << "Native exception, stopping now"; + LOG(ERROR) << "Native exception, stopping now. Check your plugins, if any."; status = -1; }
--- a/Plugins/Engine/IPluginServiceProvider.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/IPluginServiceProvider.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -32,7 +32,7 @@ #pragma once -#include "../OrthancCPlugin/OrthancCPlugin.h" +#include "../Include/OrthancCPlugin.h" #include <boost/noncopyable.hpp>
--- a/Plugins/Engine/OrthancPlugins.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -172,10 +172,13 @@ struct OrthancPlugins::PImpl { + typedef std::pair<std::string, _OrthancPluginProperty> Property; + typedef std::pair<boost::regex*, OrthancPluginRestCallback> RestCallback; typedef std::list<RestCallback> RestCallbacks; typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; + typedef std::map<Property, std::string> Properties; ServerContext& context_; RestCallbacks restCallbacks_; @@ -184,16 +187,21 @@ OnChangeCallbacks onChangeCallbacks_; bool hasStorageArea_; _OrthancPluginRegisterStorageArea storageArea_; - boost::mutex callbackMutex_; + boost::recursive_mutex callbackMutex_; SharedMessageQueue pendingChanges_; boost::thread changeThread_; bool done_; + Properties properties_; + int argc_; + char** argv_; PImpl(ServerContext& context) : context_(context), restApi_(NULL), hasStorageArea_(false), - done_(false) + done_(false), + argc_(1), + argv_(NULL) { memset(&storageArea_, 0, sizeof(storageArea_)); } @@ -207,7 +215,7 @@ if (obj.get() != NULL) { - boost::mutex::scoped_lock lock(that->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(that->callbackMutex_); PendingChange& change = *dynamic_cast<PendingChange*>(obj.get()); change.Submit(that->onChangeCallbacks_); } @@ -389,7 +397,7 @@ int32_t error; { - boost::mutex::scoped_lock lock(pimpl_->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_); error = callback(reinterpret_cast<OrthancPluginRestOutput*>(&output), flatUri.c_str(), &request); @@ -415,7 +423,7 @@ void OrthancPlugins::SignalStoredInstance(DicomInstanceToStore& instance, const std::string& instanceId) { - boost::mutex::scoped_lock lock(pimpl_->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_); for (PImpl::OnStoredCallbacks::const_iterator callback = pimpl_->onStoredCallbacks_.begin(); @@ -643,7 +651,8 @@ } - void OrthancPlugins::RestApiGet(const void* parameters) + void OrthancPlugins::RestApiGet(const void* parameters, + bool afterPlugins) { const _OrthancPluginRestApiGet& p = *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); @@ -658,12 +667,25 @@ StringHttpOutput stream; HttpOutput http(stream, false /* no keep alive */); - LOG(INFO) << "Plugin making REST GET call on URI " << p.uri; + LOG(INFO) << "Plugin making REST GET call on URI " << p.uri + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; + std::string result; - if (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, HttpMethod_Get, uri, headers, getArguments, body); + } + + if (!ok) { - std::string result; + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body)); + } + + if (ok) + { stream.GetOutput(result); CopyToMemoryBuffer(*p.target, result); } @@ -674,7 +696,9 @@ } - void OrthancPlugins::RestApiPostPut(bool isPost, const void* parameters) + void OrthancPlugins::RestApiPostPut(bool isPost, + const void* parameters, + bool afterPlugins) { const _OrthancPluginRestApiPostPut& p = *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); @@ -692,12 +716,25 @@ HttpOutput http(stream, false /* no keep alive */); HttpMethod method = (isPost ? HttpMethod_Post : HttpMethod_Put); - LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri; + LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; + std::string result; - if (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, method, uri, headers, getArguments, body); + } + + if (!ok) { - std::string result; + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body)); + } + + if (ok) + { stream.GetOutput(result); CopyToMemoryBuffer(*p.target, result); } @@ -708,7 +745,8 @@ } - void OrthancPlugins::RestApiDelete(const void* parameters) + void OrthancPlugins::RestApiDelete(const void* parameters, + bool afterPlugins) { // The "parameters" point to the URI UriComponents uri; @@ -722,10 +760,23 @@ HttpOutput http(stream, false /* no keep alive */); LOG(INFO) << "Plugin making REST DELETE call on URI " - << reinterpret_cast<const char*>(parameters); + << reinterpret_cast<const char*>(parameters) + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; - if (pimpl_->restApi_ == NULL || - !pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, HttpMethod_Delete, uri, headers, getArguments, body); + } + + if (!ok) + { + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body)); + } + + if (!ok) { throw OrthancException(ErrorCode_BadRequest); } @@ -955,19 +1006,35 @@ return true; case _OrthancPluginService_RestApiGet: - RestApiGet(parameters); + RestApiGet(parameters, false); + return true; + + case _OrthancPluginService_RestApiGetAfterPlugins: + RestApiGet(parameters, true); return true; case _OrthancPluginService_RestApiPost: - RestApiPostPut(true, parameters); + RestApiPostPut(true, parameters, false); + return true; + + case _OrthancPluginService_RestApiPostAfterPlugins: + RestApiPostPut(true, parameters, true); return true; case _OrthancPluginService_RestApiDelete: - RestApiDelete(parameters); + RestApiDelete(parameters, false); + return true; + + case _OrthancPluginService_RestApiDeleteAfterPlugins: + RestApiDelete(parameters, true); return true; case _OrthancPluginService_RestApiPut: - RestApiPostPut(false, parameters); + RestApiPostPut(false, parameters, false); + return true; + + case _OrthancPluginService_RestApiPutAfterPlugins: + RestApiPostPut(false, parameters, true); return true; case _OrthancPluginService_Redirect: @@ -1022,6 +1089,63 @@ return true; } + case _OrthancPluginService_SetPluginProperty: + { + const _OrthancPluginSetPluginProperty& p = + *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters); + pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value; + return true; + } + + case _OrthancPluginService_SetGlobalProperty: + { + const _OrthancPluginGlobalProperty& p = + *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); + if (p.property < 1024) + { + return false; + } + else + { + pimpl_->context_.GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); + return true; + } + } + + case _OrthancPluginService_GetGlobalProperty: + { + const _OrthancPluginGlobalProperty& p = + *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); + std::string result = pimpl_->context_.GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); + *(p.result) = CopyString(result); + return true; + } + + case _OrthancPluginService_GetCommandLineArgumentsCount: + { + const _OrthancPluginReturnSingleValue& p = + *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters); + *(p.resultUint32) = pimpl_->argc_ - 1; + return true; + } + + case _OrthancPluginService_GetCommandLineArgument: + { + const _OrthancPluginGlobalProperty& p = + *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); + + if (p.property + 1 > pimpl_->argc_) + { + return false; + } + else + { + std::string arg = std::string(pimpl_->argv_[p.property + 1]); + *(p.result) = CopyString(arg); + return true; + } + } + default: return false; } @@ -1051,7 +1175,7 @@ { if (buffer != NULL) { - params_.free_(buffer); + params_.free(buffer); } } @@ -1080,7 +1204,7 @@ size_t size, FileContentType type) { - if (params_.create_(uuid.c_str(), content, size, Convert(type)) != 0) + if (params_.create(uuid.c_str(), content, size, Convert(type)) != 0) { throw OrthancException(ErrorCode_Plugin); } @@ -1088,12 +1212,12 @@ virtual void Read(std::string& content, const std::string& uuid, - FileContentType type) const + FileContentType type) { void* buffer = NULL; int64_t size = 0; - if (params_.read_(&buffer, &size, uuid.c_str(), Convert(type)) != 0) + if (params_.read(&buffer, &size, uuid.c_str(), Convert(type)) != 0) { throw OrthancException(ErrorCode_Plugin); } @@ -1119,7 +1243,7 @@ virtual void Remove(const std::string& uuid, FileContentType type) { - if (params_.remove_(uuid.c_str(), Convert(type)) != 0) + if (params_.remove(uuid.c_str(), Convert(type)) != 0) { throw OrthancException(ErrorCode_Plugin); } @@ -1135,6 +1259,36 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls); } - return new PluginStorageArea(pimpl_->storageArea_);; + return new PluginStorageArea(pimpl_->storageArea_); + } + + + + const char* OrthancPlugins::GetProperty(const char* plugin, + _OrthancPluginProperty property) const + { + PImpl::Property p = std::make_pair(plugin, property); + PImpl::Properties::const_iterator it = pimpl_->properties_.find(p); + + if (it == pimpl_->properties_.end()) + { + return NULL; + } + else + { + return it->second.c_str(); + } + } + + + void OrthancPlugins::SetCommandLineArguments(int argc, char* argv[]) + { + if (argc < 1 || argv == NULL) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + pimpl_->argc_ = argc; + pimpl_->argv_ = argv; } }
--- a/Plugins/Engine/OrthancPlugins.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -36,7 +36,7 @@ #include "../../Core/HttpServer/HttpHandler.h" #include "../../OrthancServer/ServerContext.h" #include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h" -#include "../OrthancCPlugin/OrthancCPlugin.h" +#include "../Include/OrthancCPlugin.h" #include <list> #include <boost/shared_ptr.hpp> @@ -63,11 +63,15 @@ void GetDicomForInstance(const void* parameters); - void RestApiGet(const void* parameters); + void RestApiGet(const void* parameters, + bool afterPlugins); - void RestApiPostPut(bool isPost, const void* parameters); + void RestApiPostPut(bool isPost, + const void* parameters, + bool afterPlugins); - void RestApiDelete(const void* parameters); + void RestApiDelete(const void* parameters, + bool afterPlugins); void LookupResource(_OrthancPluginService service, const void* parameters); @@ -109,5 +113,10 @@ IStorageArea* GetStorageArea(); void Stop(); + + const char* GetProperty(const char* plugin, + _OrthancPluginProperty property) const; + + void SetCommandLineArguments(int argc, char* argv[]); }; }
--- a/Plugins/Engine/PluginsManager.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/PluginsManager.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -205,10 +205,10 @@ { if (it->second != NULL) { - LOG(WARNING) << "Unregistering plugin '" << CallGetName(*it->second) - << "' (version " << CallGetVersion(*it->second) << ")"; + LOG(WARNING) << "Unregistering plugin '" << it->first + << "' (version " << it->second->GetVersion() << ")"; - CallFinalize(*(it->second)); + CallFinalize(it->second->GetLibrary()); delete it->second; } } @@ -226,26 +226,39 @@ void PluginsManager::RegisterPlugin(const std::string& path) { - std::auto_ptr<SharedLibrary> plugin(new SharedLibrary(path)); + if (!boost::filesystem::exists(path)) + { + LOG(ERROR) << "Inexistent path to plugins: " << path; + return; + } - if (!IsOrthancPlugin(*plugin)) + if (boost::filesystem::is_directory(path)) { - LOG(ERROR) << "Plugin " << plugin->GetPath() + ScanFolderForPlugins(path, false); + return; + } + + std::auto_ptr<Plugin> plugin(new Plugin(path)); + + if (!IsOrthancPlugin(plugin->GetLibrary())) + { + LOG(ERROR) << "Plugin " << plugin->GetLibrary().GetPath() << " does not declare the proper entry functions"; throw OrthancException(ErrorCode_SharedLibrary); } - std::string name(CallGetName(*plugin)); + std::string name(CallGetName(plugin->GetLibrary())); if (plugins_.find(name) != plugins_.end()) { LOG(ERROR) << "Plugin '" << name << "' already registered"; throw OrthancException(ErrorCode_SharedLibrary); } + plugin->SetVersion(CallGetVersion(plugin->GetLibrary())); LOG(WARNING) << "Registering plugin '" << name - << "' (version " << CallGetVersion(*plugin) << ")"; + << "' (version " << plugin->GetVersion() << ")"; - CallInitialize(*plugin, context_); + CallInitialize(plugin->GetLibrary(), context_); plugins_[name] = plugin.release(); } @@ -283,19 +296,46 @@ { LOG(INFO) << "Found a shared library: " << it->path(); - try + SharedLibrary plugin(path); + if (IsOrthancPlugin(plugin)) { - SharedLibrary plugin(path); - if (IsOrthancPlugin(plugin)) - { - RegisterPlugin(path); - } - } - catch (OrthancException&) - { + RegisterPlugin(path); } } } } } + + + void PluginsManager::ListPlugins(std::list<std::string>& result) const + { + result.clear(); + + for (Plugins::const_iterator it = plugins_.begin(); + it != plugins_.end(); ++it) + { + result.push_back(it->first); + } + } + + + bool PluginsManager::HasPlugin(const std::string& name) const + { + return plugins_.find(name) != plugins_.end(); + } + + + const std::string& PluginsManager::GetPluginVersion(const std::string& name) const + { + Plugins::const_iterator it = plugins_.find(name); + if (it == plugins_.end()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + return it->second->GetVersion(); + } + } + }
--- a/Plugins/Engine/PluginsManager.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/PluginsManager.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -43,7 +43,34 @@ class PluginsManager : boost::noncopyable { private: - typedef std::map<std::string, SharedLibrary*> Plugins; + class Plugin + { + private: + SharedLibrary library_; + std::string version_; + + public: + Plugin(const std::string& path) : library_(path) + { + } + + SharedLibrary& GetLibrary() + { + return library_; + } + + void SetVersion(const std::string& version) + { + version_ = version; + } + + const std::string& GetVersion() const + { + return version_; + } + }; + + typedef std::map<std::string, Plugin*> Plugins; OrthancPluginContext context_; Plugins plugins_; @@ -67,5 +94,11 @@ { serviceProviders_.push_back(&provider); } + + void ListPlugins(std::list<std::string>& result) const; + + bool HasPlugin(const std::string& name) const; + + const std::string& GetPluginVersion(const std::string& name) const; }; }
--- a/Plugins/Engine/SharedLibrary.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/SharedLibrary.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Plugins/Engine/SharedLibrary.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Engine/SharedLibrary.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Include/OrthancCPlugin.h Wed Feb 11 10:32:14 2015 +0100 @@ -0,0 +1,2087 @@ +/** + * \mainpage + * + * This C/C++ SDK allows external developers to create plugins that + * can be loaded into Orthanc to extend its functionality. Each + * Orthanc plugin must expose 4 public functions with the following + * signatures: + * + * -# <tt>int32_t OrthancPluginInitialize(const OrthancPluginContext* context)</tt>: + * This function is invoked by Orthanc when it loads the plugin on startup. + * The plugin must: + * - Check its compatibility with the Orthanc version using + * ::OrthancPluginCheckVersion(). + * - Store the context pointer so that it can use the plugin + * services of Orthanc. + * - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback(). + * - Register all its callbacks for received instances using ::OrthancPluginRegisterOnStoredInstanceCallback(). + * - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea(). + * -# <tt>void OrthancPluginFinalize()</tt>: + * This function is invoked by Orthanc during its shutdown. The plugin + * must free all its memory. + * -# <tt>const char* OrthancPluginGetName()</tt>: + * The plugin must return a short string to identify itself. + * -# <tt>const char* OrthancPluginGetVersion()</tt>: + * The plugin must return a string containing its version number. + * + * The name and the version of a plugin is only used to prevent it + * from being loaded twice. + * + * The various callbacks are guaranteed to be executed in mutual + * exclusion since Orthanc 0.8.5. + **/ + + + +/** + * @defgroup CInterface C Interface + * @brief The C interface to create Orthanc plugins. + * + * These functions must be used to create C plugins for Orthanc. + **/ + + + +/** + * 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://www.gnu.org/licenses/>. + **/ + + + +#pragma once + + +#include <stdio.h> +#include <string.h> + +#ifdef WIN32 +#define ORTHANC_PLUGINS_API __declspec(dllexport) +#else +#define ORTHANC_PLUGINS_API +#endif + +#define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 0 +#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 8 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 6 + + + +/******************************************************************** + ** Check that function inlining is properly supported. The use of + ** inlining is required, to avoid the duplication of object code + ** between two compilation modules that would use the Orthanc Plugin + ** API. + ********************************************************************/ + +/* If the auto-detection of the "inline" keyword below does not work + automatically and that your compiler is known to properly support + inlining, uncomment the following #define and adapt the definition + of "static inline". */ + +/* #define ORTHANC_PLUGIN_INLINE static inline */ + +#ifndef ORTHANC_PLUGIN_INLINE +# if __STDC_VERSION__ >= 199901L +/* This is C99 or above: http://predef.sourceforge.net/prestd.html */ +# define ORTHANC_PLUGIN_INLINE static inline +# elif defined(__cplusplus) +/* This is C++ */ +# define ORTHANC_PLUGIN_INLINE static inline +# elif defined(__GNUC__) +/* This is GCC running in C89 mode */ +# define ORTHANC_PLUGIN_INLINE static __inline +# elif defined(_MSC_VER) +/* This is Visual Studio running in C89 mode */ +# define ORTHANC_PLUGIN_INLINE static __inline +# else +# error Your compiler is not known to support the "inline" keyword +# endif +#endif + + + +/******************************************************************** + ** Inclusion of standard libraries. + ********************************************************************/ + +#ifdef _MSC_VER +#include "../../Resources/ThirdParty/VisualStudio/stdint.h" +#else +#include <stdint.h> +#endif + +#include <stdlib.h> + + + +/******************************************************************** + ** Definition of the Orthanc Plugin API. + ********************************************************************/ + +/** @{ */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Forward declaration of one of the mandatory functions for Orthanc + * plugins. + **/ + ORTHANC_PLUGINS_API const char* OrthancPluginGetName(); + + + /** + * The various HTTP methods for a REST call. + **/ + typedef enum + { + OrthancPluginHttpMethod_Get = 1, /*!< GET request */ + OrthancPluginHttpMethod_Post = 2, /*!< POST request */ + OrthancPluginHttpMethod_Put = 3, /*!< PUT request */ + OrthancPluginHttpMethod_Delete = 4 /*!< DELETE request */ + } OrthancPluginHttpMethod; + + + /** + * @brief The parameters of a REST request. + **/ + typedef struct + { + /** + * @brief The HTTP method. + **/ + OrthancPluginHttpMethod method; + + /** + * @brief The number of groups of the regular expression. + **/ + uint32_t groupsCount; + + /** + * @brief The matched values for the groups of the regular expression. + **/ + const char* const* groups; + + /** + * @brief For a GET request, the number of GET parameters. + **/ + uint32_t getCount; + + /** + * @brief For a GET request, the keys of the GET parameters. + **/ + const char* const* getKeys; + + /** + * @brief For a GET request, the values of the GET parameters. + **/ + const char* const* getValues; + + /** + * @brief For a PUT or POST request, the content of the body. + **/ + const char* body; + + /** + * @brief For a PUT or POST request, the number of bytes of the body. + **/ + uint32_t bodySize; + + + /* -------------------------------------------------- + New in version 0.8.1 + -------------------------------------------------- */ + + /** + * @brief The number of HTTP headers. + **/ + uint32_t headersCount; + + /** + * @brief The keys of the HTTP headers (always converted to low-case). + **/ + const char* const* headersKeys; + + /** + * @brief The values of the HTTP headers. + **/ + const char* const* headersValues; + + } OrthancPluginHttpRequest; + + + typedef enum + { + /* Generic services */ + _OrthancPluginService_LogInfo = 1, + _OrthancPluginService_LogWarning = 2, + _OrthancPluginService_LogError = 3, + _OrthancPluginService_GetOrthancPath = 4, + _OrthancPluginService_GetOrthancDirectory = 5, + _OrthancPluginService_GetConfigurationPath = 6, + _OrthancPluginService_SetPluginProperty = 7, + _OrthancPluginService_GetGlobalProperty = 8, + _OrthancPluginService_SetGlobalProperty = 9, + _OrthancPluginService_GetCommandLineArgumentsCount = 10, + _OrthancPluginService_GetCommandLineArgument = 11, + + /* Registration of callbacks */ + _OrthancPluginService_RegisterRestCallback = 1000, + _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001, + _OrthancPluginService_RegisterStorageArea = 1002, + _OrthancPluginService_RegisterOnChangeCallback = 1003, + + /* Sending answers to REST calls */ + _OrthancPluginService_AnswerBuffer = 2000, + _OrthancPluginService_CompressAndAnswerPngImage = 2001, + _OrthancPluginService_Redirect = 2002, + _OrthancPluginService_SendHttpStatusCode = 2003, + _OrthancPluginService_SendUnauthorized = 2004, + _OrthancPluginService_SendMethodNotAllowed = 2005, + _OrthancPluginService_SetCookie = 2006, + _OrthancPluginService_SetHttpHeader = 2007, + + /* Access to the Orthanc database and API */ + _OrthancPluginService_GetDicomForInstance = 3000, + _OrthancPluginService_RestApiGet = 3001, + _OrthancPluginService_RestApiPost = 3002, + _OrthancPluginService_RestApiDelete = 3003, + _OrthancPluginService_RestApiPut = 3004, + _OrthancPluginService_LookupPatient = 3005, + _OrthancPluginService_LookupStudy = 3006, + _OrthancPluginService_LookupSeries = 3007, + _OrthancPluginService_LookupInstance = 3008, + _OrthancPluginService_LookupStudyWithAccessionNumber = 3009, + _OrthancPluginService_RestApiGetAfterPlugins = 3010, + _OrthancPluginService_RestApiPostAfterPlugins = 3011, + _OrthancPluginService_RestApiDeleteAfterPlugins = 3012, + _OrthancPluginService_RestApiPutAfterPlugins = 3013, + + /* Access to DICOM instances */ + _OrthancPluginService_GetInstanceRemoteAet = 4000, + _OrthancPluginService_GetInstanceSize = 4001, + _OrthancPluginService_GetInstanceData = 4002, + _OrthancPluginService_GetInstanceJson = 4003, + _OrthancPluginService_GetInstanceSimplifiedJson = 4004, + _OrthancPluginService_HasInstanceMetadata = 4005, + _OrthancPluginService_GetInstanceMetadata = 4006 + } _OrthancPluginService; + + + typedef enum + { + _OrthancPluginProperty_Description = 1, + _OrthancPluginProperty_RootUri = 2, + _OrthancPluginProperty_OrthancExplorer = 3 + } _OrthancPluginProperty; + + + + /** + * The memory layout of the pixels of an image. + **/ + typedef enum + { + /** + * @brief Graylevel 8bpp image. + * + * The image is graylevel. Each pixel is unsigned and stored in + * one byte. + **/ + OrthancPluginPixelFormat_Grayscale8 = 1, + + /** + * @brief Graylevel, unsigned 16bpp image. + * + * The image is graylevel. Each pixel is unsigned and stored in + * two bytes. + **/ + OrthancPluginPixelFormat_Grayscale16 = 2, + + /** + * @brief Graylevel, signed 16bpp image. + * + * The image is graylevel. Each pixel is signed and stored in two + * bytes. + **/ + OrthancPluginPixelFormat_SignedGrayscale16 = 3, + + /** + * @brief Color image in RGB24 format. + * + * This format describes a color image. The pixels are stored in 3 + * consecutive bytes. The memory layout is RGB. + **/ + OrthancPluginPixelFormat_RGB24 = 4, + + /** + * @brief Color image in RGBA32 format. + * + * This format describes a color image. The pixels are stored in 4 + * consecutive bytes. The memory layout is RGBA. + **/ + OrthancPluginPixelFormat_RGBA32 = 5 + } OrthancPluginPixelFormat; + + + + /** + * The content types that are supported by Orthanc plugins. + **/ + typedef enum + { + OrthancPluginContentType_Unknown = 0, /*!< Unknown content type */ + OrthancPluginContentType_Dicom = 1, /*!< DICOM */ + OrthancPluginContentType_DicomAsJson = 2 /*!< JSON summary of a DICOM file */ + } OrthancPluginContentType; + + + + /** + * The supported type of DICOM resources. + **/ + typedef enum + { + OrthancPluginResourceType_Patient = 0, /*!< Patient */ + OrthancPluginResourceType_Study = 1, /*!< Study */ + OrthancPluginResourceType_Series = 2, /*!< Series */ + OrthancPluginResourceType_Instance = 3 /*!< Instance */ + } OrthancPluginResourceType; + + + + /** + * The supported type of changes that can happen to DICOM resources. + **/ + typedef enum + { + OrthancPluginChangeType_CompletedSeries = 0, /*!< Series is now complete */ + OrthancPluginChangeType_Deleted = 1, /*!< Deleted resource */ + OrthancPluginChangeType_NewChildInstance = 2, /*!< A new instance was added to this resource */ + OrthancPluginChangeType_NewInstance = 3, /*!< New instance received */ + OrthancPluginChangeType_NewPatient = 4, /*!< New patient created */ + OrthancPluginChangeType_NewSeries = 5, /*!< New series created */ + OrthancPluginChangeType_NewStudy = 6, /*!< New study created */ + OrthancPluginChangeType_StablePatient = 7, /*!< Timeout: No new instance in this patient */ + OrthancPluginChangeType_StableSeries = 8, /*!< Timeout: No new instance in this series */ + OrthancPluginChangeType_StableStudy = 9 /*!< Timeout: No new instance in this study */ + } OrthancPluginChangeType; + + + + /** + * @brief A memory buffer allocated by the core system of Orthanc. + * + * A memory buffer allocated by the core system of Orthanc. When the + * content of the buffer is not useful anymore, it must be free by a + * call to ::OrthancPluginFreeMemoryBuffer(). + **/ + typedef struct + { + /** + * @brief The content of the buffer. + **/ + void* data; + + /** + * @brief The number of bytes in the buffer. + **/ + uint32_t size; + } OrthancPluginMemoryBuffer; + + + + + /** + * @brief Opaque structure that represents the HTTP connection to the client application. + **/ + typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput; + + + + /** + * @brief Opaque structure that represents a DICOM instance received by Orthanc. + **/ + typedef struct _OrthancPluginDicomInstance_t OrthancPluginDicomInstance; + + + + /** + * @brief Signature of a callback function that answers to a REST request. + **/ + typedef int32_t (*OrthancPluginRestCallback) ( + OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request); + + + + /** + * @brief Signature of a callback function that is triggered when Orthanc receives a DICOM instance. + **/ + typedef int32_t (*OrthancPluginOnStoredInstanceCallback) ( + OrthancPluginDicomInstance* instance, + const char* instanceId); + + + + /** + * @brief Signature of a callback function that is triggered when a change happens to some DICOM resource. + **/ + typedef int32_t (*OrthancPluginOnChangeCallback) ( + OrthancPluginChangeType changeType, + OrthancPluginResourceType resourceType, + const char* resourceId); + + + + /** + * @brief Signature of a function to free dynamic memory. + **/ + typedef void (*OrthancPluginFree) (void* buffer); + + + + /** + * @brief Callback for writing to the storage area. + * + * Signature of a callback function that is triggered when Orthanc writes a file to the storage area. + * + * @param uuid The UUID of the file. + * @param content The content of the file. + * @param size The size of the file. + * @param type The content type corresponding to this file. + * @return 0 if success, other value if error. + **/ + typedef int32_t (*OrthancPluginStorageCreate) ( + const char* uuid, + const void* content, + int64_t size, + OrthancPluginContentType type); + + + + /** + * @brief Callback for reading from the storage area. + * + * Signature of a callback function that is triggered when Orthanc reads a file from the storage area. + * + * @param content The content of the file (output). + * @param size The size of the file (output). + * @param uuid The UUID of the file of interest. + * @param type The content type corresponding to this file. + * @return 0 if success, other value if error. + **/ + typedef int32_t (*OrthancPluginStorageRead) ( + void** content, + int64_t* size, + const char* uuid, + OrthancPluginContentType type); + + + + /** + * @brief Callback for removing a file from the storage area. + * + * Signature of a callback function that is triggered when Orthanc deletes a file from the storage area. + * + * @param uuid The UUID of the file to be removed. + * @param type The content type corresponding to this file. + * @return 0 if success, other value if error. + **/ + typedef int32_t (*OrthancPluginStorageRemove) ( + const char* uuid, + OrthancPluginContentType type); + + + + /** + * @brief Data structure that contains information about the Orthanc core. + **/ + typedef struct _OrthancPluginContext_t + { + void* pluginsManager; + const char* orthancVersion; + OrthancPluginFree Free; + int32_t (*InvokeService) (struct _OrthancPluginContext_t* context, + _OrthancPluginService service, + const void* params); + } OrthancPluginContext; + + + + /** + * @brief Free a string. + * + * Free a string that was allocated by the core system of Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param str The string to be freed. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginFreeString( + OrthancPluginContext* context, + char* str) + { + if (str != NULL) + { + context->Free(str); + } + } + + + /** + * @brief Check the compatibility of the plugin wrt. the version of its hosting Orthanc. + * + * This function checks whether the version of this C header is + * compatible with the current version of Orthanc. The result of + * this function should always be checked in the + * OrthancPluginInitialize() entry point of the plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return 1 if and only if the versions are compatible. If the + * result is 0, the initialization of the plugin should fail. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginCheckVersion( + OrthancPluginContext* context) + { + int major, minor, revision; + + /* Assume compatibility with the mainline */ + if (!strcmp(context->orthancVersion, "mainline")) + { + return 1; + } + + /* Parse the version of the Orthanc core */ + if ( +#ifdef _MSC_VER + sscanf_s +#else + sscanf +#endif + (context->orthancVersion, "%4d.%4d.%4d", &major, &minor, &revision) != 3) + { + return 0; + } + + /* Check the major number of the version */ + + if (major > ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER) + { + return 1; + } + + if (major < ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER) + { + return 0; + } + + /* Check the minor number of the version */ + + if (minor > ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER) + { + return 1; + } + + if (minor < ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER) + { + return 0; + } + + /* Check the revision number of the version */ + + if (revision >= ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER) + { + return 1; + } + else + { + return 0; + } + } + + + /** + * @brief Free a memory buffer. + * + * Free a memory buffer that was allocated by the core system of Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param buffer The memory buffer to release. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginFreeMemoryBuffer( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* buffer) + { + context->Free(buffer->data); + } + + + /** + * @brief Log an error. + * + * Log an error message using the Orthanc logging system. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param message The message to be logged. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginLogError( + OrthancPluginContext* context, + const char* message) + { + context->InvokeService(context, _OrthancPluginService_LogError, message); + } + + + /** + * @brief Log a warning. + * + * Log a warning message using the Orthanc logging system. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param message The message to be logged. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginLogWarning( + OrthancPluginContext* context, + const char* message) + { + context->InvokeService(context, _OrthancPluginService_LogWarning, message); + } + + + /** + * @brief Log an information. + * + * Log an information message using the Orthanc logging system. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param message The message to be logged. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginLogInfo( + OrthancPluginContext* context, + const char* message) + { + context->InvokeService(context, _OrthancPluginService_LogInfo, message); + } + + + + typedef struct + { + const char* pathRegularExpression; + OrthancPluginRestCallback callback; + } _OrthancPluginRestCallback; + + /** + * @brief Register a REST callback. + * + * This function registers a REST callback against a regular + * expression for a URI. This function must be called during the + * initialization of the plugin, i.e. inside the + * OrthancPluginInitialize() public function. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param pathRegularExpression Regular expression for the URI. May contain groups. + * @param callback The callback function to handle the REST call. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallback( + OrthancPluginContext* context, + const char* pathRegularExpression, + OrthancPluginRestCallback callback) + { + _OrthancPluginRestCallback params; + params.pathRegularExpression = pathRegularExpression; + params.callback = callback; + context->InvokeService(context, _OrthancPluginService_RegisterRestCallback, ¶ms); + } + + + + typedef struct + { + OrthancPluginOnStoredInstanceCallback callback; + } _OrthancPluginOnStoredInstanceCallback; + + /** + * @brief Register a callback for received instances. + * + * This function registers a callback function that is called + * whenever a new DICOM instance is stored into the Orthanc core. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback function. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnStoredInstanceCallback( + OrthancPluginContext* context, + OrthancPluginOnStoredInstanceCallback callback) + { + _OrthancPluginOnStoredInstanceCallback params; + params.callback = callback; + + context->InvokeService(context, _OrthancPluginService_RegisterOnStoredInstanceCallback, ¶ms); + } + + + + typedef struct + { + OrthancPluginRestOutput* output; + const char* answer; + uint32_t answerSize; + const char* mimeType; + } _OrthancPluginAnswerBuffer; + + /** + * @brief Answer to a REST request. + * + * This function answers to a REST request with the content of a memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param answer Pointer to the memory buffer containing the answer. + * @param answerSize Number of bytes of the answer. + * @param mimeType The MIME type of the answer. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginAnswerBuffer( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* answer, + uint32_t answerSize, + const char* mimeType) + { + _OrthancPluginAnswerBuffer params; + params.output = output; + params.answer = answer; + params.answerSize = answerSize; + params.mimeType = mimeType; + context->InvokeService(context, _OrthancPluginService_AnswerBuffer, ¶ms); + } + + + typedef struct + { + OrthancPluginRestOutput* output; + OrthancPluginPixelFormat format; + uint32_t width; + uint32_t height; + uint32_t pitch; + const void* buffer; + } _OrthancPluginCompressAndAnswerPngImage; + + /** + * @brief Answer to a REST request with a PNG image. + * + * This function answers to a REST request with a PNG image. The + * parameters of this function describe a memory buffer that + * contains an uncompressed image. The image will be automatically compressed + * as a PNG image by the core system of Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param format The memory layout of the uncompressed image. + * @param width The width of the image. + * @param height The height of the image. + * @param pitch The pitch of the image (i.e. the number of bytes + * between 2 successive lines of the image in the memory buffer. + * @param buffer The memory buffer containing the uncompressed image. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerPngImage( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + OrthancPluginPixelFormat format, + uint32_t width, + uint32_t height, + uint32_t pitch, + const void* buffer) + { + _OrthancPluginCompressAndAnswerPngImage params; + params.output = output; + params.format = format; + params.width = width; + params.height = height; + params.pitch = pitch; + params.buffer = buffer; + context->InvokeService(context, _OrthancPluginService_CompressAndAnswerPngImage, ¶ms); + } + + + + typedef struct + { + OrthancPluginMemoryBuffer* target; + const char* instanceId; + } _OrthancPluginGetDicomForInstance; + + /** + * @brief Retrieve a DICOM instance using its Orthanc identifier. + * + * Retrieve a DICOM instance using its Orthanc identifier. The DICOM + * file is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param instanceId The Orthanc identifier of the DICOM instance of interest. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginGetDicomForInstance( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* instanceId) + { + _OrthancPluginGetDicomForInstance params; + params.target = target; + params.instanceId = instanceId; + return context->InvokeService(context, _OrthancPluginService_GetDicomForInstance, ¶ms); + } + + + + typedef struct + { + OrthancPluginMemoryBuffer* target; + const char* uri; + } _OrthancPluginRestApiGet; + + /** + * @brief Make a GET call to the built-in Orthanc REST API. + * + * Make a GET call to the built-in Orthanc REST API. The result to + * the query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiGet( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri) + { + _OrthancPluginRestApiGet params; + params.target = target; + params.uri = uri; + return context->InvokeService(context, _OrthancPluginService_RestApiGet, ¶ms); + } + + + + /** + * @brief Make a GET call to the REST API, as tainted by the plugins. + * + * Make a GET call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiGetAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri) + { + _OrthancPluginRestApiGet params; + params.target = target; + params.uri = uri; + return context->InvokeService(context, _OrthancPluginService_RestApiGetAfterPlugins, ¶ms); + } + + + + typedef struct + { + OrthancPluginMemoryBuffer* target; + const char* uri; + const char* body; + uint32_t bodySize; + } _OrthancPluginRestApiPostPut; + + /** + * @brief Make a POST call to the built-in Orthanc REST API. + * + * Make a POST call to the built-in Orthanc REST API. The result to + * the query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the POST request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPost( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPost, ¶ms); + } + + + /** + * @brief Make a POST call to the REST API, as tainted by the plugins. + * + * Make a POST call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the POST request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPostAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPostAfterPlugins, ¶ms); + } + + + + /** + * @brief Make a DELETE call to the built-in Orthanc REST API. + * + * Make a DELETE call to the built-in Orthanc REST API. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param uri The URI to delete in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiDelete( + OrthancPluginContext* context, + const char* uri) + { + return context->InvokeService(context, _OrthancPluginService_RestApiDelete, uri); + } + + + /** + * @brief Make a DELETE call to the REST API, as tainted by the plugins. + * + * Make a DELETE call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param uri The URI to delete in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiDeleteAfterPlugins( + OrthancPluginContext* context, + const char* uri) + { + return context->InvokeService(context, _OrthancPluginService_RestApiDeleteAfterPlugins, uri); + } + + + + /** + * @brief Make a PUT call to the built-in Orthanc REST API. + * + * Make a PUT call to the built-in Orthanc REST API. The result to + * the query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the PUT request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPut( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPut, ¶ms); + } + + + + /** + * @brief Make a PUT call to the REST API, as tainted by the plugins. + * + * Make a PUT call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the PUT request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPutAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPutAfterPlugins, ¶ms); + } + + + + typedef struct + { + OrthancPluginRestOutput* output; + const char* argument; + } _OrthancPluginOutputPlusArgument; + + /** + * @brief Redirect a REST request. + * + * This function answers to a REST request by redirecting the user + * to another URI using HTTP status 301. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param redirection Where to redirect. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRedirect( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* redirection) + { + _OrthancPluginOutputPlusArgument params; + params.output = output; + params.argument = redirection; + context->InvokeService(context, _OrthancPluginService_Redirect, ¶ms); + } + + + + typedef struct + { + char** result; + const char* argument; + } _OrthancPluginRetrieveDynamicString; + + /** + * @brief Look for a patient. + * + * Look for a patient stored in Orthanc, using its Patient ID tag (0x0010, 0x0020). + * This function uses the database index to run as fast as possible (it does not loop + * over all the stored patients). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param patientID The Patient ID of interest. + * @return The NULL value if the patient is non-existent, or a string containing the + * Orthanc ID of the patient. This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupPatient( + OrthancPluginContext* context, + const char* patientID) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = patientID; + + if (context->InvokeService(context, _OrthancPluginService_LookupPatient, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Look for a study. + * + * Look for a study stored in Orthanc, using its Study Instance UID tag (0x0020, 0x000d). + * This function uses the database index to run as fast as possible (it does not loop + * over all the stored studies). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param studyUID The Study Instance UID of interest. + * @return The NULL value if the study is non-existent, or a string containing the + * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudy( + OrthancPluginContext* context, + const char* studyUID) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = studyUID; + + if (context->InvokeService(context, _OrthancPluginService_LookupStudy, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Look for a study, using the accession number. + * + * Look for a study stored in Orthanc, using its Accession Number tag (0x0008, 0x0050). + * This function uses the database index to run as fast as possible (it does not loop + * over all the stored studies). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param accessionNumber The Accession Number of interest. + * @return The NULL value if the study is non-existent, or a string containing the + * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudyWithAccessionNumber( + OrthancPluginContext* context, + const char* accessionNumber) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = accessionNumber; + + if (context->InvokeService(context, _OrthancPluginService_LookupStudyWithAccessionNumber, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Look for a series. + * + * Look for a series stored in Orthanc, using its Series Instance UID tag (0x0020, 0x000e). + * This function uses the database index to run as fast as possible (it does not loop + * over all the stored series). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param seriesUID The Series Instance UID of interest. + * @return The NULL value if the series is non-existent, or a string containing the + * Orthanc ID of the series. This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupSeries( + OrthancPluginContext* context, + const char* seriesUID) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = seriesUID; + + if (context->InvokeService(context, _OrthancPluginService_LookupSeries, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Look for an instance. + * + * Look for an instance stored in Orthanc, using its SOP Instance UID tag (0x0008, 0x0018). + * This function uses the database index to run as fast as possible (it does not loop + * over all the stored instances). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param sopInstanceUID The SOP Instance UID of interest. + * @return The NULL value if the instance is non-existent, or a string containing the + * Orthanc ID of the instance. This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupInstance( + OrthancPluginContext* context, + const char* sopInstanceUID) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = sopInstanceUID; + + if (context->InvokeService(context, _OrthancPluginService_LookupInstance, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + + typedef struct + { + OrthancPluginRestOutput* output; + uint16_t status; + } _OrthancPluginSendHttpStatusCode; + + /** + * @brief Send a HTTP status code. + * + * This function answers to a REST request by sending a HTTP status + * code (such as "400 - Bad Request"). Note that: + * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer(). + * - Redirections (status 301) must use ::OrthancPluginRedirect(). + * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized(). + * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed(). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param status The HTTP status code to be sent. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatusCode( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + uint16_t status) + { + _OrthancPluginSendHttpStatusCode params; + params.output = output; + params.status = status; + context->InvokeService(context, _OrthancPluginService_SendHttpStatusCode, ¶ms); + } + + + /** + * @brief Signal that a REST request is not authorized. + * + * This function answers to a REST request by signaling that it is + * not authorized. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param realm The realm for the authorization process. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendUnauthorized( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* realm) + { + _OrthancPluginOutputPlusArgument params; + params.output = output; + params.argument = realm; + context->InvokeService(context, _OrthancPluginService_SendUnauthorized, ¶ms); + } + + + /** + * @brief Signal that this URI does not support this HTTP method. + * + * This function answers to a REST request by signaling that the + * queried URI does not support this method. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param allowedMethods The allowed methods for this URI (e.g. "GET,POST" after a PUT or a POST request). + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendMethodNotAllowed( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* allowedMethods) + { + _OrthancPluginOutputPlusArgument params; + params.output = output; + params.argument = allowedMethods; + context->InvokeService(context, _OrthancPluginService_SendMethodNotAllowed, ¶ms); + } + + + typedef struct + { + OrthancPluginRestOutput* output; + const char* key; + const char* value; + } _OrthancPluginSetHttpHeader; + + /** + * @brief Set a cookie. + * + * This function sets a cookie in the HTTP client. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param cookie The cookie to be set. + * @param value The value of the cookie. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSetCookie( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* cookie, + const char* value) + { + _OrthancPluginSetHttpHeader params; + params.output = output; + params.key = cookie; + params.value = value; + context->InvokeService(context, _OrthancPluginService_SetCookie, ¶ms); + } + + + /** + * @brief Set some HTTP header. + * + * This function sets a HTTP header in the HTTP answer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param key The HTTP header to be set. + * @param value The value of the HTTP header. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSetHttpHeader( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* key, + const char* value) + { + _OrthancPluginSetHttpHeader params; + params.output = output; + params.key = key; + params.value = value; + context->InvokeService(context, _OrthancPluginService_SetHttpHeader, ¶ms); + } + + + typedef struct + { + char** resultStringToFree; + const char** resultString; + int64_t* resultInt64; + const char* key; + OrthancPluginDicomInstance* instance; + } _OrthancPluginAccessDicomInstance; + + + /** + * @brief Get the AET of a DICOM instance. + * + * This function returns the Application Entity Title (AET) of the + * DICOM modality from which a DICOM instance originates. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @return The AET if success, NULL if error. + **/ + ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceRemoteAet( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + const char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultString = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceRemoteAet, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Get the size of a DICOM file. + * + * This function returns the number of bytes of the given DICOM instance. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @return The size of the file, -1 in case of error. + **/ + ORTHANC_PLUGIN_INLINE int64_t OrthancPluginGetInstanceSize( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + int64_t size; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultInt64 = &size; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceSize, ¶ms)) + { + /* Error */ + return -1; + } + else + { + return size; + } + } + + + /** + * @brief Get the data of a DICOM file. + * + * This function returns a pointer to the content of the given DICOM instance. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @return The pointer to the DICOM data, NULL in case of error. + **/ + ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + const char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultString = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceData, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Get the DICOM tag hierarchy as a JSON file. + * + * This function returns a pointer to a newly created string + * containing a JSON file. This JSON file encodes the tag hierarchy + * of the given DICOM instance. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @return The NULL value in case of error, or a string containing the JSON file. + * This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceJson( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultStringToFree = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceJson, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Get the DICOM tag hierarchy as a JSON file (with simplification). + * + * This function returns a pointer to a newly created string + * containing a JSON file. This JSON file encodes the tag hierarchy + * of the given DICOM instance. In contrast with + * ::OrthancPluginGetInstanceJson(), the returned JSON file is in + * its simplified version. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @return The NULL value in case of error, or a string containing the JSON file. + * This string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceSimplifiedJson( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance) + { + char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultStringToFree = &result; + params.instance = instance; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceSimplifiedJson, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Check whether a DICOM instance is associated with some metadata. + * + * This function checks whether the DICOM instance of interest is + * associated with some metadata. As of Orthanc 0.8.1, in the + * callbacks registered by + * ::OrthancPluginRegisterOnStoredInstanceCallback(), the only + * possibly available metadata are "ReceptionDate", "RemoteAET" and + * "IndexInSeries". + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @param metadata The metadata of interest. + * @return 1 if the metadata is present, 0 if it is absent, -1 in case of error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginHasInstanceMetadata( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance, + const char* metadata) + { + int64_t result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultInt64 = &result; + params.instance = instance; + params.key = metadata; + + if (context->InvokeService(context, _OrthancPluginService_HasInstanceMetadata, ¶ms)) + { + /* Error */ + return -1; + } + else + { + return (result != 0); + } + } + + + /** + * @brief Get the value of some metadata associated with a given DICOM instance. + * + * This functions returns the value of some metadata that is associated with the DICOM instance of interest. + * Before calling this function, the existence of the metadata must have been checked with + * ::OrthancPluginHasInstanceMetadata(). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param instance The instance of interest. + * @param metadata The metadata of interest. + * @return The metadata value if success, NULL if error. + **/ + ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceMetadata( + OrthancPluginContext* context, + OrthancPluginDicomInstance* instance, + const char* metadata) + { + const char* result; + + _OrthancPluginAccessDicomInstance params; + memset(¶ms, 0, sizeof(params)); + params.resultString = &result; + params.instance = instance; + params.key = metadata; + + if (context->InvokeService(context, _OrthancPluginService_GetInstanceMetadata, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + + typedef struct + { + OrthancPluginStorageCreate create; + OrthancPluginStorageRead read; + OrthancPluginStorageRemove remove; + OrthancPluginFree free; + } _OrthancPluginRegisterStorageArea; + + /** + * @brief Register a custom storage area. + * + * This function registers a custom storage area, to replace the + * built-in way Orthanc stores its files on the filesystem. This + * function must be called during the initialization of the plugin, + * i.e. inside the OrthancPluginInitialize() public function. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param create The callback function to store a file on the custom storage area. + * @param read The callback function to read a file from the custom storage area. + * @param remove The callback function to remove a file from the custom storage area. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea( + OrthancPluginContext* context, + OrthancPluginStorageCreate create, + OrthancPluginStorageRead read, + OrthancPluginStorageRemove remove) + { + _OrthancPluginRegisterStorageArea params; + params.create = create; + params.read = read; + params.remove = remove; + +#ifdef __cplusplus + params.free = ::free; +#else + params.free = free; +#endif + + context->InvokeService(context, _OrthancPluginService_RegisterStorageArea, ¶ms); + } + + + + /** + * @brief Return the path to the Orthanc executable. + * + * This function returns the path to the Orthanc executable. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return NULL in the case of an error, or a newly allocated string + * containing the path. This string must be freed by + * OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancPath(OrthancPluginContext* context) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GetOrthancPath, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Return the directory containing the Orthanc. + * + * This function returns the path to the directory containing the Orthanc executable. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return NULL in the case of an error, or a newly allocated string + * containing the path. This string must be freed by + * OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancDirectory(OrthancPluginContext* context) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GetOrthancDirectory, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Return the path to the configuration file. + * + * This function returns the path to the configuration file that was + * specified when starting Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return NULL in the case of an error, or a newly allocated string + * containing the path. This string must be freed by + * OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char *OrthancPluginGetConfigurationPath(OrthancPluginContext* context) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GetConfigurationPath, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + + typedef struct + { + OrthancPluginOnChangeCallback callback; + } _OrthancPluginOnChangeCallback; + + /** + * @brief Register a callback to monitor changes. + * + * This function registers a callback function that is called + * whenever a change happens to some DICOM resource. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback function. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnChangeCallback( + OrthancPluginContext* context, + OrthancPluginOnChangeCallback callback) + { + _OrthancPluginOnChangeCallback params; + params.callback = callback; + + context->InvokeService(context, _OrthancPluginService_RegisterOnChangeCallback, ¶ms); + } + + + + typedef struct + { + const char* plugin; + _OrthancPluginProperty property; + const char* value; + } _OrthancPluginSetPluginProperty; + + + /** + * @brief Set the URI where the plugin provides its Web interface. + * + * For plugins that come with a Web interface, this function + * declares the entry path where to find this interface. This + * information is notably used in the "Plugins" page of Orthanc + * Explorer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param uri The root URI for this plugin. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSetRootUri( + OrthancPluginContext* context, + const char* uri) + { + _OrthancPluginSetPluginProperty params; + params.plugin = OrthancPluginGetName(); + params.property = _OrthancPluginProperty_RootUri; + params.value = uri; + + context->InvokeService(context, _OrthancPluginService_SetPluginProperty, ¶ms); + } + + + /** + * @brief Set a description for this plugin. + * + * Set a description for this plugin. It is displayed in the + * "Plugins" page of Orthanc Explorer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param description The description. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSetDescription( + OrthancPluginContext* context, + const char* description) + { + _OrthancPluginSetPluginProperty params; + params.plugin = OrthancPluginGetName(); + params.property = _OrthancPluginProperty_Description; + params.value = description; + + context->InvokeService(context, _OrthancPluginService_SetPluginProperty, ¶ms); + } + + + /** + * @brief Extend the JavaScript code of Orthanc Explorer. + * + * Add JavaScript code to customize the default behavior of Orthanc + * Explorer. This can for instance be used to add new buttons. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param javascript The custom JavaScript code. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginExtendOrthancExplorer( + OrthancPluginContext* context, + const char* javascript) + { + _OrthancPluginSetPluginProperty params; + params.plugin = OrthancPluginGetName(); + params.property = _OrthancPluginProperty_OrthancExplorer; + params.value = javascript; + + context->InvokeService(context, _OrthancPluginService_SetPluginProperty, ¶ms); + } + + + typedef struct + { + char** result; + int32_t property; + const char* value; + } _OrthancPluginGlobalProperty; + + + /** + * @brief Get the value of a global property. + * + * Get the value of a global property that is stored in the Orthanc database. Global + * properties whose index is below 1024 are reserved by Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param property The global property of interest. + * @param defaultValue The value to return, if the global property is unset. + * @return The value of the global property, or NULL in the case of an error. This + * string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetGlobalProperty( + OrthancPluginContext* context, + int32_t property, + const char* defaultValue) + { + char* result; + + _OrthancPluginGlobalProperty params; + params.result = &result; + params.property = property; + params.value = defaultValue; + + if (context->InvokeService(context, _OrthancPluginService_GetGlobalProperty, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + /** + * @brief Set the value of a global property. + * + * Set the value of a global property into the Orthanc + * database. Setting a global property can be used by plugins to + * save their internal parameters. Plugins are only allowed to set + * properties whose index are above or equal to 1024 (properties + * below 1024 are read-only and reserved by Orthanc). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param property The global property of interest. + * @param value The value to be set in the global property. + * @return 0 if success, -1 in case of error. + **/ + ORTHANC_PLUGIN_INLINE int32_t OrthancPluginSetGlobalProperty( + OrthancPluginContext* context, + int32_t property, + const char* value) + { + _OrthancPluginGlobalProperty params; + params.result = NULL; + params.property = property; + params.value = value; + + if (context->InvokeService(context, _OrthancPluginService_SetGlobalProperty, ¶ms)) + { + /* Error */ + return -1; + } + else + { + return 0; + } + } + + + + typedef struct + { + int32_t *resultInt32; + uint32_t *resultUint32; + int64_t *resultInt64; + uint64_t *resultUint64; + } _OrthancPluginReturnSingleValue; + + /** + * @brief Get the number of command-line arguments. + * + * Retrieve the number of command-line arguments that were used to launch Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return The number of arguments. + **/ + ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetCommandLineArgumentsCount( + OrthancPluginContext* context) + { + uint32_t count = 0; + + _OrthancPluginReturnSingleValue params; + memset(¶ms, 0, sizeof(params)); + params.resultUint32 = &count; + + if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgumentsCount, ¶ms)) + { + /* Error */ + return 0; + } + else + { + return count; + } + } + + + + /** + * @brief Get the value of a command-line argument. + * + * Get the value of one of the command-line arguments that were used + * to launch Orthanc. The number of available arguments can be + * retrieved by OrthancPluginGetCommandLineArgumentsCount(). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param argument The index of the argument. + * @return The value of the argument, or NULL in the case of an error. This + * string must be freed by OrthancPluginFreeString(). + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGetCommandLineArgument( + OrthancPluginContext* context, + uint32_t argument) + { + char* result; + + _OrthancPluginGlobalProperty params; + params.result = &result; + params.property = (int32_t) argument; + params.value = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GetCommandLineArgument, ¶ms)) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + +#ifdef __cplusplus +} +#endif + + +/** @} */ +
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h Tue Nov 04 13:56:17 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1722 +0,0 @@ -/** - * \mainpage - * - * This C/C++ SDK allows external developers to create plugins that - * can be loaded into Orthanc to extend its functionality. Each - * Orthanc plugin must expose 4 public functions with the following - * signatures: - * - * -# <tt>int32_t OrthancPluginInitialize(const OrthancPluginContext* context)</tt>: - * This function is invoked by Orthanc when it loads the plugin on startup. - * The plugin must: - * - Check its compatibility with the Orthanc version using - * ::OrthancPluginCheckVersion(). - * - Store the context pointer so that it can use the plugin - * services of Orthanc. - * - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback(). - * - Register all its callbacks for received instances using ::OrthancPluginRegisterOnStoredInstanceCallback(). - * - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea(). - * -# <tt>void OrthancPluginFinalize()</tt>: - * This function is invoked by Orthanc during its shutdown. The plugin - * must free all its memory. - * -# <tt>const char* OrthancPluginGetName()</tt>: - * The plugin must return a short string to identify itself. - * -# <tt>const char* OrthancPluginGetVersion()</tt>: - * The plugin must return a string containing its version number. - * - * The name and the version of a plugin is only used to prevent it - * from being loaded twice. - * - * The various callbacks are guaranteed to be executed in mutual - * exclusion since Orthanc 0.8.5. - **/ - - - -/** - * @defgroup CInterface C Interface - * @brief The C interface to create Orthanc plugins. - * - * These functions must be used to create C plugins for Orthanc. - **/ - - - -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU 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://www.gnu.org/licenses/>. - **/ - - - -#pragma once - - -#include <stdio.h> -#include <string.h> - -#ifdef WIN32 -#define ORTHANC_PLUGINS_API __declspec(dllexport) -#else -#define ORTHANC_PLUGINS_API -#endif - -#define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 0 -#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 8 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 5 - - - -/******************************************************************** - ** Check that function inlining is properly supported. The use of - ** inlining is required, to avoid the duplication of object code - ** between two compilation modules that would use the Orthanc Plugin - ** API. - ********************************************************************/ - -/* If the auto-detection of the "inline" keyword below does not work - automatically and that your compiler is known to properly support - inlining, uncomment the following #define and adapt the definition - of "static inline". */ - -/* #define ORTHANC_PLUGIN_INLINE static inline */ - -#ifndef ORTHANC_PLUGIN_INLINE -# if __STDC_VERSION__ >= 199901L -/* This is C99 or above: http://predef.sourceforge.net/prestd.html */ -# define ORTHANC_PLUGIN_INLINE static inline -# elif defined(__cplusplus) -/* This is C++ */ -# define ORTHANC_PLUGIN_INLINE static inline -# elif defined(__GNUC__) -/* This is GCC running in C89 mode */ -# define ORTHANC_PLUGIN_INLINE static __inline -# elif defined(_MSC_VER) -/* This is Visual Studio running in C89 mode */ -# define ORTHANC_PLUGIN_INLINE static __inline -# else -# error Your compiler is not known to support the "inline" keyword -# endif -#endif - - - -/******************************************************************** - ** Inclusion of standard libraries. - ********************************************************************/ - -#ifdef _MSC_VER -#include "../../Resources/ThirdParty/VisualStudio/stdint.h" -#else -#include <stdint.h> -#endif - -#include <stdlib.h> - - - -/******************************************************************** - ** Definition of the Orthanc Plugin API. - ********************************************************************/ - -/** @{ */ - -#ifdef __cplusplus -extern "C" -{ -#endif - - /** - * The various HTTP methods for a REST call. - **/ - typedef enum - { - OrthancPluginHttpMethod_Get = 1, /*!< GET request */ - OrthancPluginHttpMethod_Post = 2, /*!< POST request */ - OrthancPluginHttpMethod_Put = 3, /*!< PUT request */ - OrthancPluginHttpMethod_Delete = 4 /*!< DELETE request */ - } OrthancPluginHttpMethod; - - - /** - * @brief The parameters of a REST request. - **/ - typedef struct - { - /** - * @brief The HTTP method. - **/ - OrthancPluginHttpMethod method; - - /** - * @brief The number of groups of the regular expression. - **/ - uint32_t groupsCount; - - /** - * @brief The matched values for the groups of the regular expression. - **/ - const char* const* groups; - - /** - * @brief For a GET request, the number of GET parameters. - **/ - uint32_t getCount; - - /** - * @brief For a GET request, the keys of the GET parameters. - **/ - const char* const* getKeys; - - /** - * @brief For a GET request, the values of the GET parameters. - **/ - const char* const* getValues; - - /** - * @brief For a PUT or POST request, the content of the body. - **/ - const char* body; - - /** - * @brief For a PUT or POST request, the number of bytes of the body. - **/ - uint32_t bodySize; - - - /* -------------------------------------------------- - New in version 0.8.1 - -------------------------------------------------- */ - - /** - * @brief The number of HTTP headers. - **/ - uint32_t headersCount; - - /** - * @brief The keys of the HTTP headers (always converted to low-case). - **/ - const char* const* headersKeys; - - /** - * @brief The values of the HTTP headers. - **/ - const char* const* headersValues; - - } OrthancPluginHttpRequest; - - - typedef enum - { - /* Generic services */ - _OrthancPluginService_LogInfo = 1, - _OrthancPluginService_LogWarning = 2, - _OrthancPluginService_LogError = 3, - _OrthancPluginService_GetOrthancPath = 4, - _OrthancPluginService_GetOrthancDirectory = 5, - _OrthancPluginService_GetConfigurationPath = 6, - - /* Registration of callbacks */ - _OrthancPluginService_RegisterRestCallback = 1000, - _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001, - _OrthancPluginService_RegisterStorageArea = 1002, - _OrthancPluginService_RegisterOnChangeCallback = 1003, - - /* Sending answers to REST calls */ - _OrthancPluginService_AnswerBuffer = 2000, - _OrthancPluginService_CompressAndAnswerPngImage = 2001, - _OrthancPluginService_Redirect = 2002, - _OrthancPluginService_SendHttpStatusCode = 2003, - _OrthancPluginService_SendUnauthorized = 2004, - _OrthancPluginService_SendMethodNotAllowed = 2005, - _OrthancPluginService_SetCookie = 2006, - _OrthancPluginService_SetHttpHeader = 2007, - - /* Access to the Orthanc database and API */ - _OrthancPluginService_GetDicomForInstance = 3000, - _OrthancPluginService_RestApiGet = 3001, - _OrthancPluginService_RestApiPost = 3002, - _OrthancPluginService_RestApiDelete = 3003, - _OrthancPluginService_RestApiPut = 3004, - _OrthancPluginService_LookupPatient = 3005, - _OrthancPluginService_LookupStudy = 3006, - _OrthancPluginService_LookupSeries = 3007, - _OrthancPluginService_LookupInstance = 3008, - _OrthancPluginService_LookupStudyWithAccessionNumber = 3009, - - /* Access to DICOM instances */ - _OrthancPluginService_GetInstanceRemoteAet = 4000, - _OrthancPluginService_GetInstanceSize = 4001, - _OrthancPluginService_GetInstanceData = 4002, - _OrthancPluginService_GetInstanceJson = 4003, - _OrthancPluginService_GetInstanceSimplifiedJson = 4004, - _OrthancPluginService_HasInstanceMetadata = 4005, - _OrthancPluginService_GetInstanceMetadata = 4006 - } _OrthancPluginService; - - - - /** - * The memory layout of the pixels of an image. - **/ - typedef enum - { - /** - * @brief Graylevel 8bpp image. - * - * The image is graylevel. Each pixel is unsigned and stored in - * one byte. - **/ - OrthancPluginPixelFormat_Grayscale8 = 1, - - /** - * @brief Graylevel, unsigned 16bpp image. - * - * The image is graylevel. Each pixel is unsigned and stored in - * two bytes. - **/ - OrthancPluginPixelFormat_Grayscale16 = 2, - - /** - * @brief Graylevel, signed 16bpp image. - * - * The image is graylevel. Each pixel is signed and stored in two - * bytes. - **/ - OrthancPluginPixelFormat_SignedGrayscale16 = 3, - - /** - * @brief Color image in RGB24 format. - * - * This format describes a color image. The pixels are stored in 3 - * consecutive bytes. The memory layout is RGB. - **/ - OrthancPluginPixelFormat_RGB24 = 4, - - /** - * @brief Color image in RGBA32 format. - * - * This format describes a color image. The pixels are stored in 4 - * consecutive bytes. The memory layout is RGBA. - **/ - OrthancPluginPixelFormat_RGBA32 = 5 - } OrthancPluginPixelFormat; - - - - /** - * The content types that are supported by Orthanc plugins. - **/ - typedef enum - { - OrthancPluginContentType_Unknown = 0, /*!< Unknown content type */ - OrthancPluginContentType_Dicom = 1, /*!< DICOM */ - OrthancPluginContentType_DicomAsJson = 2 /*!< JSON summary of a DICOM file */ - } OrthancPluginContentType; - - - - /** - * The supported type of DICOM resources. - **/ - typedef enum - { - OrthancPluginResourceType_Patient = 0, /*!< Patient */ - OrthancPluginResourceType_Study = 1, /*!< Study */ - OrthancPluginResourceType_Series = 2, /*!< Series */ - OrthancPluginResourceType_Instance = 3 /*!< Instance */ - } OrthancPluginResourceType; - - - - /** - * The supported type of changes that can happen to DICOM resources. - **/ - typedef enum - { - OrthancPluginChangeType_CompletedSeries = 0, /*!< Series is now complete */ - OrthancPluginChangeType_Deleted = 1, /*!< Deleted resource */ - OrthancPluginChangeType_NewChildInstance = 2, /*!< A new instance was added to this resource */ - OrthancPluginChangeType_NewInstance = 3, /*!< New instance received */ - OrthancPluginChangeType_NewPatient = 4, /*!< New patient created */ - OrthancPluginChangeType_NewSeries = 5, /*!< New series created */ - OrthancPluginChangeType_NewStudy = 6, /*!< New study created */ - OrthancPluginChangeType_StablePatient = 7, /*!< Timeout: No new instance in this patient */ - OrthancPluginChangeType_StableSeries = 8, /*!< Timeout: No new instance in this series */ - OrthancPluginChangeType_StableStudy = 9 /*!< Timeout: No new instance in this study */ - } OrthancPluginChangeType; - - - - /** - * @brief A memory buffer allocated by the core system of Orthanc. - * - * A memory buffer allocated by the core system of Orthanc. When the - * content of the buffer is not useful anymore, it must be free by a - * call to ::OrthancPluginFreeMemoryBuffer(). - **/ - typedef struct - { - /** - * @brief The content of the buffer. - **/ - void* data; - - /** - * @brief The number of bytes in the buffer. - **/ - uint32_t size; - } OrthancPluginMemoryBuffer; - - - - - /** - * @brief Opaque structure that represents the HTTP connection to the client application. - **/ - typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput; - - - - /** - * @brief Opaque structure that represents a DICOM instance received by Orthanc. - **/ - typedef struct _OrthancPluginDicomInstance_t OrthancPluginDicomInstance; - - - - /** - * @brief Signature of a callback function that answers to a REST request. - **/ - typedef int32_t (*OrthancPluginRestCallback) ( - OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request); - - - - /** - * @brief Signature of a callback function that is triggered when Orthanc receives a DICOM instance. - **/ - typedef int32_t (*OrthancPluginOnStoredInstanceCallback) ( - OrthancPluginDicomInstance* instance, - const char* instanceId); - - - - /** - * @brief Signature of a callback function that is triggered when a change happens to some DICOM resource. - **/ - typedef int32_t (*OrthancPluginOnChangeCallback) ( - OrthancPluginChangeType changeType, - OrthancPluginResourceType resourceType, - const char* resourceId); - - - - /** - * @brief Signature of a function to free dynamic memory. - **/ - typedef void (*OrthancPluginFree) (void* buffer); - - - - /** - * @brief Callback for writing to the storage area. - * - * Signature of a callback function that is triggered when Orthanc writes a file to the storage area. - * - * @param uuid The UUID of the file. - * @param content The content of the file. - * @param size The size of the file. - * @param type The content type corresponding to this file. - * @return 0 if success, other value if error. - **/ - typedef int32_t (*OrthancPluginStorageCreate) ( - const char* uuid, - const void* content, - int64_t size, - OrthancPluginContentType type); - - - - /** - * @brief Callback for reading from the storage area. - * - * Signature of a callback function that is triggered when Orthanc reads a file from the storage area. - * - * @param content The content of the file (output). - * @param size The size of the file (output). - * @param uuid The UUID of the file of interest. - * @param type The content type corresponding to this file. - * @return 0 if success, other value if error. - **/ - typedef int32_t (*OrthancPluginStorageRead) ( - void** content, - int64_t* size, - const char* uuid, - OrthancPluginContentType type); - - - - /** - * @brief Callback for removing a file from the storage area. - * - * Signature of a callback function that is triggered when Orthanc deletes a file from the storage area. - * - * @param uuid The UUID of the file to be removed. - * @param type The content type corresponding to this file. - * @return 0 if success, other value if error. - **/ - typedef int32_t (*OrthancPluginStorageRemove) ( - const char* uuid, - OrthancPluginContentType type); - - - - /** - * @brief Opaque structure that contains information about the Orthanc core. - **/ - typedef struct _OrthancPluginContext_t - { - void* pluginsManager; - const char* orthancVersion; - OrthancPluginFree Free; - int32_t (*InvokeService) (struct _OrthancPluginContext_t* context, - _OrthancPluginService service, - const void* params); - } OrthancPluginContext; - - - - /** - * @brief Free a string. - * - * Free a string that was allocated by the core system of Orthanc. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param str The string to be freed. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginFreeString( - OrthancPluginContext* context, - char* str) - { - if (str != NULL) - { - context->Free(str); - } - } - - - /** - * @brief Check the compatibility of the plugin wrt. the version of its hosting Orthanc. - * - * This function checks whether the version of this C header is - * compatible with the current version of Orthanc. The result of - * this function should always be checked in the - * OrthancPluginInitialize() entry point of the plugin. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @return 1 if and only if the versions are compatible. If the - * result is 0, the initialization of the plugin should fail. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginCheckVersion( - OrthancPluginContext* context) - { - int major, minor, revision; - - /* Assume compatibility with the mainline */ - if (!strcmp(context->orthancVersion, "mainline")) - { - return 1; - } - - /* Parse the version of the Orthanc core */ - if ( -#ifdef _MSC_VER - sscanf_s -#else - sscanf -#endif - (context->orthancVersion, "%d.%d.%d", &major, &minor, &revision) != 3) - { - return 0; - } - - /* Check the major number of the version */ - - if (major > ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER) - { - return 1; - } - - if (major < ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER) - { - return 0; - } - - /* Check the minor number of the version */ - - if (minor > ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER) - { - return 1; - } - - if (minor < ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER) - { - return 0; - } - - /* Check the revision number of the version */ - - if (revision >= ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER) - { - return 1; - } - else - { - return 0; - } - } - - - /** - * @brief Free a memory buffer. - * - * Free a memory buffer that was allocated by the core system of Orthanc. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param buffer The memory buffer to release. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginFreeMemoryBuffer( - OrthancPluginContext* context, - OrthancPluginMemoryBuffer* buffer) - { - context->Free(buffer->data); - } - - - /** - * @brief Log an error. - * - * Log an error message using the Orthanc logging system. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param message The message to be logged. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginLogError( - OrthancPluginContext* context, - const char* message) - { - context->InvokeService(context, _OrthancPluginService_LogError, message); - } - - - /** - * @brief Log a warning. - * - * Log a warning message using the Orthanc logging system. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param message The message to be logged. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginLogWarning( - OrthancPluginContext* context, - const char* message) - { - context->InvokeService(context, _OrthancPluginService_LogWarning, message); - } - - - /** - * @brief Log an information. - * - * Log an information message using the Orthanc logging system. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param message The message to be logged. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginLogInfo( - OrthancPluginContext* context, - const char* message) - { - context->InvokeService(context, _OrthancPluginService_LogInfo, message); - } - - - - typedef struct - { - const char* pathRegularExpression; - OrthancPluginRestCallback callback; - } _OrthancPluginRestCallback; - - /** - * @brief Register a REST callback. - * - * This function registers a REST callback against a regular - * expression for a URI. This function must be called during the - * initialization of the plugin, i.e. inside the - * OrthancPluginInitialize() public function. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param pathRegularExpression Regular expression for the URI. May contain groups. - * @param callback The callback function to handle the REST call. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallback( - OrthancPluginContext* context, - const char* pathRegularExpression, - OrthancPluginRestCallback callback) - { - _OrthancPluginRestCallback params; - params.pathRegularExpression = pathRegularExpression; - params.callback = callback; - context->InvokeService(context, _OrthancPluginService_RegisterRestCallback, ¶ms); - } - - - - typedef struct - { - OrthancPluginOnStoredInstanceCallback callback; - } _OrthancPluginOnStoredInstanceCallback; - - /** - * @brief Register a callback for received instances. - * - * This function registers a callback function that is called - * whenever a new DICOM instance is stored into the Orthanc core. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param callback The callback function. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnStoredInstanceCallback( - OrthancPluginContext* context, - OrthancPluginOnStoredInstanceCallback callback) - { - _OrthancPluginOnStoredInstanceCallback params; - params.callback = callback; - - context->InvokeService(context, _OrthancPluginService_RegisterOnStoredInstanceCallback, ¶ms); - } - - - - typedef struct - { - OrthancPluginRestOutput* output; - const char* answer; - uint32_t answerSize; - const char* mimeType; - } _OrthancPluginAnswerBuffer; - - /** - * @brief Answer to a REST request. - * - * This function answers to a REST request with the content of a memory buffer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param answer Pointer to the memory buffer containing the answer. - * @param answerSize Number of bytes of the answer. - * @param mimeType The MIME type of the answer. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginAnswerBuffer( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* answer, - uint32_t answerSize, - const char* mimeType) - { - _OrthancPluginAnswerBuffer params; - params.output = output; - params.answer = answer; - params.answerSize = answerSize; - params.mimeType = mimeType; - context->InvokeService(context, _OrthancPluginService_AnswerBuffer, ¶ms); - } - - - typedef struct - { - OrthancPluginRestOutput* output; - OrthancPluginPixelFormat format; - uint32_t width; - uint32_t height; - uint32_t pitch; - const void* buffer; - } _OrthancPluginCompressAndAnswerPngImage; - - /** - * @brief Answer to a REST request with a PNG image. - * - * This function answers to a REST request with a PNG image. The - * parameters of this function describe a memory buffer that - * contains an uncompressed image. The image will be automatically compressed - * as a PNG image by the core system of Orthanc. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param format The memory layout of the uncompressed image. - * @param width The width of the image. - * @param height The height of the image. - * @param pitch The pitch of the image (i.e. the number of bytes - * between 2 successive lines of the image in the memory buffer. - * @param buffer The memory buffer containing the uncompressed image. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerPngImage( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - OrthancPluginPixelFormat format, - uint32_t width, - uint32_t height, - uint32_t pitch, - const void* buffer) - { - _OrthancPluginCompressAndAnswerPngImage params; - params.output = output; - params.format = format; - params.width = width; - params.height = height; - params.pitch = pitch; - params.buffer = buffer; - context->InvokeService(context, _OrthancPluginService_CompressAndAnswerPngImage, ¶ms); - } - - - - typedef struct - { - OrthancPluginMemoryBuffer* target; - const char* instanceId; - } _OrthancPluginGetDicomForInstance; - - /** - * @brief Retrieve a DICOM instance using its Orthanc identifier. - * - * Retrieve a DICOM instance using its Orthanc identifier. The DICOM - * file is stored into a newly allocated memory buffer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param target The target memory buffer. - * @param instanceId The Orthanc identifier of the DICOM instance of interest. - * @return 0 if success, other value if error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginGetDicomForInstance( - OrthancPluginContext* context, - OrthancPluginMemoryBuffer* target, - const char* instanceId) - { - _OrthancPluginGetDicomForInstance params; - params.target = target; - params.instanceId = instanceId; - return context->InvokeService(context, _OrthancPluginService_GetDicomForInstance, ¶ms); - } - - - - typedef struct - { - OrthancPluginMemoryBuffer* target; - const char* uri; - } _OrthancPluginRestApiGet; - - /** - * @brief Make a GET call to the built-in Orthanc REST API. - * - * Make a GET call to the built-in Orthanc REST API. The result to - * the query is stored into a newly allocated memory buffer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param target The target memory buffer. - * @param uri The URI in the built-in Orthanc API. - * @return 0 if success, other value if error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiGet( - OrthancPluginContext* context, - OrthancPluginMemoryBuffer* target, - const char* uri) - { - _OrthancPluginRestApiGet params; - params.target = target; - params.uri = uri; - return context->InvokeService(context, _OrthancPluginService_RestApiGet, ¶ms); - } - - - - typedef struct - { - OrthancPluginMemoryBuffer* target; - const char* uri; - const char* body; - uint32_t bodySize; - } _OrthancPluginRestApiPostPut; - - /** - * @brief Make a POST call to the built-in Orthanc REST API. - * - * Make a POST call to the built-in Orthanc REST API. The result to - * the query is stored into a newly allocated memory buffer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param target The target memory buffer. - * @param uri The URI in the built-in Orthanc API. - * @param body The body of the POST request. - * @param bodySize The size of the body. - * @return 0 if success, other value if error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPost( - OrthancPluginContext* context, - OrthancPluginMemoryBuffer* target, - const char* uri, - const char* body, - uint32_t bodySize) - { - _OrthancPluginRestApiPostPut params; - params.target = target; - params.uri = uri; - params.body = body; - params.bodySize = bodySize; - return context->InvokeService(context, _OrthancPluginService_RestApiPost, ¶ms); - } - - - - /** - * @brief Make a DELETE call to the built-in Orthanc REST API. - * - * Make a DELETE call to the built-in Orthanc REST API. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param uri The URI to delete in the built-in Orthanc API. - * @return 0 if success, other value if error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiDelete( - OrthancPluginContext* context, - const char* uri) - { - return context->InvokeService(context, _OrthancPluginService_RestApiDelete, uri); - } - - - - /** - * @brief Make a PUT call to the built-in Orthanc REST API. - * - * Make a PUT call to the built-in Orthanc REST API. The result to - * the query is stored into a newly allocated memory buffer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param target The target memory buffer. - * @param uri The URI in the built-in Orthanc API. - * @param body The body of the PUT request. - * @param bodySize The size of the body. - * @return 0 if success, other value if error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPut( - OrthancPluginContext* context, - OrthancPluginMemoryBuffer* target, - const char* uri, - const char* body, - uint32_t bodySize) - { - _OrthancPluginRestApiPostPut params; - params.target = target; - params.uri = uri; - params.body = body; - params.bodySize = bodySize; - return context->InvokeService(context, _OrthancPluginService_RestApiPut, ¶ms); - } - - - - typedef struct - { - OrthancPluginRestOutput* output; - const char* argument; - } _OrthancPluginOutputPlusArgument; - - /** - * @brief Redirect a REST request. - * - * This function answers to a REST request by redirecting the user - * to another URI using HTTP status 301. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param redirection Where to redirect. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginRedirect( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* redirection) - { - _OrthancPluginOutputPlusArgument params; - params.output = output; - params.argument = redirection; - context->InvokeService(context, _OrthancPluginService_Redirect, ¶ms); - } - - - - typedef struct - { - char** result; - const char* argument; - } _OrthancPluginRetrieveDynamicString; - - /** - * @brief Look for a patient. - * - * Look for a patient stored in Orthanc, using its Patient ID tag (0x0010, 0x0020). - * This function uses the database index to run as fast as possible (it does not loop - * over all the stored patients). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param patientID The Patient ID of interest. - * @return The NULL value if the patient is non-existent, or a string containing the - * Orthanc ID of the patient. This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupPatient( - OrthancPluginContext* context, - const char* patientID) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = patientID; - - if (context->InvokeService(context, _OrthancPluginService_LookupPatient, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Look for a study. - * - * Look for a study stored in Orthanc, using its Study Instance UID tag (0x0020, 0x000d). - * This function uses the database index to run as fast as possible (it does not loop - * over all the stored studies). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param studyUID The Study Instance UID of interest. - * @return The NULL value if the study is non-existent, or a string containing the - * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudy( - OrthancPluginContext* context, - const char* studyUID) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = studyUID; - - if (context->InvokeService(context, _OrthancPluginService_LookupStudy, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Look for a study, using the accession number. - * - * Look for a study stored in Orthanc, using its Accession Number tag (0x0008, 0x0050). - * This function uses the database index to run as fast as possible (it does not loop - * over all the stored studies). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param accessionNumber The Accession Number of interest. - * @return The NULL value if the study is non-existent, or a string containing the - * Orthanc ID of the study. This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupStudyWithAccessionNumber( - OrthancPluginContext* context, - const char* accessionNumber) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = accessionNumber; - - if (context->InvokeService(context, _OrthancPluginService_LookupStudyWithAccessionNumber, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Look for a series. - * - * Look for a series stored in Orthanc, using its Series Instance UID tag (0x0020, 0x000e). - * This function uses the database index to run as fast as possible (it does not loop - * over all the stored series). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param seriesUID The Series Instance UID of interest. - * @return The NULL value if the series is non-existent, or a string containing the - * Orthanc ID of the series. This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupSeries( - OrthancPluginContext* context, - const char* seriesUID) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = seriesUID; - - if (context->InvokeService(context, _OrthancPluginService_LookupSeries, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Look for an instance. - * - * Look for an instance stored in Orthanc, using its SOP Instance UID tag (0x0008, 0x0018). - * This function uses the database index to run as fast as possible (it does not loop - * over all the stored instances). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param sopInstanceUID The SOP Instance UID of interest. - * @return The NULL value if the instance is non-existent, or a string containing the - * Orthanc ID of the instance. This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginLookupInstance( - OrthancPluginContext* context, - const char* sopInstanceUID) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = sopInstanceUID; - - if (context->InvokeService(context, _OrthancPluginService_LookupInstance, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - - typedef struct - { - OrthancPluginRestOutput* output; - uint16_t status; - } _OrthancPluginSendHttpStatusCode; - - /** - * @brief Send a HTTP status code. - * - * This function answers to a REST request by sending a HTTP status - * code (such as "400 - Bad Request"). Note that: - * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer(). - * - Redirections (status 301) must use ::OrthancPluginRedirect(). - * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized(). - * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed(). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param status The HTTP status code to be sent. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatusCode( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - uint16_t status) - { - _OrthancPluginSendHttpStatusCode params; - params.output = output; - params.status = status; - context->InvokeService(context, _OrthancPluginService_SendHttpStatusCode, ¶ms); - } - - - /** - * @brief Signal that a REST request is not authorized. - * - * This function answers to a REST request by signaling that it is - * not authorized. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param realm The realm for the authorization process. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginSendUnauthorized( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* realm) - { - _OrthancPluginOutputPlusArgument params; - params.output = output; - params.argument = realm; - context->InvokeService(context, _OrthancPluginService_SendUnauthorized, ¶ms); - } - - - /** - * @brief Signal that this URI does not support this HTTP method. - * - * This function answers to a REST request by signaling that the - * queried URI does not support this method. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param allowedMethods The allowed methods for this URI (e.g. "GET,POST" after a PUT or a POST request). - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginSendMethodNotAllowed( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* allowedMethods) - { - _OrthancPluginOutputPlusArgument params; - params.output = output; - params.argument = allowedMethods; - context->InvokeService(context, _OrthancPluginService_SendMethodNotAllowed, ¶ms); - } - - - typedef struct - { - OrthancPluginRestOutput* output; - const char* key; - const char* value; - } _OrthancPluginSetHttpHeader; - - /** - * @brief Set a cookie. - * - * This function sets a cookie in the HTTP client. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param cookie The cookie to be set. - * @param value The value of the cookie. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginSetCookie( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* cookie, - const char* value) - { - _OrthancPluginSetHttpHeader params; - params.output = output; - params.key = cookie; - params.value = value; - context->InvokeService(context, _OrthancPluginService_SetCookie, ¶ms); - } - - - /** - * @brief Set some HTTP header. - * - * This function sets a HTTP header in the HTTP answer. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param output The HTTP connection to the client application. - * @param key The HTTP header to be set. - * @param value The value of the HTTP header. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginSetHttpHeader( - OrthancPluginContext* context, - OrthancPluginRestOutput* output, - const char* key, - const char* value) - { - _OrthancPluginSetHttpHeader params; - params.output = output; - params.key = key; - params.value = value; - context->InvokeService(context, _OrthancPluginService_SetHttpHeader, ¶ms); - } - - - typedef struct - { - char** resultStringToFree; - const char** resultString; - int64_t* resultInt64; - const char* key; - OrthancPluginDicomInstance* instance; - } _OrthancPluginAccessDicomInstance; - - - /** - * @brief Get the AET of a DICOM instance. - * - * This function returns the Application Entity Title (AET) of the - * DICOM modality from which a DICOM instance originates. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @return The AET if success, NULL if error. - **/ - ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceRemoteAet( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance) - { - const char* result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultString = &result; - params.instance = instance; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceRemoteAet, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Get the size of a DICOM file. - * - * This function returns the number of bytes of the given DICOM instance. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @return The size of the file, -1 in case of error. - **/ - ORTHANC_PLUGIN_INLINE int64_t OrthancPluginGetInstanceSize( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance) - { - int64_t size; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultInt64 = &size; - params.instance = instance; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceSize, ¶ms)) - { - /* Error */ - return -1; - } - else - { - return size; - } - } - - - /** - * @brief Get the data of a DICOM file. - * - * This function returns a pointer to the content of the given DICOM instance. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @return The pointer to the DICOM data, NULL in case of error. - **/ - ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance) - { - const char* result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultString = &result; - params.instance = instance; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceData, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Get the DICOM tag hierarchy as a JSON file. - * - * This function returns a pointer to a newly created string - * containing a JSON file. This JSON file encodes the tag hierarchy - * of the given DICOM instance. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @return The NULL value in case of error, or a string containing the JSON file. - * This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceJson( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance) - { - char* result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultStringToFree = &result; - params.instance = instance; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceJson, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Get the DICOM tag hierarchy as a JSON file (with simplification). - * - * This function returns a pointer to a newly created string - * containing a JSON file. This JSON file encodes the tag hierarchy - * of the given DICOM instance. In contrast with - * ::OrthancPluginGetInstanceJson(), the returned JSON file is in - * its simplified version. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @return The NULL value in case of error, or a string containing the JSON file. - * This string must be freed by OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char* OrthancPluginGetInstanceSimplifiedJson( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance) - { - char* result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultStringToFree = &result; - params.instance = instance; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceSimplifiedJson, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Check whether a DICOM instance is associated with some metadata. - * - * This function checks whether the DICOM instance of interest is - * associated with some metadata. As of Orthanc 0.8.1, in the - * callbacks registered by - * ::OrthancPluginRegisterOnStoredInstanceCallback(), the only - * possibly available metadata are "ReceptionDate", "RemoteAET" and - * "IndexInSeries". - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @param metadata The metadata of interest. - * @return 1 if the metadata is present, 0 if it is absent, -1 in case of error. - **/ - ORTHANC_PLUGIN_INLINE int OrthancPluginHasInstanceMetadata( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance, - const char* metadata) - { - int64_t result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultInt64 = &result; - params.instance = instance; - params.key = metadata; - - if (context->InvokeService(context, _OrthancPluginService_HasInstanceMetadata, ¶ms)) - { - /* Error */ - return -1; - } - else - { - return (result != 0); - } - } - - - /** - * @brief Get the value of some metadata associated with a given DICOM instance. - * - * This functions returns the value of some metadata that is associated with the DICOM instance of interest. - * Before calling this function, the existence of the metadata must have been checked with - * ::OrthancPluginHasInstanceMetadata(). - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param instance The instance of interest. - * @param metadata The metadata of interest. - * @return The metadata value if success, NULL if error. - **/ - ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceMetadata( - OrthancPluginContext* context, - OrthancPluginDicomInstance* instance, - const char* metadata) - { - const char* result; - - _OrthancPluginAccessDicomInstance params; - memset(¶ms, 0, sizeof(params)); - params.resultString = &result; - params.instance = instance; - params.key = metadata; - - if (context->InvokeService(context, _OrthancPluginService_GetInstanceMetadata, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - - typedef struct - { - OrthancPluginStorageCreate create_; - OrthancPluginStorageRead read_; - OrthancPluginStorageRemove remove_; - OrthancPluginFree free_; - } _OrthancPluginRegisterStorageArea; - - /** - * @brief Register a custom storage area. - * - * This function registers a custom storage area, to replace the - * built-in way Orthanc stores its files on the filesystem. This - * function must be called during the initialization of the plugin, - * i.e. inside the OrthancPluginInitialize() public function. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param create The callback function to store a file on the custom storage area. - * @param read The callback function to read a file from the custom storage area. - * @param remove The callback function to remove a file from the custom storage area. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea( - OrthancPluginContext* context, - OrthancPluginStorageCreate create, - OrthancPluginStorageRead read, - OrthancPluginStorageRemove remove) - { - _OrthancPluginRegisterStorageArea params; - params.create_ = create; - params.read_ = read; - params.remove_ = remove; - -#ifdef __cplusplus - params.free_ = ::free; -#else - params.free_ = free; -#endif - - context->InvokeService(context, _OrthancPluginService_RegisterStorageArea, ¶ms); - } - - - - /** - * @brief Return the path to the Orthanc executable. - * - * This function returns the path to the Orthanc executable. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @return NULL in the case of an error, or a newly allocated string - * containing the path. This string must be freed by - * OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancPath(OrthancPluginContext* context) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = NULL; - - if (context->InvokeService(context, _OrthancPluginService_GetOrthancPath, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Return the directory containing the Orthanc. - * - * This function returns the path to the directory containing the Orthanc executable. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @return NULL in the case of an error, or a newly allocated string - * containing the path. This string must be freed by - * OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char *OrthancPluginGetOrthancDirectory(OrthancPluginContext* context) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = NULL; - - if (context->InvokeService(context, _OrthancPluginService_GetOrthancDirectory, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - /** - * @brief Return the path to the configuration file. - * - * This function returns the path to the configuration file that was - * specified when starting Orthanc. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @return NULL in the case of an error, or a newly allocated string - * containing the path. This string must be freed by - * OrthancPluginFreeString(). - **/ - ORTHANC_PLUGIN_INLINE char *OrthancPluginGetConfigurationPath(OrthancPluginContext* context) - { - char* result; - - _OrthancPluginRetrieveDynamicString params; - params.result = &result; - params.argument = NULL; - - if (context->InvokeService(context, _OrthancPluginService_GetConfigurationPath, ¶ms)) - { - /* Error */ - return NULL; - } - else - { - return result; - } - } - - - - typedef struct - { - OrthancPluginOnChangeCallback callback; - } _OrthancPluginOnChangeCallback; - - /** - * @brief Register a callback to monitor changes. - * - * This function registers a callback function that is called - * whenever a change happens to some DICOM resource. - * - * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). - * @param callback The callback function. - **/ - ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterOnChangeCallback( - OrthancPluginContext* context, - OrthancPluginOnChangeCallback callback) - { - _OrthancPluginOnChangeCallback params; - params.callback = callback; - - context->InvokeService(context, _OrthancPluginService_RegisterOnChangeCallback, ¶ms); - } - - - - -#ifdef __cplusplus -} -#endif - - -/** @} */ -
--- a/Plugins/Samples/Basic/Plugin.c Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/Basic/Plugin.c Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -165,6 +165,58 @@ } +ORTHANC_PLUGINS_API int32_t Callback5(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request) +{ + /** + * Demonstration the difference between the + * "OrthancPluginRestApiXXX()" and the + * "OrthancPluginRestApiXXXAfterPlugins()" mechanisms to forward + * REST calls. + * + * # curl http://localhost:8042/forward/built-in/system + * # curl http://localhost:8042/forward/plugins/system + * # curl http://localhost:8042/forward/built-in/plugin/image + * => FAILURE (because the "/plugin/image" URI is implemented by this plugin) + * # curl http://localhost:8042/forward/plugins/plugin/image => SUCCESS + **/ + + OrthancPluginMemoryBuffer tmp; + int isBuiltIn, error; + + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context, output, "GET"); + return 0; + } + + isBuiltIn = strcmp("plugins", request->groups[0]); + + if (isBuiltIn) + { + error = OrthancPluginRestApiGet(context, &tmp, request->groups[1]); + } + else + { + printf("ICI1\n"); + error = OrthancPluginRestApiGetAfterPlugins(context, &tmp, request->groups[1]); + printf("ICI2\n"); + } + + if (error) + { + return -1; + } + else + { + OrthancPluginAnswerBuffer(context, output, tmp.data, tmp.size, "application/octet-stream"); + OrthancPluginFreeMemoryBuffer(context, &tmp); + return 0; + } +} + + ORTHANC_PLUGINS_API int32_t CallbackCreateDicom(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request) @@ -280,6 +332,7 @@ { OrthancPluginMemoryBuffer tmp; char info[1024], *s; + int counter, i; context = c; OrthancPluginLogWarning(context, "Sample plugin is initializing"); @@ -315,17 +368,34 @@ OrthancPluginLogWarning(context, info); OrthancPluginFreeString(context, s); + /* Print the command-line arguments of Orthanc */ + counter = OrthancPluginGetCommandLineArgumentsCount(context); + for (i = 0; i < counter; i++) + { + s = OrthancPluginGetCommandLineArgument(context, i); + sprintf(info, " Command-line argument %d: \"%s\"", i, s); + OrthancPluginLogWarning(context, info); + OrthancPluginFreeString(context, s); + } + /* Register the callbacks */ OrthancPluginRegisterRestCallback(context, "/(plu.*)/hello", Callback1); OrthancPluginRegisterRestCallback(context, "/plu.*/image", Callback2); OrthancPluginRegisterRestCallback(context, "/plugin/instances/([^/]+)/info", Callback3); OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4); + OrthancPluginRegisterRestCallback(context, "/forward/(built-in)(/.+)", Callback5); + OrthancPluginRegisterRestCallback(context, "/forward/(plugins)(/.+)", Callback5); OrthancPluginRegisterRestCallback(context, "/plugin/create", CallbackCreateDicom); OrthancPluginRegisterOnStoredInstanceCallback(context, OnStoredCallback); OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); + /* Declare several properties of the plugin */ + OrthancPluginSetRootUri(context, "/plugin/hello"); + OrthancPluginSetDescription(context, "This is the description of the sample plugin that can be seen in Orthanc Explorer."); + OrthancPluginExtendOrthancExplorer(context, "alert('Hello Orthanc! From sample plugin with love.');"); + /* Make REST requests to the built-in Orthanc API */ OrthancPluginRestApiGet(context, &tmp, "/changes"); OrthancPluginFreeMemoryBuffer(context, &tmp); @@ -335,7 +405,18 @@ /* Play with PUT by defining a new target modality. */ sprintf(info, "[ \"STORESCP\", \"localhost\", 2000 ]"); OrthancPluginRestApiPut(context, &tmp, "/modalities/demo", info, strlen(info)); - + + /* Play with global properties: A global counter is incremented + each time the plugin starts. */ + s = OrthancPluginGetGlobalProperty(context, 1024, "0"); + sscanf(s, "%d", &counter); + sprintf(info, "Number of times this plugin was started: %d", counter); + OrthancPluginLogWarning(context, info); + counter++; + sprintf(info, "%d", counter); + OrthancPluginSetGlobalProperty(context, 1024, info); + OrthancPluginFreeString(context, s); + return 0; }
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/GdcmDecoding/OrthancContext.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/GdcmDecoding/OrthancContext.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Plugins/Samples/GdcmDecoding/Plugin.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/GdcmDecoding/Plugin.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Plugins/Samples/StorageArea/Plugin.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/StorageArea/Plugin.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -96,7 +96,7 @@ else { *content = malloc(*size); - if (content == NULL || + if (*content == NULL || fread(*content, *size, 1, fp) != 1) { ok = false; @@ -121,14 +121,13 @@ { ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) { - char info[1024]; - context = c; OrthancPluginLogWarning(context, "Storage plugin is initializing"); /* Check the version of the Orthanc core */ if (OrthancPluginCheckVersion(c) == 0) { + char info[1024]; sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", c->orthancVersion, ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
--- a/Plugins/Samples/WebSkeleton/Configuration.h Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/WebSkeleton/Configuration.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py Wed Feb 11 10:32:14 2015 +0100 @@ -1,6 +1,6 @@ # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Plugins/Samples/WebSkeleton/Framework/Framework.cmake Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/WebSkeleton/Framework/Framework.cmake Wed Feb 11 10:32:14 2015 +0100 @@ -1,6 +1,6 @@ # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/README Tue Nov 04 13:56:17 2014 +0100 +++ b/README Wed Feb 11 10:32:14 2015 +0100 @@ -41,11 +41,11 @@ exception: http://people.gnome.org/~markmc/openssl-and-the-gpl.html -We also kindly require scientific works and clinical studies that make -use of Orthanc to cite Orthanc in their associated -publications. Similarly, we require open-source and closed-source -products that make use of Orthanc to warn us about this use. You can -cite our work using the following BibTeX entry: +We also kindly ask scientific works and clinical studies that make +use of Orthanc to cite Orthanc in their associated publications. +Similarly, we ask open-source and closed-source products that make +use of Orthanc to warn us about this use. You can cite our work +using the following BibTeX entry: @inproceedings{Jodogne:ISBI2013, author = {Jodogne, S. and Bernard, C. and Devillers, M. and Lenaerts, E. and Coucke, P.}, @@ -78,8 +78,9 @@ * OrthancCppClient/ - Code of the C++ client * OrthancExplorer/ - Code of the Web application (HTML5/Javascript) * OrthancServer/ - Code of the Orthanc server (depends on DCMTK) +* Plugins/ - Code of the plugin framework * Resources/ - Scripts, resources for building third-party code -* UnitTests/ - Unit tests +* UnitTestsSources/ - Unit tests This archive contains the following files:
--- a/Resources/CMake/LibCurlConfiguration.cmake Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/CMake/LibCurlConfiguration.cmake Wed Feb 11 10:32:14 2015 +0100 @@ -26,7 +26,7 @@ -DCURL_DISABLE_LDAP=1 -DCURL_DISABLE_LDAPS=1 -DCURL_DISABLE_POP3=1 - -DCURL_DISABLE_PROXY=1 + #-DCURL_DISABLE_PROXY=1 -DCURL_DISABLE_RTSP=1 -DCURL_DISABLE_TELNET=1 -DCURL_DISABLE_TFTP=1
--- a/Resources/CMake/PlustacheConfiguration.cmake Tue Nov 04 13:56:17 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -if (USE_PLUSTACHE) - add_definitions(-DORTHANC_PLUSTACHE_ENABLED=1) - - if (STATIC_BUILD OR NOT USE_SYSTEM_PLUSTACHE) - set(PLUSTACHE_SOURCES_DIR ${CMAKE_BINARY_DIR}/plustache-0.3.0) - DownloadPackage( - "6162946bdb3dccf3b2185fcf149671ee" - "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/plustache-0.3.0.tar.gz" - "${PLUSTACHE_SOURCES_DIR}") - - list(APPEND THIRD_PARTY_SOURCES - ${PLUSTACHE_SOURCES_DIR}/src/context.cpp - ${PLUSTACHE_SOURCES_DIR}/src/template.cpp - ) - - include_directories( - ${PLUSTACHE_SOURCES_DIR} - ) - - execute_process( - COMMAND patch -p0 -i ${ORTHANC_ROOT}/Resources/CMake/PlustacheConfiguration.patch - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - - source_group(ThirdParty\\Plustache REGULAR_EXPRESSION ${PLUSTACHE_SOURCES_DIR}/.*) - - else() - message(FATAL_ERROR "Dynamic linking against plustache not implemented (a patch is required)") - endif() - -else() - add_definitions(-DORTHANC_PLUSTACHE_ENABLED=0) - -endif()
--- a/Resources/CMake/PlustacheConfiguration.patch Tue Nov 04 13:56:17 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -diff -r -u plustache-0.3.0.orig/include/template.hpp plustache-0.3.0/include/template.hpp ---- plustache-0.3.0.orig/include/template.hpp 2014-01-29 13:26:52.000000000 +0100 -+++ plustache-0.3.0/include/template.hpp 2014-05-28 17:51:51.623305914 +0200 -@@ -21,7 +21,7 @@ - public: - template_t (); - template_t (std::string& tmpl_path); -- ~template_t (); -+ virtual ~template_t (); - std::string render(const std::string& tmplate, const Context& ctx); - std::string render(const std::string& tmplate, const ObjectType& ctx); - -@@ -42,11 +42,13 @@ - std::string render_sections(const std::string& tmplate, - const Context& ctx); - std::string html_escape(const std::string& s); -- std::string get_partial(const std::string& partial) const; - void change_delimiter(const std::string& opentag, - const std::string& closetag); - void compile_data(); -- std::string get_template(const std::string& tmpl); -+ -+ protected: -+ virtual std::string get_partial(const std::string& partial) const; -+ virtual std::string get_template(const std::string& tmpl); - }; - } // namespace Plustache - #endif -Only in plustache-0.3.0/include: template.hpp~ -diff -r -u plustache-0.3.0.orig/src/template.cpp plustache-0.3.0/src/template.cpp ---- plustache-0.3.0.orig/src/template.cpp 2014-01-29 13:26:52.000000000 +0100 -+++ plustache-0.3.0/src/template.cpp 2014-05-28 17:51:32.599306393 +0200 -@@ -126,7 +126,7 @@ - // found a partial - else if (modifier == ">") - { -- std::string partial = template_t::get_partial(key); -+ std::string partial = get_partial(key); - repl.assign(template_t::render(partial, ctx)); - } - // normal tag -Only in plustache-0.3.0/src: template.cpp~
--- a/Resources/Configuration.json Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Configuration.json Wed Feb 11 10:32:14 2015 +0100 @@ -35,7 +35,9 @@ // List of paths to the plugins that are to be loaded into this // instance of Orthanc (e.g. "./libPluginTest.so" for Linux, or - // "./PluginTest.dll" for Windows). + // "./PluginTest.dll" for Windows). These paths can refer to + // folders, in which case they will be scanned non-recursively to + // find shared libraries. "Plugins" : [ ], @@ -138,6 +140,11 @@ // "peer2" : [ "http://localhost:8044/" ] }, + // Parameters of the HTTP proxy to be used by Orthanc. If set to the + // empty string, no HTTP proxy is used. For instance: + // "HttpProxy" : "192.168.0.1:3128" + // "HttpProxy" : "proxyUser:proxyPassword@192.168.0.1:3128" + "HttpProxy" : "", /** @@ -203,11 +210,17 @@ // to grow indefinitely in auto-routing tasks. "LogExportedResources" : true, - // Enable or disable HTTP Keep-Alive (experimental). Set this option + // Enable or disable HTTP Keep-Alive (deprecated). Set this option // to "true" only in the case of high HTTP loads. "KeepAlive" : false, // If this option is set to "false", Orthanc will run in index-only // mode. The DICOM files will not be stored on the drive. - "StoreDicom" : true + "StoreDicom" : true, + + // DICOM associations are kept open as long as new DICOM commands + // are issued. This option sets the number of seconds of inactivity + // to wait before automatically closing a DICOM association. If set + // to 0, the connection is closed immediately. + "DicomAssociationCloseDelay" : 5 }
--- a/Resources/EmbedResources.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/EmbedResources.py Wed Feb 11 10:32:14 2015 +0100 @@ -1,6 +1,6 @@ # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/OrthancPlugin.doxygen Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/OrthancPlugin.doxygen Wed Feb 11 10:32:14 2015 +0100 @@ -655,7 +655,7 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @CMAKE_SOURCE_DIR@/Plugins/OrthancCPlugin/OrthancCPlugin.h +INPUT = @CMAKE_SOURCE_DIR@/Plugins/Include/OrthancCPlugin.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -709,7 +709,7 @@ # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test -EXCLUDE_SYMBOLS = _OrthancPlugin* +EXCLUDE_SYMBOLS = _OrthancPlugin* OrthancPluginGetName # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see
--- a/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py Wed Feb 11 10:32:14 2015 +0100 @@ -1,8 +1,8 @@ #!/usr/bin/python # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Lua/AutoroutingConditional.lua Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Lua/AutoroutingConditional.lua Wed Feb 11 10:32:14 2015 +0100 @@ -1,4 +1,10 @@ -function OnStoredInstance(instanceId, tags, metadata) +function OnStoredInstance(instanceId, tags, metadata, remoteAet, calledAet) + -- The "remoteAet" and "calledAet" arguments are only available + -- since Orthanc 0.8.6 + if remoteAet ~=nil and calledAet ~= nil then + print ("Source AET: " .. remoteAet .. " => Called AET: " .. calledAet) + end + -- Extract the value of the "PatientName" DICOM tag local patientName = string.lower(tags['PatientName'])
--- a/Resources/Samples/Lua/AutoroutingModification.lua Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Lua/AutoroutingModification.lua Wed Feb 11 10:32:14 2015 +0100 @@ -7,6 +7,7 @@ -- The tags to be replaced local replace = {} replace['StationName'] = 'My Medical Device' + replace['0031-1020'] = 'Some private tag' -- The tags to be removed local remove = { 'MilitaryRank' }
--- a/Resources/Samples/Lua/CallWebService.js Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Lua/CallWebService.js Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Resources/Samples/OrthancClient/Basic/main.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/OrthancClient/Basic/main.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Resources/Samples/OrthancClient/Vtk/main.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/OrthancClient/Vtk/main.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Resources/Samples/Python/AnonymizeAllPatients.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/AnonymizeAllPatients.py Wed Feb 11 10:32:14 2015 +0100 @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Python/AutoClassify.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/AutoClassify.py Wed Feb 11 10:32:14 2015 +0100 @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Python/ChangesLoop.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/ChangesLoop.py Wed Feb 11 10:32:14 2015 +0100 @@ -1,8 +1,8 @@ #!/usr/bin/python # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Python/DownloadAnonymized.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/DownloadAnonymized.py Wed Feb 11 10:32:14 2015 +0100 @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Python/HighPerformanceAutoRouting.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/HighPerformanceAutoRouting.py Wed Feb 11 10:32:14 2015 +0100 @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Python/RestToolbox.py Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Python/RestToolbox.py Wed Feb 11 10:32:14 2015 +0100 @@ -1,6 +1,6 @@ # Orthanc - A Lightweight, RESTful DICOM Store -# Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, -# Belgium +# 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
--- a/Resources/Samples/Tools/RecoverCompressedFile.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/Tools/RecoverCompressedFile.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/Resources/Samples/WebApplications/DrawingDicomizer.js Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/WebApplications/DrawingDicomizer.js Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/Resources/Samples/WebApplications/NodeToolbox.js Tue Nov 04 13:56:17 2014 +0100 +++ b/Resources/Samples/WebApplications/NodeToolbox.js Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation
--- a/UnitTestsSources/DicomMapTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/DicomMapTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -133,16 +133,17 @@ -static void TestModule(ResourceType level) +static void TestModule(ResourceType level, + DicomModule module) { - std::set<DicomTag> module, main; - DicomTag::GetTagsForModule(module, level); + std::set<DicomTag> moduleTags, main; + DicomTag::GetTagsForModule(moduleTags, module); DicomMap::GetMainDicomTags(main, level); // The main dicom tags are a subset of the module - for (std::set<DicomTag>::const_iterator it = main.begin(); it != main.end(); it++) + for (std::set<DicomTag>::const_iterator it = main.begin(); it != main.end(); ++it) { - bool ok = module.find(*it) != module.end(); + bool ok = moduleTags.find(*it) != moduleTags.end(); // Exceptions for the Series level /*if ((// @@ -182,8 +183,8 @@ TEST(DicomMap, Modules) { - TestModule(ResourceType_Patient); - TestModule(ResourceType_Study); - //TestModule(ResourceType_Series); // TODO - TestModule(ResourceType_Instance); + TestModule(ResourceType_Patient, DicomModule_Patient); + TestModule(ResourceType_Study, DicomModule_Study); + //TestModule(ResourceType_Series, DicomModule_Series); // TODO + TestModule(ResourceType_Instance, DicomModule_Instance); }
--- a/UnitTestsSources/FileStorageTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/FileStorageTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/FromDcmtkTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/FromDcmtkTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -89,14 +89,35 @@ TEST(DicomModification, Anonymization) { + ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName")); + const DicomTag privateTag(0x0045, 0x0010); + const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020")); ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag)); + ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag2)); + ASSERT_EQ(0x0031, privateTag2.GetGroup()); + ASSERT_EQ(0x1020, privateTag2.GetElement()); + std::string s; ParsedDicomFile o; o.Replace(DICOM_TAG_PATIENT_NAME, "coucou"); - o.Replace(privateTag, "private tag"); + ASSERT_FALSE(o.GetTagValue(s, privateTag)); + o.Insert(privateTag, "private tag"); + ASSERT_TRUE(o.GetTagValue(s, privateTag)); + ASSERT_STREQ("private tag", s.c_str()); - std::string s; + ASSERT_FALSE(o.GetTagValue(s, privateTag2)); + ASSERT_THROW(o.Replace(privateTag2, "hello", DicomReplaceMode_ThrowIfAbsent), OrthancException); + ASSERT_FALSE(o.GetTagValue(s, privateTag2)); + o.Replace(privateTag2, "hello", DicomReplaceMode_IgnoreIfAbsent); + ASSERT_FALSE(o.GetTagValue(s, privateTag2)); + o.Replace(privateTag2, "hello", DicomReplaceMode_InsertIfAbsent); + ASSERT_TRUE(o.GetTagValue(s, privateTag2)); + ASSERT_STREQ("hello", s.c_str()); + o.Replace(privateTag2, "hello world"); + ASSERT_TRUE(o.GetTagValue(s, privateTag2)); + ASSERT_STREQ("hello world", s.c_str()); + ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); ASSERT_FALSE(Toolbox::IsUuid(s)); @@ -109,7 +130,7 @@ ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); ASSERT_TRUE(Toolbox::IsUuid(s)); ASSERT_TRUE(o.GetTagValue(s, privateTag)); - ASSERT_EQ("private tag", s); + ASSERT_STREQ("private tag", s.c_str()); m.SetupAnonymization(); m.Apply(o);
--- a/UnitTestsSources/ImageProcessingTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/ImageProcessingTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/JpegLosslessTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/JpegLosslessTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/LuaTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/LuaTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/MemoryCacheTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/MemoryCacheTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/MultiThreadingTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/MultiThreadingTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -291,7 +291,7 @@ const ListOfStrings& inputs) { for (ListOfStrings::const_iterator - it = inputs.begin(); it != inputs.end(); it++) + it = inputs.begin(); it != inputs.end(); ++it) { int a = boost::lexical_cast<int>(*it); int b = factor_ * a; @@ -318,8 +318,10 @@ { ListOfStrings l; s->GetListOfJobs(l); - for (ListOfStrings::iterator i = l.begin(); i != l.end(); i++) - printf(">> %s: %0.1f\n", i->c_str(), 100.0f * s->GetProgress(*i)); + for (ListOfStrings::iterator it = l.begin(); it != l.end(); ++it) + { + printf(">> %s: %0.1f\n", it->c_str(), 100.0f * s->GetProgress(*it)); + } Toolbox::USleep(10000); } }
--- a/UnitTestsSources/PluginsTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/PluginsTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/PlustacheTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU 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://www.gnu.org/licenses/>. - **/ - - -#include "PrecompiledHeadersUnitTests.h" -#include "gtest/gtest.h" - -#if ORTHANC_PLUSTACHE_ENABLED == 1 - -#include <include/template.hpp> - -class OrthancPlustache : public Plustache::template_t -{ -public: -protected: - virtual std::string get_template(const std::string& tmpl) - { - //printf("OK [%s]\n", tmpl.c_str()); - return Plustache::template_t::get_template(tmpl); - } - - virtual std::string get_partial(const std::string& partial) const - { - //printf("OK2 [%s]\n", partial.c_str()); - //return Plustache::template_t::get_partial(partial); - return "<li>{{name}}</li>"; - } -}; - - -TEST(Plustache, Basic1) -{ - PlustacheTypes::ObjectType ctx; - ctx["title"] = "About"; - - OrthancPlustache t; - ASSERT_EQ("<h1>About</h1>", t.render("<h1>{{title}}</h1>", ctx)); -} - - -TEST(Plustache, Basic2) -{ - Plustache::Context ctx; - ctx.add("title", "About"); - - OrthancPlustache t; - ASSERT_EQ("<h1>About</h1>", t.render("<h1>{{title}}</h1>", ctx)); -} - - -TEST(Plustache, Context) -{ - PlustacheTypes::ObjectType a; - a["name"] = "Orthanc"; - - PlustacheTypes::ObjectType b; - b["name"] = "Jodogne"; - - PlustacheTypes::CollectionType c; - c.push_back(a); - c.push_back(b); - - Plustache::Context ctx; - ctx.add("items", c); - - OrthancPlustache t; - ASSERT_EQ("<ul><li>Orthanc</li><li>Jodogne</li></ul>", - t.render("<ul>{{#items}}<li>{{name}}</li>{{/items}}</ul>", ctx)); -} - - -TEST(Plustache, Partials) -{ - PlustacheTypes::ObjectType a; - a["name"] = "Orthanc"; - - PlustacheTypes::ObjectType b; - b["name"] = "Jodogne"; - - PlustacheTypes::CollectionType c; - c.push_back(a); - c.push_back(b); - - Plustache::Context ctx; - ctx.add("items", c); - - OrthancPlustache t; - ASSERT_EQ("<ul><li>Orthanc</li><li>Jodogne</li></ul>", - t.render("<ul>{{#items}}{{>partial}}{{/items}}</ul>", ctx)); -} - -#endif
--- a/UnitTestsSources/PngTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/PngTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/PrecompiledHeadersUnitTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/PrecompiledHeadersUnitTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/PrecompiledHeadersUnitTests.h Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/PrecompiledHeadersUnitTests.h Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/RestApiTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/RestApiTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/SQLiteChromiumTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/SQLiteChromiumTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/SQLiteTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/SQLiteTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/ServerIndexTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/ServerIndexTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -101,7 +101,7 @@ { protected: std::auto_ptr<ServerIndexListener> listener_; - std::auto_ptr<DatabaseWrapper> index_; + std::auto_ptr<IDatabaseWrapper> index_; DatabaseWrapperTest() { @@ -110,7 +110,18 @@ virtual void SetUp() { listener_.reset(new ServerIndexListener); - index_.reset(new DatabaseWrapper(*listener_)); + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + index_.reset(new DatabaseWrapper()); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + index_->SetListener(*listener_); } virtual void TearDown() @@ -118,6 +129,121 @@ index_.reset(NULL); listener_.reset(NULL); } + + void CheckTableRecordCount(uint32_t expected, const char* table) + { + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + ASSERT_EQ(expected, sqlite->GetTableRecordCount(table)); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void CheckNoParent(int64_t id) + { + std::string s; + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + ASSERT_FALSE(sqlite->GetParentPublicId(s, id)); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void CheckParentPublicId(const char* expected, int64_t id) + { + std::string s; + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + ASSERT_TRUE(sqlite->GetParentPublicId(s, id)); + ASSERT_EQ(expected, s); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void CheckNoChild(int64_t id) + { + std::list<std::string> j; + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + sqlite->GetChildren(j, id); + ASSERT_EQ(0, j.size()); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void CheckOneChild(const char* expected, int64_t id) + { + std::list<std::string> j; + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + sqlite->GetChildren(j, id); + ASSERT_EQ(1, j.size()); + ASSERT_EQ(expected, j.front()); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void CheckTwoChildren(const char* expected1, + const char* expected2, + int64_t id) + { + std::list<std::string> j; + + switch (GetParam()) + { + case DatabaseWrapperClass_SQLite: + { + DatabaseWrapper* sqlite = dynamic_cast<DatabaseWrapper*>(index_.get()); + sqlite->GetChildren(j, id); + ASSERT_EQ(2, j.size()); + ASSERT_TRUE((expected1 == j.front() && expected2 == j.back()) || + (expected1 == j.back() && expected2 == j.front())); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } }; } @@ -156,15 +282,15 @@ ASSERT_EQ(ResourceType_Study, index_->GetResourceType(a[6])); { - Json::Value t; + std::list<std::string> t; index_->GetAllPublicIds(t, ResourceType_Patient); ASSERT_EQ(1u, t.size()); - ASSERT_EQ("a", t[0u].asString()); + ASSERT_EQ("a", t.front()); index_->GetAllPublicIds(t, ResourceType_Series); ASSERT_EQ(1u, t.size()); - ASSERT_EQ("c", t[0u].asString()); + ASSERT_EQ("c", t.front()); index_->GetAllPublicIds(t, ResourceType_Study); ASSERT_EQ(2u, t.size()); @@ -191,14 +317,14 @@ ASSERT_FALSE(index_->LookupParent(parent, a[6])); std::string s; - - ASSERT_FALSE(index_->GetParentPublicId(s, a[0])); - ASSERT_FALSE(index_->GetParentPublicId(s, a[6])); - ASSERT_TRUE(index_->GetParentPublicId(s, a[1])); ASSERT_EQ("a", s); - ASSERT_TRUE(index_->GetParentPublicId(s, a[2])); ASSERT_EQ("b", s); - ASSERT_TRUE(index_->GetParentPublicId(s, a[3])); ASSERT_EQ("c", s); - ASSERT_TRUE(index_->GetParentPublicId(s, a[4])); ASSERT_EQ("c", s); - ASSERT_TRUE(index_->GetParentPublicId(s, a[5])); ASSERT_EQ("g", s); + + CheckNoParent(a[0]); + CheckNoParent(a[6]); + CheckParentPublicId("a", a[1]); + CheckParentPublicId("b", a[2]); + CheckParentPublicId("c", a[3]); + CheckParentPublicId("c", a[4]); + CheckParentPublicId("g", a[5]); std::list<std::string> l; index_->GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); @@ -224,7 +350,7 @@ ASSERT_EQ(0u, md.size()); index_->AddAttachment(a[4], FileInfo("my json file", FileContentType_DicomAsJson, 42, "md5", - CompressionType_Zlib, 21, "compressedMD5")); + CompressionType_Zlib, 21, "compressedMD5")); index_->AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42, "md5")); index_->AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44, "md5")); index_->SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE"); @@ -255,27 +381,26 @@ ASSERT_EQ(21u + 42u + 44u, index_->GetTotalCompressedSize()); ASSERT_EQ(42u + 42u + 44u, index_->GetTotalUncompressedSize()); - DicomMap m; - m.SetValue(0x0010, 0x0010, "PatientName"); - index_->SetMainDicomTags(a[3], m); + index_->SetMainDicomTag(a[3], DicomTag(0x0010, 0x0010), "PatientName"); int64_t b; ResourceType t; - ASSERT_TRUE(index_->LookupResource("g", b, t)); + ASSERT_TRUE(index_->LookupResource(b, t, "g")); ASSERT_EQ(7, b); ASSERT_EQ(ResourceType_Study, t); ASSERT_TRUE(index_->LookupMetadata(s, a[4], MetadataType_Instance_RemoteAet)); ASSERT_FALSE(index_->LookupMetadata(s, a[4], MetadataType_Instance_IndexInSeries)); ASSERT_EQ("PINNACLE", s); - ASSERT_EQ("PINNACLE", index_->GetMetadata(a[4], MetadataType_Instance_RemoteAet)); - ASSERT_EQ("None", index_->GetMetadata(a[4], MetadataType_Instance_IndexInSeries, "None")); + + std::string u; + ASSERT_TRUE(index_->LookupMetadata(u, a[4], MetadataType_Instance_RemoteAet)); + ASSERT_EQ("PINNACLE", u); + ASSERT_FALSE(index_->LookupMetadata(u, a[4], MetadataType_Instance_IndexInSeries)); ASSERT_TRUE(index_->LookupGlobalProperty(s, GlobalProperty_FlushSleep)); ASSERT_FALSE(index_->LookupGlobalProperty(s, static_cast<GlobalProperty>(42))); ASSERT_EQ("World", s); - ASSERT_EQ("World", index_->GetGlobalProperty(GlobalProperty_FlushSleep)); - ASSERT_EQ("None", index_->GetGlobalProperty(static_cast<GlobalProperty>(42), "None")); FileInfo att; ASSERT_TRUE(index_->LookupAttachment(att, a[4], FileContentType_DicomAsJson)); @@ -296,10 +421,11 @@ ASSERT_EQ(0u, listener_->deletedFiles_.size()); ASSERT_EQ(0u, listener_->deletedResources_.size()); - ASSERT_EQ(7u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(3u, index_->GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(1u, index_->GetTableRecordCount("Metadata")); - ASSERT_EQ(1u, index_->GetTableRecordCount("MainDicomTags")); + + CheckTableRecordCount(7, "Resources"); + CheckTableRecordCount(3, "AttachedFiles"); + CheckTableRecordCount(1, "Metadata"); + CheckTableRecordCount(1, "MainDicomTags"); index_->DeleteResource(a[0]); ASSERT_EQ(5u, listener_->deletedResources_.size()); @@ -311,15 +437,17 @@ listener_->deletedFiles_.end(), "my dicom file") == listener_->deletedFiles_.end()); - ASSERT_EQ(2u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index_->GetTableRecordCount("Metadata")); - ASSERT_EQ(1u, index_->GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(0u, index_->GetTableRecordCount("MainDicomTags")); + CheckTableRecordCount(2, "Resources"); + CheckTableRecordCount(0, "Metadata"); + CheckTableRecordCount(1, "AttachedFiles"); + CheckTableRecordCount(0, "MainDicomTags"); + index_->DeleteResource(a[5]); ASSERT_EQ(7u, listener_->deletedResources_.size()); - ASSERT_EQ(0u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index_->GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(2u, index_->GetTableRecordCount("GlobalProperties")); + + CheckTableRecordCount(0, "Resources"); + CheckTableRecordCount(0, "AttachedFiles"); + CheckTableRecordCount(2, "GlobalProperties"); ASSERT_EQ(3u, listener_->deletedFiles_.size()); ASSERT_FALSE(std::find(listener_->deletedFiles_.begin(), @@ -351,29 +479,14 @@ index_->AttachChild(a[0], a[5]); index_->AttachChild(a[5], a[7]); - { - Json::Value j; - index_->GetChildren(j, a[0]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "b" && j[1u] == "f") || - (j[1u] == "b" && j[0u] == "f")); - - index_->GetChildren(j, a[1]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "c" && j[1u] == "g") || - (j[1u] == "c" && j[0u] == "g")); - - index_->GetChildren(j, a[2]); - ASSERT_EQ(2u, j.size()); - ASSERT_TRUE((j[0u] == "d" && j[1u] == "e") || - (j[1u] == "d" && j[0u] == "e")); - - index_->GetChildren(j, a[3]); ASSERT_EQ(0u, j.size()); - index_->GetChildren(j, a[4]); ASSERT_EQ(0u, j.size()); - index_->GetChildren(j, a[5]); ASSERT_EQ(1u, j.size()); ASSERT_EQ("h", j[0u].asString()); - index_->GetChildren(j, a[6]); ASSERT_EQ(0u, j.size()); - index_->GetChildren(j, a[7]); ASSERT_EQ(0u, j.size()); - } + CheckTwoChildren("b", "f", a[0]); + CheckTwoChildren("c", "g", a[1]); + CheckTwoChildren("d", "e", a[2]); + CheckNoChild(a[3]); + CheckNoChild(a[4]); + CheckOneChild("h", a[5]); + CheckNoChild(a[6]); + CheckNoChild(a[7]); listener_->Reset(); index_->DeleteResource(a[3]); @@ -404,12 +517,12 @@ std::string p = "Patient " + boost::lexical_cast<std::string>(i); patients.push_back(index_->CreateResource(p, ResourceType_Patient)); index_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, - "md5-" + boost::lexical_cast<std::string>(i))); + "md5-" + boost::lexical_cast<std::string>(i))); ASSERT_FALSE(index_->IsProtectedPatient(patients[i])); } - ASSERT_EQ(10u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(10u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(10u, "Resources"); + CheckTableRecordCount(10u, "PatientRecyclingOrder"); listener_->Reset(); ASSERT_EQ(0u, listener_->deletedResources_.size()); @@ -417,8 +530,9 @@ index_->DeleteResource(patients[5]); index_->DeleteResource(patients[0]); ASSERT_EQ(2u, listener_->deletedResources_.size()); - ASSERT_EQ(8u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(8u, index_->GetTableRecordCount("PatientRecyclingOrder")); + + CheckTableRecordCount(8u, "Resources"); + CheckTableRecordCount(8u, "PatientRecyclingOrder"); ASSERT_EQ(2u, listener_->deletedFiles_.size()); ASSERT_EQ("Patient 5", listener_->deletedFiles_[0]); @@ -450,8 +564,9 @@ ASSERT_EQ(10u, listener_->deletedResources_.size()); ASSERT_EQ(10u, listener_->deletedFiles_.size()); - ASSERT_EQ(0u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index_->GetTableRecordCount("PatientRecyclingOrder")); + + CheckTableRecordCount(0, "Resources"); + CheckTableRecordCount(0, "PatientRecyclingOrder"); } @@ -463,42 +578,40 @@ std::string p = "Patient " + boost::lexical_cast<std::string>(i); patients.push_back(index_->CreateResource(p, ResourceType_Patient)); index_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, - "md5-" + boost::lexical_cast<std::string>(i))); + "md5-" + boost::lexical_cast<std::string>(i))); ASSERT_FALSE(index_->IsProtectedPatient(patients[i])); } - ASSERT_EQ(5u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(5u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(5, "Resources"); + CheckTableRecordCount(5, "PatientRecyclingOrder"); ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); index_->SetProtectedPatient(patients[2], true); ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index_->GetTableRecordCount("PatientRecyclingOrder")); - ASSERT_EQ(5u, index_->GetTableRecordCount("Resources")); + CheckTableRecordCount(5, "Resources"); + CheckTableRecordCount(4, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[2], true); ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(4, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[2], false); ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(5, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[2], false); ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index_->GetTableRecordCount("PatientRecyclingOrder")); - - ASSERT_EQ(5u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(5u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(5, "PatientRecyclingOrder"); + CheckTableRecordCount(5, "Resources"); index_->SetProtectedPatient(patients[2], true); ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(4u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(4, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[2], false); ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); - ASSERT_EQ(5u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(5, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[3], true); ASSERT_TRUE(index_->IsProtectedPatient(patients[3])); - ASSERT_EQ(4u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(4, "PatientRecyclingOrder"); - ASSERT_EQ(5u, index_->GetTableRecordCount("Resources")); + CheckTableRecordCount(5, "Resources"); ASSERT_EQ(0u, listener_->deletedFiles_.size()); // Unprotecting a patient puts it at the last position in the recycling queue @@ -522,11 +635,11 @@ ASSERT_FALSE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(4u, listener_->deletedFiles_.size()); - ASSERT_EQ(1u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(1, "Resources"); + CheckTableRecordCount(0, "PatientRecyclingOrder"); index_->SetProtectedPatient(patients[3], false); - ASSERT_EQ(1u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(1, "PatientRecyclingOrder"); ASSERT_FALSE(index_->SelectPatientToRecycle(p, patients[3])); ASSERT_TRUE(index_->SelectPatientToRecycle(p, patients[2])); ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); @@ -534,18 +647,27 @@ ASSERT_EQ(5u, listener_->deletedResources_.size()); ASSERT_EQ(5u, listener_->deletedFiles_.size()); - ASSERT_EQ(0u, index_->GetTableRecordCount("Resources")); - ASSERT_EQ(0u, index_->GetTableRecordCount("PatientRecyclingOrder")); + CheckTableRecordCount(0, "Resources"); + CheckTableRecordCount(0, "PatientRecyclingOrder"); } -TEST_P(DatabaseWrapperTest, Sequence) +TEST(ServerIndex, Sequence) { - ASSERT_EQ(1u, index_->IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(2u, index_->IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(3u, index_->IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); - ASSERT_EQ(4u, index_->IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + const std::string path = "UnitTestsStorage"; + + Toolbox::RemoveFile(path + "/index"); + FilesystemStorage storage(path); + DatabaseWrapper db; // The SQLite DB is in memory + ServerContext context(db); + context.SetStorageArea(storage); + ServerIndex& index = context.GetIndex(); + + ASSERT_EQ(1u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(2u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(3u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); + ASSERT_EQ(4u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence)); } @@ -559,11 +681,10 @@ index_->CreateResource("d", ResourceType_Series) // 3 }; - DicomMap m; - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index_->SetMainDicomTags(a[0], m); - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "1"); index_->SetMainDicomTags(a[1], m); - m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index_->SetMainDicomTags(a[2], m); - m.Clear(); m.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "0"); index_->SetMainDicomTags(a[3], m); + index_->SetMainDicomTag(a[0], DICOM_TAG_STUDY_INSTANCE_UID, "0"); + index_->SetMainDicomTag(a[1], DICOM_TAG_STUDY_INSTANCE_UID, "1"); + index_->SetMainDicomTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0"); + index_->SetMainDicomTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0"); std::list<int64_t> s; @@ -588,13 +709,13 @@ /*{ - std::list<std::string> s; - context.GetIndex().LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); - for (std::list<std::string>::iterator i = s.begin(); i != s.end(); i++) - { - std::cout << "*** " << *i << std::endl;; - } - }*/ + std::list<std::string> s; + context.GetIndex().LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); + for (std::list<std::string>::iterator i = s.begin(); i != s.end(); i++) + { + std::cout << "*** " << *i << std::endl;; + } + }*/ } @@ -605,7 +726,8 @@ Toolbox::RemoveFile(path + "/index"); FilesystemStorage storage(path); - ServerContext context(":memory:"); // The SQLite DB is in memory + DatabaseWrapper db; // The SQLite DB is in memory + ServerContext context(db); context.SetStorageArea(storage); ServerIndex& index = context.GetIndex();
--- a/UnitTestsSources/UnitTestsMain.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/VersionsTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/VersionsTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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
--- a/UnitTestsSources/ZipTests.cpp Tue Nov 04 13:56:17 2014 +0100 +++ b/UnitTestsSources/ZipTests.cpp Wed Feb 11 10:32:14 2015 +0100 @@ -1,7 +1,7 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, - * Belgium + * 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 @@ -72,6 +72,28 @@ } +TEST(ZipWriter, Append) +{ + { + Orthanc::ZipWriter w; + w.SetAppendToExisting(false); + w.SetOutputPath("UnitTestsResults/append.zip"); + w.Open(); + w.OpenFile("world/hello"); + w.Write("Hello world 1"); + } + + { + Orthanc::ZipWriter w; + w.SetAppendToExisting(true); + w.SetOutputPath("UnitTestsResults/append.zip"); + w.Open(); + w.OpenFile("world/appended"); + w.Write("Hello world 2"); + } +} + +