[med-svn] [orthanc] 02/06: Imported Upstream version 1.1.0+dfsg
Sebastien Jodogne
jodogne-guest at moszumanska.debian.org
Mon Jun 27 14:53:12 UTC 2016
This is an automated email from the git hooks/post-receive script.
jodogne-guest pushed a commit to branch master
in repository orthanc.
commit d00e231ec5b4004f0288ddb7abca8f8027b6c8c6
Author: jodogne-guest <s.jodogne at gmail.com>
Date: Mon Jun 27 15:59:24 2016 +0200
Imported Upstream version 1.1.0+dfsg
---
.hg_archival.txt | 6 +-
CMakeLists.txt | 220 +++-
Core/Cache/ICachePageProvider.h | 2 +-
Core/Cache/LeastRecentlyUsedIndex.h | 2 +-
Core/Cache/MemoryCache.cpp | 2 +-
Core/Cache/MemoryCache.h | 2 +-
Core/Cache/SharedArchive.cpp | 2 +-
Core/Cache/SharedArchive.h | 2 +-
Core/ChunkedBuffer.cpp | 15 +-
Core/ChunkedBuffer.h | 4 +-
Core/Compression/DeflateBaseCompressor.cpp | 2 +-
Core/Compression/DeflateBaseCompressor.h | 2 +-
Core/Compression/GzipCompressor.cpp | 2 +-
Core/Compression/GzipCompressor.h | 2 +-
Core/Compression/HierarchicalZipWriter.cpp | 2 +-
Core/Compression/HierarchicalZipWriter.h | 2 +-
Core/Compression/IBufferCompressor.h | 2 +-
Core/Compression/ZipWriter.cpp | 2 +-
Core/Compression/ZipWriter.h | 2 +-
Core/Compression/ZlibCompressor.cpp | 2 +-
Core/Compression/ZlibCompressor.h | 2 +-
Core/DicomFormat/DicomArray.cpp | 2 +-
Core/DicomFormat/DicomArray.h | 2 +-
Core/DicomFormat/DicomElement.h | 2 +-
Core/DicomFormat/DicomImageInformation.cpp | 26 +-
Core/DicomFormat/DicomImageInformation.h | 7 +-
Core/DicomFormat/DicomInstanceHasher.cpp | 2 +-
Core/DicomFormat/DicomInstanceHasher.h | 2 +-
Core/DicomFormat/DicomIntegerPixelAccessor.cpp | 7 +-
Core/DicomFormat/DicomIntegerPixelAccessor.h | 2 +-
Core/DicomFormat/DicomMap.cpp | 339 +++++-
Core/DicomFormat/DicomMap.h | 18 +-
Core/DicomFormat/DicomTag.cpp | 2 +-
Core/DicomFormat/DicomTag.h | 12 +-
Core/DicomFormat/DicomValue.cpp | 2 +-
Core/DicomFormat/DicomValue.h | 2 +-
Core/Endianness.h | 157 +++
Core/EnumerationDictionary.h | 2 +-
Core/Enumerations.cpp | 248 +++-
Core/Enumerations.h | 84 +-
Core/FileStorage/FileInfo.h | 2 +-
Core/FileStorage/FilesystemStorage.cpp | 4 +-
Core/FileStorage/FilesystemStorage.h | 2 +-
Core/FileStorage/IStorageArea.h | 2 +-
Core/FileStorage/StorageAccessor.cpp | 2 +-
Core/FileStorage/StorageAccessor.h | 2 +-
Core/HttpClient.cpp | 493 ++++++--
Core/HttpClient.h | 120 +-
Core/HttpServer/BufferHttpSender.cpp | 2 +-
Core/HttpServer/BufferHttpSender.h | 2 +-
Core/HttpServer/EmbeddedResourceHttpHandler.cpp | 2 +-
Core/HttpServer/EmbeddedResourceHttpHandler.h | 2 +-
Core/HttpServer/FilesystemHttpHandler.cpp | 8 +-
Core/HttpServer/FilesystemHttpHandler.h | 2 +-
Core/HttpServer/FilesystemHttpSender.cpp | 2 +-
Core/HttpServer/FilesystemHttpSender.h | 2 +-
Core/HttpServer/HttpContentNegociation.cpp | 2 +-
Core/HttpServer/HttpContentNegociation.h | 2 +-
Core/HttpServer/HttpFileSender.cpp | 2 +-
Core/HttpServer/HttpFileSender.h | 2 +-
Core/HttpServer/HttpOutput.cpp | 6 +-
Core/HttpServer/HttpOutput.h | 2 +-
Core/HttpServer/HttpStreamTranscoder.cpp | 2 +-
Core/HttpServer/HttpStreamTranscoder.h | 2 +-
Core/HttpServer/HttpToolbox.cpp | 4 +-
Core/HttpServer/HttpToolbox.h | 2 +-
Core/HttpServer/IHttpHandler.h | 2 +-
Core/HttpServer/IHttpOutputStream.h | 2 +-
Core/HttpServer/IHttpStreamAnswer.h | 2 +-
...OutputStream.h => IIncomingHttpRequestFilter.h} | 19 +-
Core/HttpServer/MongooseServer.cpp | 12 +-
Core/HttpServer/MongooseServer.h | 20 +-
Core/HttpServer/StringHttpOutput.cpp | 27 +-
Core/HttpServer/StringHttpOutput.h | 12 +-
Core/ICommand.h | 2 +-
Core/IDynamicObject.h | 2 +-
Core/Images/Font.cpp | 2 +-
Core/Images/Font.h | 2 +-
Core/Images/FontRegistry.cpp | 2 +-
Core/Images/FontRegistry.h | 2 +-
Core/Images/{ImageBuffer.h => IImageWriter.h} | 92 +-
Core/Images/Image.h | 2 +-
Core/Images/ImageAccessor.cpp | 65 +-
Core/Images/ImageAccessor.h | 10 +-
Core/Images/ImageBuffer.cpp | 2 +-
Core/Images/ImageBuffer.h | 2 +-
Core/Images/ImageProcessing.cpp | 90 +-
Core/Images/ImageProcessing.h | 2 +-
Core/Images/JpegErrorManager.cpp | 2 +-
Core/Images/JpegErrorManager.h | 2 +-
Core/Images/JpegReader.cpp | 7 +-
Core/Images/JpegReader.h | 14 +-
Core/Images/JpegWriter.cpp | 31 +-
Core/Images/JpegWriter.h | 52 +-
Core/Images/PngReader.cpp | 10 +-
Core/Images/PngReader.h | 14 +-
Core/Images/PngWriter.cpp | 29 +-
Core/Images/PngWriter.h | 50 +-
Core/Logging.cpp | 193 +--
Core/Logging.h | 15 +-
Core/Lua/LuaContext.cpp | 2 +-
Core/Lua/LuaContext.h | 7 +-
Core/Lua/LuaFunctionCall.cpp | 16 +-
Core/Lua/LuaFunctionCall.h | 4 +-
.../BagOfTasks.h} | 48 +-
Core/MultiThreading/BagOfTasksProcessor.cpp | 259 ++++
Core/MultiThreading/BagOfTasksProcessor.h | 146 +++
Core/MultiThreading/ILockable.h | 2 +-
Core/MultiThreading/IRunnableBySteps.h | 2 +-
Core/MultiThreading/Locker.h | 2 +-
Core/MultiThreading/Mutex.cpp | 6 +-
Core/MultiThreading/Mutex.h | 2 +-
Core/MultiThreading/ReaderWriterLock.cpp | 2 +-
Core/MultiThreading/ReaderWriterLock.h | 2 +-
Core/MultiThreading/RunnableWorkersPool.cpp | 2 +-
Core/MultiThreading/RunnableWorkersPool.h | 2 +-
Core/MultiThreading/Semaphore.cpp | 2 +-
Core/MultiThreading/Semaphore.h | 2 +-
Core/MultiThreading/SharedMessageQueue.cpp | 2 +-
Core/MultiThreading/SharedMessageQueue.h | 2 +-
Core/OrthancException.h | 2 +-
Core/Pkcs11.cpp | 312 +++++
Core/{HttpServer/IHttpOutputStream.h => Pkcs11.h} | 25 +-
Core/PrecompiledHeaders.cpp | 2 +-
Core/PrecompiledHeaders.h | 2 +-
Core/RestApi/RestApi.cpp | 2 +-
Core/RestApi/RestApi.h | 2 +-
Core/RestApi/RestApiCall.cpp | 2 +-
Core/RestApi/RestApiCall.h | 2 +-
Core/RestApi/RestApiDeleteCall.h | 2 +-
Core/RestApi/RestApiGetCall.cpp | 2 +-
Core/RestApi/RestApiGetCall.h | 2 +-
Core/RestApi/RestApiHierarchy.cpp | 2 +-
Core/RestApi/RestApiHierarchy.h | 2 +-
Core/RestApi/RestApiOutput.cpp | 2 +-
Core/RestApi/RestApiOutput.h | 2 +-
Core/RestApi/RestApiPath.cpp | 2 +-
Core/RestApi/RestApiPath.h | 2 +-
Core/RestApi/RestApiPostCall.h | 2 +-
Core/RestApi/RestApiPutCall.h | 2 +-
Core/SQLite/Connection.cpp | 5 +-
Core/SQLite/Connection.h | 2 +-
Core/SQLite/FunctionContext.cpp | 2 +-
Core/SQLite/FunctionContext.h | 2 +-
Core/SQLite/IScalarFunction.h | 2 +-
Core/SQLite/ITransaction.h | 2 +-
Core/SQLite/NonCopyable.h | 2 +-
Core/SQLite/OrthancSQLiteException.h | 2 +-
Core/SQLite/Statement.cpp | 2 +-
Core/SQLite/Statement.h | 2 +-
Core/SQLite/StatementId.cpp | 2 +-
Core/SQLite/StatementId.h | 2 +-
Core/SQLite/StatementReference.cpp | 5 +-
Core/SQLite/StatementReference.h | 2 +-
Core/SQLite/Transaction.cpp | 2 +-
Core/SQLite/Transaction.h | 2 +-
Core/Toolbox.cpp | 232 +++-
Core/Toolbox.h | 20 +-
Core/Uuid.cpp | 2 +-
Core/Uuid.h | 2 +-
Core/WebServiceParameters.cpp | 265 +++++
.../WebServiceParameters.h | 46 +-
LinuxCompilation.txt | 7 +-
NEWS | 72 +-
OrthancServer/DatabaseWrapper.cpp | 2 +-
OrthancServer/DatabaseWrapper.h | 2 +-
OrthancServer/DatabaseWrapperBase.cpp | 4 +-
OrthancServer/DatabaseWrapperBase.h | 2 +-
...DicomDirWriter.h => DefaultDicomImageDecoder.h} | 31 +-
OrthancServer/DicomDirWriter.cpp | 2 +-
OrthancServer/DicomDirWriter.h | 2 +-
OrthancServer/DicomInstanceToStore.cpp | 10 +-
OrthancServer/DicomInstanceToStore.h | 2 +-
OrthancServer/DicomModification.cpp | 6 +-
OrthancServer/DicomModification.h | 2 +-
OrthancServer/DicomProtocol/DicomFindAnswers.cpp | 6 +-
OrthancServer/DicomProtocol/DicomFindAnswers.h | 4 +-
OrthancServer/DicomProtocol/DicomServer.cpp | 4 +-
OrthancServer/DicomProtocol/DicomServer.h | 2 +-
.../DicomProtocol/DicomUserConnection.cpp | 135 ++-
OrthancServer/DicomProtocol/DicomUserConnection.h | 16 +-
.../DicomProtocol/IApplicationEntityFilter.h | 2 +-
OrthancServer/DicomProtocol/IFindRequestHandler.h | 2 +-
.../DicomProtocol/IFindRequestHandlerFactory.h | 2 +-
OrthancServer/DicomProtocol/IMoveRequestHandler.h | 5 +-
.../DicomProtocol/IMoveRequestHandlerFactory.h | 2 +-
OrthancServer/DicomProtocol/IStoreRequestHandler.h | 2 +-
.../DicomProtocol/IStoreRequestHandlerFactory.h | 2 +-
.../DicomProtocol/IWorklistRequestHandler.h | 2 +-
.../DicomProtocol/IWorklistRequestHandlerFactory.h | 2 +-
.../DicomProtocol/RemoteModalityParameters.cpp | 4 +-
.../DicomProtocol/RemoteModalityParameters.h | 2 +-
.../DicomProtocol/ReusableDicomUserConnection.cpp | 2 +-
.../DicomProtocol/ReusableDicomUserConnection.h | 2 +-
OrthancServer/ExportedResource.cpp | 2 +-
OrthancServer/ExportedResource.h | 2 +-
OrthancServer/FromDcmtkBridge.cpp | 413 +++++--
OrthancServer/FromDcmtkBridge.h | 40 +-
OrthancServer/IDatabaseListener.h | 2 +-
OrthancServer/IDatabaseWrapper.h | 2 +-
OrthancServer/IDicomImageDecoder.h | 7 +-
OrthancServer/IServerListener.h | 2 +-
OrthancServer/Internals/CommandDispatcher.cpp | 2 +-
OrthancServer/Internals/CommandDispatcher.h | 2 +-
OrthancServer/Internals/DicomFrameIndex.cpp | 440 +++++++
.../DicomFrameIndex.h} | 58 +-
OrthancServer/Internals/DicomImageDecoder.cpp | 341 ++++--
OrthancServer/Internals/DicomImageDecoder.h | 55 +-
OrthancServer/Internals/FindScp.cpp | 6 +-
OrthancServer/Internals/FindScp.h | 2 +-
OrthancServer/Internals/MoveScp.cpp | 8 +-
OrthancServer/Internals/MoveScp.h | 2 +-
OrthancServer/Internals/StoreScp.cpp | 11 +-
OrthancServer/Internals/StoreScp.h | 2 +-
OrthancServer/LuaScripting.cpp | 8 +-
OrthancServer/LuaScripting.h | 2 +-
OrthancServer/OrthancFindRequestHandler.cpp | 313 ++++-
OrthancServer/OrthancFindRequestHandler.h | 2 +-
OrthancServer/OrthancHttpHandler.cpp | 2 +-
OrthancServer/OrthancHttpHandler.h | 2 +-
OrthancServer/OrthancInitialization.cpp | 174 ++-
OrthancServer/OrthancInitialization.h | 23 +-
OrthancServer/OrthancMoveRequestHandler.cpp | 47 +-
OrthancServer/OrthancMoveRequestHandler.h | 5 +-
OrthancServer/OrthancPeerParameters.cpp | 96 --
.../OrthancRestApi/OrthancRestAnonymizeModify.cpp | 42 +-
OrthancServer/OrthancRestApi/OrthancRestApi.cpp | 2 +-
OrthancServer/OrthancRestApi/OrthancRestApi.h | 2 +-
.../OrthancRestApi/OrthancRestArchive.cpp | 2 +-
.../OrthancRestApi/OrthancRestChanges.cpp | 2 +-
.../OrthancRestApi/OrthancRestModalities.cpp | 121 +-
.../OrthancRestApi/OrthancRestResources.cpp | 183 +--
OrthancServer/OrthancRestApi/OrthancRestSystem.cpp | 2 +-
OrthancServer/ParsedDicomFile.cpp | 449 +++----
OrthancServer/ParsedDicomFile.h | 46 +-
OrthancServer/PrecompiledHeadersServer.cpp | 2 +-
OrthancServer/PrecompiledHeadersServer.h | 2 +-
OrthancServer/QueryRetrieveHandler.cpp | 4 +-
OrthancServer/QueryRetrieveHandler.h | 2 +-
OrthancServer/Scheduler/CallSystemCommand.cpp | 2 +-
OrthancServer/Scheduler/CallSystemCommand.h | 2 +-
OrthancServer/Scheduler/DeleteInstanceCommand.cpp | 2 +-
OrthancServer/Scheduler/DeleteInstanceCommand.h | 2 +-
OrthancServer/Scheduler/IServerCommand.h | 2 +-
OrthancServer/Scheduler/ModifyInstanceCommand.cpp | 2 +-
OrthancServer/Scheduler/ModifyInstanceCommand.h | 2 +-
OrthancServer/Scheduler/ServerCommandInstance.cpp | 2 +-
OrthancServer/Scheduler/ServerCommandInstance.h | 2 +-
OrthancServer/Scheduler/ServerJob.cpp | 2 +-
OrthancServer/Scheduler/ServerJob.h | 2 +-
OrthancServer/Scheduler/ServerScheduler.cpp | 2 +-
OrthancServer/Scheduler/ServerScheduler.h | 2 +-
OrthancServer/Scheduler/StorePeerCommand.cpp | 15 +-
OrthancServer/Scheduler/StorePeerCommand.h | 6 +-
OrthancServer/Scheduler/StoreScuCommand.cpp | 11 +-
OrthancServer/Scheduler/StoreScuCommand.h | 7 +-
OrthancServer/Search/HierarchicalMatcher.cpp | 12 +-
OrthancServer/Search/HierarchicalMatcher.h | 2 +-
OrthancServer/Search/IFindConstraint.cpp | 4 +-
OrthancServer/Search/IFindConstraint.h | 2 +-
OrthancServer/Search/ListConstraint.cpp | 2 +-
OrthancServer/Search/ListConstraint.h | 2 +-
OrthancServer/Search/LookupIdentifierQuery.cpp | 2 +-
OrthancServer/Search/LookupIdentifierQuery.h | 2 +-
OrthancServer/Search/LookupResource.cpp | 2 +-
OrthancServer/Search/LookupResource.h | 2 +-
OrthancServer/Search/RangeConstraint.cpp | 2 +-
OrthancServer/Search/RangeConstraint.h | 2 +-
OrthancServer/Search/SetOfResources.cpp | 2 +-
OrthancServer/Search/SetOfResources.h | 2 +-
OrthancServer/Search/ValueConstraint.cpp | 2 +-
OrthancServer/Search/ValueConstraint.h | 2 +-
OrthancServer/Search/WildcardConstraint.cpp | 2 +-
OrthancServer/Search/WildcardConstraint.h | 2 +-
OrthancServer/ServerContext.cpp | 2 +-
OrthancServer/ServerContext.h | 2 +-
OrthancServer/ServerEnumerations.cpp | 2 +-
OrthancServer/ServerEnumerations.h | 12 +-
OrthancServer/ServerIndex.cpp | 34 +-
OrthancServer/ServerIndex.h | 6 +-
OrthancServer/ServerIndexChange.h | 2 +-
OrthancServer/ServerToolbox.cpp | 2 +-
OrthancServer/ServerToolbox.h | 2 +-
OrthancServer/SliceOrdering.cpp | 2 +-
OrthancServer/SliceOrdering.h | 4 +-
OrthancServer/ToDcmtkBridge.cpp | 117 +-
OrthancServer/ToDcmtkBridge.h | 9 +-
OrthancServer/main.cpp | 144 ++-
Plugins/Engine/IPluginServiceProvider.h | 2 +-
Plugins/Engine/OrthancPluginDatabase.cpp | 4 +-
Plugins/Engine/OrthancPluginDatabase.h | 2 +-
Plugins/Engine/OrthancPlugins.cpp | 1240 +++++++++++++++-----
Plugins/Engine/OrthancPlugins.h | 60 +-
Plugins/Engine/PluginsEnumerations.cpp | 158 ++-
Plugins/Engine/PluginsEnumerations.h | 14 +-
Plugins/Engine/PluginsErrorDictionary.cpp | 2 +-
Plugins/Engine/PluginsErrorDictionary.h | 2 +-
Plugins/Engine/PluginsManager.cpp | 10 +-
Plugins/Engine/PluginsManager.h | 2 +-
Plugins/Engine/SharedLibrary.cpp | 10 +-
Plugins/Engine/SharedLibrary.h | 2 +-
Plugins/Include/orthanc/OrthancCDatabasePlugin.h | 2 +-
Plugins/Include/orthanc/OrthancCPlugin.h | 675 ++++++++++-
Plugins/Include/orthanc/OrthancCppDatabasePlugin.h | 2 +-
.../Samples/AutomatedJpeg2kCompression/Plugin.cpp | 2 +-
Plugins/Samples/Basic/Plugin.c | 36 +-
Plugins/Samples/Common/OrthancPluginCppWrapper.cpp | 829 +++++++++++++
Plugins/Samples/Common/OrthancPluginCppWrapper.h | 384 ++++++
Plugins/Samples/CustomImageDecoder/Plugin.cpp | 2 +-
Plugins/Samples/DatabasePlugin/Database.cpp | 2 +-
Plugins/Samples/DatabasePlugin/Database.h | 2 +-
Plugins/Samples/DatabasePlugin/Plugin.cpp | 2 +-
Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp | 2 +-
Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h | 2 +-
Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp | 9 +-
Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h | 2 +-
.../Samples/GdcmDecoder/OrthancImageWrapper.cpp | 2 +-
Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h | 2 +-
Plugins/Samples/GdcmDecoder/Plugin.cpp | 2 +-
Plugins/Samples/ModalityWorklists/Plugin.cpp | 19 +-
Plugins/Samples/ServeFolders/Plugin.cpp | 7 +-
Plugins/Samples/StorageArea/Plugin.cpp | 2 +-
Plugins/Samples/WebSkeleton/Configuration.h | 2 +-
.../WebSkeleton/Framework/EmbedResources.py | 2 +-
.../Samples/WebSkeleton/Framework/Framework.cmake | 2 +-
Plugins/Samples/WebSkeleton/Framework/Plugin.cpp | 2 +-
Resources/CMake/BoostConfiguration.cmake | 65 +-
Resources/CMake/BoostConfiguration.sh | 23 +-
Resources/CMake/Compiler.cmake | 35 +-
Resources/CMake/DcmtkConfiguration.cmake | 79 +-
Resources/CMake/DownloadPackage.cmake | 14 +-
Resources/CMake/GoogleLogConfiguration.cmake | 197 ----
Resources/CMake/GoogleLogConfiguration.h | 175 ---
Resources/CMake/GoogleLogConfigurationDarwin.h | 175 ---
Resources/CMake/GoogleLogConfigurationLSB.h | 175 ---
Resources/CMake/JsonCppConfiguration.cmake | 25 +
Resources/CMake/LibCurlConfiguration.cmake | 1 +
Resources/CMake/LibP11Configuration.cmake | 69 ++
Resources/CMake/OpenSslConfiguration.cmake | 28 +-
Resources/Configuration.json | 46 +-
Resources/DicomConformanceStatement.py | 2 +-
Resources/EmbedResources.py | 2 +-
Resources/ErrorCodes.json | 9 +-
Resources/Fonts/GenerateFont.py | 2 +-
Resources/GenerateErrorCodes.py | 2 +-
Resources/OrthancLogo.png | Bin 48745 -> 9026 bytes
Resources/OrthancLogoDocumentation.png | Bin 11678 -> 3761 bytes
...mtk-mingw64.patch => dcmtk-3.6.0-mingw64.patch} | 0
Resources/Patches/dcmtk-3.6.0-speed.patch | 44 +
Resources/Patches/dcmtk-3.6.1-speed.patch | 26 +
Resources/Patches/dcmtk-linux-speed.patch | 24 -
Resources/Patches/dcmtk-mingw64.txt | 1 -
Resources/Patches/dcmtk.txt | 1 +
Resources/Patches/glog-port-cc.diff | 15 -
Resources/Patches/glog-port-h-v2.diff | 52 -
Resources/Patches/glog-port-h.diff | 30 -
Resources/Patches/glog-utilities-lsb.diff | 52 -
Resources/Patches/glog-utilities.diff | 11 -
Resources/Patches/glog-visual-studio-port.h | 178 ---
Resources/Patches/libp11-0.4.0.patch | 36 +
Resources/Patches/log4cplus-patch.diff | 10 -
Resources/Patches/mongoose-3.1-patch.diff | 2 +-
Resources/RetrieveCACertificates.py | 2 +-
.../Samples/ImportDicomFiles/ImportDicomFiles.py | 4 +-
Resources/Samples/Lua/AutoroutingModification.lua | 28 +-
Resources/Samples/Lua/CallWebService.js | 2 +-
Resources/Samples/Lua/CallWebService.lua | 2 +-
Resources/Samples/Python/AnonymizeAllPatients.py | 4 +-
...onymizeAllPatients.py => ArchiveAllPatients.py} | 40 +-
.../Samples/Python/ArchiveStudiesInTimeRange.py | 4 +-
Resources/Samples/Python/AutoClassify.py | 4 +-
Resources/Samples/Python/ChangesLoop.py | 4 +-
.../Python/ContinuousPatientAnonymization.py | 4 +-
Resources/Samples/Python/DownloadAnonymized.py | 4 +-
.../Samples/Python/HighPerformanceAutoRouting.py | 4 +-
Resources/Samples/Python/ManualModification.py | 67 ++
Resources/Samples/Python/Replicate.py | 4 +-
Resources/Samples/Python/RestToolbox.py | 2 +-
Resources/Samples/Tools/CMakeLists.txt | 11 +-
Resources/Samples/Tools/RecoverCompressedFile.cpp | 6 +-
.../Samples/WebApplications/DrawingDicomizer.js | 2 +-
.../WebApplications/DrawingDicomizer/orthanc.js | 2 +-
Resources/Samples/WebApplications/NodeToolbox.js | 2 +-
Resources/WindowsResources.py | 2 +-
UnitTestsSources/DicomMapTests.cpp | 10 +-
UnitTestsSources/FileStorageTests.cpp | 2 +-
UnitTestsSources/FromDcmtkTests.cpp | 399 ++++++-
UnitTestsSources/ImageProcessingTests.cpp | 38 +-
UnitTestsSources/ImageTests.cpp | 31 +-
UnitTestsSources/JpegLosslessTests.cpp | 2 +-
UnitTestsSources/LuaTests.cpp | 8 +-
UnitTestsSources/MemoryCacheTests.cpp | 2 +-
UnitTestsSources/MultiThreadingTests.cpp | 6 +-
UnitTestsSources/PluginsTests.cpp | 4 +-
UnitTestsSources/PrecompiledHeadersUnitTests.cpp | 2 +-
UnitTestsSources/PrecompiledHeadersUnitTests.h | 2 +-
UnitTestsSources/RestApiTests.cpp | 2 +-
UnitTestsSources/SQLiteChromiumTests.cpp | 2 +-
UnitTestsSources/SQLiteTests.cpp | 2 +-
UnitTestsSources/ServerIndexTests.cpp | 12 +-
UnitTestsSources/StreamTests.cpp | 9 +-
UnitTestsSources/UnitTestsMain.cpp | 229 +++-
UnitTestsSources/VersionsTests.cpp | 6 +-
UnitTestsSources/ZipTests.cpp | 2 +-
404 files changed, 10426 insertions(+), 3540 deletions(-)
diff --git a/.hg_archival.txt b/.hg_archival.txt
index 6473729..fa815d1 100644
--- a/.hg_archival.txt
+++ b/.hg_archival.txt
@@ -1,5 +1,5 @@
repo: 3959d33612ccaadc0d4d707227fbed09ac35e5fe
-node: 74cf1f350b45473e847fed4116c1b428fa3d7352
-branch: Orthanc-1.0.0
+node: 8fdeb348f72be7848a1b227a77a7a6adc7153286
+branch: Orthanc-1.1.0
latesttag: null
-latesttagdistance: 1626
+latesttagdistance: 1779
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7f6ae71..a0a3fe3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
project(Orthanc)
# Version of the build, should always be "mainline" except in release branches
-set(ORTHANC_VERSION "1.0.0")
+set(ORTHANC_VERSION "1.1.0")
# Version of the database schema. History:
# * Orthanc 0.1.0 -> Orthanc 0.3.0 = no versioning
@@ -26,16 +26,17 @@ SET(ENABLE_SSL ON CACHE BOOL "Include support for SSL")
SET(DCMTK_DICTIONARY_DIR "" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (only when using system version of DCMTK)")
SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests")
-SET(ENABLE_GOOGLE_LOG OFF CACHE BOOL "Enable Google Log (otherwise, an internal logger is used)")
SET(ENABLE_JPEG ON CACHE BOOL "Enable JPEG decompression")
SET(ENABLE_JPEG_LOSSLESS ON CACHE BOOL "Enable JPEG-LS (Lossless) decompression")
SET(ENABLE_PLUGINS ON CACHE BOOL "Enable plugins")
-SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Build the ServeFolders plugin")
-SET(BUILD_MODALITY_WORKLISTS ON CACHE BOOL "Build the sample plugin to serve modality worklists")
+SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Whether to build the ServeFolders plugin")
+SET(BUILD_MODALITY_WORKLISTS ON CACHE BOOL "Whether to build the sample plugin to serve modality worklists")
+SET(BUILD_RECOVER_COMPRESSED_FILE ON CACHE BOOL "Whether to build the companion tool to recover files compressed using Orthanc")
+SET(USE_DCMTK_361 OFF CACHE BOOL "Use forthcoming DCMTK version 3.6.1 in static builds (instead of 3.6.0)")
+SET(ENABLE_PKCS11 OFF CACHE BOOL "Enable PKCS#11 for HTTPS client authentication using hardware security modules and smart cards")
# Advanced parameters to fine-tune linking against system libraries
SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
-SET(USE_SYSTEM_GOOGLE_LOG ON CACHE BOOL "Use the system version of Google Log")
SET(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test")
SET(USE_SYSTEM_SQLITE ON CACHE BOOL "Use the system version of SQLite")
SET(USE_SYSTEM_MONGOOSE ON CACHE BOOL "Use the system version of Mongoose")
@@ -47,7 +48,8 @@ SET(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg")
SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl")
SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL")
SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib")
-SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml)")
+SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml")
+SET(USE_SYSTEM_LIBP11 OFF CACHE BOOL "Use the system version of libp11 (PKCS#11 wrapper library)")
# Experimental options
SET(USE_PUGIXML ON CACHE BOOL "Use the Pugixml parser (turn off only for debug)")
@@ -64,6 +66,7 @@ mark_as_advanced(USE_PUGIXML)
# Path to the root folder of the Orthanc distribution
set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR})
+set(ENABLE_DCMTK_NETWORK ON)
# Some basic inclusions
include(CheckIncludeFiles)
@@ -77,7 +80,6 @@ include(${CMAKE_SOURCE_DIR}/Resources/CMake/VisualStudioPrecompiledHeaders.cmake
-
#####################################################################
## List of source files
#####################################################################
@@ -120,6 +122,7 @@ set(ORTHANC_CORE_SOURCES
Core/RestApi/RestApiPath.cpp
Core/RestApi/RestApiOutput.cpp
Core/RestApi/RestApi.cpp
+ Core/MultiThreading/BagOfTasksProcessor.cpp
Core/MultiThreading/Mutex.cpp
Core/MultiThreading/ReaderWriterLock.cpp
Core/MultiThreading/RunnableWorkersPool.cpp
@@ -143,6 +146,7 @@ set(ORTHANC_CORE_SOURCES
Core/SQLite/Transaction.cpp
Core/Toolbox.cpp
Core/Uuid.cpp
+ Core/WebServiceParameters.cpp
Core/Lua/LuaContext.cpp
Core/Lua/LuaFunctionCall.cpp
)
@@ -161,6 +165,7 @@ set(ORTHANC_SERVER_SOURCES
OrthancServer/ExportedResource.cpp
OrthancServer/FromDcmtkBridge.cpp
OrthancServer/Internals/CommandDispatcher.cpp
+ OrthancServer/Internals/DicomFrameIndex.cpp
OrthancServer/Internals/DicomImageDecoder.cpp
OrthancServer/Internals/FindScp.cpp
OrthancServer/Internals/MoveScp.cpp
@@ -170,7 +175,6 @@ set(ORTHANC_SERVER_SOURCES
OrthancServer/OrthancHttpHandler.cpp
OrthancServer/OrthancInitialization.cpp
OrthancServer/OrthancMoveRequestHandler.cpp
- OrthancServer/OrthancPeerParameters.cpp
OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp
OrthancServer/OrthancRestApi/OrthancRestApi.cpp
OrthancServer/OrthancRestApi/OrthancRestArchive.cpp
@@ -246,6 +250,27 @@ if (ENABLE_PLUGINS)
endif()
+set(ORTHANC_ALL_SOURCES
+ ${ORTHANC_CORE_SOURCES}
+ ${ORTHANC_SERVER_SOURCES}
+ ${ORTHANC_UNIT_TESTS_SOURCES}
+ Plugins/Samples/ServeFolders/Plugin.cpp
+ Plugins/Samples/ModalityWorklists/Plugin.cpp
+ OrthancServer/main.cpp
+ )
+
+
+if (CMAKE_COMPILER_IS_GNUCXX
+ AND NOT CMAKE_CROSSCOMPILING
+ AND NOT USE_DCMTK_361)
+ # Add the "-pedantic" flag only on the Orthanc sources, and only if
+ # using DCMTK 3.6.0
+ set_source_files_properties(${ORTHANC_ALL_SOURCES}
+ PROPERTIES COMPILE_FLAGS -pedantic
+ )
+endif()
+
+
set(ORTHANC_EMBEDDED_FILES
PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql
UPGRADE_DATABASE_3_TO_4 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql
@@ -262,10 +287,6 @@ set(ORTHANC_EMBEDDED_FILES
## Inclusion of third-party dependencies
#####################################################################
-if (ENABLE_GOOGLE_LOG)
- include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.cmake)
-endif()
-
include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake)
include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake)
include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake)
@@ -275,6 +296,7 @@ include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake)
include(${CMAKE_SOURCE_DIR}/Resources/CMake/PugixmlConfiguration.cmake)
include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake)
include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake)
# These are the two most heavyweight dependencies. We put them as the
# last includes to quickly spot problems when configuring static
@@ -312,6 +334,20 @@ else()
endif()
+if (ENABLE_PKCS11)
+ if (ENABLE_SSL)
+ include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibP11Configuration.cmake)
+
+ add_definitions(-DORTHANC_PKCS11_ENABLED=1)
+ list(APPEND ORTHANC_CORE_SOURCES Core/Pkcs11.cpp)
+ else()
+ message(FATAL_ERROR "OpenSSL is required to enable PKCS#11")
+ endif()
+else()
+ add_definitions(-DORTHANC_PKCS11_ENABLED=0)
+endif()
+
+
#####################################################################
## Autogeneration of files
@@ -335,7 +371,7 @@ else()
)
endif()
-if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
execute_process(
COMMAND
${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
@@ -354,10 +390,36 @@ endif()
#####################################################################
-## Build the core of Orthanc
+## Configuration of the C/C++ macros
#####################################################################
+if (UNIT_TESTS_WITH_HTTP_CONNEXIONS)
+ add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1)
+else()
+ add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=0)
+endif()
+
+include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include)
+
+add_definitions(
+ -DORTHANC_VERSION="${ORTHANC_VERSION}"
+ -DORTHANC_DATABASE_VERSION=${ORTHANC_DATABASE_VERSION}
+ -DORTHANC_ENABLE_LOGGING=1
+ -DORTHANC_MAXIMUM_TAG_LENGTH=256
+ -DORTHANC_BUILD_UNIT_TESTS=1
+
+ # Macros for the plugins
+ -DMODALITY_WORKLISTS_VERSION="${ORTHANC_VERSION}"
+ -DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}"
+ )
+
+
# Setup precompiled headers for Microsoft Visual Studio
+
+# WARNING: There must be NO MORE "add_definitions()", "include()" or
+# "include_directories()" below, otherwise the generated precompiled
+# headers might get broken!
+
if (MSVC)
add_definitions(-DORTHANC_USE_PRECOMPILED_HEADERS=1)
@@ -372,18 +434,18 @@ if (MSVC)
endif()
-add_definitions(
- -DORTHANC_VERSION="${ORTHANC_VERSION}"
- -DORTHANC_DATABASE_VERSION=${ORTHANC_DATABASE_VERSION}
- -DORTHANC_ENABLE_LOGGING=1
- -DORTHANC_MAXIMUM_TAG_LENGTH=256
- )
+
+#####################################################################
+## Build the core of Orthanc
+#####################################################################
list(LENGTH OPENSSL_SOURCES OPENSSL_SOURCES_LENGTH)
if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
add_library(OpenSSL STATIC ${OPENSSL_SOURCES})
endif()
+# "CodeLibrary" contains all the third-party dependencies and the
+# content of the "Core" folder, but not OpenSSL, nor DCMTK.
add_library(CoreLibrary
STATIC
${ORTHANC_CORE_SOURCES}
@@ -391,7 +453,6 @@ add_library(CoreLibrary
${BOOST_SOURCES}
${CURL_SOURCES}
- ${GOOGLE_LOG_SOURCES}
${JSONCPP_SOURCES}
${LIBPNG_SOURCES}
${LIBJPEG_SOURCES}
@@ -400,6 +461,7 @@ add_library(CoreLibrary
${PUGIXML_SOURCES}
${SQLITE_SOURCES}
${ZLIB_SOURCES}
+ ${LIBP11_SOURCES}
${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c
${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp
@@ -410,11 +472,11 @@ add_library(CoreLibrary
)
-
#####################################################################
## Build the Orthanc server
#####################################################################
+# "ServerLibrary" contains DCMTK
add_library(ServerLibrary
STATIC
${DCMTK_SOURCES}
@@ -446,21 +508,11 @@ install(
## Build the unit tests
#####################################################################
-if (UNIT_TESTS_WITH_HTTP_CONNEXIONS)
- add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1)
-else()
- add_definitions(-DUNIT_TESTS_WITH_HTTP_CONNEXIONS=0)
-endif()
-
-add_definitions(
- -DORTHANC_BUILD_UNIT_TESTS=1
- )
-
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake)
add_executable(UnitTests
${GTEST_SOURCES}
${ORTHANC_UNIT_TESTS_SOURCES}
)
+
target_link_libraries(UnitTests ServerLibrary CoreLibrary ${DCMTK_LIBRARIES})
if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
@@ -474,27 +526,27 @@ endif()
#####################################################################
if (ENABLE_PLUGINS AND BUILD_SERVE_FOLDERS)
- execute_process(
- COMMAND
- ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
- ${ORTHANC_VERSION} ServeFolders ServeFolders.dll "Orthanc plugin to serve additional folders"
- ERROR_VARIABLE Failure
- OUTPUT_FILE ${AUTOGENERATED_DIR}/ServeFolders.rc
- )
-
- if (Failure)
- message(FATAL_ERROR "Error while computing the version information: ${Failure}")
- endif()
-
- add_definitions(-DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}")
-
- include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include)
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ execute_process(
+ COMMAND
+ ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+ ${ORTHANC_VERSION} ServeFolders ServeFolders.dll "Orthanc plugin to serve additional folders"
+ ERROR_VARIABLE Failure
+ OUTPUT_FILE ${AUTOGENERATED_DIR}/ServeFolders.rc
+ )
+
+ if (Failure)
+ message(FATAL_ERROR "Error while computing the version information: ${Failure}")
+ endif()
+
+ list(APPEND SERVE_FOLDERS_RESOURCES ${AUTOGENERATED_DIR}/ServeFolders.rc)
+ endif()
add_library(ServeFolders SHARED
${BOOST_SOURCES}
${JSONCPP_SOURCES}
Plugins/Samples/ServeFolders/Plugin.cpp
- ${AUTOGENERATED_DIR}/ServeFolders.rc
+ ${SERVE_FOLDERS_RESOURCES}
)
set_target_properties(
@@ -517,27 +569,27 @@ endif()
#####################################################################
if (ENABLE_PLUGINS AND BUILD_MODALITY_WORKLISTS)
- execute_process(
- COMMAND
- ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
- ${ORTHANC_VERSION} ModalityWorklists ModalityWorklists.dll "Sample Orthanc plugin to serve modality worklists"
- ERROR_VARIABLE Failure
- OUTPUT_FILE ${AUTOGENERATED_DIR}/ModalityWorklists.rc
- )
-
- if (Failure)
- message(FATAL_ERROR "Error while computing the version information: ${Failure}")
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ execute_process(
+ COMMAND
+ ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+ ${ORTHANC_VERSION} ModalityWorklists ModalityWorklists.dll "Sample Orthanc plugin to serve modality worklists"
+ ERROR_VARIABLE Failure
+ OUTPUT_FILE ${AUTOGENERATED_DIR}/ModalityWorklists.rc
+ )
+
+ if (Failure)
+ message(FATAL_ERROR "Error while computing the version information: ${Failure}")
+ endif()
+
+ list(APPEND MODALITY_WORKLISTS_RESOURCES ${AUTOGENERATED_DIR}/ModalityWorklists.rc)
endif()
- add_definitions(-DMODALITY_WORKLISTS_VERSION="${ORTHANC_VERSION}")
-
- include_directories(${CMAKE_SOURCE_DIR}/Plugins/Include)
-
add_library(ModalityWorklists SHARED
${BOOST_SOURCES}
${JSONCPP_SOURCES}
Plugins/Samples/ModalityWorklists/Plugin.cpp
- ${AUTOGENERATED_DIR}/ModalityWorklists.rc
+ ${MODALITY_WORKLISTS_RESOURCES}
)
set_target_properties(
@@ -556,6 +608,46 @@ endif()
#####################################################################
+## Build the companion tool to recover files compressed using Orthanc
+#####################################################################
+
+if (BUILD_RECOVER_COMPRESSED_FILE)
+ set(RECOVER_COMPRESSED_SOURCES
+ Resources/Samples/Tools/RecoverCompressedFile.cpp
+ )
+
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ execute_process(
+ COMMAND
+ ${PYTHON_EXECUTABLE} ${ORTHANC_ROOT}/Resources/WindowsResources.py
+ ${ORTHANC_VERSION} OrthancRecoverCompressedFile OrthancRecoverCompressedFile.exe
+ "Lightweight, RESTful DICOM server for medical imaging"
+ ERROR_VARIABLE Failure
+ OUTPUT_FILE ${AUTOGENERATED_DIR}/OrthancRecoverCompressedFile.rc
+ )
+
+ if (Failure)
+ message(FATAL_ERROR "Error while computing the version information: ${Failure}")
+ endif()
+
+ list(APPEND RECOVER_COMPRESSED_SOURCES
+ ${AUTOGENERATED_DIR}/OrthancRecoverCompressedFile.rc
+ )
+ endif()
+
+ add_executable(OrthancRecoverCompressedFile ${RECOVER_COMPRESSED_SOURCES})
+
+ target_link_libraries(OrthancRecoverCompressedFile CoreLibrary)
+
+ install(
+ TARGETS OrthancRecoverCompressedFile
+ RUNTIME DESTINATION bin
+ )
+endif()
+
+
+
+#####################################################################
## Generate the documentation if Doxygen is present
#####################################################################
diff --git a/Core/Cache/ICachePageProvider.h b/Core/Cache/ICachePageProvider.h
index ca7e4c6..e795655 100644
--- a/Core/Cache/ICachePageProvider.h
+++ b/Core/Cache/ICachePageProvider.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Cache/LeastRecentlyUsedIndex.h b/Core/Cache/LeastRecentlyUsedIndex.h
index 9863959..a5bf7b2 100644
--- a/Core/Cache/LeastRecentlyUsedIndex.h
+++ b/Core/Cache/LeastRecentlyUsedIndex.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Cache/MemoryCache.cpp b/Core/Cache/MemoryCache.cpp
index 67aba2f..a2d5450 100644
--- a/Core/Cache/MemoryCache.cpp
+++ b/Core/Cache/MemoryCache.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Cache/MemoryCache.h b/Core/Cache/MemoryCache.h
index b6de54a..d29bc1f 100644
--- a/Core/Cache/MemoryCache.h
+++ b/Core/Cache/MemoryCache.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Cache/SharedArchive.cpp b/Core/Cache/SharedArchive.cpp
index a8ecf7f..93303e8 100644
--- a/Core/Cache/SharedArchive.cpp
+++ b/Core/Cache/SharedArchive.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Cache/SharedArchive.h b/Core/Cache/SharedArchive.h
index df73c6b..c4c0f8b 100644
--- a/Core/Cache/SharedArchive.h
+++ b/Core/Cache/SharedArchive.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/ChunkedBuffer.cpp b/Core/ChunkedBuffer.cpp
index dc6d091..5d2c2c8 100644
--- a/Core/ChunkedBuffer.cpp
+++ b/Core/ChunkedBuffer.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -51,17 +51,19 @@ namespace Orthanc
}
- void ChunkedBuffer::AddChunk(const char* chunkData,
+ void ChunkedBuffer::AddChunk(const void* chunkData,
size_t chunkSize)
{
if (chunkSize == 0)
{
return;
}
-
- assert(chunkData != NULL);
- chunks_.push_back(new std::string(chunkData, chunkSize));
- numBytes_ += chunkSize;
+ else
+ {
+ assert(chunkData != NULL);
+ chunks_.push_back(new std::string(reinterpret_cast<const char*>(chunkData), chunkSize));
+ numBytes_ += chunkSize;
+ }
}
@@ -95,5 +97,6 @@ namespace Orthanc
}
chunks_.clear();
+ numBytes_ = 0;
}
}
diff --git a/Core/ChunkedBuffer.h b/Core/ChunkedBuffer.h
index 93ffc2b..552c1ec 100644
--- a/Core/ChunkedBuffer.h
+++ b/Core/ChunkedBuffer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -61,7 +61,7 @@ namespace Orthanc
return numBytes_;
}
- void AddChunk(const char* chunkData,
+ void AddChunk(const void* chunkData,
size_t chunkSize);
void AddChunk(const std::string& chunk);
diff --git a/Core/Compression/DeflateBaseCompressor.cpp b/Core/Compression/DeflateBaseCompressor.cpp
index c9cde3c..b605a3a 100644
--- a/Core/Compression/DeflateBaseCompressor.cpp
+++ b/Core/Compression/DeflateBaseCompressor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/DeflateBaseCompressor.h b/Core/Compression/DeflateBaseCompressor.h
index fce8074..9b8298c 100644
--- a/Core/Compression/DeflateBaseCompressor.h
+++ b/Core/Compression/DeflateBaseCompressor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/GzipCompressor.cpp b/Core/Compression/GzipCompressor.cpp
index 4d9b930..1557eb8 100644
--- a/Core/Compression/GzipCompressor.cpp
+++ b/Core/Compression/GzipCompressor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/GzipCompressor.h b/Core/Compression/GzipCompressor.h
index 6189920..d43a6f2 100644
--- a/Core/Compression/GzipCompressor.h
+++ b/Core/Compression/GzipCompressor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/HierarchicalZipWriter.cpp b/Core/Compression/HierarchicalZipWriter.cpp
index e26b328..c3d1048 100644
--- a/Core/Compression/HierarchicalZipWriter.cpp
+++ b/Core/Compression/HierarchicalZipWriter.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/HierarchicalZipWriter.h b/Core/Compression/HierarchicalZipWriter.h
index 9f90010..ea384cf 100644
--- a/Core/Compression/HierarchicalZipWriter.h
+++ b/Core/Compression/HierarchicalZipWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/IBufferCompressor.h b/Core/Compression/IBufferCompressor.h
index c272670..dceab2e 100644
--- a/Core/Compression/IBufferCompressor.h
+++ b/Core/Compression/IBufferCompressor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/ZipWriter.cpp b/Core/Compression/ZipWriter.cpp
index c531c95..b36c88a 100644
--- a/Core/Compression/ZipWriter.cpp
+++ b/Core/Compression/ZipWriter.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/ZipWriter.h b/Core/Compression/ZipWriter.h
index 13464e8..e6b8db7 100644
--- a/Core/Compression/ZipWriter.h
+++ b/Core/Compression/ZipWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/ZlibCompressor.cpp b/Core/Compression/ZlibCompressor.cpp
index 049c691..acba7c0 100644
--- a/Core/Compression/ZlibCompressor.cpp
+++ b/Core/Compression/ZlibCompressor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Compression/ZlibCompressor.h b/Core/Compression/ZlibCompressor.h
index eb70854..3b9e898 100644
--- a/Core/Compression/ZlibCompressor.h
+++ b/Core/Compression/ZlibCompressor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomArray.cpp b/Core/DicomFormat/DicomArray.cpp
index ece2f60..4900419 100644
--- a/Core/DicomFormat/DicomArray.cpp
+++ b/Core/DicomFormat/DicomArray.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomArray.h b/Core/DicomFormat/DicomArray.h
index 55f35bc..a223685 100644
--- a/Core/DicomFormat/DicomArray.h
+++ b/Core/DicomFormat/DicomArray.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomElement.h b/Core/DicomFormat/DicomElement.h
index e32ea60..c6f47e5 100644
--- a/Core/DicomFormat/DicomElement.h
+++ b/Core/DicomFormat/DicomElement.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomImageInformation.cpp b/Core/DicomFormat/DicomImageInformation.cpp
index 89d8abc..d498583 100644
--- a/Core/DicomFormat/DicomImageInformation.cpp
+++ b/Core/DicomFormat/DicomImageInformation.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -157,7 +157,7 @@ namespace Orthanc
if (samplesPerPixel_ > 1)
{
// The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
- // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3
try
{
planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).GetContent());
@@ -218,9 +218,16 @@ namespace Orthanc
}
- bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const
+ bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format,
+ bool ignorePhotometricInterpretation) const
{
- if (photometric_ == PhotometricInterpretation_Monochrome1 ||
+ if (photometric_ == PhotometricInterpretation_Palette)
+ {
+ return false;
+ }
+
+ if (ignorePhotometricInterpretation ||
+ photometric_ == PhotometricInterpretation_Monochrome1 ||
photometric_ == PhotometricInterpretation_Monochrome2)
{
if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
@@ -245,7 +252,7 @@ namespace Orthanc
if (GetBitsStored() == 8 &&
GetChannelCount() == 3 &&
!IsSigned() &&
- photometric_ == PhotometricInterpretation_RGB)
+ (ignorePhotometricInterpretation || photometric_ == PhotometricInterpretation_RGB))
{
format = PixelFormat_RGB24;
return true;
@@ -253,4 +260,13 @@ namespace Orthanc
return false;
}
+
+
+ size_t DicomImageInformation::GetFrameSize() const
+ {
+ return (GetHeight() *
+ GetWidth() *
+ GetBytesPerValue() *
+ GetChannelCount());
+ }
}
diff --git a/Core/DicomFormat/DicomImageInformation.h b/Core/DicomFormat/DicomImageInformation.h
index e4a351d..568e6e3 100644
--- a/Core/DicomFormat/DicomImageInformation.h
+++ b/Core/DicomFormat/DicomImageInformation.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -119,6 +119,9 @@ namespace Orthanc
return photometric_;
}
- bool ExtractPixelFormat(PixelFormat& format) const;
+ bool ExtractPixelFormat(PixelFormat& format,
+ bool ignorePhotometricInterpretation) const;
+
+ size_t GetFrameSize() const;
};
}
diff --git a/Core/DicomFormat/DicomInstanceHasher.cpp b/Core/DicomFormat/DicomInstanceHasher.cpp
index 7e95e2e..4577258 100644
--- a/Core/DicomFormat/DicomInstanceHasher.cpp
+++ b/Core/DicomFormat/DicomInstanceHasher.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomInstanceHasher.h b/Core/DicomFormat/DicomInstanceHasher.h
index 46fa0c7..feb22ec 100644
--- a/Core/DicomFormat/DicomInstanceHasher.h
+++ b/Core/DicomFormat/DicomInstanceHasher.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp
index 1176118..1ef43ed 100644
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -54,8 +54,7 @@ namespace Orthanc
size_(size)
{
frame_ = 0;
- frameOffset_ = (information_.GetHeight() * information_.GetWidth() *
- information_.GetBytesPerValue() * information_.GetChannelCount());
+ frameOffset_ = information_.GetFrameSize();
if (information_.GetNumberOfFrames() * frameOffset_ > size)
{
@@ -137,7 +136,7 @@ namespace Orthanc
const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) +
y * rowOffset_ + frame_ * frameOffset_;
- // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3
if (information_.IsPlanar())
{
/**
diff --git a/Core/DicomFormat/DicomIntegerPixelAccessor.h b/Core/DicomFormat/DicomIntegerPixelAccessor.h
index ffcf934..bc60e4e 100644
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomMap.cpp b/Core/DicomFormat/DicomMap.cpp
index 794bca1..cd72d09 100644
--- a/Core/DicomFormat/DicomMap.cpp
+++ b/Core/DicomFormat/DicomMap.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -35,7 +35,7 @@
#include <stdio.h>
#include <memory>
-#include "DicomArray.h"
+#include "../Endianness.h"
#include "../OrthancException.h"
@@ -292,7 +292,7 @@ namespace Orthanc
for (size_t i = 0; i < count; i++)
{
- result.SetValue(tags[i], "");
+ result.SetValue(tags[i], "", false);
}
}
@@ -304,8 +304,8 @@ namespace Orthanc
void DicomMap::SetupFindStudyTemplate(DicomMap& result)
{
SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag));
- result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
- result.SetValue(DICOM_TAG_PATIENT_ID, "");
+ result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+ result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
// These main DICOM tags are only indirectly related to the
// General Study Module, remove them
@@ -317,9 +317,9 @@ namespace Orthanc
void DicomMap::SetupFindSeriesTemplate(DicomMap& result)
{
SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag));
- result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
- result.SetValue(DICOM_TAG_PATIENT_ID, "");
- result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "");
+ result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+ result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
+ result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
// These tags are considered as "main" by Orthanc, but are not in the Series module
result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer
@@ -339,10 +339,10 @@ namespace Orthanc
void DicomMap::SetupFindInstanceTemplate(DicomMap& result)
{
SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag));
- result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
- result.SetValue(DICOM_TAG_PATIENT_ID, "");
- result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "");
- result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "");
+ result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+ result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
+ result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
+ result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false);
}
@@ -462,13 +462,6 @@ namespace Orthanc
}
- void DicomMap::Print(FILE* fp) const
- {
- DicomArray a(*this);
- a.Print(fp);
- }
-
-
void DicomMap::GetTags(std::set<DicomTag>& tags) const
{
tags.clear();
@@ -479,4 +472,312 @@ namespace Orthanc
tags.insert(it->first);
}
}
+
+
+ static uint16_t ReadUnsignedInteger16(const char* dicom)
+ {
+ return le16toh(*reinterpret_cast<const uint16_t*>(dicom));
+ }
+
+
+ static uint32_t ReadUnsignedInteger32(const char* dicom)
+ {
+ return le32toh(*reinterpret_cast<const uint32_t*>(dicom));
+ }
+
+
+ static bool ValidateTag(const ValueRepresentation& vr,
+ const std::string& value)
+ {
+ switch (vr)
+ {
+ case ValueRepresentation_ApplicationEntity:
+ return value.size() <= 16;
+
+ case ValueRepresentation_AgeString:
+ return (value.size() == 4 &&
+ isdigit(value[0]) &&
+ isdigit(value[1]) &&
+ isdigit(value[2]) &&
+ (value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y'));
+
+ case ValueRepresentation_AttributeTag:
+ return value.size() == 4;
+
+ case ValueRepresentation_CodeString:
+ return value.size() <= 16;
+
+ case ValueRepresentation_Date:
+ return value.size() <= 18;
+
+ case ValueRepresentation_DecimalString:
+ return value.size() <= 16;
+
+ case ValueRepresentation_DateTime:
+ return value.size() <= 54;
+
+ case ValueRepresentation_FloatingPointSingle:
+ return value.size() == 4;
+
+ case ValueRepresentation_FloatingPointDouble:
+ return value.size() == 8;
+
+ case ValueRepresentation_IntegerString:
+ return value.size() <= 12;
+
+ case ValueRepresentation_LongString:
+ return value.size() <= 64;
+
+ case ValueRepresentation_LongText:
+ return value.size() <= 10240;
+
+ case ValueRepresentation_OtherByte:
+ return true;
+
+ case ValueRepresentation_OtherDouble:
+ return value.size() <= (static_cast<uint64_t>(1) << 32) - 8;
+
+ case ValueRepresentation_OtherFloat:
+ return value.size() <= (static_cast<uint64_t>(1) << 32) - 4;
+
+ case ValueRepresentation_OtherLong:
+ return true;
+
+ case ValueRepresentation_OtherWord:
+ return true;
+
+ case ValueRepresentation_PersonName:
+ return true;
+
+ case ValueRepresentation_ShortString:
+ return value.size() <= 16;
+
+ case ValueRepresentation_SignedLong:
+ return value.size() == 4;
+
+ case ValueRepresentation_Sequence:
+ return true;
+
+ case ValueRepresentation_SignedShort:
+ return value.size() == 2;
+
+ case ValueRepresentation_ShortText:
+ return value.size() <= 1024;
+
+ case ValueRepresentation_Time:
+ return value.size() <= 28;
+
+ case ValueRepresentation_UnlimitedCharacters:
+ return value.size() <= (static_cast<uint64_t>(1) << 32) - 2;
+
+ case ValueRepresentation_UniqueIdentifier:
+ return value.size() <= 64;
+
+ case ValueRepresentation_UnsignedLong:
+ return value.size() == 4;
+
+ case ValueRepresentation_Unknown:
+ return true;
+
+ case ValueRepresentation_UniversalResource:
+ return value.size() <= (static_cast<uint64_t>(1) << 32) - 2;
+
+ case ValueRepresentation_UnsignedShort:
+ return value.size() == 2;
+
+ case ValueRepresentation_UnlimitedText:
+ return value.size() <= (static_cast<uint64_t>(1) << 32) - 2;
+
+ default:
+ // Assume unsupported tags are OK
+ return true;
+ }
+ }
+
+
+ static void RemoveTagPadding(std::string& value,
+ const ValueRepresentation& vr)
+ {
+ /**
+ * Remove padding from character strings, if need be. For the time
+ * being, only the UI VR is supported.
+ * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+ **/
+
+ switch (vr)
+ {
+ case ValueRepresentation_UniqueIdentifier:
+ {
+ /**
+ * "Values with a VR of UI shall be padded with a single
+ * trailing NULL (00H) character when necessary to achieve even
+ * length."
+ **/
+
+ if (!value.empty() &&
+ value[value.size() - 1] == '\0')
+ {
+ value.resize(value.size() - 1);
+ }
+
+ break;
+ }
+
+ /**
+ * TODO implement other VR
+ **/
+
+ default:
+ // No padding is applicable to this VR
+ break;
+ }
+ }
+
+
+ static bool ReadNextTag(DicomTag& tag,
+ ValueRepresentation& vr,
+ std::string& value,
+ const char* dicom,
+ size_t size,
+ size_t& position)
+ {
+ /**
+ * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
+ * This function reads a data element with Explicit VR encoded using Little-Endian.
+ **/
+
+ if (position + 6 > size)
+ {
+ return false;
+ }
+
+ tag = DicomTag(ReadUnsignedInteger16(dicom + position),
+ ReadUnsignedInteger16(dicom + position + 2));
+
+ vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true);
+ if (vr == ValueRepresentation_NotSupported)
+ {
+ return false;
+ }
+
+ if (vr == ValueRepresentation_OtherByte ||
+ vr == ValueRepresentation_OtherDouble ||
+ vr == ValueRepresentation_OtherFloat ||
+ vr == ValueRepresentation_OtherLong ||
+ vr == ValueRepresentation_OtherWord ||
+ vr == ValueRepresentation_Sequence ||
+ vr == ValueRepresentation_UnlimitedCharacters ||
+ vr == ValueRepresentation_UniversalResource ||
+ vr == ValueRepresentation_UnlimitedText ||
+ vr == ValueRepresentation_Unknown) // Note that "UN" should never appear in the Meta Information
+ {
+ if (position + 12 > size)
+ {
+ return false;
+ }
+
+ uint32_t length = ReadUnsignedInteger32(dicom + position + 8);
+
+ if (position + 12 + length > size)
+ {
+ return false;
+ }
+
+ value.assign(dicom + position + 12, length);
+ position += (12 + length);
+ }
+ else
+ {
+ if (position + 8 > size)
+ {
+ return false;
+ }
+
+ uint16_t length = ReadUnsignedInteger16(dicom + position + 6);
+
+ if (position + 8 + length > size)
+ {
+ return false;
+ }
+
+ value.assign(dicom + position + 8, length);
+ position += (8 + length);
+ }
+
+ if (!ValidateTag(vr, value))
+ {
+ return false;
+ }
+
+ RemoveTagPadding(value, vr);
+
+ return true;
+ }
+
+
+ bool DicomMap::ParseDicomMetaInformation(DicomMap& result,
+ const char* dicom,
+ size_t size)
+ {
+ /**
+ * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html
+ * According to Table 7.1-1, besides the "DICM" DICOM prefix, the
+ * file preamble (i.e. dicom[0..127]) should not be taken into
+ * account to determine whether the file is or is not a DICOM file.
+ **/
+
+ if (size < 132 ||
+ dicom[128] != 'D' ||
+ dicom[129] != 'I' ||
+ dicom[130] != 'C' ||
+ dicom[131] != 'M')
+ {
+ return false;
+ }
+
+
+ /**
+ * The DICOM File Meta Information must be encoded using the
+ * Explicit VR Little Endian Transfer Syntax
+ * (UID=1.2.840.10008.1.2.1).
+ **/
+
+ result.Clear();
+
+ // First, we read the "File Meta Information Group Length" tag
+ // (0002,0000) to know where to stop reading the meta header
+ size_t position = 132;
+
+ DicomTag tag(0x0000, 0x0000); // Dummy initialization
+ ValueRepresentation vr;
+ std::string value;
+ if (!ReadNextTag(tag, vr, value, dicom, size, position) ||
+ tag.GetGroup() != 0x0002 ||
+ tag.GetElement() != 0x0000 ||
+ vr != ValueRepresentation_UnsignedLong ||
+ value.size() != 4)
+ {
+ return false;
+ }
+
+ size_t stopPosition = position + ReadUnsignedInteger32(value.c_str());
+ if (stopPosition > size)
+ {
+ return false;
+ }
+
+ while (position < stopPosition)
+ {
+ if (ReadNextTag(tag, vr, value, dicom, size, position))
+ {
+ result.SetValue(tag, value, IsBinaryValueRepresentation(vr));
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/Core/DicomFormat/DicomMap.h b/Core/DicomFormat/DicomMap.h
index c2816e7..58e50b8 100644
--- a/Core/DicomFormat/DicomMap.h
+++ b/Core/DicomFormat/DicomMap.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -102,16 +102,18 @@ namespace Orthanc
}
void SetValue(const DicomTag& tag,
- const std::string& str)
+ const std::string& str,
+ bool isBinary)
{
- SetValue(tag, new DicomValue(str, false));
+ SetValue(tag, new DicomValue(str, isBinary));
}
void SetValue(uint16_t group,
uint16_t element,
- const std::string& str)
+ const std::string& str,
+ bool isBinary)
{
- SetValue(group, element, new DicomValue(str, false));
+ SetValue(group, element, new DicomValue(str, isBinary));
}
bool HasTag(uint16_t group, uint16_t element) const
@@ -169,12 +171,14 @@ namespace Orthanc
static void GetMainDicomTags(std::set<DicomTag>& result);
- void Print(FILE* fp) const;
-
void GetTags(std::set<DicomTag>& tags) const;
static void LoadMainDicomTags(const DicomTag*& tags,
size_t& size,
ResourceType level);
+
+ static bool ParseDicomMetaInformation(DicomMap& result,
+ const char* dicom,
+ size_t size);
};
}
diff --git a/Core/DicomFormat/DicomTag.cpp b/Core/DicomFormat/DicomTag.cpp
index 9d876f3..12ae7a4 100644
--- a/Core/DicomFormat/DicomTag.cpp
+++ b/Core/DicomFormat/DicomTag.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomTag.h b/Core/DicomFormat/DicomTag.h
index 993f7a3..25f4259 100644
--- a/Core/DicomFormat/DicomTag.h
+++ b/Core/DicomFormat/DicomTag.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -169,4 +169,14 @@ namespace Orthanc
static const DicomTag DICOM_TAG_IMAGE_COMMENTS(0x0020, 0x4000);
static const DicomTag DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION(0x0018, 0x1400);
static const DicomTag DICOM_TAG_CONTRAST_BOLUS_AGENT(0x0018, 0x0010);
+
+ // Counting patients, studies and series
+ // https://www.medicalconnections.co.uk/kb/Counting_Studies_Series_and_Instances
+ static const DicomTag DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES(0x0020, 0x1200);
+ static const DicomTag DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES(0x0020, 0x1202);
+ static const DicomTag DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES(0x0020, 0x1204);
+ static const DicomTag DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES(0x0020, 0x1206);
+ static const DicomTag DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES(0x0020, 0x1208);
+ static const DicomTag DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES(0x0020, 0x1209);
+ static const DicomTag DICOM_TAG_SOP_CLASSES_IN_STUDY(0x0008, 0x0062);
}
diff --git a/Core/DicomFormat/DicomValue.cpp b/Core/DicomFormat/DicomValue.cpp
index 0947668..32a17b5 100644
--- a/Core/DicomFormat/DicomValue.cpp
+++ b/Core/DicomFormat/DicomValue.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/DicomFormat/DicomValue.h b/Core/DicomFormat/DicomValue.h
index 76be5cb..ad0ec54 100644
--- a/Core/DicomFormat/DicomValue.h
+++ b/Core/DicomFormat/DicomValue.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Endianness.h b/Core/Endianness.h
new file mode 100644
index 0000000..a68fd83
--- /dev/null
+++ b/Core/Endianness.h
@@ -0,0 +1,157 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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
+
+
+/********************************************************************
+ ** LINUX ARCHITECTURES
+ ********************************************************************/
+
+#if defined(__linux__)
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
+# include <endian.h>
+#endif
+
+
+/********************************************************************
+ ** WINDOWS ARCHITECTURES
+ **
+ ** On Windows x86, "host" will always be little-endian ("le").
+ ********************************************************************/
+
+#if defined(_WIN32)
+# if defined(_MSC_VER)
+// Visual Studio - http://msdn.microsoft.com/en-us/library/a3140177.aspx
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
+# define be16toh(x) _byteswap_ushort(x)
+# define be32toh(x) _byteswap_ulong(x)
+# define be64toh(x) _byteswap_uint64(x)
+# elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+// MinGW >= 4.3 - Use builtin intrinsic for byte swapping
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
+# define be16toh(x) __builtin_bswap16(x)
+# define be32toh(x) __builtin_bswap32(x)
+# define be64toh(x) __builtin_bswap64(x)
+# else
+// MinGW <= 4.2, we must manually implement the byte swapping
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 0
+# define be16toh(x) __orthanc_bswap16(x)
+# define be32toh(x) __orthanc_bswap32(x)
+# define be64toh(x) __orthanc_bswap64(x)
+# endif
+
+# define htobe16(x) be16toh(x)
+# define htobe32(x) be32toh(x)
+# define htobe64(x) be64toh(x)
+
+# define htole16(x) x
+# define htole32(x) x
+# define htole64(x) x
+
+# define le16toh(x) x
+# define le32toh(x) x
+# define le64toh(x) x
+#endif
+
+
+/********************************************************************
+ ** FREEBSD ARCHITECTURES
+ ********************************************************************/
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
+# include <arpa/inet.h>
+#endif
+
+
+/********************************************************************
+ ** APPLE ARCHITECTURES (including OS X)
+ ********************************************************************/
+
+#if defined(__APPLE__)
+# define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
+# include <libkern/OSByteOrder.h>
+# define be16toh(x) OSSwapBigToHostInt16(x)
+# define be32toh(x) OSSwapBigToHostInt32(x)
+# define be64toh(x) OSSwapBigToHostInt64(x)
+
+# define htobe16(x) OSSwapHostToBigInt16(x)
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define htobe64(x) OSSwapHostToBigInt64(x)
+
+# define htole16(x) OSSwapHostToLittleInt16(x)
+# define htole32(x) OSSwapHostToLittleInt32(x)
+# define htole64(x) OSSwapHostToLittleInt64(x)
+
+# define le16toh(x) OSSwapLittleToHostInt16(x)
+# define le32toh(x) OSSwapLittleToHostInt32(x)
+# define le64toh(x) OSSwapLittleToHostInt64(x)
+#endif
+
+
+/********************************************************************
+ ** PORTABLE (BUT SLOW) IMPLEMENTATION OF BYTE-SWAPPING
+ ********************************************************************/
+
+#if ORTHANC_HAS_BUILTIN_BYTE_SWAP != 1
+
+#include <stdint.h>
+
+static inline uint16_t __orthanc_bswap16(uint16_t a)
+{
+ return (a << 8) | (a >> 8);
+}
+
+static inline uint32_t __orthanc_bswap32(uint32_t a)
+{
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(&a);
+ return (static_cast<uint32_t>(p[0]) << 24 |
+ static_cast<uint32_t>(p[1]) << 16 |
+ static_cast<uint32_t>(p[2]) << 8 |
+ static_cast<uint32_t>(p[3]));
+}
+
+static inline uint64_t __orthanc_bswap64(uint64_t a)
+{
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(&a);
+ return (static_cast<uint64_t>(p[0]) << 56 |
+ static_cast<uint64_t>(p[1]) << 48 |
+ static_cast<uint64_t>(p[2]) << 40 |
+ static_cast<uint64_t>(p[3]) << 32 |
+ static_cast<uint64_t>(p[4]) << 24 |
+ static_cast<uint64_t>(p[5]) << 16 |
+ static_cast<uint64_t>(p[6]) << 8 |
+ static_cast<uint64_t>(p[7]));
+}
+
+#endif
diff --git a/Core/EnumerationDictionary.h b/Core/EnumerationDictionary.h
index 13e7af5..9479401 100644
--- a/Core/EnumerationDictionary.h
+++ b/Core/EnumerationDictionary.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Enumerations.cpp b/Core/Enumerations.cpp
index eec80e6..e600828 100644
--- a/Core/Enumerations.cpp
+++ b/Core/Enumerations.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -35,8 +35,10 @@
#include "OrthancException.h"
#include "Toolbox.h"
+#include "Logging.h"
#include <string.h>
+#include <cassert>
namespace Orthanc
{
@@ -212,10 +214,10 @@ namespace Orthanc
return "The specified path does not point to a directory";
case ErrorCode_HttpPortInUse:
- return "The TCP port of the HTTP server is already in use";
+ return "The TCP port of the HTTP server is privileged or already in use";
case ErrorCode_DicomPortInUse:
- return "The TCP port of the DICOM server is already in use";
+ return "The TCP port of the DICOM server is privileged or already in use";
case ErrorCode_BadHttpStatusInRest:
return "This HTTP status is not allowed in a REST API";
@@ -328,6 +330,9 @@ namespace Orthanc
case ErrorCode_NoWorklistHandler:
return "No request handler factory for DICOM C-Find Modality SCP";
+ case ErrorCode_AlreadyExistingTag:
+ return "Cannot override the value of a tag that already exists";
+
default:
if (error >= ErrorCode_START_PLUGINS)
{
@@ -718,6 +723,34 @@ namespace Orthanc
}
+ const char* EnumerationToString(PixelFormat format)
+ {
+ switch (format)
+ {
+ case PixelFormat_RGB24:
+ return "RGB24";
+
+ case PixelFormat_RGBA32:
+ return "RGBA32";
+
+ case PixelFormat_Grayscale8:
+ return "Grayscale (unsigned 8bpp)";
+
+ case PixelFormat_Grayscale16:
+ return "Grayscale (unsigned 16bpp)";
+
+ case PixelFormat_SignedGrayscale16:
+ return "Grayscale (signed 16bpp)";
+
+ case PixelFormat_Float32:
+ return "Grayscale (float 32bpp)";
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
Encoding StringToEncoding(const char* encoding)
{
std::string s(encoding);
@@ -868,6 +901,151 @@ namespace Orthanc
}
+ ValueRepresentation StringToValueRepresentation(const std::string& vr,
+ bool throwIfUnsupported)
+ {
+ if (vr == "AE")
+ {
+ return ValueRepresentation_ApplicationEntity;
+ }
+ else if (vr == "AS")
+ {
+ return ValueRepresentation_AgeString;
+ }
+ else if (vr == "AT")
+ {
+ return ValueRepresentation_AttributeTag;
+ }
+ else if (vr == "CS")
+ {
+ return ValueRepresentation_CodeString;
+ }
+ else if (vr == "DA")
+ {
+ return ValueRepresentation_Date;
+ }
+ else if (vr == "DS")
+ {
+ return ValueRepresentation_DecimalString;
+ }
+ else if (vr == "DT")
+ {
+ return ValueRepresentation_DateTime;
+ }
+ else if (vr == "FL")
+ {
+ return ValueRepresentation_FloatingPointSingle;
+ }
+ else if (vr == "FD")
+ {
+ return ValueRepresentation_FloatingPointDouble;
+ }
+ else if (vr == "IS")
+ {
+ return ValueRepresentation_IntegerString;
+ }
+ else if (vr == "LO")
+ {
+ return ValueRepresentation_LongString;
+ }
+ else if (vr == "LT")
+ {
+ return ValueRepresentation_LongText;
+ }
+ else if (vr == "OB")
+ {
+ return ValueRepresentation_OtherByte;
+ }
+ else if (vr == "OD")
+ {
+ return ValueRepresentation_OtherDouble;
+ }
+ else if (vr == "OF")
+ {
+ return ValueRepresentation_OtherFloat;
+ }
+ else if (vr == "OL")
+ {
+ return ValueRepresentation_OtherLong;
+ }
+ else if (vr == "OW")
+ {
+ return ValueRepresentation_OtherWord;
+ }
+ else if (vr == "PN")
+ {
+ return ValueRepresentation_PersonName;
+ }
+ else if (vr == "SH")
+ {
+ return ValueRepresentation_ShortString;
+ }
+ else if (vr == "SL")
+ {
+ return ValueRepresentation_SignedLong;
+ }
+ else if (vr == "SQ")
+ {
+ return ValueRepresentation_Sequence;
+ }
+ else if (vr == "SS")
+ {
+ return ValueRepresentation_SignedShort;
+ }
+ else if (vr == "ST")
+ {
+ return ValueRepresentation_ShortText;
+ }
+ else if (vr == "TM")
+ {
+ return ValueRepresentation_Time;
+ }
+ else if (vr == "UC")
+ {
+ return ValueRepresentation_UnlimitedCharacters;
+ }
+ else if (vr == "UI")
+ {
+ return ValueRepresentation_UniqueIdentifier;
+ }
+ else if (vr == "UL")
+ {
+ return ValueRepresentation_UnsignedLong;
+ }
+ else if (vr == "UN")
+ {
+ return ValueRepresentation_Unknown;
+ }
+ else if (vr == "UR")
+ {
+ return ValueRepresentation_UniversalResource;
+ }
+ else if (vr == "US")
+ {
+ return ValueRepresentation_UnsignedShort;
+ }
+ else if (vr == "UT")
+ {
+ return ValueRepresentation_UnlimitedText;
+ }
+ else
+ {
+ std::string s = "Unsupported value representation encountered: " + vr;
+
+ if (throwIfUnsupported)
+ {
+ LOG(ERROR) << s;
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ else
+ {
+ LOG(INFO) << s;
+ return ValueRepresentation_NotSupported;
+ }
+ }
+ }
+
+
unsigned int GetBytesPerPixel(PixelFormat format)
{
switch (format)
@@ -885,6 +1063,10 @@ namespace Orthanc
case PixelFormat_RGBA32:
return 4;
+ case PixelFormat_Float32:
+ assert(sizeof(float) == 4);
+ return 4;
+
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
@@ -897,7 +1079,7 @@ namespace Orthanc
std::string s = specificCharacterSet;
Toolbox::ToUpperCase(s);
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
// https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java
if (s == "ISO_IR 6" ||
s == "ISO_IR 192" ||
@@ -1046,7 +1228,7 @@ namespace Orthanc
const char* GetDicomSpecificCharacterSet(Encoding encoding)
{
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
switch (encoding)
{
case Encoding_Utf8:
@@ -1151,4 +1333,60 @@ namespace Orthanc
return (type >= FileContentType_StartUser &&
type <= FileContentType_EndUser);
}
+
+
+ bool IsBinaryValueRepresentation(ValueRepresentation vr)
+ {
+ // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+
+ switch (vr)
+ {
+ case ValueRepresentation_ApplicationEntity: // AE
+ case ValueRepresentation_AgeString: // AS
+ case ValueRepresentation_CodeString: // CS
+ case ValueRepresentation_Date: // DA
+ case ValueRepresentation_DecimalString: // DS
+ case ValueRepresentation_DateTime: // DT
+ case ValueRepresentation_IntegerString: // IS
+ case ValueRepresentation_LongString: // LO
+ case ValueRepresentation_LongText: // LT
+ case ValueRepresentation_PersonName: // PN
+ case ValueRepresentation_ShortString: // SH
+ case ValueRepresentation_ShortText: // ST
+ case ValueRepresentation_Time: // TM
+ case ValueRepresentation_UnlimitedCharacters: // UC
+ case ValueRepresentation_UniqueIdentifier: // UI (UID)
+ case ValueRepresentation_UniversalResource: // UR (URI or URL)
+ case ValueRepresentation_UnlimitedText: // UT
+ {
+ return false;
+ }
+
+ /**
+ * Below are all the VR whose character repertoire is tagged as
+ * "not applicable"
+ **/
+ case ValueRepresentation_AttributeTag: // AT (2 x uint16_t)
+ case ValueRepresentation_FloatingPointSingle: // FL (float)
+ case ValueRepresentation_FloatingPointDouble: // FD (double)
+ case ValueRepresentation_OtherByte: // OB
+ case ValueRepresentation_OtherDouble: // OD
+ case ValueRepresentation_OtherFloat: // OF
+ case ValueRepresentation_OtherLong: // OL
+ case ValueRepresentation_OtherWord: // OW
+ case ValueRepresentation_SignedLong: // SL (int32_t)
+ case ValueRepresentation_Sequence: // SQ
+ case ValueRepresentation_SignedShort: // SS (int16_t)
+ case ValueRepresentation_UnsignedLong: // UL (uint32_t)
+ case ValueRepresentation_Unknown: // UN
+ case ValueRepresentation_UnsignedShort: // US (uint16_t)
+ {
+ return true;
+ }
+
+ case ValueRepresentation_NotSupported:
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
}
diff --git a/Core/Enumerations.h b/Core/Enumerations.h
index 19b6b31..7eef119 100644
--- a/Core/Enumerations.h
+++ b/Core/Enumerations.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,6 +32,8 @@
#pragma once
+#include <string>
+
namespace Orthanc
{
enum Endianness
@@ -100,8 +102,8 @@ namespace Orthanc
ErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */,
ErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */,
ErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */,
- ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is already in use */,
- ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is already in use */,
+ ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use */,
+ ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is privileged or already in use */,
ErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */,
ErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */,
ErrorCode_PathToExecutable = 2007 /*!< Unable to get the path to the executable */,
@@ -139,6 +141,7 @@ namespace Orthanc
ErrorCode_SslDisabled = 2039 /*!< Orthanc has been built without SSL support */,
ErrorCode_CannotOrderSlices = 2040 /*!< Unable to order the slices of the series */,
ErrorCode_NoWorklistHandler = 2041 /*!< No request handler factory for DICOM C-Find Modality SCP */,
+ ErrorCode_AlreadyExistingTag = 2042 /*!< Cannot override the value of a tag that already exists */,
ErrorCode_START_PLUGINS = 1000000
};
@@ -186,7 +189,13 @@ namespace Orthanc
* {summary}{Graylevel, signed 16bpp image.}
* {description}{The image is graylevel. Each pixel is signed and stored in two bytes.}
**/
- PixelFormat_SignedGrayscale16 = 5
+ PixelFormat_SignedGrayscale16 = 5,
+
+ /**
+ * {summary}{Graylevel, floating-point image.}
+ * {description}{The image is graylevel. Each pixel is floating-point and stored in 4 bytes.}
+ **/
+ PixelFormat_Float32 = 6
};
@@ -314,7 +323,8 @@ namespace Orthanc
};
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/
+ // Specific Character Sets
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
enum Encoding
{
Encoding_Ascii,
@@ -338,7 +348,7 @@ namespace Orthanc
};
- // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.2/
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.2
enum PhotometricInterpretation
{
PhotometricInterpretation_ARGB, // Retired
@@ -375,6 +385,59 @@ namespace Orthanc
RequestOrigin_Lua
};
+ enum ServerBarrierEvent
+ {
+ ServerBarrierEvent_Stop,
+ ServerBarrierEvent_Reload // SIGHUP signal: reload configuration file
+ };
+
+ enum FileMode
+ {
+ FileMode_ReadBinary,
+ FileMode_WriteBinary
+ };
+
+ /**
+ * The value representations Orthanc knows about. They correspond to
+ * the DICOM 2016b version of the standard.
+ * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+ **/
+ enum ValueRepresentation
+ {
+ ValueRepresentation_ApplicationEntity = 1, // AE
+ ValueRepresentation_AgeString = 2, // AS
+ ValueRepresentation_AttributeTag = 3, // AT (2 x uint16_t)
+ ValueRepresentation_CodeString = 4, // CS
+ ValueRepresentation_Date = 5, // DA
+ ValueRepresentation_DecimalString = 6, // DS
+ ValueRepresentation_DateTime = 7, // DT
+ ValueRepresentation_FloatingPointSingle = 8, // FL (float)
+ ValueRepresentation_FloatingPointDouble = 9, // FD (double)
+ ValueRepresentation_IntegerString = 10, // IS
+ ValueRepresentation_LongString = 11, // LO
+ ValueRepresentation_LongText = 12, // LT
+ ValueRepresentation_OtherByte = 13, // OB
+ ValueRepresentation_OtherDouble = 14, // OD
+ ValueRepresentation_OtherFloat = 15, // OF
+ ValueRepresentation_OtherLong = 16, // OL
+ ValueRepresentation_OtherWord = 17, // OW
+ ValueRepresentation_PersonName = 18, // PN
+ ValueRepresentation_ShortString = 19, // SH
+ ValueRepresentation_SignedLong = 20, // SL (int32_t)
+ ValueRepresentation_Sequence = 21, // SQ
+ ValueRepresentation_SignedShort = 22, // SS (int16_t)
+ ValueRepresentation_ShortText = 23, // ST
+ ValueRepresentation_Time = 24, // TM
+ ValueRepresentation_UnlimitedCharacters = 25, // UC
+ ValueRepresentation_UniqueIdentifier = 26, // UI (UID)
+ ValueRepresentation_UnsignedLong = 27, // UL (uint32_t)
+ ValueRepresentation_Unknown = 28, // UN
+ ValueRepresentation_UniversalResource = 29, // UR (URI or URL)
+ ValueRepresentation_UnsignedShort = 30, // US (uint16_t)
+ ValueRepresentation_UnlimitedText = 31, // UT
+ ValueRepresentation_NotSupported // Not supported by Orthanc, or tag not in dictionary
+ };
+
/**
* WARNING: Do not change the explicit values in the enumerations
@@ -443,13 +506,18 @@ namespace Orthanc
const char* EnumerationToString(RequestOrigin origin);
+ const char* EnumerationToString(PixelFormat format);
+
Encoding StringToEncoding(const char* encoding);
ResourceType StringToResourceType(const char* type);
ImageFormat StringToImageFormat(const char* format);
- LogLevel StringToLogLevel(const char* format);
+ LogLevel StringToLogLevel(const char* level);
+
+ ValueRepresentation StringToValueRepresentation(const std::string& vr,
+ bool throwIfUnsupported);
unsigned int GetBytesPerPixel(PixelFormat format);
@@ -467,4 +535,6 @@ namespace Orthanc
HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error);
bool IsUserContentType(FileContentType type);
+
+ bool IsBinaryValueRepresentation(ValueRepresentation vr);
}
diff --git a/Core/FileStorage/FileInfo.h b/Core/FileStorage/FileInfo.h
index e6f9da2..81dc7ef 100644
--- a/Core/FileStorage/FileInfo.h
+++ b/Core/FileStorage/FileInfo.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/FileStorage/FilesystemStorage.cpp b/Core/FileStorage/FilesystemStorage.cpp
index effa16c..e282065 100644
--- a/Core/FileStorage/FilesystemStorage.cpp
+++ b/Core/FileStorage/FilesystemStorage.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -165,7 +165,7 @@ namespace Orthanc
{
for (fs::recursive_directory_iterator current(root_), end; current != end ; ++current)
{
- if (fs::is_regular_file(current->status()))
+ if (Toolbox::IsRegularFile(current->path().string()))
{
try
{
diff --git a/Core/FileStorage/FilesystemStorage.h b/Core/FileStorage/FilesystemStorage.h
index 80d5f9e..d003bc5 100644
--- a/Core/FileStorage/FilesystemStorage.h
+++ b/Core/FileStorage/FilesystemStorage.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/FileStorage/IStorageArea.h b/Core/FileStorage/IStorageArea.h
index dda93ca..7e3eafc 100644
--- a/Core/FileStorage/IStorageArea.h
+++ b/Core/FileStorage/IStorageArea.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/FileStorage/StorageAccessor.cpp b/Core/FileStorage/StorageAccessor.cpp
index dce00e8..a202d60 100644
--- a/Core/FileStorage/StorageAccessor.cpp
+++ b/Core/FileStorage/StorageAccessor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/FileStorage/StorageAccessor.h b/Core/FileStorage/StorageAccessor.h
index a3fca8f..990418a 100644
--- a/Core/FileStorage/StorageAccessor.h
+++ b/Core/FileStorage/StorageAccessor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpClient.cpp b/Core/HttpClient.cpp
index dd52212..1c98f8e 100644
--- a/Core/HttpClient.cpp
+++ b/Core/HttpClient.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -36,15 +36,18 @@
#include "Toolbox.h"
#include "OrthancException.h"
#include "Logging.h"
+#include "ChunkedBuffer.h"
#include <string.h>
#include <curl/curl.h>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/thread/mutex.hpp>
-static std::string globalCACertificates_;
-static bool globalVerifyPeers_ = true;
-static long globalTimeout_ = 0;
+#if ORTHANC_PKCS11_ENABLED == 1
+# include "Pkcs11.h"
+#endif
+
extern "C"
{
@@ -77,10 +80,100 @@ extern "C"
namespace Orthanc
{
+ class HttpClient::GlobalParameters
+ {
+ private:
+ boost::mutex mutex_;
+ bool httpsVerifyPeers_;
+ std::string httpsCACertificates_;
+ std::string proxy_;
+ long timeout_;
+
+ GlobalParameters() :
+ httpsVerifyPeers_(true),
+ timeout_(0)
+ {
+ }
+
+ public:
+ // Singleton pattern
+ static GlobalParameters& GetInstance()
+ {
+ static GlobalParameters parameters;
+ return parameters;
+ }
+
+ void ConfigureSsl(bool httpsVerifyPeers,
+ const std::string& httpsCACertificates)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ httpsVerifyPeers_ = httpsVerifyPeers;
+ httpsCACertificates_ = httpsCACertificates;
+ }
+
+ void GetSslConfiguration(bool& httpsVerifyPeers,
+ std::string& httpsCACertificates)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ httpsVerifyPeers = httpsVerifyPeers_;
+ httpsCACertificates = httpsCACertificates_;
+ }
+
+ void SetDefaultProxy(const std::string& proxy)
+ {
+ LOG(INFO) << "Setting the default proxy for HTTP client connections: " << proxy;
+
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ proxy_ = proxy;
+ }
+ }
+
+ void GetDefaultProxy(std::string& target)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ target = proxy_;
+ }
+
+ void SetDefaultTimeout(long seconds)
+ {
+ LOG(INFO) << "Setting the default timeout for HTTP client connections: " << seconds << " seconds";
+
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ timeout_ = seconds;
+ }
+ }
+
+ long GetDefaultTimeout()
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ return timeout_;
+ }
+
+#if ORTHANC_PKCS11_ENABLED == 1
+ bool IsPkcs11Initialized()
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ return Pkcs11::IsInitialized();
+ }
+
+ void InitializePkcs11(const std::string& module,
+ const std::string& pin,
+ bool verbose)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ Pkcs11::Initialize(module, pin, verbose);
+ }
+#endif
+ };
+
+
struct HttpClient::PImpl
{
CURL* curl_;
- struct curl_slist *postHeaders_;
+ struct curl_slist *defaultPostHeaders_;
+ struct curl_slist *userHeaders_;
};
@@ -92,10 +185,11 @@ namespace Orthanc
throw OrthancException(ErrorCode_BadRequest);
case HttpStatus_401_Unauthorized:
+ case HttpStatus_403_Forbidden:
throw OrthancException(ErrorCode_Unauthorized);
case HttpStatus_404_NotFound:
- throw OrthancException(ErrorCode_InexistentItem);
+ throw OrthancException(ErrorCode_UnknownResource);
default:
throw OrthancException(ErrorCode_NetworkProtocol);
@@ -103,9 +197,15 @@ namespace Orthanc
}
-
static CURLcode CheckCode(CURLcode code)
{
+ if (code == CURLE_NOT_BUILT_IN)
+ {
+ LOG(ERROR) << "Your libcurl does not contain a required feature, "
+ << "please recompile Orthanc with -DUSE_SYSTEM_CURL=OFF";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
if (code != CURLE_OK)
{
LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code));
@@ -116,27 +216,74 @@ namespace Orthanc
}
- static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *payload)
+ static size_t CurlBodyCallback(void *buffer, size_t size, size_t nmemb, void *payload)
{
- std::string& target = *(static_cast<std::string*>(payload));
+ ChunkedBuffer& target = *(static_cast<ChunkedBuffer*>(payload));
size_t length = size * nmemb;
if (length == 0)
+ {
return 0;
+ }
+ else
+ {
+ target.AddChunk(buffer, length);
+ return length;
+ }
+ }
- size_t pos = target.size();
- target.resize(pos + length);
- memcpy(&target.at(pos), buffer, length);
+ struct CurlHeaderParameters
+ {
+ bool lowerCase_;
+ HttpClient::HttpHeaders* headers_;
+ };
+
+
+ static size_t CurlHeaderCallback(void *buffer, size_t size, size_t nmemb, void *payload)
+ {
+ CurlHeaderParameters& parameters = *(static_cast<CurlHeaderParameters*>(payload));
+ assert(parameters.headers_ != NULL);
- return length;
+ size_t length = size * nmemb;
+ if (length == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ std::string s(reinterpret_cast<const char*>(buffer), length);
+ std::size_t colon = s.find(':');
+ std::size_t eol = s.find("\r\n");
+ if (colon != std::string::npos &&
+ eol != std::string::npos)
+ {
+ std::string tmp(s.substr(0, colon));
+
+ if (parameters.lowerCase_)
+ {
+ Toolbox::ToLowerCase(tmp);
+ }
+
+ std::string key = Toolbox::StripSpaces(tmp);
+
+ if (!key.empty())
+ {
+ std::string value = Toolbox::StripSpaces(s.substr(colon + 1, eol));
+ (*parameters.headers_) [key] = value;
+ }
+ }
+
+ return length;
+ }
}
void HttpClient::Setup()
{
- pimpl_->postHeaders_ = NULL;
- if ((pimpl_->postHeaders_ = curl_slist_append(pimpl_->postHeaders_, "Expect:")) == NULL)
+ pimpl_->userHeaders_ = NULL;
+ pimpl_->defaultPostHeaders_ = NULL;
+ if ((pimpl_->defaultPostHeaders_ = curl_slist_append(pimpl_->defaultPostHeaders_, "Expect:")) == NULL)
{
throw OrthancException(ErrorCode_NotEnoughMemory);
}
@@ -144,11 +291,11 @@ namespace Orthanc
pimpl_->curl_ = curl_easy_init();
if (!pimpl_->curl_)
{
- curl_slist_free_all(pimpl_->postHeaders_);
+ curl_slist_free_all(pimpl_->defaultPostHeaders_);
throw OrthancException(ErrorCode_NotEnoughMemory);
}
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlCallback));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlBodyCallback));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1));
@@ -161,37 +308,55 @@ namespace Orthanc
method_ = HttpMethod_Get;
lastStatus_ = HttpStatus_200_Ok;
isVerbose_ = false;
- timeout_ = globalTimeout_;
- verifyPeers_ = globalVerifyPeers_;
+ timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout();
+ GlobalParameters::GetInstance().GetDefaultProxy(proxy_);
+ GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_);
}
- HttpClient::HttpClient() : pimpl_(new PImpl)
+ HttpClient::HttpClient() :
+ pimpl_(new PImpl),
+ verifyPeers_(true),
+ pkcs11Enabled_(false),
+ headersToLowerCase_(true)
{
Setup();
}
- HttpClient::HttpClient(const HttpClient& other) : pimpl_(new PImpl)
+ HttpClient::HttpClient(const WebServiceParameters& service,
+ const std::string& uri) :
+ pimpl_(new PImpl),
+ verifyPeers_(true),
+ headersToLowerCase_(true)
{
Setup();
- if (other.IsVerbose())
+ if (service.GetUsername().size() != 0 &&
+ service.GetPassword().size() != 0)
{
- SetVerbose(true);
+ SetCredentials(service.GetUsername().c_str(),
+ service.GetPassword().c_str());
}
- if (other.credentials_.size() != 0)
+ if (!service.GetCertificateFile().empty())
{
- credentials_ = other.credentials_;
+ SetClientCertificate(service.GetCertificateFile(),
+ service.GetCertificateKeyFile(),
+ service.GetCertificateKeyPassword());
}
+
+ SetPkcs11Enabled(service.IsPkcs11Enabled());
+
+ SetUrl(service.GetUrl() + uri);
}
HttpClient::~HttpClient()
{
curl_easy_cleanup(pimpl_->curl_);
- curl_slist_free_all(pimpl_->postHeaders_);
+ curl_slist_free_all(pimpl_->defaultPostHeaders_);
+ ClearHeaders();
}
@@ -210,27 +375,123 @@ namespace Orthanc
}
- bool HttpClient::Apply(std::string& answer)
+ void HttpClient::AddHeader(const std::string& key,
+ const std::string& value)
+ {
+ if (key.empty())
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ std::string s = key + ": " + value;
+
+ if ((pimpl_->userHeaders_ = curl_slist_append(pimpl_->userHeaders_, s.c_str())) == NULL)
+ {
+ throw OrthancException(ErrorCode_NotEnoughMemory);
+ }
+ }
+
+
+ void HttpClient::ClearHeaders()
{
- answer.clear();
+ if (pimpl_->userHeaders_ != NULL)
+ {
+ curl_slist_free_all(pimpl_->userHeaders_);
+ pimpl_->userHeaders_ = NULL;
+ }
+ }
+
+
+ bool HttpClient::ApplyInternal(std::string& answerBody,
+ HttpHeaders* answerHeaders)
+ {
+ answerBody.clear();
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
- // Setup HTTPS-related options
+ CurlHeaderParameters headerParameters;
+
+ if (answerHeaders == NULL)
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, NULL));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, NULL));
+ }
+ else
+ {
+ headerParameters.lowerCase_ = headersToLowerCase_;
+ headerParameters.headers_ = answerHeaders;
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, &CurlHeaderCallback));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, &headerParameters));
+ }
+
#if ORTHANC_SSL_ENABLED == 1
- if (IsHttpsVerifyPeers())
+ // Setup HTTPS-related options
+
+ if (verifyPeers_)
{
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, GetHttpsCACertificates().c_str()));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str()));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 2)); // libcurl default is strict verifyhost
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1));
}
else
{
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 0));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0));
}
#endif
+ // Setup the HTTPS client certificate
+ if (!clientCertificateFile_.empty() &&
+ pkcs11Enabled_)
+ {
+ LOG(ERROR) << "Cannot enable both client certificates and PKCS#11 authentication";
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ if (pkcs11Enabled_)
+ {
+#if ORTHANC_PKCS11_ENABLED == 1
+ if (GlobalParameters::GetInstance().IsPkcs11Initialized())
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLENGINE, Pkcs11::GetEngineIdentifier()));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "ENG"));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "ENG"));
+ }
+ else
+ {
+ LOG(ERROR) << "Cannot use PKCS#11 for a HTTPS request, because it has not been initialized";
+ throw OrthancException(ErrorCode_BadSequenceOfCalls);
+ }
+#else
+ LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+#endif
+ }
+ else if (!clientCertificateFile_.empty())
+ {
+#if ORTHANC_SSL_ENABLED == 1
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM"));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str()));
+
+ if (!clientCertificateKeyPassword_.empty())
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str()));
+ }
+
+ // NB: If no "clientKeyFile_" is provided, the key must be
+ // prepended to the certificate file
+ if (!clientCertificateKeyFile_.empty())
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM"));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str()));
+ }
+#else
+ LOG(ERROR) << "This version of Orthanc is compiled without OpenSSL support, cannot use HTTPS client authentication";
+ throw OrthancException(ErrorCode_InternalError);
+#endif
+ }
+
// Reset the parameters from previous calls to Apply()
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL));
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->userHeaders_));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 0L));
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 0L));
@@ -269,7 +530,12 @@ namespace Orthanc
case HttpMethod_Post:
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L));
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_));
+
+ if (pimpl_->userHeaders_ == NULL)
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_));
+ }
+
break;
case HttpMethod_Delete:
@@ -284,7 +550,12 @@ namespace Orthanc
// CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L));
curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */
- CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_));
+
+ if (pimpl_->userHeaders_ == NULL)
+ {
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_));
+ }
+
break;
default:
@@ -312,6 +583,9 @@ namespace Orthanc
CURLcode code;
long status = 0;
+ ChunkedBuffer buffer;
+ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &buffer));
+
if (boost::starts_with(url_, "https://"))
{
code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status);
@@ -333,17 +607,31 @@ namespace Orthanc
lastStatus_ = static_cast<HttpStatus>(status);
}
- return (status >= 200 && status < 300);
+ bool success = (status >= 200 && status < 300);
+
+ if (success)
+ {
+ buffer.Flatten(answerBody);
+ }
+ else
+ {
+ answerBody.clear();
+ LOG(INFO) << "Error in HTTP request, received HTTP status " << status
+ << " (" << EnumerationToString(lastStatus_) << ")";
+ }
+
+ return success;
}
- bool HttpClient::Apply(Json::Value& answer)
+ bool HttpClient::ApplyInternal(Json::Value& answerBody,
+ HttpClient::HttpHeaders* answerHeaders)
{
std::string s;
- if (Apply(s))
+ if (ApplyInternal(s, answerHeaders))
{
Json::Reader reader;
- return reader.parse(s, answer);
+ return reader.parse(s, answerBody);
}
else
{
@@ -358,75 +646,144 @@ namespace Orthanc
credentials_ = std::string(username) + ":" + std::string(password);
}
-
- const std::string& HttpClient::GetHttpsCACertificates() const
- {
- if (caCertificates_.empty())
- {
- return globalCACertificates_;
- }
- else
- {
- return caCertificates_;
- }
- }
-
- void HttpClient::GlobalInitialize(bool httpsVerifyPeers,
- const std::string& httpsVerifyCertificates)
+ void HttpClient::ConfigureSsl(bool httpsVerifyPeers,
+ const std::string& httpsVerifyCertificates)
{
- globalVerifyPeers_ = httpsVerifyPeers;
- globalCACertificates_ = httpsVerifyCertificates;
-
#if ORTHANC_SSL_ENABLED == 1
if (httpsVerifyPeers)
{
- if (globalCACertificates_.empty())
+ if (httpsVerifyCertificates.empty())
{
LOG(WARNING) << "No certificates are provided to validate peers, "
<< "set \"HttpsCACertificates\" if you need to do HTTPS requests";
}
else
{
- LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << globalCACertificates_;
+ LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << httpsVerifyCertificates;
}
}
else
{
- LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!";
+ LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled";
}
#endif
- CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT));
+ GlobalParameters::GetInstance().ConfigureSsl(httpsVerifyPeers, httpsVerifyCertificates);
}
+ void HttpClient::GlobalInitialize()
+ {
+#if ORTHANC_SSL_ENABLED == 1
+ CheckCode(curl_global_init(CURL_GLOBAL_ALL));
+#else
+ CheckCode(curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL));
+#endif
+
+ CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT));
+ }
+
+
void HttpClient::GlobalFinalize()
{
curl_global_cleanup();
- }
+#if ORTHANC_PKCS11_ENABLED == 1
+ Pkcs11::Finalize();
+#endif
+ }
+
+ void HttpClient::SetDefaultProxy(const std::string& proxy)
+ {
+ GlobalParameters::GetInstance().SetDefaultProxy(proxy);
+ }
+
+
void HttpClient::SetDefaultTimeout(long timeout)
{
- LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds";
- globalTimeout_ = timeout;
+ GlobalParameters::GetInstance().SetDefaultTimeout(timeout);
+ }
+
+
+ void HttpClient::ApplyAndThrowException(std::string& answerBody)
+ {
+ if (!Apply(answerBody))
+ {
+ ThrowException(GetLastStatus());
+ }
+ }
+
+
+ void HttpClient::ApplyAndThrowException(Json::Value& answerBody)
+ {
+ if (!Apply(answerBody))
+ {
+ ThrowException(GetLastStatus());
+ }
}
- void HttpClient::ApplyAndThrowException(std::string& answer)
+ void HttpClient::ApplyAndThrowException(std::string& answerBody,
+ HttpHeaders& answerHeaders)
{
- if (!Apply(answer))
+ if (!Apply(answerBody, answerHeaders))
{
ThrowException(GetLastStatus());
}
}
- void HttpClient::ApplyAndThrowException(Json::Value& answer)
+
+ void HttpClient::ApplyAndThrowException(Json::Value& answerBody,
+ HttpHeaders& answerHeaders)
{
- if (!Apply(answer))
+ if (!Apply(answerBody, answerHeaders))
{
ThrowException(GetLastStatus());
}
}
+
+
+ void HttpClient::SetClientCertificate(const std::string& certificateFile,
+ const std::string& certificateKeyFile,
+ const std::string& certificateKeyPassword)
+ {
+ if (certificateFile.empty())
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ if (!Toolbox::IsRegularFile(certificateFile))
+ {
+ LOG(ERROR) << "Cannot open certificate file: " << certificateFile;
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ if (!certificateKeyFile.empty() &&
+ !Toolbox::IsRegularFile(certificateKeyFile))
+ {
+ LOG(ERROR) << "Cannot open key file: " << certificateKeyFile;
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ clientCertificateFile_ = certificateFile;
+ clientCertificateKeyFile_ = certificateKeyFile;
+ clientCertificateKeyPassword_ = certificateKeyPassword;
+ }
+
+
+ void HttpClient::InitializePkcs11(const std::string& module,
+ const std::string& pin,
+ bool verbose)
+ {
+#if ORTHANC_PKCS11_ENABLED == 1
+ LOG(INFO) << "Initializing PKCS#11 using " << module
+ << (pin.empty() ? " (no PIN provided)" : " (PIN is provided)");
+ GlobalParameters::GetInstance().InitializePkcs11(module, pin, verbose);
+#else
+ LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+#endif
+ }
}
diff --git a/Core/HttpClient.h b/Core/HttpClient.h
index c348ed7..2576644 100644
--- a/Core/HttpClient.h
+++ b/Core/HttpClient.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -33,6 +33,7 @@
#pragma once
#include "Enumerations.h"
+#include "WebServiceParameters.h"
#include <string>
#include <boost/shared_ptr.hpp>
@@ -42,7 +43,12 @@ namespace Orthanc
{
class HttpClient
{
+ public:
+ typedef std::map<std::string, std::string> HttpHeaders;
+
private:
+ class GlobalParameters;
+
struct PImpl;
boost::shared_ptr<PImpl> pimpl_;
@@ -56,16 +62,29 @@ namespace Orthanc
std::string proxy_;
bool verifyPeers_;
std::string caCertificates_;
+ std::string clientCertificateFile_;
+ std::string clientCertificateKeyFile_;
+ std::string clientCertificateKeyPassword_;
+ bool pkcs11Enabled_;
+ bool headersToLowerCase_;
void Setup();
- void operator= (const HttpClient&); // Forbidden
+ void operator= (const HttpClient&); // Assignment forbidden
+ HttpClient(const HttpClient& base); // Copy forbidden
- public:
- HttpClient(const HttpClient& base);
+ bool ApplyInternal(std::string& answerBody,
+ HttpHeaders* answerHeaders);
+
+ bool ApplyInternal(Json::Value& answerBody,
+ HttpHeaders* answerHeaders);
+ public:
HttpClient();
+ HttpClient(const WebServiceParameters& service,
+ const std::string& uri);
+
~HttpClient();
void SetUrl(const char* url)
@@ -125,9 +144,32 @@ namespace Orthanc
return isVerbose_;
}
- bool Apply(std::string& answer);
+ void AddHeader(const std::string& key,
+ const std::string& value);
+
+ void ClearHeaders();
+
+ bool Apply(std::string& answerBody)
+ {
+ return ApplyInternal(answerBody, NULL);
+ }
+
+ bool Apply(Json::Value& answerBody)
+ {
+ return ApplyInternal(answerBody, NULL);
+ }
+
+ bool Apply(std::string& answerBody,
+ HttpHeaders& answerHeaders)
+ {
+ return ApplyInternal(answerBody, &answerHeaders);
+ }
- bool Apply(Json::Value& answer);
+ bool Apply(Json::Value& answerBody,
+ HttpHeaders& answerHeaders)
+ {
+ return ApplyInternal(answerBody, &answerHeaders);
+ }
HttpStatus GetLastStatus() const
{
@@ -157,17 +199,73 @@ namespace Orthanc
caCertificates_ = certificates;
}
- const std::string& GetHttpsCACertificates() const;
+ const std::string& GetHttpsCACertificates() const
+ {
+ return caCertificates_;
+ }
+
+ void SetClientCertificate(const std::string& certificateFile,
+ const std::string& certificateKeyFile,
+ const std::string& certificateKeyPassword);
+
+ void SetPkcs11Enabled(bool enabled)
+ {
+ pkcs11Enabled_ = enabled;
+ }
+
+ bool IsPkcs11Enabled() const
+ {
+ return pkcs11Enabled_;
+ }
+
+ const std::string& GetClientCertificateFile() const
+ {
+ return clientCertificateFile_;
+ }
+
+ const std::string& GetClientCertificateKeyFile() const
+ {
+ return clientCertificateKeyFile_;
+ }
+
+ const std::string& GetClientCertificateKeyPassword() const
+ {
+ return clientCertificateKeyPassword_;
+ }
+
+ void SetConvertHeadersToLowerCase(bool lowerCase)
+ {
+ headersToLowerCase_ = lowerCase;
+ }
+
+ bool IsConvertHeadersToLowerCase() const
+ {
+ return headersToLowerCase_;
+ }
- static void GlobalInitialize(bool httpsVerifyPeers,
- const std::string& httpsCACertificates);
+ static void GlobalInitialize();
static void GlobalFinalize();
+ static void InitializePkcs11(const std::string& module,
+ const std::string& pin,
+ bool verbose);
+
+ static void ConfigureSsl(bool httpsVerifyPeers,
+ const std::string& httpsCACertificates);
+
+ static void SetDefaultProxy(const std::string& proxy);
+
static void SetDefaultTimeout(long timeout);
- void ApplyAndThrowException(std::string& answer);
+ void ApplyAndThrowException(std::string& answerBody);
+
+ void ApplyAndThrowException(Json::Value& answerBody);
+
+ void ApplyAndThrowException(std::string& answerBody,
+ HttpHeaders& answerHeaders);
- void ApplyAndThrowException(Json::Value& answer);
+ void ApplyAndThrowException(Json::Value& answerBody,
+ HttpHeaders& answerHeaders);
};
}
diff --git a/Core/HttpServer/BufferHttpSender.cpp b/Core/HttpServer/BufferHttpSender.cpp
index d9714a4..a61389d 100644
--- a/Core/HttpServer/BufferHttpSender.cpp
+++ b/Core/HttpServer/BufferHttpSender.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/BufferHttpSender.h b/Core/HttpServer/BufferHttpSender.h
index 0d7b1c0..646a5e5 100644
--- a/Core/HttpServer/BufferHttpSender.h
+++ b/Core/HttpServer/BufferHttpSender.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp
index 466a4bd..175fee5 100644
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/EmbeddedResourceHttpHandler.h b/Core/HttpServer/EmbeddedResourceHttpHandler.h
index 9dc625a..d262eb5 100644
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.h
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/FilesystemHttpHandler.cpp b/Core/HttpServer/FilesystemHttpHandler.cpp
index bf9d680..84fe5cf 100644
--- a/Core/HttpServer/FilesystemHttpHandler.cpp
+++ b/Core/HttpServer/FilesystemHttpHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -95,8 +95,10 @@ namespace Orthanc
#endif
std::string h = Toolbox::FlattenUri(uri) + "/" + f;
- if (fs::is_regular_file(it->status()))
+ if (Toolbox::IsRegularFile(it->path().string()))
+ {
s += "<li><a href=\"" + h + "\">" + f + "</a></li>";
+ }
}
s += " </ul>";
@@ -156,7 +158,7 @@ namespace Orthanc
p /= uri[i];
}
- if (fs::exists(p) && fs::is_regular_file(p))
+ if (Toolbox::IsRegularFile(p.string()))
{
FilesystemHttpSender sender(p);
output.Answer(sender); // TODO COMPRESSION
diff --git a/Core/HttpServer/FilesystemHttpHandler.h b/Core/HttpServer/FilesystemHttpHandler.h
index 8430642..2ca059b 100644
--- a/Core/HttpServer/FilesystemHttpHandler.h
+++ b/Core/HttpServer/FilesystemHttpHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/FilesystemHttpSender.cpp b/Core/HttpServer/FilesystemHttpSender.cpp
index 54c8d59..cdef503 100644
--- a/Core/HttpServer/FilesystemHttpSender.cpp
+++ b/Core/HttpServer/FilesystemHttpSender.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/FilesystemHttpSender.h b/Core/HttpServer/FilesystemHttpSender.h
index ab2f6de..367835f 100644
--- a/Core/HttpServer/FilesystemHttpSender.h
+++ b/Core/HttpServer/FilesystemHttpSender.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpContentNegociation.cpp b/Core/HttpServer/HttpContentNegociation.cpp
index 224216f..ac8731c 100644
--- a/Core/HttpServer/HttpContentNegociation.cpp
+++ b/Core/HttpServer/HttpContentNegociation.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpContentNegociation.h b/Core/HttpServer/HttpContentNegociation.h
index c8fcffa..2c36821 100644
--- a/Core/HttpServer/HttpContentNegociation.h
+++ b/Core/HttpServer/HttpContentNegociation.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpFileSender.cpp b/Core/HttpServer/HttpFileSender.cpp
index a5b6ea9..2f520cf 100644
--- a/Core/HttpServer/HttpFileSender.cpp
+++ b/Core/HttpServer/HttpFileSender.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpFileSender.h b/Core/HttpServer/HttpFileSender.h
index 61a6af2..e653b68 100644
--- a/Core/HttpServer/HttpFileSender.h
+++ b/Core/HttpServer/HttpFileSender.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpOutput.cpp b/Core/HttpServer/HttpOutput.cpp
index b749e90..7e6bc34 100644
--- a/Core/HttpServer/HttpOutput.cpp
+++ b/Core/HttpServer/HttpOutput.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -291,8 +291,7 @@ namespace Orthanc
const char* message,
size_t messageSize)
{
- if (status == HttpStatus_200_Ok ||
- status == HttpStatus_301_MovedPermanently ||
+ if (status == HttpStatus_301_MovedPermanently ||
status == HttpStatus_401_Unauthorized ||
status == HttpStatus_405_MethodNotAllowed)
{
@@ -300,7 +299,6 @@ namespace Orthanc
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
- stateMachine_.ClearHeaders();
stateMachine_.SetHttpStatus(status);
stateMachine_.SendBody(message, messageSize);
}
diff --git a/Core/HttpServer/HttpOutput.h b/Core/HttpServer/HttpOutput.h
index 8e2386b..ea0e889 100644
--- a/Core/HttpServer/HttpOutput.h
+++ b/Core/HttpServer/HttpOutput.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpStreamTranscoder.cpp b/Core/HttpServer/HttpStreamTranscoder.cpp
index 5a8b1c2..8c865a4 100644
--- a/Core/HttpServer/HttpStreamTranscoder.cpp
+++ b/Core/HttpServer/HttpStreamTranscoder.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpStreamTranscoder.h b/Core/HttpServer/HttpStreamTranscoder.h
index db0b422..6afe583 100644
--- a/Core/HttpServer/HttpStreamTranscoder.h
+++ b/Core/HttpServer/HttpStreamTranscoder.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/HttpToolbox.cpp b/Core/HttpServer/HttpToolbox.cpp
index c64be33..be69f52 100644
--- a/Core/HttpServer/HttpToolbox.cpp
+++ b/Core/HttpServer/HttpToolbox.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -41,7 +41,7 @@
#include "StringHttpOutput.h"
-static const char* LOCALHOST = "localhost";
+static const char* LOCALHOST = "127.0.0.1";
diff --git a/Core/HttpServer/HttpToolbox.h b/Core/HttpServer/HttpToolbox.h
index 673c4f5..7f4141c 100644
--- a/Core/HttpServer/HttpToolbox.h
+++ b/Core/HttpServer/HttpToolbox.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/IHttpHandler.h b/Core/HttpServer/IHttpHandler.h
index 74c749c..f99cbb5 100644
--- a/Core/HttpServer/IHttpHandler.h
+++ b/Core/HttpServer/IHttpHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/IHttpOutputStream.h b/Core/HttpServer/IHttpOutputStream.h
index 756e71f..822aa7a 100644
--- a/Core/HttpServer/IHttpOutputStream.h
+++ b/Core/HttpServer/IHttpOutputStream.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/IHttpStreamAnswer.h b/Core/HttpServer/IHttpStreamAnswer.h
index cc6c212..0e5a54c 100644
--- a/Core/HttpServer/IHttpStreamAnswer.h
+++ b/Core/HttpServer/IHttpStreamAnswer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/HttpServer/IHttpOutputStream.h b/Core/HttpServer/IIncomingHttpRequestFilter.h
similarity index 77%
copy from Core/HttpServer/IHttpOutputStream.h
copy to Core/HttpServer/IIncomingHttpRequestFilter.h
index 756e71f..eff47af 100644
--- a/Core/HttpServer/IHttpOutputStream.h
+++ b/Core/HttpServer/IIncomingHttpRequestFilter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,22 +32,21 @@
#pragma once
-#include "../Enumerations.h"
-
-#include <string>
-#include <boost/noncopyable.hpp>
+#include "IHttpHandler.h"
namespace Orthanc
{
- class IHttpOutputStream : public boost::noncopyable
+ class IIncomingHttpRequestFilter : public boost::noncopyable
{
public:
- virtual ~IHttpOutputStream()
+ virtual ~IIncomingHttpRequestFilter()
{
}
- virtual void OnHttpStatusReceived(HttpStatus status) = 0;
-
- virtual void Send(bool isHeader, const void* buffer, size_t length) = 0;
+ virtual bool IsAllowed(HttpMethod method,
+ const char* uri,
+ const char* ip,
+ const char* username,
+ const IHttpHandler::Arguments& httpHeaders) const = 0;
};
}
diff --git a/Core/HttpServer/MongooseServer.cpp b/Core/HttpServer/MongooseServer.cpp
index ba753de..8270e76 100644
--- a/Core/HttpServer/MongooseServer.cpp
+++ b/Core/HttpServer/MongooseServer.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -597,8 +597,11 @@ namespace Orthanc
for (int i = 0; i < request->num_headers; i++)
{
std::string name = request->http_headers[i].name;
+ std::string value = request->http_headers[i].value;
+
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
- headers.insert(std::make_pair(name, request->http_headers[i].value));
+ headers.insert(std::make_pair(name, value));
+ VLOG(1) << "HTTP header: [" << name << "]: [" << value << "]";
}
if (that->IsHttpCompressionEnabled())
@@ -645,9 +648,10 @@ namespace Orthanc
const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter();
if (filter != NULL)
{
- if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str()))
+ if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str(), headers))
{
- output.SendUnauthorized(ORTHANC_REALM);
+ //output.SendUnauthorized(ORTHANC_REALM);
+ output.SendStatus(HttpStatus_403_Forbidden);
return;
}
}
diff --git a/Core/HttpServer/MongooseServer.h b/Core/HttpServer/MongooseServer.h
index cc4760c..2e0e5ad 100644
--- a/Core/HttpServer/MongooseServer.h
+++ b/Core/HttpServer/MongooseServer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,7 +32,7 @@
#pragma once
-#include "IHttpHandler.h"
+#include "IIncomingHttpRequestFilter.h"
#include "../OrthancException.h"
@@ -46,21 +46,7 @@ namespace Orthanc
{
class ChunkStore;
- class IIncomingHttpRequestFilter
- {
- public:
- virtual ~IIncomingHttpRequestFilter()
- {
- }
-
- virtual bool IsAllowed(HttpMethod method,
- const char* uri,
- const char* ip,
- const char* username) const = 0;
- };
-
-
- class IHttpExceptionFormatter
+ class IHttpExceptionFormatter : public boost::noncopyable
{
public:
virtual ~IHttpExceptionFormatter()
diff --git a/Core/HttpServer/StringHttpOutput.cpp b/Core/HttpServer/StringHttpOutput.cpp
index d32eb42..8e6699a 100644
--- a/Core/HttpServer/StringHttpOutput.cpp
+++ b/Core/HttpServer/StringHttpOutput.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -39,9 +39,18 @@ namespace Orthanc
{
void StringHttpOutput::OnHttpStatusReceived(HttpStatus status)
{
- if (status != HttpStatus_200_Ok)
+ switch (status)
{
- throw OrthancException(ErrorCode_BadRequest);
+ case HttpStatus_200_Ok:
+ found_ = true;
+ break;
+
+ case HttpStatus_404_NotFound:
+ found_ = false;
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_BadRequest);
}
}
@@ -52,4 +61,16 @@ namespace Orthanc
buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
}
}
+
+ void StringHttpOutput::GetOutput(std::string& output)
+ {
+ if (found_)
+ {
+ buffer_.Flatten(output);
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
+ }
+ }
}
diff --git a/Core/HttpServer/StringHttpOutput.h b/Core/HttpServer/StringHttpOutput.h
index ec9f5bd..fab996f 100644
--- a/Core/HttpServer/StringHttpOutput.h
+++ b/Core/HttpServer/StringHttpOutput.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -41,16 +41,18 @@ namespace Orthanc
class StringHttpOutput : public IHttpOutputStream
{
private:
+ bool found_;
ChunkedBuffer buffer_;
public:
+ StringHttpOutput() : found_(false)
+ {
+ }
+
virtual void OnHttpStatusReceived(HttpStatus status);
virtual void Send(bool isHeader, const void* buffer, size_t length);
- void GetOutput(std::string& output)
- {
- buffer_.Flatten(output);
- }
+ void GetOutput(std::string& output);
};
}
diff --git a/Core/ICommand.h b/Core/ICommand.h
index 1c8bea7..a0805b9 100644
--- a/Core/ICommand.h
+++ b/Core/ICommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/IDynamicObject.h b/Core/IDynamicObject.h
index 225b6b8..8c36617 100644
--- a/Core/IDynamicObject.h
+++ b/Core/IDynamicObject.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/Font.cpp b/Core/Images/Font.cpp
index fa8254a..5253ea2 100644
--- a/Core/Images/Font.cpp
+++ b/Core/Images/Font.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/Font.h b/Core/Images/Font.h
index 43d8f54..825d2d8 100644
--- a/Core/Images/Font.h
+++ b/Core/Images/Font.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/FontRegistry.cpp b/Core/Images/FontRegistry.cpp
index c49fbf1..3035b88 100644
--- a/Core/Images/FontRegistry.cpp
+++ b/Core/Images/FontRegistry.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/FontRegistry.h b/Core/Images/FontRegistry.h
index 627cb70..ddaca49 100644
--- a/Core/Images/FontRegistry.h
+++ b/Core/Images/FontRegistry.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/ImageBuffer.h b/Core/Images/IImageWriter.h
similarity index 50%
copy from Core/Images/ImageBuffer.h
copy to Core/Images/IImageWriter.h
index d278af5..d8c446d 100644
--- a/Core/Images/ImageBuffer.h
+++ b/Core/Images/IImageWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -33,83 +33,51 @@
#pragma once
#include "ImageAccessor.h"
+#include "../Toolbox.h"
-#include <vector>
-#include <stdint.h>
#include <boost/noncopyable.hpp>
namespace Orthanc
{
- class ImageBuffer : public boost::noncopyable
+ class IImageWriter : public boost::noncopyable
{
- private:
- bool changed_;
-
- bool forceMinimalPitch_; // Currently unused
- PixelFormat format_;
- unsigned int width_;
- unsigned int height_;
- unsigned int pitch_;
- void *buffer_;
-
- void Initialize();
-
- void Allocate();
-
- void Deallocate();
-
- public:
- ImageBuffer(PixelFormat format,
- unsigned int width,
- unsigned int height);
-
- ImageBuffer()
- {
- Initialize();
- }
-
- ~ImageBuffer()
- {
- Deallocate();
- }
-
- PixelFormat GetFormat() const
+ protected:
+ virtual void WriteToMemoryInternal(std::string& compressed,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer) = 0;
+
+ virtual void WriteToFileInternal(const std::string& path,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer)
{
- return format_;
+ std::string compressed;
+ WriteToMemoryInternal(compressed, width, height, pitch, format, buffer);
+ Toolbox::WriteFile(compressed, path);
}
- void SetFormat(PixelFormat format);
-
- unsigned int GetWidth() const
+ public:
+ virtual ~IImageWriter()
{
- return width_;
}
- void SetWidth(unsigned int width);
-
- unsigned int GetHeight() const
+ virtual void WriteToMemory(std::string& compressed,
+ const ImageAccessor& accessor)
{
- return height_;
+ WriteToMemoryInternal(compressed, accessor.GetWidth(), accessor.GetHeight(),
+ accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
}
- void SetHeight(unsigned int height);
-
- unsigned int GetBytesPerPixel() const
+ virtual void WriteToFile(const std::string& path,
+ const ImageAccessor& accessor)
{
- return ::Orthanc::GetBytesPerPixel(format_);
+ WriteToFileInternal(path, accessor.GetWidth(), accessor.GetHeight(),
+ accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
}
-
- ImageAccessor GetAccessor();
-
- ImageAccessor GetConstAccessor();
-
- bool IsMinimalPitchForced() const
- {
- return forceMinimalPitch_;
- }
-
- void SetMinimalPitchForced(bool force);
-
- void AcquireOwnership(ImageBuffer& other);
};
}
diff --git a/Core/Images/Image.h b/Core/Images/Image.h
index df3e44b..16877f0 100644
--- a/Core/Images/Image.h
+++ b/Core/Images/Image.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/ImageAccessor.cpp b/Core/Images/ImageAccessor.cpp
index bc65925..bd7a05a 100644
--- a/Core/Images/ImageAccessor.cpp
+++ b/Core/Images/ImageAccessor.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -65,7 +65,7 @@ namespace Orthanc
for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
{
- s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+ s += boost::lexical_cast<std::string>(static_cast<double>(*p)) + " ";
}
target.AddChunk(s);
@@ -121,7 +121,7 @@ namespace Orthanc
{
if (buffer_ != NULL)
{
- return reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
+ return buffer_ + y * pitch_;
}
else
{
@@ -143,7 +143,7 @@ namespace Orthanc
if (buffer_ != NULL)
{
- return reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
+ return buffer_ + y * pitch_;
}
else
{
@@ -174,9 +174,12 @@ namespace Orthanc
width_ = width;
height_ = height;
pitch_ = pitch;
- buffer_ = const_cast<void*>(buffer);
+ buffer_ = reinterpret_cast<uint8_t*>(const_cast<void*>(buffer));
- assert(GetBytesPerPixel() * width_ <= pitch_);
+ if (GetBytesPerPixel() * width_ > pitch_)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
}
@@ -191,9 +194,12 @@ namespace Orthanc
width_ = width;
height_ = height;
pitch_ = pitch;
- buffer_ = buffer;
+ buffer_ = reinterpret_cast<uint8_t*>(buffer);
- assert(GetBytesPerPixel() * width_ <= pitch_);
+ if (GetBytesPerPixel() * width_ > pitch_)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
}
@@ -215,6 +221,10 @@ namespace Orthanc
ToMatlabStringInternal<int16_t>(buffer, *this);
break;
+ case PixelFormat_Float32:
+ ToMatlabStringInternal<float>(buffer, *this);
+ break;
+
case PixelFormat_RGB24:
RGB24ToMatlabString(buffer, *this);
break;
@@ -226,4 +236,43 @@ namespace Orthanc
buffer.Flatten(target);
}
+
+
+ ImageAccessor ImageAccessor::GetRegion(unsigned int x,
+ unsigned int y,
+ unsigned int width,
+ unsigned int height) const
+ {
+ if (x + width > width_ ||
+ y + height > height_)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ ImageAccessor result;
+
+ if (width == 0 ||
+ height == 0)
+ {
+ result.AssignWritable(format_, 0, 0, 0, NULL);
+ }
+ else
+ {
+ uint8_t* p = (buffer_ +
+ y * pitch_ +
+ x * GetBytesPerPixel());
+
+ if (readOnly_)
+ {
+ result.AssignReadOnly(format_, width, height, pitch_, p);
+ }
+ else
+ {
+ result.AssignWritable(format_, width, height, pitch_, p);
+ }
+ }
+
+ return result;
+ }
+
}
diff --git a/Core/Images/ImageAccessor.h b/Core/Images/ImageAccessor.h
index 002598b..02d86d4 100644
--- a/Core/Images/ImageAccessor.h
+++ b/Core/Images/ImageAccessor.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -35,6 +35,7 @@
#include "../Enumerations.h"
#include <string>
+#include <stdint.h>
namespace Orthanc
{
@@ -46,7 +47,7 @@ namespace Orthanc
unsigned int width_;
unsigned int height_;
unsigned int pitch_;
- void *buffer_;
+ uint8_t *buffer_;
public:
ImageAccessor()
@@ -119,5 +120,10 @@ namespace Orthanc
void *buffer);
void ToMatlabString(std::string& target) const;
+
+ ImageAccessor GetRegion(unsigned int x,
+ unsigned int y,
+ unsigned int width,
+ unsigned int height) const;
};
}
diff --git a/Core/Images/ImageBuffer.cpp b/Core/Images/ImageBuffer.cpp
index 22345a2..a932e47 100644
--- a/Core/Images/ImageBuffer.cpp
+++ b/Core/Images/ImageBuffer.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/ImageBuffer.h b/Core/Images/ImageBuffer.h
index d278af5..381269e 100644
--- a/Core/Images/ImageBuffer.h
+++ b/Core/Images/ImageBuffer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/ImageProcessing.cpp b/Core/Images/ImageProcessing.cpp
index b776ee7..a9405b1 100644
--- a/Core/Images/ImageProcessing.cpp
+++ b/Core/Images/ImageProcessing.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -75,9 +75,28 @@ namespace Orthanc
}
+ template <typename SourceType>
+ static void ConvertGrayscaleToFloat(ImageAccessor& target,
+ const ImageAccessor& source)
+ {
+ assert(sizeof(float) == 4);
+
+ for (unsigned int y = 0; y < source.GetHeight(); y++)
+ {
+ float* t = reinterpret_cast<float*>(target.GetRow(y));
+ const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
+
+ for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
+ {
+ *t = static_cast<float>(*s);
+ }
+ }
+ }
+
+
template <typename TargetType>
static void ConvertColorToGrayscale(ImageAccessor& target,
- const ImageAccessor& source)
+ const ImageAccessor& source)
{
assert(source.GetFormat() == PixelFormat_RGB24);
@@ -378,6 +397,27 @@ namespace Orthanc
return;
}
+ if (target.GetFormat() == PixelFormat_Float32 &&
+ source.GetFormat() == PixelFormat_Grayscale8)
+ {
+ ConvertGrayscaleToFloat<uint8_t>(target, source);
+ return;
+ }
+
+ if (target.GetFormat() == PixelFormat_Float32 &&
+ source.GetFormat() == PixelFormat_Grayscale16)
+ {
+ ConvertGrayscaleToFloat<uint16_t>(target, source);
+ return;
+ }
+
+ if (target.GetFormat() == PixelFormat_Float32 &&
+ source.GetFormat() == PixelFormat_SignedGrayscale16)
+ {
+ ConvertGrayscaleToFloat<int16_t>(target, source);
+ return;
+ }
+
if (target.GetFormat() == PixelFormat_Grayscale8 &&
source.GetFormat() == PixelFormat_RGBA32)
{
@@ -438,6 +478,47 @@ namespace Orthanc
return;
}
+ if (target.GetFormat() == PixelFormat_RGB24 &&
+ source.GetFormat() == PixelFormat_Grayscale8)
+ {
+ for (unsigned int y = 0; y < source.GetHeight(); y++)
+ {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+ uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+ for (unsigned int x = 0; x < source.GetWidth(); x++)
+ {
+ q[0] = *p;
+ q[1] = *p;
+ q[2] = *p;
+ p += 1;
+ q += 3;
+ }
+ }
+
+ return;
+ }
+
+ if (target.GetFormat() == PixelFormat_RGBA32 &&
+ source.GetFormat() == PixelFormat_Grayscale8)
+ {
+ for (unsigned int y = 0; y < source.GetHeight(); y++)
+ {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+ uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+ for (unsigned int x = 0; x < source.GetWidth(); x++)
+ {
+ q[0] = *p;
+ q[1] = *p;
+ q[2] = *p;
+ q[3] = 255;
+ p += 1;
+ q += 4;
+ }
+ }
+
+ return;
+ }
+
throw OrthancException(ErrorCode_NotImplemented);
}
@@ -460,6 +541,11 @@ namespace Orthanc
SetInternal<int16_t>(image, value);
return;
+ case PixelFormat_Float32:
+ assert(sizeof(float) == 4);
+ SetInternal<float>(image, value);
+ return;
+
default:
throw OrthancException(ErrorCode_NotImplemented);
}
diff --git a/Core/Images/ImageProcessing.h b/Core/Images/ImageProcessing.h
index b2a18c8..9c4ab81 100644
--- a/Core/Images/ImageProcessing.h
+++ b/Core/Images/ImageProcessing.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/JpegErrorManager.cpp b/Core/Images/JpegErrorManager.cpp
index c6790fe..42c3842 100644
--- a/Core/Images/JpegErrorManager.cpp
+++ b/Core/Images/JpegErrorManager.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/JpegErrorManager.h b/Core/Images/JpegErrorManager.h
index c9010fd..610cd73 100644
--- a/Core/Images/JpegErrorManager.h
+++ b/Core/Images/JpegErrorManager.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Images/JpegReader.cpp b/Core/Images/JpegReader.cpp
index 25aa07a..9ddcb37 100644
--- a/Core/Images/JpegReader.cpp
+++ b/Core/Images/JpegReader.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -36,6 +36,7 @@
#include "JpegErrorManager.h"
#include "../OrthancException.h"
#include "../Logging.h"
+#include "../Toolbox.h"
namespace Orthanc
{
@@ -93,9 +94,9 @@ namespace Orthanc
}
- void JpegReader::ReadFromFile(const char* filename)
+ void JpegReader::ReadFromFile(const std::string& filename)
{
- FILE* fp = fopen(filename, "rb");
+ FILE* fp = Toolbox::OpenFile(filename, FileMode_ReadBinary);
if (!fp)
{
throw OrthancException(ErrorCode_InexistentFile);
diff --git a/Core/Images/JpegReader.h b/Core/Images/JpegReader.h
index 9f87945..5cb5551 100644
--- a/Core/Images/JpegReader.h
+++ b/Core/Images/JpegReader.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -35,21 +35,19 @@
#include "ImageAccessor.h"
#include <string>
+#include <boost/noncopyable.hpp>
namespace Orthanc
{
- class JpegReader : public ImageAccessor
+ class JpegReader :
+ public ImageAccessor,
+ public boost::noncopyable
{
private:
std::string content_;
public:
- void ReadFromFile(const char* filename);
-
- void ReadFromFile(const std::string& filename)
- {
- ReadFromFile(filename.c_str());
- }
+ void ReadFromFile(const std::string& filename);
void ReadFromMemory(const void* buffer,
size_t size);
diff --git a/Core/Images/JpegWriter.cpp b/Core/Images/JpegWriter.cpp
index 5665f95..86f9d3c 100644
--- a/Core/Images/JpegWriter.cpp
+++ b/Core/Images/JpegWriter.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -38,6 +38,7 @@
#include "JpegErrorManager.h"
+#include <stdlib.h>
#include <vector>
namespace Orthanc
@@ -110,17 +111,17 @@ namespace Orthanc
}
- void JpegWriter::WriteToFile(const char* filename,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer)
+ void JpegWriter::WriteToFileInternal(const std::string& filename,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer)
{
- FILE* fp = fopen(filename, "wb");
+ FILE* fp = Toolbox::OpenFile(filename, FileMode_WriteBinary);
if (fp == NULL)
{
- throw OrthancException(ErrorCode_FullStorage);
+ throw OrthancException(ErrorCode_CannotWriteFile);
}
std::vector<uint8_t*> lines;
@@ -155,12 +156,12 @@ namespace Orthanc
}
- void JpegWriter::WriteToMemory(std::string& jpeg,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer)
+ void JpegWriter::WriteToMemoryInternal(std::string& jpeg,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer)
{
std::vector<uint8_t*> lines;
GetLines(lines, height, pitch, format, buffer);
diff --git a/Core/Images/JpegWriter.h b/Core/Images/JpegWriter.h
index 006d2d6..5d18af3 100644
--- a/Core/Images/JpegWriter.h
+++ b/Core/Images/JpegWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,15 +32,27 @@
#pragma once
-#include "ImageAccessor.h"
-
-#include <string>
-#include <stdint.h>
+#include "IImageWriter.h"
namespace Orthanc
{
- class JpegWriter
+ class JpegWriter : public IImageWriter
{
+ protected:
+ virtual void WriteToFileInternal(const std::string& filename,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer);
+
+ virtual void WriteToMemoryInternal(std::string& jpeg,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer);
+
private:
uint8_t quality_;
@@ -55,33 +67,5 @@ namespace Orthanc
{
return quality_;
}
-
- void WriteToFile(const char* filename,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer);
-
- void WriteToMemory(std::string& jpeg,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer);
-
- void WriteToFile(const char* filename,
- const ImageAccessor& accessor)
- {
- WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
- accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
- }
-
- void WriteToMemory(std::string& jpeg,
- const ImageAccessor& accessor)
- {
- WriteToMemory(jpeg, accessor.GetWidth(), accessor.GetHeight(),
- accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
- }
};
}
diff --git a/Core/Images/PngReader.cpp b/Core/Images/PngReader.cpp
index 0a173e1..b93aee0 100644
--- a/Core/Images/PngReader.cpp
+++ b/Core/Images/PngReader.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -49,7 +49,7 @@ namespace Orthanc
FileRabi(const char* filename)
{
- fp_ = fopen(filename, "rb");
+ fp_ = Toolbox::OpenFile(filename, FileMode_ReadBinary);
if (!fp_)
{
throw OrthancException(ErrorCode_InexistentFile);
@@ -59,7 +59,9 @@ namespace Orthanc
~FileRabi()
{
if (fp_)
+ {
fclose(fp_);
+ }
}
};
}
@@ -204,9 +206,9 @@ namespace Orthanc
AssignWritable(format, width, height, pitch, &data_[0]);
}
- void PngReader::ReadFromFile(const char* filename)
+ void PngReader::ReadFromFile(const std::string& filename)
{
- FileRabi f(filename);
+ FileRabi f(filename.c_str());
char header[8];
if (fread(header, 1, 8, f.fp_) != 8)
diff --git a/Core/Images/PngReader.h b/Core/Images/PngReader.h
index 3ad2c71..b6ad811 100644
--- a/Core/Images/PngReader.h
+++ b/Core/Images/PngReader.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -39,10 +39,13 @@
#include <vector>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
namespace Orthanc
{
- class PngReader : public ImageAccessor
+ class PngReader :
+ public ImageAccessor,
+ public boost::noncopyable
{
private:
struct PngRabi;
@@ -56,12 +59,7 @@ namespace Orthanc
public:
PngReader();
- void ReadFromFile(const char* filename);
-
- void ReadFromFile(const std::string& filename)
- {
- ReadFromFile(filename.c_str());
- }
+ void ReadFromFile(const std::string& filename);
void ReadFromMemory(const void* buffer,
size_t size);
diff --git a/Core/Images/PngWriter.cpp b/Core/Images/PngWriter.cpp
index 5cec94b..8a2d66a 100644
--- a/Core/Images/PngWriter.cpp
+++ b/Core/Images/PngWriter.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -202,16 +202,16 @@ namespace Orthanc
}
- void PngWriter::WriteToFile(const char* filename,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer)
+ void PngWriter::WriteToFileInternal(const std::string& filename,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer)
{
Prepare(width, height, pitch, format, buffer);
- FILE* fp = fopen(filename, "wb");
+ FILE* fp = Toolbox::OpenFile(filename, FileMode_WriteBinary);
if (!fp)
{
throw OrthancException(ErrorCode_CannotWriteFile);
@@ -232,7 +232,6 @@ namespace Orthanc
-
static void MemoryCallback(png_structp png_ptr,
png_bytep data,
png_size_t size)
@@ -243,12 +242,12 @@ namespace Orthanc
- void PngWriter::WriteToMemory(std::string& png,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer)
+ void PngWriter::WriteToMemoryInternal(std::string& png,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer)
{
ChunkedBuffer chunks;
diff --git a/Core/Images/PngWriter.h b/Core/Images/PngWriter.h
index c0e09bc..591112f 100644
--- a/Core/Images/PngWriter.h
+++ b/Core/Images/PngWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,15 +32,29 @@
#pragma once
-#include "ImageAccessor.h"
+#include "IImageWriter.h"
#include <boost/shared_ptr.hpp>
-#include <string>
namespace Orthanc
{
- class PngWriter
+ class PngWriter : public IImageWriter
{
+ protected:
+ virtual void WriteToFileInternal(const std::string& filename,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer);
+
+ virtual void WriteToMemoryInternal(std::string& png,
+ unsigned int width,
+ unsigned int height,
+ unsigned int pitch,
+ PixelFormat format,
+ const void* buffer);
+
private:
struct PImpl;
boost::shared_ptr<PImpl> pimpl_;
@@ -60,33 +74,5 @@ namespace Orthanc
PngWriter();
~PngWriter();
-
- void WriteToFile(const char* filename,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer);
-
- void WriteToMemory(std::string& png,
- unsigned int width,
- unsigned int height,
- unsigned int pitch,
- PixelFormat format,
- const void* buffer);
-
- void WriteToFile(const char* filename,
- const ImageAccessor& accessor)
- {
- WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
- accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
- }
-
- void WriteToMemory(std::string& png,
- const ImageAccessor& accessor)
- {
- WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(),
- accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
- }
};
}
diff --git a/Core/Logging.cpp b/Core/Logging.cpp
index 3bdb76f..7bce92b 100644
--- a/Core/Logging.cpp
+++ b/Core/Logging.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -47,67 +47,28 @@ namespace Orthanc
{
}
- void EnableInfoLevel(bool enabled)
+ void Reset()
{
}
- void EnableTraceLevel(bool enabled)
+ void Flush()
{
}
- void SetTargetFolder(const std::string& path)
- {
- }
- }
-}
-
-#elif ORTHANC_ENABLE_GOOGLE_LOG == 1
-
-/*********************************************************
- * Wrapper around Google Log
- *********************************************************/
-
-namespace Orthanc
-{
- namespace Logging
- {
- void Initialize()
- {
- // Initialize Google's logging library.
- FLAGS_logtostderr = true;
- FLAGS_minloglevel = 1; // Do not print LOG(INFO) by default
- FLAGS_v = 0; // Do not print trace-level VLOG(1) by default
-
- google::InitGoogleLogging("Orthanc");
- }
-
- void Finalize()
+ void EnableInfoLevel(bool enabled)
{
- google::ShutdownGoogleLogging();
}
- void EnableInfoLevel(bool enabled)
+ void EnableTraceLevel(bool enabled)
{
- FLAGS_minloglevel = (enabled ? 0 : 1);
}
- void EnableTraceLevel(bool enabled)
+ void SetTargetFile(const std::string& path)
{
- if (enabled)
- {
- FLAGS_minloglevel = 0;
- FLAGS_v = 1;
- }
- else
- {
- FLAGS_v = 0;
- }
}
void SetTargetFolder(const std::string& path)
{
- FLAGS_logtostderr = false;
- FLAGS_log_dir = path;
}
}
}
@@ -115,7 +76,8 @@ namespace Orthanc
#else
/*********************************************************
- * Use internal logger, not Google Log
+ * Internal logger of Orthanc, that mimics some
+ * behavior from Google Log.
*********************************************************/
#include "OrthancException.h"
@@ -135,10 +97,12 @@ namespace Orthanc
namespace
{
- struct LoggingState
+ struct LoggingContext
{
bool infoEnabled_;
bool traceEnabled_;
+ std::string targetFile_;
+ std::string targetFolder_;
std::ostream* error_;
std::ostream* warning_;
@@ -146,7 +110,7 @@ namespace
std::auto_ptr<std::ofstream> file_;
- LoggingState() :
+ LoggingContext() :
infoEnabled_(false),
traceEnabled_(false),
error_(&std::cerr),
@@ -159,7 +123,7 @@ namespace
-static std::auto_ptr<LoggingState> loggingState_;
+static std::auto_ptr<LoggingContext> loggingContext_;
static boost::mutex loggingMutex_;
@@ -211,9 +175,9 @@ namespace Orthanc
}
- static void PrepareLogFile(std::auto_ptr<std::ofstream>& file,
- const std::string& suffix,
- const std::string& directory)
+ static void PrepareLogFolder(std::auto_ptr<std::ofstream>& file,
+ const std::string& suffix,
+ const std::string& directory)
{
boost::filesystem::path log, link;
GetLogPath(log, link, suffix, directory);
@@ -230,56 +194,125 @@ namespace Orthanc
void Initialize()
{
boost::mutex::scoped_lock lock(loggingMutex_);
- loggingState_.reset(new LoggingState);
+ loggingContext_.reset(new LoggingContext);
}
void Finalize()
{
boost::mutex::scoped_lock lock(loggingMutex_);
- loggingState_.reset(NULL);
+ loggingContext_.reset(NULL);
+ }
+
+ void Reset()
+ {
+ // Recover the old logging context
+ std::auto_ptr<LoggingContext> old;
+
+ {
+ boost::mutex::scoped_lock lock(loggingMutex_);
+ if (loggingContext_.get() == NULL)
+ {
+ return;
+ }
+ else
+ {
+ old = loggingContext_;
+
+ // Create a new logging context,
+ loggingContext_.reset(new LoggingContext);
+ }
+ }
+
+ EnableInfoLevel(old->infoEnabled_);
+ EnableTraceLevel(old->traceEnabled_);
+
+ if (!old->targetFolder_.empty())
+ {
+ SetTargetFolder(old->targetFolder_);
+ }
+ else if (!old->targetFile_.empty())
+ {
+ SetTargetFile(old->targetFile_);
+ }
}
void EnableInfoLevel(bool enabled)
{
boost::mutex::scoped_lock lock(loggingMutex_);
- assert(loggingState_.get() != NULL);
+ assert(loggingContext_.get() != NULL);
- loggingState_->infoEnabled_ = enabled;
+ loggingContext_->infoEnabled_ = enabled;
+
+ if (!enabled)
+ {
+ // Also disable the "TRACE" level when info-level debugging is disabled
+ loggingContext_->traceEnabled_ = false;
+ }
}
void EnableTraceLevel(bool enabled)
{
boost::mutex::scoped_lock lock(loggingMutex_);
- assert(loggingState_.get() != NULL);
+ assert(loggingContext_.get() != NULL);
- loggingState_->traceEnabled_ = enabled;
+ loggingContext_->traceEnabled_ = enabled;
if (enabled)
{
// Also enable the "INFO" level when trace-level debugging is enabled
- loggingState_->infoEnabled_ = true;
+ loggingContext_->infoEnabled_ = true;
+ }
+ }
+
+
+ static void CheckFile(std::auto_ptr<std::ofstream>& f)
+ {
+ if (loggingContext_->file_.get() == NULL ||
+ !loggingContext_->file_->is_open())
+ {
+ throw OrthancException(ErrorCode_CannotWriteFile);
}
}
void SetTargetFolder(const std::string& path)
{
boost::mutex::scoped_lock lock(loggingMutex_);
- assert(loggingState_.get() != NULL);
+ assert(loggingContext_.get() != NULL);
+
+ PrepareLogFolder(loggingContext_->file_, "" /* no suffix */, path);
+ CheckFile(loggingContext_->file_);
+
+ loggingContext_->targetFile_.clear();
+ loggingContext_->targetFolder_ = path;
+ loggingContext_->warning_ = loggingContext_->file_.get();
+ loggingContext_->error_ = loggingContext_->file_.get();
+ loggingContext_->info_ = loggingContext_->file_.get();
+ }
+
+
+ void SetTargetFile(const std::string& path)
+ {
+ boost::mutex::scoped_lock lock(loggingMutex_);
+ assert(loggingContext_.get() != NULL);
- PrepareLogFile(loggingState_->file_, "" /* no suffix */, path);
+ loggingContext_->file_.reset(new std::ofstream(path.c_str(), std::fstream::app));
+ CheckFile(loggingContext_->file_);
- loggingState_->warning_ = loggingState_->file_.get();
- loggingState_->error_ = loggingState_->file_.get();
- loggingState_->info_ = loggingState_->file_.get();
+ loggingContext_->targetFile_ = path;
+ loggingContext_->targetFolder_.clear();
+ loggingContext_->warning_ = loggingContext_->file_.get();
+ loggingContext_->error_ = loggingContext_->file_.get();
+ loggingContext_->info_ = loggingContext_->file_.get();
}
+
InternalLogger::InternalLogger(const char* level,
const char* file,
int line) :
lock_(loggingMutex_),
stream_(&null_) // By default, logging to "/dev/null" is simulated
{
- if (loggingState_.get() == NULL)
+ if (loggingContext_.get() == NULL)
{
fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
return;
@@ -287,8 +320,8 @@ namespace Orthanc
LogLevel l = StringToLogLevel(level);
- if ((l == LogLevel_Info && !loggingState_->infoEnabled_) ||
- (l == LogLevel_Trace && !loggingState_->traceEnabled_))
+ if ((l == LogLevel_Info && !loggingContext_->infoEnabled_) ||
+ (l == LogLevel_Trace && !loggingContext_->traceEnabled_))
{
// This logging level is disabled, directly exit and unlock
// the mutex to speed-up things. The stream is set to "/dev/null"
@@ -342,12 +375,12 @@ namespace Orthanc
// The header is computed, we now re-lock the mutex to access
- // the stream objects. Pay attention that "loggingState_",
+ // the stream objects. Pay attention that "loggingContext_",
// "infoEnabled_" or "traceEnabled_" might have changed while
// the mutex was unlocked.
lock_.lock();
- if (loggingState_.get() == NULL)
+ if (loggingContext_.get() == NULL)
{
fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
return;
@@ -356,25 +389,25 @@ namespace Orthanc
switch (l)
{
case LogLevel_Error:
- stream_ = loggingState_->error_;
+ stream_ = loggingContext_->error_;
break;
case LogLevel_Warning:
- stream_ = loggingState_->warning_;
+ stream_ = loggingContext_->warning_;
break;
case LogLevel_Info:
- if (loggingState_->infoEnabled_)
+ if (loggingContext_->infoEnabled_)
{
- stream_ = loggingState_->info_;
+ stream_ = loggingContext_->info_;
}
break;
case LogLevel_Trace:
- if (loggingState_->traceEnabled_)
+ if (loggingContext_->traceEnabled_)
{
- stream_ = loggingState_->info_;
+ stream_ = loggingContext_->info_;
}
break;
@@ -410,6 +443,16 @@ namespace Orthanc
}
+ void Flush()
+ {
+ boost::mutex::scoped_lock lock(loggingMutex_);
+
+ if (loggingContext_.get() != NULL &&
+ loggingContext_->file_.get() != NULL)
+ {
+ loggingContext_->file_->flush();
+ }
+ }
}
}
diff --git a/Core/Logging.h b/Core/Logging.h
index 586f7b3..7318246 100644
--- a/Core/Logging.h
+++ b/Core/Logging.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -42,10 +42,16 @@ namespace Orthanc
void Finalize();
+ void Reset();
+
+ void Flush();
+
void EnableInfoLevel(bool enabled);
void EnableTraceLevel(bool enabled);
+ void SetTargetFile(const std::string& path);
+
void SetTargetFolder(const std::string& path);
struct NullStream : public std::ostream
@@ -72,16 +78,10 @@ namespace Orthanc
#else /* ORTHANC_ENABLE_LOGGING == 1 */
-#if ORTHANC_ENABLE_GOOGLE_LOG == 1
-# include <stdlib.h> // Including this fixes a problem in glog for recent releases of MinGW
-# include <glog/logging.h>
-#else
# include <boost/thread/mutex.hpp>
# define LOG(level) ::Orthanc::Logging::InternalLogger(#level, __FILE__, __LINE__)
# define VLOG(level) ::Orthanc::Logging::InternalLogger("TRACE", __FILE__, __LINE__)
-#endif
-#if ORTHANC_ENABLE_GOOGLE_LOG != 1
namespace Orthanc
{
namespace Logging
@@ -107,6 +107,5 @@ namespace Orthanc
};
}
}
-#endif
#endif // ORTHANC_ENABLE_LOGGING
diff --git a/Core/Lua/LuaContext.cpp b/Core/Lua/LuaContext.cpp
index b4e8bfd..cc6b710 100644
--- a/Core/Lua/LuaContext.cpp
+++ b/Core/Lua/LuaContext.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Lua/LuaContext.h b/Core/Lua/LuaContext.h
index 692933e..bd08b1b 100644
--- a/Core/Lua/LuaContext.h
+++ b/Core/Lua/LuaContext.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -104,11 +104,6 @@ namespace Orthanc
httpClient_.SetCredentials(username, password);
}
- void SetHttpProxy(const std::string& proxy)
- {
- httpClient_.SetProxy(proxy);
- }
-
void RegisterFunction(const char* name,
lua_CFunction func);
diff --git a/Core/Lua/LuaFunctionCall.cpp b/Core/Lua/LuaFunctionCall.cpp
index 4e3f02f..1e9c941 100644
--- a/Core/Lua/LuaFunctionCall.cpp
+++ b/Core/Lua/LuaFunctionCall.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -152,4 +152,18 @@ namespace Orthanc
throw OrthancException(ErrorCode_LuaReturnsNoString);
}
}
+
+
+ void LuaFunctionCall::PushStringMap(const std::map<std::string, std::string>& value)
+ {
+ Json::Value json = Json::objectValue;
+
+ for (std::map<std::string, std::string>::const_iterator
+ it = value.begin(); it != value.end(); ++it)
+ {
+ json[it->first] = it->second;
+ }
+
+ PushJson(json);
+ }
}
diff --git a/Core/Lua/LuaFunctionCall.h b/Core/Lua/LuaFunctionCall.h
index 62fe2b1..3dff4f1 100644
--- a/Core/Lua/LuaFunctionCall.h
+++ b/Core/Lua/LuaFunctionCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -62,6 +62,8 @@ namespace Orthanc
void PushJson(const Json::Value& value);
+ void PushStringMap(const std::map<std::string, std::string>& value);
+
void Execute()
{
ExecuteInternal(0);
diff --git a/Core/ChunkedBuffer.h b/Core/MultiThreading/BagOfTasks.h
similarity index 70%
copy from Core/ChunkedBuffer.h
copy to Core/MultiThreading/BagOfTasks.h
index 93ffc2b..7b992f6 100644
--- a/Core/ChunkedBuffer.h
+++ b/Core/MultiThreading/BagOfTasks.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,40 +32,52 @@
#pragma once
+#include "../ICommand.h"
+
#include <list>
-#include <string>
+#include <cstddef>
namespace Orthanc
{
- class ChunkedBuffer
+ class BagOfTasks : public boost::noncopyable
{
private:
- typedef std::list<std::string*> Chunks;
- size_t numBytes_;
- Chunks chunks_;
-
- void Clear();
+ typedef std::list<ICommand*> Tasks;
+
+ Tasks tasks_;
public:
- ChunkedBuffer() : numBytes_(0)
+ ~BagOfTasks()
{
+ for (Tasks::iterator it = tasks_.begin(); it != tasks_.end(); ++it)
+ {
+ delete *it;
+ }
}
- ~ChunkedBuffer()
+ ICommand* Pop()
{
- Clear();
+ ICommand* task = tasks_.front();
+ tasks_.pop_front();
+ return task;
}
- size_t GetNumBytes() const
+ void Push(ICommand* task) // Takes ownership
{
- return numBytes_;
+ if (task != NULL)
+ {
+ tasks_.push_back(task);
+ }
}
- void AddChunk(const char* chunkData,
- size_t chunkSize);
-
- void AddChunk(const std::string& chunk);
+ size_t GetSize() const
+ {
+ return tasks_.size();
+ }
- void Flatten(std::string& result);
+ bool IsEmpty() const
+ {
+ return tasks_.empty();
+ }
};
}
diff --git a/Core/MultiThreading/BagOfTasksProcessor.cpp b/Core/MultiThreading/BagOfTasksProcessor.cpp
new file mode 100644
index 0000000..dc2034f
--- /dev/null
+++ b/Core/MultiThreading/BagOfTasksProcessor.cpp
@@ -0,0 +1,259 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 "../PrecompiledHeaders.h"
+#include "BagOfTasksProcessor.h"
+
+#include "../OrthancException.h"
+
+
+namespace Orthanc
+{
+ class BagOfTasksProcessor::Task : public IDynamicObject
+ {
+ private:
+ uint64_t bag_;
+ std::auto_ptr<ICommand> command_;
+
+ public:
+ Task(uint64_t bag,
+ ICommand* command) :
+ bag_(bag),
+ command_(command)
+ {
+ }
+
+ bool Execute()
+ {
+ try
+ {
+ return command_->Execute();
+ }
+ catch (OrthancException&)
+ {
+ return false;
+ }
+ catch (std::runtime_error&)
+ {
+ return false;
+ }
+ }
+
+ uint64_t GetBag()
+ {
+ return bag_;
+ }
+ };
+
+
+ void BagOfTasksProcessor::Worker(BagOfTasksProcessor* that)
+ {
+ while (that->continue_)
+ {
+ std::auto_ptr<IDynamicObject> obj(that->queue_.Dequeue(100));
+ if (obj.get() != NULL)
+ {
+ Task& task = *dynamic_cast<Task*>(obj.get());
+
+ {
+ boost::mutex::scoped_lock lock(that->mutex_);
+
+ Bags::iterator bag = that->bags_.find(task.GetBag());
+ assert(bag != that->bags_.end());
+ assert(bag->second.done_ < bag->second.size_);
+
+ if (bag->second.status_ != BagStatus_Running)
+ {
+ // This bag of task has failed or is tagged as canceled, do nothing
+ bag->second.done_ += 1;
+ continue;
+ }
+ }
+
+ bool success = task.Execute();
+
+ {
+ boost::mutex::scoped_lock lock(that->mutex_);
+
+ Bags::iterator bag = that->bags_.find(task.GetBag());
+ assert(bag != that->bags_.end());
+
+ if (!success)
+ {
+ bag->second.status_ = BagStatus_Failed;
+ }
+
+ assert(bag->second.done_ < bag->second.size_);
+ bag->second.done_ += 1;
+
+ if (bag->second.done_ == bag->second.size_)
+ {
+ that->exitStatus_[task.GetBag()] = (bag->second.status_ == BagStatus_Running);
+ that->bagFinished_.notify_all();
+ }
+ }
+ }
+ }
+ }
+
+
+ void BagOfTasksProcessor::Cancel(int64_t bag)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ Bags::iterator it = bags_.find(bag);
+ if (it != bags_.end())
+ {
+ it->second.status_ = BagStatus_Canceled;
+ }
+ }
+
+
+ bool BagOfTasksProcessor::Join(int64_t bag)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ while (continue_)
+ {
+ ExitStatus::iterator it = exitStatus_.find(bag);
+ if (it == exitStatus_.end()) // The bag is still running
+ {
+ bagFinished_.wait(lock);
+ }
+ else
+ {
+ bool status = it->second;
+ exitStatus_.erase(it);
+ return status;
+ }
+ }
+
+ return false; // The processor is stopping
+ }
+
+
+ float BagOfTasksProcessor::GetProgress(int64_t bag)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ Bags::const_iterator it = bags_.find(bag);
+ if (it == bags_.end())
+ {
+ // The bag of tasks has finished
+ return 1.0f;
+ }
+ else
+ {
+ return (static_cast<float>(it->second.done_) /
+ static_cast<float>(it->second.size_));
+ }
+ }
+
+
+ bool BagOfTasksProcessor::Handle::Join()
+ {
+ if (hasJoined_)
+ {
+ return status_;
+ }
+ else
+ {
+ status_ = that_.Join(bag_);
+ hasJoined_ = true;
+ return status_;
+ }
+ }
+
+
+ BagOfTasksProcessor::BagOfTasksProcessor(size_t countThreads) :
+ countBags_(0),
+ continue_(true)
+ {
+ if (countThreads == 0)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ threads_.resize(countThreads);
+
+ for (size_t i = 0; i < threads_.size(); i++)
+ {
+ threads_[i] = new boost::thread(Worker, this);
+ }
+ }
+
+
+ BagOfTasksProcessor::~BagOfTasksProcessor()
+ {
+ continue_ = false;
+
+ bagFinished_.notify_all(); // Wakes up all the pending "Join()"
+
+ for (size_t i = 0; i < threads_.size(); i++)
+ {
+ if (threads_[i])
+ {
+ if (threads_[i]->joinable())
+ {
+ threads_[i]->join();
+ }
+
+ delete threads_[i];
+ threads_[i] = NULL;
+ }
+ }
+ }
+
+
+ BagOfTasksProcessor::Handle* BagOfTasksProcessor::Submit(BagOfTasks& tasks)
+ {
+ if (tasks.GetSize() == 0)
+ {
+ return new Handle(*this, 0, true);
+ }
+
+ boost::mutex::scoped_lock lock(mutex_);
+
+ uint64_t id = countBags_;
+ countBags_ += 1;
+
+ Bag bag(tasks.GetSize());
+ bags_[id] = bag;
+
+ while (!tasks.IsEmpty())
+ {
+ queue_.Enqueue(new Task(id, tasks.Pop()));
+ }
+
+ return new Handle(*this, id, false);
+ }
+}
diff --git a/Core/MultiThreading/BagOfTasksProcessor.h b/Core/MultiThreading/BagOfTasksProcessor.h
new file mode 100644
index 0000000..51c0ff1
--- /dev/null
+++ b/Core/MultiThreading/BagOfTasksProcessor.h
@@ -0,0 +1,146 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 "BagOfTasks.h"
+#include "SharedMessageQueue.h"
+
+#include <stdint.h>
+#include <map>
+
+namespace Orthanc
+{
+ class BagOfTasksProcessor : public boost::noncopyable
+ {
+ private:
+ enum BagStatus
+ {
+ BagStatus_Running,
+ BagStatus_Canceled,
+ BagStatus_Failed
+ };
+
+
+ struct Bag
+ {
+ size_t size_;
+ size_t done_;
+ BagStatus status_;
+
+ Bag() :
+ size_(0),
+ done_(0),
+ status_(BagStatus_Failed)
+ {
+ }
+
+ Bag(size_t size) :
+ size_(size),
+ done_(0),
+ status_(BagStatus_Running)
+ {
+ }
+ };
+
+ class Task;
+
+
+ typedef std::map<uint64_t, Bag> Bags;
+ typedef std::map<uint64_t, bool> ExitStatus;
+
+ SharedMessageQueue queue_;
+
+ boost::mutex mutex_;
+ uint64_t countBags_;
+ Bags bags_;
+ std::vector<boost::thread*> threads_;
+ ExitStatus exitStatus_;
+ bool continue_;
+
+ boost::condition_variable bagFinished_;
+
+ static void Worker(BagOfTasksProcessor* that);
+
+ void Cancel(int64_t bag);
+
+ bool Join(int64_t bag);
+
+ float GetProgress(int64_t bag);
+
+ public:
+ class Handle : public boost::noncopyable
+ {
+ friend class BagOfTasksProcessor;
+
+ private:
+ BagOfTasksProcessor& that_;
+ uint64_t bag_;
+ bool hasJoined_;
+ bool status_;
+
+ Handle(BagOfTasksProcessor& that,
+ uint64_t bag,
+ bool empty) :
+ that_(that),
+ bag_(bag),
+ hasJoined_(empty)
+ {
+ }
+
+ public:
+ ~Handle()
+ {
+ Join();
+ }
+
+ void Cancel()
+ {
+ that_.Cancel(bag_);
+ }
+
+ bool Join();
+
+ float GetProgress()
+ {
+ return that_.GetProgress(bag_);
+ }
+ };
+
+
+ BagOfTasksProcessor(size_t countThreads);
+
+ ~BagOfTasksProcessor();
+
+ Handle* Submit(BagOfTasks& tasks);
+ };
+}
diff --git a/Core/MultiThreading/ILockable.h b/Core/MultiThreading/ILockable.h
index 2306a18..9e2e6bf 100644
--- a/Core/MultiThreading/ILockable.h
+++ b/Core/MultiThreading/ILockable.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/IRunnableBySteps.h b/Core/MultiThreading/IRunnableBySteps.h
index b701439..4d91f17 100644
--- a/Core/MultiThreading/IRunnableBySteps.h
+++ b/Core/MultiThreading/IRunnableBySteps.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/Locker.h b/Core/MultiThreading/Locker.h
index b78d064..4f38409 100644
--- a/Core/MultiThreading/Locker.h
+++ b/Core/MultiThreading/Locker.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/Mutex.cpp b/Core/MultiThreading/Mutex.cpp
index c7d4049..35cc732 100644
--- a/Core/MultiThreading/Mutex.cpp
+++ b/Core/MultiThreading/Mutex.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -37,7 +37,7 @@
#if defined(_WIN32)
#include <windows.h>
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
#include <pthread.h>
#else
#error Support your platform here
@@ -75,7 +75,7 @@ namespace Orthanc
}
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__)
struct Mutex::PImpl
{
diff --git a/Core/MultiThreading/Mutex.h b/Core/MultiThreading/Mutex.h
index ad4c374..bbcbebf 100644
--- a/Core/MultiThreading/Mutex.h
+++ b/Core/MultiThreading/Mutex.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/ReaderWriterLock.cpp b/Core/MultiThreading/ReaderWriterLock.cpp
index bd5c533..a2c6acb 100644
--- a/Core/MultiThreading/ReaderWriterLock.cpp
+++ b/Core/MultiThreading/ReaderWriterLock.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/ReaderWriterLock.h b/Core/MultiThreading/ReaderWriterLock.h
index 853fc85..899b3d3 100644
--- a/Core/MultiThreading/ReaderWriterLock.h
+++ b/Core/MultiThreading/ReaderWriterLock.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/RunnableWorkersPool.cpp b/Core/MultiThreading/RunnableWorkersPool.cpp
index a4a147d..30c67e6 100644
--- a/Core/MultiThreading/RunnableWorkersPool.cpp
+++ b/Core/MultiThreading/RunnableWorkersPool.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/RunnableWorkersPool.h b/Core/MultiThreading/RunnableWorkersPool.h
index 17b582b..bde1edc 100644
--- a/Core/MultiThreading/RunnableWorkersPool.h
+++ b/Core/MultiThreading/RunnableWorkersPool.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/Semaphore.cpp b/Core/MultiThreading/Semaphore.cpp
index ca1a554..4de6cb8 100644
--- a/Core/MultiThreading/Semaphore.cpp
+++ b/Core/MultiThreading/Semaphore.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/Semaphore.h b/Core/MultiThreading/Semaphore.h
index 629d097..7bd6376 100644
--- a/Core/MultiThreading/Semaphore.h
+++ b/Core/MultiThreading/Semaphore.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/SharedMessageQueue.cpp b/Core/MultiThreading/SharedMessageQueue.cpp
index 4a1c1ac..34508ae 100644
--- a/Core/MultiThreading/SharedMessageQueue.cpp
+++ b/Core/MultiThreading/SharedMessageQueue.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/MultiThreading/SharedMessageQueue.h b/Core/MultiThreading/SharedMessageQueue.h
index 244e205..71728c0 100644
--- a/Core/MultiThreading/SharedMessageQueue.h
+++ b/Core/MultiThreading/SharedMessageQueue.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/OrthancException.h b/Core/OrthancException.h
index 26d937e..5afa41f 100644
--- a/Core/OrthancException.h
+++ b/Core/OrthancException.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Pkcs11.cpp b/Core/Pkcs11.cpp
new file mode 100644
index 0000000..d0ed318
--- /dev/null
+++ b/Core/Pkcs11.cpp
@@ -0,0 +1,312 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h"
+#include "Pkcs11.h"
+
+#if ORTHANC_PKCS11_ENABLED != 1 || ORTHANC_SSL_ENABLED != 1
+# error This file cannot be used if OpenSSL or PKCS#11 support is disabled
+#endif
+
+
+#if defined(OPENSSL_NO_RSA) || defined(OPENSSL_NO_EC) || defined(OPENSSL_NO_ECDSA) || defined(OPENSSL_NO_ECDH)
+# error OpenSSL was compiled without support for RSA, EC, ECDSA or ECDH
+#endif
+
+
+#include "Logging.h"
+#include "OrthancException.h"
+#include "Toolbox.h"
+
+extern "C"
+{
+#include <libp11/engine.h> // This is P11's "engine.h"
+#include <libp11/libp11.h>
+}
+
+#include <openssl/engine.h>
+
+
+namespace Orthanc
+{
+ namespace Pkcs11
+ {
+ static const char* PKCS11_ENGINE_ID = "pkcs11";
+ static const char* PKCS11_ENGINE_NAME = "PKCS#11 for Orthanc";
+ static const ENGINE_CMD_DEFN PKCS11_ENGINE_COMMANDS[] =
+ {
+ {
+ CMD_MODULE_PATH,
+ "MODULE_PATH",
+ "Specifies the path to the PKCS#11 module shared library",
+ ENGINE_CMD_FLAG_STRING
+ },
+ {
+ CMD_PIN,
+ "PIN",
+ "Specifies the pin code",
+ ENGINE_CMD_FLAG_STRING
+ },
+ {
+ CMD_VERBOSE,
+ "VERBOSE",
+ "Print additional details",
+ ENGINE_CMD_FLAG_NO_INPUT
+ },
+ {
+ CMD_LOAD_CERT_CTRL,
+ "LOAD_CERT_CTRL",
+ "Get the certificate from card",
+ ENGINE_CMD_FLAG_INTERNAL
+ },
+ {
+ 0,
+ NULL,
+ NULL,
+ 0
+ }
+ };
+
+
+ static bool pkcs11Initialized_ = false;
+ static ENGINE_CTX *context_ = NULL;
+
+ static int EngineInitialize(ENGINE* engine)
+ {
+ if (context_ == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return pkcs11_init(context_);
+ }
+ }
+
+
+ static int EngineFinalize(ENGINE* engine)
+ {
+ if (context_ == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return pkcs11_finish(context_);
+ }
+ }
+
+
+ static int EngineDestroy(ENGINE* engine)
+ {
+ return (context_ == NULL ? 0 : 1);
+ }
+
+
+ static int EngineControl(ENGINE *engine,
+ int command,
+ long i,
+ void *p,
+ void (*f) ())
+ {
+ if (context_ == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return pkcs11_engine_ctrl(context_, command, i, p, f);
+ }
+ }
+
+
+ static EVP_PKEY *EngineLoadPublicKey(ENGINE *engine,
+ const char *s_key_id,
+ UI_METHOD *ui_method,
+ void *callback_data)
+ {
+ if (context_ == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return pkcs11_load_public_key(context_, s_key_id, ui_method, callback_data);
+ }
+ }
+
+
+ static EVP_PKEY *EngineLoadPrivateKey(ENGINE *engine,
+ const char *s_key_id,
+ UI_METHOD *ui_method,
+ void *callback_data)
+ {
+ if (context_ == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return pkcs11_load_private_key(context_, s_key_id, ui_method, callback_data);
+ }
+ }
+
+
+ static ENGINE* LoadEngine()
+ {
+ // This function creates an engine for PKCS#11 and inspired by
+ // the "ENGINE_load_dynamic" function from OpenSSL, in file
+ // "crypto/engine/eng_dyn.c"
+
+ ENGINE* engine = ENGINE_new();
+ if (!engine)
+ {
+ LOG(ERROR) << "Cannot create an OpenSSL engine for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ // Create a PKCS#11 context using libp11
+ context_ = pkcs11_new();
+ if (!context_)
+ {
+ LOG(ERROR) << "Cannot create a libp11 context for PKCS#11";
+ ENGINE_free(engine);
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ if (!ENGINE_set_id(engine, PKCS11_ENGINE_ID) ||
+ !ENGINE_set_name(engine, PKCS11_ENGINE_NAME) ||
+ !ENGINE_set_cmd_defns(engine, PKCS11_ENGINE_COMMANDS) ||
+
+ // Register the callback functions
+ !ENGINE_set_init_function(engine, EngineInitialize) ||
+ !ENGINE_set_finish_function(engine, EngineFinalize) ||
+ !ENGINE_set_destroy_function(engine, EngineDestroy) ||
+ !ENGINE_set_ctrl_function(engine, EngineControl) ||
+ !ENGINE_set_load_pubkey_function(engine, EngineLoadPublicKey) ||
+ !ENGINE_set_load_privkey_function(engine, EngineLoadPrivateKey) ||
+
+ !ENGINE_set_RSA(engine, PKCS11_get_rsa_method()) ||
+ !ENGINE_set_ECDSA(engine, PKCS11_get_ecdsa_method()) ||
+ !ENGINE_set_ECDH(engine, PKCS11_get_ecdh_method()) ||
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100002L
+ !ENGINE_set_EC(engine, PKCS11_get_ec_key_method()) ||
+#endif
+
+ // Make OpenSSL know about our PKCS#11 engine
+ !ENGINE_add(engine))
+ {
+ LOG(ERROR) << "Cannot initialize the OpenSSL engine for PKCS#11";
+ pkcs11_finish(context_);
+ ENGINE_free(engine);
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ // If the "ENGINE_add" worked, it gets a structural
+ // reference. We release our just-created reference.
+ ENGINE_free(engine);
+
+ return ENGINE_by_id(PKCS11_ENGINE_ID);
+ }
+
+
+ bool IsInitialized()
+ {
+ return pkcs11Initialized_;
+ }
+
+ const char* GetEngineIdentifier()
+ {
+ return PKCS11_ENGINE_ID;
+ }
+
+ void Initialize(const std::string& module,
+ const std::string& pin,
+ bool verbose)
+ {
+ if (pkcs11Initialized_)
+ {
+ LOG(ERROR) << "The PKCS#11 engine has already been initialized";
+ throw OrthancException(ErrorCode_BadSequenceOfCalls);
+ }
+
+ if (module.empty() ||
+ !Toolbox::IsRegularFile(module))
+ {
+ LOG(ERROR) << "The PKCS#11 module must be a path to one shared library (DLL or .so)";
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ ENGINE* engine = LoadEngine();
+ if (!engine)
+ {
+ LOG(ERROR) << "Cannot create an OpenSSL engine for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", module.c_str(), 0))
+ {
+ LOG(ERROR) << "Cannot configure the OpenSSL dynamic engine for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ if (verbose)
+ {
+ ENGINE_ctrl_cmd_string(engine, "VERBOSE", NULL, 0);
+ }
+
+ if (!pin.empty() &&
+ !ENGINE_ctrl_cmd_string(engine, "PIN", pin.c_str(), 0))
+ {
+ LOG(ERROR) << "Cannot set the PIN code for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ if (!ENGINE_init(engine))
+ {
+ LOG(ERROR) << "Cannot initialize the OpenSSL dynamic engine for PKCS#11";
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ LOG(WARNING) << "The PKCS#11 engine has been successfully initialized";
+ pkcs11Initialized_ = true;
+ }
+
+
+ void Finalize()
+ {
+ // Nothing to do, the unregistration of the engine is
+ // automatically done by OpenSSL
+ }
+ }
+}
diff --git a/Core/HttpServer/IHttpOutputStream.h b/Core/Pkcs11.h
similarity index 78%
copy from Core/HttpServer/IHttpOutputStream.h
copy to Core/Pkcs11.h
index 756e71f..3a56c74 100644
--- a/Core/HttpServer/IHttpOutputStream.h
+++ b/Core/Pkcs11.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,22 +32,25 @@
#pragma once
-#include "../Enumerations.h"
+#if ORTHANC_PKCS11_ENABLED != 1 || ORTHANC_SSL_ENABLED != 1
+# error This file cannot be used if OpenSSL or PKCS#11 support is disabled
+#endif
+
#include <string>
-#include <boost/noncopyable.hpp>
namespace Orthanc
{
- class IHttpOutputStream : public boost::noncopyable
+ namespace Pkcs11
{
- public:
- virtual ~IHttpOutputStream()
- {
- }
+ const char* GetEngineIdentifier();
+
+ bool IsInitialized();
- virtual void OnHttpStatusReceived(HttpStatus status) = 0;
+ void Initialize(const std::string& module,
+ const std::string& pin,
+ bool verbose);
- virtual void Send(bool isHeader, const void* buffer, size_t length) = 0;
- };
+ void Finalize();
+ }
}
diff --git a/Core/PrecompiledHeaders.cpp b/Core/PrecompiledHeaders.cpp
index 4659ffc..01ea20f 100644
--- a/Core/PrecompiledHeaders.cpp
+++ b/Core/PrecompiledHeaders.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/PrecompiledHeaders.h b/Core/PrecompiledHeaders.h
index da70ea4..e22f012 100644
--- a/Core/PrecompiledHeaders.h
+++ b/Core/PrecompiledHeaders.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApi.cpp b/Core/RestApi/RestApi.cpp
index 5f3fc14..c314582 100644
--- a/Core/RestApi/RestApi.cpp
+++ b/Core/RestApi/RestApi.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApi.h b/Core/RestApi/RestApi.h
index 6134590..467970a 100644
--- a/Core/RestApi/RestApi.h
+++ b/Core/RestApi/RestApi.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiCall.cpp b/Core/RestApi/RestApiCall.cpp
index 4f460bf..822c772 100644
--- a/Core/RestApi/RestApiCall.cpp
+++ b/Core/RestApi/RestApiCall.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiCall.h b/Core/RestApi/RestApiCall.h
index 2ad4954..454c75c 100644
--- a/Core/RestApi/RestApiCall.h
+++ b/Core/RestApi/RestApiCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiDeleteCall.h b/Core/RestApi/RestApiDeleteCall.h
index e80623e..d1a2439 100644
--- a/Core/RestApi/RestApiDeleteCall.h
+++ b/Core/RestApi/RestApiDeleteCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiGetCall.cpp b/Core/RestApi/RestApiGetCall.cpp
index ed5bf7d..ecd4afa 100644
--- a/Core/RestApi/RestApiGetCall.cpp
+++ b/Core/RestApi/RestApiGetCall.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiGetCall.h b/Core/RestApi/RestApiGetCall.h
index 1527134..1d0c05e 100644
--- a/Core/RestApi/RestApiGetCall.h
+++ b/Core/RestApi/RestApiGetCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiHierarchy.cpp b/Core/RestApi/RestApiHierarchy.cpp
index 49493df..3b7274d 100644
--- a/Core/RestApi/RestApiHierarchy.cpp
+++ b/Core/RestApi/RestApiHierarchy.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiHierarchy.h b/Core/RestApi/RestApiHierarchy.h
index 0f7ed85..0510951 100644
--- a/Core/RestApi/RestApiHierarchy.h
+++ b/Core/RestApi/RestApiHierarchy.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiOutput.cpp b/Core/RestApi/RestApiOutput.cpp
index 55a731b..0d137b9 100644
--- a/Core/RestApi/RestApiOutput.cpp
+++ b/Core/RestApi/RestApiOutput.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiOutput.h b/Core/RestApi/RestApiOutput.h
index c947a3c..d6bf6b8 100644
--- a/Core/RestApi/RestApiOutput.h
+++ b/Core/RestApi/RestApiOutput.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiPath.cpp b/Core/RestApi/RestApiPath.cpp
index dd88268..cb2f63f 100644
--- a/Core/RestApi/RestApiPath.cpp
+++ b/Core/RestApi/RestApiPath.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiPath.h b/Core/RestApi/RestApiPath.h
index 65c2b6a..d60bc75 100644
--- a/Core/RestApi/RestApiPath.h
+++ b/Core/RestApi/RestApiPath.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiPostCall.h b/Core/RestApi/RestApiPostCall.h
index 8e6125b..4a36609 100644
--- a/Core/RestApi/RestApiPostCall.h
+++ b/Core/RestApi/RestApiPostCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/RestApi/RestApiPutCall.h b/Core/RestApi/RestApiPutCall.h
index f71aceb..27bc6f3 100644
--- a/Core/RestApi/RestApiPutCall.h
+++ b/Core/RestApi/RestApiPutCall.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/SQLite/Connection.cpp b/Core/SQLite/Connection.cpp
index ce95d7c..378d501 100644
--- a/Core/SQLite/Connection.cpp
+++ b/Core/SQLite/Connection.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
@@ -163,7 +163,8 @@ namespace Orthanc
if (error == SQLITE_ERROR)
{
#if ORTHANC_SQLITE_STANDALONE != 1
- LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_);
+ LOG(ERROR) << "SQLite execute error: " << sqlite3_errmsg(db_)
+ << " (" << sqlite3_extended_errcode(db_) << ")";
#endif
throw OrthancSQLiteException(ErrorCode_SQLiteExecute);
diff --git a/Core/SQLite/Connection.h b/Core/SQLite/Connection.h
index 7dc68b1..b58c42c 100644
--- a/Core/SQLite/Connection.h
+++ b/Core/SQLite/Connection.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/FunctionContext.cpp b/Core/SQLite/FunctionContext.cpp
index 4c2344e..a953f2f 100644
--- a/Core/SQLite/FunctionContext.cpp
+++ b/Core/SQLite/FunctionContext.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Redistribution and use in source and binary forms, with or without
diff --git a/Core/SQLite/FunctionContext.h b/Core/SQLite/FunctionContext.h
index a43addb..243a518 100644
--- a/Core/SQLite/FunctionContext.h
+++ b/Core/SQLite/FunctionContext.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Redistribution and use in source and binary forms, with or without
diff --git a/Core/SQLite/IScalarFunction.h b/Core/SQLite/IScalarFunction.h
index 3817ef5..8c58bc0 100644
--- a/Core/SQLite/IScalarFunction.h
+++ b/Core/SQLite/IScalarFunction.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Redistribution and use in source and binary forms, with or without
diff --git a/Core/SQLite/ITransaction.h b/Core/SQLite/ITransaction.h
index 5c9a2f9..8b5a359 100644
--- a/Core/SQLite/ITransaction.h
+++ b/Core/SQLite/ITransaction.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/NonCopyable.h b/Core/SQLite/NonCopyable.h
index c76c9b5..54d8959 100644
--- a/Core/SQLite/NonCopyable.h
+++ b/Core/SQLite/NonCopyable.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Redistribution and use in source and binary forms, with or without
diff --git a/Core/SQLite/OrthancSQLiteException.h b/Core/SQLite/OrthancSQLiteException.h
index f005496..9ffd382 100644
--- a/Core/SQLite/OrthancSQLiteException.h
+++ b/Core/SQLite/OrthancSQLiteException.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/Statement.cpp b/Core/SQLite/Statement.cpp
index 8618c44..5342a9f 100644
--- a/Core/SQLite/Statement.cpp
+++ b/Core/SQLite/Statement.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/Statement.h b/Core/SQLite/Statement.h
index 6f06c85..f48af30 100644
--- a/Core/SQLite/Statement.h
+++ b/Core/SQLite/Statement.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/StatementId.cpp b/Core/SQLite/StatementId.cpp
index afb34ac..b0d07b8 100644
--- a/Core/SQLite/StatementId.cpp
+++ b/Core/SQLite/StatementId.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/StatementId.h b/Core/SQLite/StatementId.h
index 108e4f6..af27740 100644
--- a/Core/SQLite/StatementId.h
+++ b/Core/SQLite/StatementId.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/StatementReference.cpp b/Core/SQLite/StatementReference.cpp
index b4def40..8d6c30f 100644
--- a/Core/SQLite/StatementReference.cpp
+++ b/Core/SQLite/StatementReference.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
@@ -82,7 +82,8 @@ namespace Orthanc
if (error != SQLITE_OK)
{
#if ORTHANC_SQLITE_STANDALONE != 1
- LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database);
+ LOG(ERROR) << "SQLite: " << sqlite3_errmsg(database)
+ << " (" << sqlite3_extended_errcode(database) << ")";
#endif
throw OrthancSQLiteException(ErrorCode_SQLitePrepareStatement);
diff --git a/Core/SQLite/StatementReference.h b/Core/SQLite/StatementReference.h
index 33292d8..db121dc 100644
--- a/Core/SQLite/StatementReference.h
+++ b/Core/SQLite/StatementReference.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/Transaction.cpp b/Core/SQLite/Transaction.cpp
index 440a172..e7429eb 100644
--- a/Core/SQLite/Transaction.cpp
+++ b/Core/SQLite/Transaction.cpp
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/SQLite/Transaction.h b/Core/SQLite/Transaction.h
index 5579d87..e531753 100644
--- a/Core/SQLite/Transaction.h
+++ b/Core/SQLite/Transaction.h
@@ -1,7 +1,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
*
- * Copyright (C) 2012-2015 Sebastien Jodogne <s.jodogne at gmail.com>,
+ * Copyright (C) 2012-2016 Sebastien Jodogne <s.jodogne at gmail.com>,
* Medical Physics Department, CHU of Liege, Belgium
*
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/Core/Toolbox.cpp b/Core/Toolbox.cpp
index c89d9f2..4990204 100644
--- a/Core/Toolbox.cpp
+++ b/Core/Toolbox.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -67,7 +67,7 @@
#include <limits.h> /* PATH_MAX */
#endif
-#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
#include <limits.h> /* PATH_MAX */
#include <signal.h>
#include <unistd.h>
@@ -111,27 +111,34 @@ extern "C"
namespace Orthanc
{
- static bool finish;
+ static bool finish_;
+ static ServerBarrierEvent barrierEvent_;
#if defined(_WIN32)
static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType)
{
// http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
- finish = true;
+ finish_ = true;
return true;
}
#else
- static void SignalHandler(int)
+ static void SignalHandler(int signal)
{
- finish = true;
+ if (signal == SIGHUP)
+ {
+ barrierEvent_ = ServerBarrierEvent_Reload;
+ }
+
+ finish_ = true;
}
#endif
+
void Toolbox::USleep(uint64_t microSeconds)
{
#if defined(_WIN32)
::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
-#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
usleep(microSeconds);
#else
#error Support your platform here
@@ -139,7 +146,7 @@ namespace Orthanc
}
- static void ServerBarrierInternal(const bool* stopFlag)
+ static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag)
{
#if defined(_WIN32)
SetConsoleCtrlHandler(ConsoleControlHandler, true);
@@ -147,11 +154,13 @@ namespace Orthanc
signal(SIGINT, SignalHandler);
signal(SIGQUIT, SignalHandler);
signal(SIGTERM, SignalHandler);
+ signal(SIGHUP, SignalHandler);
#endif
// Active loop that awakens every 100ms
- finish = false;
- while (!(*stopFlag || finish))
+ finish_ = false;
+ barrierEvent_ = ServerBarrierEvent_Stop;
+ while (!(*stopFlag || finish_))
{
Toolbox::USleep(100 * 1000);
}
@@ -162,19 +171,22 @@ namespace Orthanc
signal(SIGINT, NULL);
signal(SIGQUIT, NULL);
signal(SIGTERM, NULL);
+ signal(SIGHUP, NULL);
#endif
+
+ return barrierEvent_;
}
- void Toolbox::ServerBarrier(const bool& stopFlag)
+ ServerBarrierEvent Toolbox::ServerBarrier(const bool& stopFlag)
{
- ServerBarrierInternal(&stopFlag);
+ return ServerBarrierInternal(&stopFlag);
}
- void Toolbox::ServerBarrier()
+ ServerBarrierEvent Toolbox::ServerBarrier()
{
const bool stopFlag = false;
- ServerBarrierInternal(&stopFlag);
+ return ServerBarrierInternal(&stopFlag);
}
@@ -205,10 +217,21 @@ namespace Orthanc
}
+ static std::streamsize GetStreamSize(std::istream& f)
+ {
+ // http://www.cplusplus.com/reference/iostream/istream/tellg/
+ f.seekg(0, std::ios::end);
+ std::streamsize size = f.tellg();
+ f.seekg(0, std::ios::beg);
+
+ return size;
+ }
+
+
void Toolbox::ReadFile(std::string& content,
const std::string& path)
{
- if (!boost::filesystem::is_regular_file(path))
+ if (!IsRegularFile(path))
{
LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
throw OrthancException(ErrorCode_RegularFileExpected);
@@ -221,11 +244,7 @@ namespace Orthanc
throw OrthancException(ErrorCode_InexistentFile);
}
- // http://www.cplusplus.com/reference/iostream/istream/tellg/
- f.seekg(0, std::ios::end);
- std::streamsize size = f.tellg();
- f.seekg(0, std::ios::beg);
-
+ std::streamsize size = GetStreamSize(f);
content.resize(size);
if (size != 0)
{
@@ -236,6 +255,51 @@ namespace Orthanc
}
+ bool Toolbox::ReadHeader(std::string& header,
+ const std::string& path,
+ size_t headerSize)
+ {
+ if (!IsRegularFile(path))
+ {
+ LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
+ throw OrthancException(ErrorCode_RegularFileExpected);
+ }
+
+ boost::filesystem::ifstream f;
+ f.open(path, std::ifstream::in | std::ifstream::binary);
+ if (!f.good())
+ {
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ bool full = true;
+
+ {
+ std::streamsize size = GetStreamSize(f);
+ if (size <= 0)
+ {
+ headerSize = 0;
+ full = false;
+ }
+ else if (static_cast<size_t>(size) < headerSize)
+ {
+ headerSize = size; // Truncate to the size of the file
+ full = false;
+ }
+ }
+
+ header.resize(headerSize);
+ if (headerSize != 0)
+ {
+ f.read(reinterpret_cast<char*>(&header[0]), headerSize);
+ }
+
+ f.close();
+
+ return full;
+ }
+
+
void Toolbox::WriteFile(const void* content,
size_t size,
const std::string& path)
@@ -268,7 +332,7 @@ namespace Orthanc
{
if (boost::filesystem::exists(path))
{
- if (boost::filesystem::is_regular_file(path))
+ if (IsRegularFile(path))
{
boost::filesystem::remove(path);
}
@@ -529,12 +593,24 @@ namespace Orthanc
void Toolbox::DecodeBase64(std::string& result,
const std::string& data)
{
+ for (size_t i = 0; i < data.length(); i++)
+ {
+ if (!isalnum(data[i]) &&
+ data[i] != '+' &&
+ data[i] != '/' &&
+ data[i] != '=')
+ {
+ // This is not a valid character for a Base64 string
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
result = base64_decode(data);
}
# if BOOST_HAS_REGEX == 1
- void Toolbox::DecodeDataUriScheme(std::string& mime,
+ bool Toolbox::DecodeDataUriScheme(std::string& mime,
std::string& content,
const std::string& source)
{
@@ -546,10 +622,11 @@ namespace Orthanc
{
mime = what[1];
DecodeBase64(content, what[2]);
+ return true;
}
else
{
- throw OrthancException(ErrorCode_BadFileFormat);
+ return false;
}
}
# endif
@@ -576,7 +653,7 @@ namespace Orthanc
return std::string(&buffer[0]);
}
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
static std::string GetPathToExecutableInternal()
{
std::vector<char> buffer(PATH_MAX + 1);
@@ -1382,5 +1459,110 @@ namespace Orthanc
return static_cast<int>(getpid());
#endif
}
-}
+
+ bool Toolbox::IsRegularFile(const std::string& path)
+ {
+ namespace fs = boost::filesystem;
+
+ try
+ {
+ if (fs::exists(path))
+ {
+ fs::file_status status = fs::status(path);
+ return (status.type() == boost::filesystem::regular_file ||
+ status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11
+ }
+ }
+ catch (fs::filesystem_error&)
+ {
+ }
+
+ return false;
+ }
+
+
+ FILE* Toolbox::OpenFile(const std::string& path,
+ FileMode mode)
+ {
+#if defined(_WIN32)
+ // TODO Deal with special characters by converting to the current locale
+#endif
+
+ const char* m;
+ switch (mode)
+ {
+ case FileMode_ReadBinary:
+ m = "rb";
+ break;
+
+ case FileMode_WriteBinary:
+ m = "wb";
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ return fopen(path.c_str(), m);
+ }
+
+
+
+ static bool IsUnreservedCharacter(char c)
+ {
+ // This function checks whether "c" is an unserved character
+ // wrt. an URI percent-encoding
+ // https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding%5Fin%5Fa%5FURI
+
+ return ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.' ||
+ c == '~');
+ }
+
+ void Toolbox::UriEncode(std::string& target,
+ const std::string& source)
+ {
+ // Estimate the length of the percent-encoded URI
+ size_t length = 0;
+
+ for (size_t i = 0; i < source.size(); i++)
+ {
+ if (IsUnreservedCharacter(source[i]))
+ {
+ length += 1;
+ }
+ else
+ {
+ // This character must be percent-encoded
+ length += 3;
+ }
+ }
+
+ target.clear();
+ target.reserve(length);
+
+ for (size_t i = 0; i < source.size(); i++)
+ {
+ if (IsUnreservedCharacter(source[i]))
+ {
+ target.push_back(source[i]);
+ }
+ else
+ {
+ // This character must be percent-encoded
+ uint8_t byte = static_cast<uint8_t>(source[i]);
+ uint8_t a = byte >> 4;
+ uint8_t b = byte & 0x0f;
+
+ target.push_back('%');
+ target.push_back(a < 10 ? a + '0' : a - 10 + 'A');
+ target.push_back(b < 10 ? b + '0' : b - 10 + 'A');
+ }
+ }
+ }
+}
diff --git a/Core/Toolbox.h b/Core/Toolbox.h
index 456a482..cb67473 100644
--- a/Core/Toolbox.h
+++ b/Core/Toolbox.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -49,9 +49,9 @@ namespace Orthanc
namespace Toolbox
{
- void ServerBarrier(const bool& stopFlag);
+ ServerBarrierEvent ServerBarrier(const bool& stopFlag);
- void ServerBarrier();
+ ServerBarrierEvent ServerBarrier();
void ToUpperCase(std::string& s); // Inplace version
@@ -66,6 +66,10 @@ namespace Orthanc
void ReadFile(std::string& content,
const std::string& path);
+ bool ReadHeader(std::string& header,
+ const std::string& path,
+ size_t headerSize);
+
void WriteFile(const std::string& content,
const std::string& path);
@@ -123,7 +127,7 @@ namespace Orthanc
const std::string& data);
# if BOOST_HAS_REGEX == 1
- void DecodeDataUriScheme(std::string& mime,
+ bool DecodeDataUriScheme(std::string& mime,
std::string& content,
const std::string& source);
# endif
@@ -190,5 +194,13 @@ namespace Orthanc
const std::string& prefix);
int GetProcessId();
+
+ bool IsRegularFile(const std::string& path);
+
+ FILE* OpenFile(const std::string& path,
+ FileMode mode);
+
+ void UriEncode(std::string& target,
+ const std::string& source);
}
}
diff --git a/Core/Uuid.cpp b/Core/Uuid.cpp
index d8abf0c..8681095 100644
--- a/Core/Uuid.cpp
+++ b/Core/Uuid.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/Uuid.h b/Core/Uuid.h
index 60b4e50..88819a3 100644
--- a/Core/Uuid.h
+++ b/Core/Uuid.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Core/WebServiceParameters.cpp b/Core/WebServiceParameters.cpp
new file mode 100644
index 0000000..52e0ea6
--- /dev/null
+++ b/Core/WebServiceParameters.cpp
@@ -0,0 +1,265 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 "PrecompiledHeaders.h"
+#include "WebServiceParameters.h"
+
+#include "../Core/Logging.h"
+#include "../Core/Toolbox.h"
+#include "../Core/OrthancException.h"
+
+#include <cassert>
+
+namespace Orthanc
+{
+ WebServiceParameters::WebServiceParameters() :
+ advancedFormat_(false),
+ url_("http://127.0.0.1:8042/"),
+ pkcs11Enabled_(false)
+ {
+ }
+
+
+ void WebServiceParameters::ClearClientCertificate()
+ {
+ certificateFile_.clear();
+ certificateKeyFile_.clear();
+ certificateKeyPassword_.clear();
+ }
+
+
+ void WebServiceParameters::SetClientCertificate(const std::string& certificateFile,
+ const std::string& certificateKeyFile,
+ const std::string& certificateKeyPassword)
+ {
+ if (certificateFile.empty())
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ if (!Toolbox::IsRegularFile(certificateFile))
+ {
+ LOG(ERROR) << "Cannot open certificate file: " << certificateFile;
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ if (!certificateKeyFile.empty() &&
+ !Toolbox::IsRegularFile(certificateKeyFile))
+ {
+ LOG(ERROR) << "Cannot open key file: " << certificateKeyFile;
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ advancedFormat_ = true;
+ certificateFile_ = certificateFile;
+ certificateKeyFile_ = certificateKeyFile;
+ certificateKeyPassword_ = certificateKeyPassword;
+ }
+
+
+ static void AddTrailingSlash(std::string& url)
+ {
+ if (url.size() != 0 &&
+ url[url.size() - 1] != '/')
+ {
+ url += '/';
+ }
+ }
+
+
+ void WebServiceParameters::FromJsonArray(const Json::Value& peer)
+ {
+ assert(peer.isArray());
+
+ advancedFormat_ = false;
+ pkcs11Enabled_ = false;
+
+ if (peer.size() != 1 &&
+ peer.size() != 3)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ std::string url = peer.get(0u, "").asString();
+ if (url.empty())
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ AddTrailingSlash(url);
+ SetUrl(url);
+
+ if (peer.size() == 1)
+ {
+ SetUsername("");
+ SetPassword("");
+ }
+ else if (peer.size() == 3)
+ {
+ SetUsername(peer.get(1u, "").asString());
+ SetPassword(peer.get(2u, "").asString());
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+
+ static std::string GetStringMember(const Json::Value& peer,
+ const std::string& key,
+ const std::string& defaultValue)
+ {
+ if (!peer.isMember(key))
+ {
+ return defaultValue;
+ }
+ else if (peer[key].type() != Json::stringValue)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ else
+ {
+ return peer[key].asString();
+ }
+ }
+
+
+ void WebServiceParameters::FromJsonObject(const Json::Value& peer)
+ {
+ assert(peer.isObject());
+ advancedFormat_ = true;
+
+ std::string url = GetStringMember(peer, "Url", "");
+ if (url.empty())
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ AddTrailingSlash(url);
+ SetUrl(url);
+
+ SetUsername(GetStringMember(peer, "Username", ""));
+ SetPassword(GetStringMember(peer, "Password", ""));
+
+ if (peer.isMember("CertificateFile"))
+ {
+ SetClientCertificate(GetStringMember(peer, "CertificateFile", ""),
+ GetStringMember(peer, "CertificateKeyFile", ""),
+ GetStringMember(peer, "CertificateKeyPassword", ""));
+ }
+
+ if (peer.isMember("Pkcs11"))
+ {
+ if (peer["Pkcs11"].type() == Json::booleanValue)
+ {
+ pkcs11Enabled_ = peer["Pkcs11"].asBool();
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+ }
+
+
+ void WebServiceParameters::FromJson(const Json::Value& peer)
+ {
+ try
+ {
+ if (peer.isArray())
+ {
+ FromJsonArray(peer);
+ }
+ else if (peer.isObject())
+ {
+ FromJsonObject(peer);
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+ catch (OrthancException&)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+
+ void WebServiceParameters::ToJson(Json::Value& value) const
+ {
+ if (advancedFormat_)
+ {
+ value = Json::objectValue;
+ value["Url"] = url_;
+
+ if (!username_.empty() ||
+ !password_.empty())
+ {
+ value["Username"] = username_;
+ value["Password"] = password_;
+ }
+
+ if (!certificateFile_.empty())
+ {
+ value["CertificateFile"] = certificateFile_;
+ }
+
+ if (!certificateKeyFile_.empty())
+ {
+ value["CertificateKeyFile"] = certificateKeyFile_;
+ }
+
+ if (!certificateKeyPassword_.empty())
+ {
+ value["CertificateKeyPassword"] = certificateKeyPassword_;
+ }
+ }
+ else
+ {
+ value = Json::arrayValue;
+ value.append(url_);
+
+ if (!username_.empty() ||
+ !password_.empty())
+ {
+ value.append(username_);
+ value.append(password_);
+ }
+ }
+ }
+}
diff --git a/OrthancServer/OrthancPeerParameters.h b/Core/WebServiceParameters.h
similarity index 67%
rename from OrthancServer/OrthancPeerParameters.h
rename to Core/WebServiceParameters.h
index d630ca4..40d6cf9 100644
--- a/OrthancServer/OrthancPeerParameters.h
+++ b/Core/WebServiceParameters.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -37,15 +37,24 @@
namespace Orthanc
{
- class OrthancPeerParameters
+ class WebServiceParameters
{
private:
+ bool advancedFormat_;
std::string url_;
std::string username_;
std::string password_;
+ std::string certificateFile_;
+ std::string certificateKeyFile_;
+ std::string certificateKeyPassword_;
+ bool pkcs11Enabled_;
+
+ void FromJsonArray(const Json::Value& peer);
+
+ void FromJsonObject(const Json::Value& peer);
public:
- OrthancPeerParameters();
+ WebServiceParameters();
const std::string& GetUrl() const
{
@@ -77,6 +86,37 @@ namespace Orthanc
password_ = password;
}
+ void ClearClientCertificate();
+
+ void SetClientCertificate(const std::string& certificateFile,
+ const std::string& certificateKeyFile,
+ const std::string& certificateKeyPassword);
+
+ const std::string& GetCertificateFile() const
+ {
+ return certificateFile_;
+ }
+
+ const std::string& GetCertificateKeyFile() const
+ {
+ return certificateKeyFile_;
+ }
+
+ const std::string& GetCertificateKeyPassword() const
+ {
+ return certificateKeyPassword_;
+ }
+
+ void SetPkcs11Enabled(bool pkcs11Enabled)
+ {
+ pkcs11Enabled_ = pkcs11Enabled;
+ }
+
+ bool IsPkcs11Enabled() const
+ {
+ return pkcs11Enabled_;
+ }
+
void FromJson(const Json::Value& peer);
void ToJson(Json::Value& value) const;
diff --git a/LinuxCompilation.txt b/LinuxCompilation.txt
index 1367bb0..15e7bb2 100644
--- a/LinuxCompilation.txt
+++ b/LinuxCompilation.txt
@@ -79,7 +79,7 @@ SUPPORTED - Debian Jessie/Sid
# sudo apt-get install build-essential unzip cmake mercurial \
uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
- libgoogle-glog-dev libgtest-dev libpng-dev libjpeg-dev \
+ libgtest-dev libpng-dev libjpeg-dev \
libsqlite3-dev libssl-dev zlib1g-dev libdcmtk2-dev \
libboost-all-dev libwrap0-dev libjsoncpp-dev libpugixml-dev
@@ -106,7 +106,6 @@ SUPPORTED - Ubuntu 12.04.5 LTS
-DALLOW_DOWNLOADS=ON \
-DUSE_SYSTEM_MONGOOSE=OFF \
-DUSE_SYSTEM_JSONCPP=OFF \
- -DUSE_SYSTEM_GOOGLE_LOG=OFF \
-DUSE_SYSTEM_PUGIXML=OFF \
-DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
~/Orthanc
@@ -134,7 +133,7 @@ SUPPORTED - Fedora 20-22
------------------------
# sudo yum install unzip make automake gcc gcc-c++ python cmake \
- boost-devel curl-devel dcmtk-devel glog-devel \
+ boost-devel curl-devel dcmtk-devel \
gtest-devel libpng-devel libsqlite3x-devel libuuid-devel jpeg-devel \
mongoose-devel openssl-devel jsoncpp-devel lua-devel pugixml-devel
@@ -155,7 +154,7 @@ SUPPORTED - FreeBSD 10.1
------------------------
# pkg install jsoncpp pugixml lua51 curl googletest dcmtk cmake jpeg \
- e2fsprogs-libuuid glog boost-libs sqlite3 python libiconv
+ e2fsprogs-libuuid boost-libs sqlite3 python libiconv
# cmake -DALLOW_DOWNLOADS=ON \
-DUSE_SYSTEM_MONGOOSE=OFF \
diff --git a/NEWS b/NEWS
index 6120d99..ffb4f7a 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,76 @@ Pending changes in the mainline
===============================
+Version 1.1.0 (2016/07/27)
+==========================
+
+General
+-------
+
+* HTTPS client certificates can be associated with Orthanc peers to enhance security over Internet
+* Possibility to use PKCS#11 authentication for hardware security modules with Orthanc peers
+* New command-line option "--logfile" to output the Orthanc log to the given file
+* Support of SIGHUP signal (restart Orthanc only if the configuration files have changed)
+
+REST API
+--------
+
+* New URI: "/instances/.../frames/.../raw" to access the raw frames (bypass image decoding)
+* New URI "/modalities/.../move" to issue C-Move SCU requests
+* "MoveOriginatorID" can be specified for "/modalities/.../store"
+
+Dicom protocol
+--------------
+
+* Support of optional tags for counting resources in C-Find:
+ 0008-0061, 0008-0062, 0020-1200, 0020-1202, 0020-1204, 0020-1206, 0020-1208, 0020-1209
+* Support of Move Originator Message ID (0000,1031) in C-Store responses driven by C-Move
+
+Plugins
+-------
+
+* Speedup in plugins by removing unnecessary locks
+* New callback to filter incoming HTTP requests: OrthancPluginRegisterIncomingHttpRequestFilter()
+* New callback to handle non-worklists C-Find requests: OrthancPluginRegisterFindCallback()
+* New callback to handle C-Move requests: OrthancPluginRegisterMoveCallback()
+* New function: "OrthancPluginHttpClient()" to do HTTP requests with full control
+* New function: "OrthancPluginGenerateUuid()" to generate a UUID
+* More than one custom image decoder can be installed (e.g. to handle different transfer syntaxes)
+
+Lua
+---
+
+* Access to the HTTP headers in the "IncomingHttpRequestFilter()" callback
+
+Image decoding
+--------------
+
+* Huge speedup if decoding the family of JPEG transfer syntaxes
+* Refactoring leading to speedups with custom image decoders (including Web viewer plugin)
+* Support decoding of RLE Lossless transfer syntax
+* Support of signed 16bpp images in ParsedDicomFile
+
+Maintenance
+-----------
+
+* New logo of Orthanc
+* Fix issue 11 (is_regular_file() fails for FILE_ATTRIBUTE_REPARSE_POINT)
+* Fix issue 16 ("Limit" parameter error in REST API /tools/find method)
+* Fix of Debian bug #818512 ("FTBFS: Please install libdcmtk*-dev")
+* Fix of Debian bug #823139 ("orthanc: Please provide RecoverCompressedFile.cpp")
+* Replaced "localhost" by "127.0.0.1", as it might impact performance on Windows
+* Compatibility with CMake >= 3.5.0
+* Possibility to use forthcoming DCMTK 3.6.1 in static builds (instead of 3.6.0)
+* Upgrade to Boost 1.60.0 for static builds
+* Use of HTTP status 403 Forbidden (instead of 401) if access to a REST resource is disallowed
+* Option "HttpsVerifyPeers" can be used to connect against self-signed HTTPS certificates
+* New configuration option "AllowFindSopClassesInStudy"
+* Macro "__linux" (now obsolete) replaced by macro "__linux__" (maybe solves Debian bug #821011)
+* Modification of instances can now replace PixelData (resp. EncapsulatedDocument) with
+ provided a PNG/JPEG image (resp. PDF file) if it is encoded using Data URI Scheme
+* Dropped support of Google Log
+
+
Version 1.0.0 (2015/12/15)
==========================
@@ -640,7 +710,7 @@ Version 0.2.3 (2012/10/26)
Version 0.2.2 (2012/10/04)
==========================
-* Switch to Google Logging
+* Switch to Google Log
* Fixes to Debian packaging
diff --git a/OrthancServer/DatabaseWrapper.cpp b/OrthancServer/DatabaseWrapper.cpp
index 285a50a..4433255 100644
--- a/OrthancServer/DatabaseWrapper.cpp
+++ b/OrthancServer/DatabaseWrapper.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DatabaseWrapper.h b/OrthancServer/DatabaseWrapper.h
index ca63526..e9aeecb 100644
--- a/OrthancServer/DatabaseWrapper.h
+++ b/OrthancServer/DatabaseWrapper.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DatabaseWrapperBase.cpp b/OrthancServer/DatabaseWrapperBase.cpp
index 8cba049..0667a67 100644
--- a/OrthancServer/DatabaseWrapperBase.cpp
+++ b/OrthancServer/DatabaseWrapperBase.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -350,7 +350,7 @@ namespace Orthanc
{
map.SetValue(s.ColumnInt(1),
s.ColumnInt(2),
- s.ColumnString(3));
+ s.ColumnString(3), false);
}
}
diff --git a/OrthancServer/DatabaseWrapperBase.h b/OrthancServer/DatabaseWrapperBase.h
index d00e75e..014e997 100644
--- a/OrthancServer/DatabaseWrapperBase.h
+++ b/OrthancServer/DatabaseWrapperBase.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomDirWriter.h b/OrthancServer/DefaultDicomImageDecoder.h
similarity index 77%
copy from OrthancServer/DicomDirWriter.h
copy to OrthancServer/DefaultDicomImageDecoder.h
index 892c1f3..c82fd5b 100644
--- a/OrthancServer/DicomDirWriter.h
+++ b/OrthancServer/DefaultDicomImageDecoder.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,30 +32,21 @@
#pragma once
+#include "IDicomImageDecoder.h"
#include "ParsedDicomFile.h"
-
-#include <boost/noncopyable.hpp>
+#include "Internals/DicomImageDecoder.h"
namespace Orthanc
{
- class DicomDirWriter : public boost::noncopyable
+ class DefaultDicomImageDecoder : public IDicomImageDecoder
{
- private:
- class PImpl;
- PImpl* pimpl_;
-
public:
- DicomDirWriter();
-
- ~DicomDirWriter();
-
- void SetFileSetId(const std::string& id);
-
- void Add(const std::string& directory,
- const std::string& filename,
- ParsedDicomFile& dicom);
-
- void Encode(std::string& target);
+ virtual ImageAccessor* Decode(const void* dicom,
+ size_t size,
+ unsigned int frame)
+ {
+ ParsedDicomFile parsed(dicom, size);
+ return DicomImageDecoder::Decode(parsed, frame);
+ }
};
-
}
diff --git a/OrthancServer/DicomDirWriter.cpp b/OrthancServer/DicomDirWriter.cpp
index a7d838d..6f77011 100644
--- a/OrthancServer/DicomDirWriter.cpp
+++ b/OrthancServer/DicomDirWriter.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomDirWriter.h b/OrthancServer/DicomDirWriter.h
index 892c1f3..05e4f31 100644
--- a/OrthancServer/DicomDirWriter.h
+++ b/OrthancServer/DicomDirWriter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomInstanceToStore.cpp b/OrthancServer/DicomInstanceToStore.cpp
index 08ae7c9..2f0717c 100644
--- a/OrthancServer/DicomInstanceToStore.cpp
+++ b/OrthancServer/DicomInstanceToStore.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -34,6 +34,7 @@
#include "DicomInstanceToStore.h"
#include "FromDcmtkBridge.h"
+#include "OrthancInitialization.h"
#include "../Core/Logging.h"
#include <dcmtk/dcmdata/dcfilefo.h>
@@ -104,7 +105,9 @@ namespace Orthanc
{
summary_.Allocate();
FromDcmtkBridge::Convert(summary_.GetContent(),
- *parsed_.GetContent().GetDcmtkObject().getDataset());
+ *parsed_.GetContent().GetDcmtkObject().getDataset(),
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
}
if (!json_.HasContent())
@@ -114,7 +117,8 @@ namespace Orthanc
*parsed_.GetContent().GetDcmtkObject().getDataset(),
DicomToJsonFormat_Full,
DicomToJsonFlags_Default,
- ORTHANC_MAXIMUM_TAG_LENGTH);
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
}
}
diff --git a/OrthancServer/DicomInstanceToStore.h b/OrthancServer/DicomInstanceToStore.h
index de86f91..5d7f318 100644
--- a/OrthancServer/DicomInstanceToStore.h
+++ b/OrthancServer/DicomInstanceToStore.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomModification.cpp b/OrthancServer/DicomModification.cpp
index bd073b5..896300b 100644
--- a/OrthancServer/DicomModification.cpp
+++ b/OrthancServer/DicomModification.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -141,7 +141,7 @@ namespace Orthanc
mapped = previous->second;
}
- dicom.Replace(*tag, mapped);
+ dicom.Replace(*tag, mapped, false /* don't try and decode data URI scheme for UIDs */, DicomReplaceMode_InsertIfAbsent);
}
DicomModification::DicomModification()
@@ -459,7 +459,7 @@ namespace Orthanc
for (Replacements::const_iterator it = replacements_.begin();
it != replacements_.end(); ++it)
{
- toModify.Replace(it->first, *it->second, DicomReplaceMode_InsertIfAbsent);
+ toModify.Replace(it->first, *it->second, true /* decode data URI scheme */, DicomReplaceMode_InsertIfAbsent);
}
// (4) Update the DICOM identifiers
diff --git a/OrthancServer/DicomModification.h b/OrthancServer/DicomModification.h
index 1c635a6..5e10de4 100644
--- a/OrthancServer/DicomModification.h
+++ b/OrthancServer/DicomModification.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/DicomFindAnswers.cpp b/OrthancServer/DicomProtocol/DicomFindAnswers.cpp
index 227f830..c649932 100644
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.cpp
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -67,7 +67,7 @@ namespace Orthanc
CleanupDicom();
}
- Answer(const char* dicom,
+ Answer(const void* dicom,
size_t size) :
dicom_(new ParsedDicomFile(dicom, size)),
map_(NULL)
@@ -153,7 +153,7 @@ namespace Orthanc
}
- void DicomFindAnswers::Add(const char* dicom,
+ void DicomFindAnswers::Add(const void* dicom,
size_t size)
{
answers_.push_back(new Answer(dicom, size));
diff --git a/OrthancServer/DicomProtocol/DicomFindAnswers.h b/OrthancServer/DicomProtocol/DicomFindAnswers.h
index ab32cb7..5dfc23c 100644
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.h
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -64,7 +64,7 @@ namespace Orthanc
void Add(ParsedDicomFile& dicom);
- void Add(const char* dicom,
+ void Add(const void* dicom,
size_t size);
size_t GetSize() const
diff --git a/OrthancServer/DicomProtocol/DicomServer.cpp b/OrthancServer/DicomProtocol/DicomServer.cpp
index 08a7a49..deb80aa 100644
--- a/OrthancServer/DicomProtocol/DicomServer.cpp
+++ b/OrthancServer/DicomProtocol/DicomServer.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -44,7 +44,7 @@
#include <boost/thread.hpp>
-#if defined(__linux)
+#if defined(__linux__)
#include <cstdlib>
#endif
diff --git a/OrthancServer/DicomProtocol/DicomServer.h b/OrthancServer/DicomProtocol/DicomServer.h
index ad42974..807f13c 100644
--- a/OrthancServer/DicomProtocol/DicomServer.h
+++ b/OrthancServer/DicomProtocol/DicomServer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/DicomUserConnection.cpp b/OrthancServer/DicomProtocol/DicomUserConnection.cpp
index 2606ae4..2636536 100644
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -86,6 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../../Core/OrthancException.h"
#include "../FromDcmtkBridge.h"
#include "../ToDcmtkBridge.h"
+#include "../OrthancInitialization.h"
#include <dcmtk/dcmdata/dcistrmb.h>
#include <dcmtk/dcmdata/dcistrmf.h>
@@ -148,7 +149,9 @@ namespace Orthanc
void CheckIsOpen() const;
- void Store(DcmInputStream& is, DicomUserConnection& connection);
+ void Store(DcmInputStream& is,
+ DicomUserConnection& connection,
+ uint16_t moveOriginatorID);
};
@@ -251,7 +254,9 @@ namespace Orthanc
}
- void DicomUserConnection::PImpl::Store(DcmInputStream& is, DicomUserConnection& connection)
+ void DicomUserConnection::PImpl::Store(DcmInputStream& is,
+ DicomUserConnection& connection,
+ uint16_t moveOriginatorID)
{
CheckIsOpen();
@@ -325,18 +330,28 @@ namespace Orthanc
}
// Prepare the transmission of data
- T_DIMSE_C_StoreRQ req;
- memset(&req, 0, sizeof(req));
- req.MessageID = assoc_->nextMsgID++;
- strcpy(req.AffectedSOPClassUID, sopClass);
- strcpy(req.AffectedSOPInstanceUID, sopInstance);
- req.DataSetType = DIMSE_DATASET_PRESENT;
- req.Priority = DIMSE_PRIORITY_MEDIUM;
+ T_DIMSE_C_StoreRQ request;
+ memset(&request, 0, sizeof(request));
+ request.MessageID = assoc_->nextMsgID++;
+ strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN);
+ request.Priority = DIMSE_PRIORITY_MEDIUM;
+ request.DataSetType = DIMSE_DATASET_PRESENT;
+ strncpy(request.AffectedSOPInstanceUID, sopInstance, DIC_UI_LEN);
+
+ strncpy(request.MoveOriginatorApplicationEntityTitle,
+ connection.GetLocalApplicationEntityTitle().c_str(), DIC_AE_LEN);
+ request.opts = O_STORE_MOVEORIGINATORAETITLE;
+
+ if (moveOriginatorID != 0)
+ {
+ request.MoveOriginatorID = moveOriginatorID; // The type DIC_US is an alias for uint16_t
+ request.opts |= O_STORE_MOVEORIGINATORID;
+ }
// Finally conduct transmission of data
T_DIMSE_C_StoreRSP rsp;
DcmDataset* statusDetail = NULL;
- Check(DIMSE_storeUser(assoc_, presID, &req,
+ Check(DIMSE_storeUser(assoc_, presID, &request,
NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL,
/*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ dimseTimeout_,
&rsp, &statusDetail, NULL));
@@ -380,11 +395,13 @@ namespace Orthanc
else
{
DicomMap m;
- FromDcmtkBridge::Convert(m, *responseIdentifiers);
+ FromDcmtkBridge::Convert(m, *responseIdentifiers,
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
if (!m.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
{
- m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level);
+ m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level, false);
}
payload.answers->Add(m);
@@ -419,9 +436,27 @@ namespace Orthanc
throw OrthancException(ErrorCode_InternalError);
}
- if (level == ResourceType_Study)
+ switch (level)
{
- allowedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY);
+ case ResourceType_Patient:
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES);
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES);
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES);
+ break;
+
+ case ResourceType_Study:
+ allowedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY);
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES);
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES);
+ allowedTags.insert(DICOM_TAG_SOP_CLASSES_IN_STUDY);
+ break;
+
+ case ResourceType_Series:
+ allowedTags.insert(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES);
+ break;
+
+ default:
+ break;
}
allowedTags.insert(DICOM_TAG_SPECIFIC_CHARACTER_SET);
@@ -461,7 +496,7 @@ namespace Orthanc
for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
- if (FromDcmtkBridge::GetValueRepresentation(*it) == ValueRepresentation_Date)
+ if (FromDcmtkBridge::LookupValueRepresentation(*it) == ValueRepresentation_Date)
{
// Replace a "*" query by an empty query ("") for "date"
// value representations. Necessary to search over dates
@@ -472,7 +507,7 @@ namespace Orthanc
!value->IsNull() &&
value->GetContent() == "*")
{
- fix->SetValue(*it, "");
+ fix->SetValue(*it, "", false);
}
}
}
@@ -511,9 +546,9 @@ namespace Orthanc
T_DIMSE_C_FindRQ request;
memset(&request, 0, sizeof(request));
request.MessageID = association->nextMsgID++;
- strcpy(request.AffectedSOPClassUID, sopClass);
- request.DataSetType = DIMSE_DATASET_PRESENT;
+ strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN);
request.Priority = DIMSE_PRIORITY_MEDIUM;
+ request.DataSetType = DIMSE_DATASET_PRESENT;
T_DIMSE_C_FindRSP response;
DcmDataset* statusDetail = NULL;
@@ -678,10 +713,10 @@ namespace Orthanc
T_DIMSE_C_MoveRQ request;
memset(&request, 0, sizeof(request));
request.MessageID = pimpl_->assoc_->nextMsgID++;
- strcpy(request.AffectedSOPClassUID, sopClass);
- request.DataSetType = DIMSE_DATASET_PRESENT;
+ strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN);
request.Priority = DIMSE_PRIORITY_MEDIUM;
- strncpy(request.MoveDestination, targetAet.c_str(), sizeof(DIC_AE) / sizeof(char));
+ request.DataSetType = DIMSE_DATASET_PRESENT;
+ strncpy(request.MoveDestination, targetAet.c_str(), DIC_AE_LEN);
T_DIMSE_C_MoveRSP response;
DcmDataset* statusDetail = NULL;
@@ -920,7 +955,9 @@ namespace Orthanc
return pimpl_->IsOpen();
}
- void DicomUserConnection::Store(const char* buffer, size_t size)
+ void DicomUserConnection::Store(const char* buffer,
+ size_t size,
+ uint16_t moveOriginatorID)
{
// Prepare an input stream for the memory buffer
DcmInputBufferStream is;
@@ -928,22 +965,24 @@ namespace Orthanc
is.setBuffer(buffer, size);
is.setEos();
- pimpl_->Store(is, *this);
+ pimpl_->Store(is, *this, moveOriginatorID);
}
- void DicomUserConnection::Store(const std::string& buffer)
+ void DicomUserConnection::Store(const std::string& buffer,
+ uint16_t moveOriginatorID)
{
if (buffer.size() > 0)
- Store(reinterpret_cast<const char*>(&buffer[0]), buffer.size());
+ Store(reinterpret_cast<const char*>(&buffer[0]), buffer.size(), moveOriginatorID);
else
- Store(NULL, 0);
+ Store(NULL, 0, moveOriginatorID);
}
- void DicomUserConnection::StoreFile(const std::string& path)
+ void DicomUserConnection::StoreFile(const std::string& path,
+ uint16_t moveOriginatorID)
{
// Prepare an input stream for the file
DcmInputFileStream is(path.c_str());
- pimpl_->Store(is, *this);
+ pimpl_->Store(is, *this, moveOriginatorID);
}
bool DicomUserConnection::Echo()
@@ -974,16 +1013,9 @@ namespace Orthanc
void DicomUserConnection::Move(const std::string& targetAet,
+ ResourceType level,
const DicomMap& findResult)
{
- if (!findResult.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
- {
- throw OrthancException(ErrorCode_InternalError);
- }
-
- const std::string tmp = findResult.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).GetContent();
- ResourceType level = StringToResourceType(tmp.c_str());
-
DicomMap move;
switch (level)
{
@@ -1014,11 +1046,26 @@ namespace Orthanc
}
+ void DicomUserConnection::Move(const std::string& targetAet,
+ const DicomMap& findResult)
+ {
+ if (!findResult.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ const std::string tmp = findResult.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).GetContent();
+ ResourceType level = StringToResourceType(tmp.c_str());
+
+ Move(targetAet, level, findResult);
+ }
+
+
void DicomUserConnection::MovePatient(const std::string& targetAet,
const std::string& patientId)
{
DicomMap query;
- query.SetValue(DICOM_TAG_PATIENT_ID, patientId);
+ query.SetValue(DICOM_TAG_PATIENT_ID, patientId, false);
MoveInternal(targetAet, ResourceType_Patient, query);
}
@@ -1026,7 +1073,7 @@ namespace Orthanc
const std::string& studyUid)
{
DicomMap query;
- query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
+ query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
MoveInternal(targetAet, ResourceType_Study, query);
}
@@ -1035,8 +1082,8 @@ namespace Orthanc
const std::string& seriesUid)
{
DicomMap query;
- query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
- query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
+ query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
+ query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
MoveInternal(targetAet, ResourceType_Series, query);
}
@@ -1046,9 +1093,9 @@ namespace Orthanc
const std::string& instanceUid)
{
DicomMap query;
- query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
- query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
- query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid);
+ query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
+ query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
+ query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, false);
MoveInternal(targetAet, ResourceType_Instance, query);
}
diff --git a/OrthancServer/DicomProtocol/DicomUserConnection.h b/OrthancServer/DicomProtocol/DicomUserConnection.h
index efc3eb5..48ea451 100644
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -133,17 +133,25 @@ namespace Orthanc
bool Echo();
- void Store(const char* buffer, size_t size);
+ void Store(const char* buffer,
+ size_t size,
+ uint16_t moveOriginatorID);
- void Store(const std::string& buffer);
+ void Store(const std::string& buffer,
+ uint16_t moveOriginatorID);
- void StoreFile(const std::string& path);
+ void StoreFile(const std::string& path,
+ uint16_t moveOriginatorID);
void Find(DicomFindAnswers& result,
ResourceType level,
const DicomMap& fields);
void Move(const std::string& targetAet,
+ ResourceType level,
+ const DicomMap& findResult);
+
+ void Move(const std::string& targetAet,
const DicomMap& findResult);
void MovePatient(const std::string& targetAet,
diff --git a/OrthancServer/DicomProtocol/IApplicationEntityFilter.h b/OrthancServer/DicomProtocol/IApplicationEntityFilter.h
index 466035d..1518fd6 100644
--- a/OrthancServer/DicomProtocol/IApplicationEntityFilter.h
+++ b/OrthancServer/DicomProtocol/IApplicationEntityFilter.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IFindRequestHandler.h b/OrthancServer/DicomProtocol/IFindRequestHandler.h
index 64f92f6..eaa0362 100644
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h b/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h
index 70460bb..4f76263 100644
--- a/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h
+++ b/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IMoveRequestHandler.h b/OrthancServer/DicomProtocol/IMoveRequestHandler.h
index 1586954..e840e02 100644
--- a/OrthancServer/DicomProtocol/IMoveRequestHandler.h
+++ b/OrthancServer/DicomProtocol/IMoveRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -71,7 +71,8 @@ namespace Orthanc
const DicomMap& input,
const std::string& remoteIp,
const std::string& remoteAet,
- const std::string& calledAet) = 0;
+ const std::string& calledAet,
+ uint16_t messageId) = 0;
};
}
diff --git a/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h b/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h
index f0289bf..b826f4e 100644
--- a/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h
+++ b/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IStoreRequestHandler.h b/OrthancServer/DicomProtocol/IStoreRequestHandler.h
index 5a3c200..6be724d 100644
--- a/OrthancServer/DicomProtocol/IStoreRequestHandler.h
+++ b/OrthancServer/DicomProtocol/IStoreRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h b/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h
index 95640de..7a3754f 100644
--- a/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h
+++ b/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IWorklistRequestHandler.h b/OrthancServer/DicomProtocol/IWorklistRequestHandler.h
index 0bb54da..7f57b71 100644
--- a/OrthancServer/DicomProtocol/IWorklistRequestHandler.h
+++ b/OrthancServer/DicomProtocol/IWorklistRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h b/OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h
index 731d85b..039e8d3 100644
--- a/OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h
+++ b/OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp
index 1338f33..8c158cc 100644
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp
+++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -43,7 +43,7 @@ namespace Orthanc
{
RemoteModalityParameters::RemoteModalityParameters() :
aet_("ORTHANC"),
- host_("localhost"),
+ host_("127.0.0.1"),
port_(104),
manufacturer_(ModalityManufacturer_Generic)
{
diff --git a/OrthancServer/DicomProtocol/RemoteModalityParameters.h b/OrthancServer/DicomProtocol/RemoteModalityParameters.h
index 8b4a08b..d0d3700 100644
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.h
+++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp
index 1cce99d..e2c3892 100644
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp
+++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h
index 0586b61..aee23b1 100644
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h
+++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ExportedResource.cpp b/OrthancServer/ExportedResource.cpp
index 45b29ca..552fa2d 100644
--- a/OrthancServer/ExportedResource.cpp
+++ b/OrthancServer/ExportedResource.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ExportedResource.h b/OrthancServer/ExportedResource.h
index 289a073..ad76921 100644
--- a/OrthancServer/ExportedResource.h
+++ b/OrthancServer/ExportedResource.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/FromDcmtkBridge.cpp b/OrthancServer/FromDcmtkBridge.cpp
index 581fa7b..921d250 100644
--- a/OrthancServer/FromDcmtkBridge.cpp
+++ b/OrthancServer/FromDcmtkBridge.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -30,42 +30,38 @@
**/
-
#include "PrecompiledHeadersServer.h"
#ifndef NOMINMAX
#define NOMINMAX
#endif
-#include "Internals/DicomImageDecoder.h"
-
#include "FromDcmtkBridge.h"
#include "ToDcmtkBridge.h"
-#include "OrthancInitialization.h"
#include "../Core/Logging.h"
#include "../Core/Toolbox.h"
-#include "../Core/OrthancException.h"
-#include "../Core/Images/PngWriter.h"
#include "../Core/Uuid.h"
-#include "../Core/DicomFormat/DicomIntegerPixelAccessor.h"
+#include "../Core/OrthancException.h"
#include <list>
#include <limits>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/predicate.hpp>
-#include <dcmtk/dcmdata/dcchrstr.h>
+#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmdata/dcdicent.h>
#include <dcmtk/dcmdata/dcdict.h>
#include <dcmtk/dcmdata/dcfilefo.h>
-#include <dcmtk/dcmdata/dcistrmb.h>
+#include <dcmtk/dcmdata/dcostrmb.h>
+#include <dcmtk/dcmdata/dcpixel.h>
#include <dcmtk/dcmdata/dcuid.h>
-#include <dcmtk/dcmdata/dcmetinf.h>
-#include <dcmtk/dcmdata/dcdeftag.h>
+#include <dcmtk/dcmdata/dcistrmb.h>
#include <dcmtk/dcmdata/dcvrae.h>
#include <dcmtk/dcmdata/dcvras.h>
+#include <dcmtk/dcmdata/dcvrat.h>
#include <dcmtk/dcmdata/dcvrcs.h>
#include <dcmtk/dcmdata/dcvrda.h>
#include <dcmtk/dcmdata/dcvrds.h>
@@ -85,16 +81,11 @@
#include <dcmtk/dcmdata/dcvrul.h>
#include <dcmtk/dcmdata/dcvrus.h>
#include <dcmtk/dcmdata/dcvrut.h>
-#include <dcmtk/dcmdata/dcpixel.h>
-#include <dcmtk/dcmdata/dcpixseq.h>
-#include <dcmtk/dcmdata/dcpxitem.h>
-#include <dcmtk/dcmdata/dcvrat.h>
-#include <dcmtk/dcmnet/dul.h>
-#include <boost/math/special_functions/round.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-#include <dcmtk/dcmdata/dcostrmb.h>
+#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
+#include <EmbeddedResources.h>
+#endif
namespace Orthanc
@@ -124,15 +115,16 @@ namespace Orthanc
static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
EmbeddedResources::FileResourceId resource)
{
- Toolbox::TemporaryFile tmp;
+ std::string content;
+ EmbeddedResources::GetFileResource(content, resource);
- FILE* fp = fopen(tmp.GetPath().c_str(), "wb");
- fwrite(EmbeddedResources::GetFileResourceBuffer(resource),
- EmbeddedResources::GetFileResourceSize(resource), 1, fp);
- fclose(fp);
+ Toolbox::TemporaryFile tmp;
+ tmp.Write(content);
if (!dictionary.loadDictionary(tmp.GetPath().c_str()))
{
+ LOG(ERROR) << "Cannot read embedded dictionary. Under Windows, make sure that "
+ << "your TEMP directory does not contain special characters.";
throw OrthancException(ErrorCode_InternalError);
}
}
@@ -188,9 +180,6 @@ namespace Orthanc
void FromDcmtkBridge::InitializeDictionary()
{
- /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */
- dcmDisableGethostbyaddr.set(OFTrue);
-
{
DictionaryLocker locker;
@@ -208,7 +197,7 @@ namespace Orthanc
LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICOM);
LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_PRIVATE);
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__)
std::string path = DCMTK_DICTIONARY_DIR;
const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
@@ -245,7 +234,7 @@ namespace Orthanc
void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag,
- const DcmEVR& vr,
+ ValueRepresentation vr,
const std::string& name,
unsigned int minMultiplicity,
unsigned int maxMultiplicity)
@@ -266,13 +255,15 @@ namespace Orthanc
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
- LOG(INFO) << "Registering tag in dictionary: " << tag << " " << (DcmVR(vr).getValidVRName()) << " "
+ DcmEVR evr = ToDcmtkBridge::Convert(vr);
+
+ LOG(INFO) << "Registering tag in dictionary: " << tag << " " << (DcmVR(evr).getValidVRName()) << " "
<< name << " (multiplicity: " << minMultiplicity << "-"
<< (arbitrary ? "n" : boost::lexical_cast<std::string>(maxMultiplicity)) << ")";
std::auto_ptr<DcmDictEntry> entry(new DcmDictEntry(tag.GetGroup(),
tag.GetElement(),
- vr, name.c_str(),
+ evr, name.c_str(),
static_cast<int>(minMultiplicity),
static_cast<int>(maxMultiplicity),
NULL /* version */,
@@ -289,11 +280,10 @@ namespace Orthanc
}
- Encoding FromDcmtkBridge::DetectEncoding(DcmDataset& dataset)
+ Encoding FromDcmtkBridge::DetectEncoding(DcmItem& dataset,
+ Encoding defaultEncoding)
{
- // By default, Latin1 encoding is assumed
- std::string s = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
- Encoding encoding = s.empty() ? Encoding_Latin1 : StringToEncoding(s.c_str());
+ Encoding encoding = defaultEncoding;
OFString tmp;
if (dataset.findAndGetOFString(DCM_SpecificCharacterSet, tmp).good())
@@ -324,9 +314,12 @@ namespace Orthanc
}
- void FromDcmtkBridge::Convert(DicomMap& target, DcmDataset& dataset)
+ void FromDcmtkBridge::Convert(DicomMap& target,
+ DcmItem& dataset,
+ unsigned int maxStringLength,
+ Encoding defaultEncoding)
{
- Encoding encoding = DetectEncoding(dataset);
+ Encoding encoding = DetectEncoding(dataset, defaultEncoding);
target.Clear();
for (unsigned long i = 0; i < dataset.card(); i++)
@@ -336,7 +329,7 @@ namespace Orthanc
{
target.SetValue(element->getTag().getGTag(),
element->getTag().getETag(),
- ConvertLeafElement(*element, DicomToJsonFlags_Default, encoding));
+ ConvertLeafElement(*element, DicomToJsonFlags_Default, maxStringLength, encoding));
}
}
}
@@ -356,6 +349,7 @@ namespace Orthanc
DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
DicomToJsonFlags flags,
+ unsigned int maxStringLength,
Encoding encoding)
{
if (!element.isLeaf())
@@ -377,7 +371,8 @@ namespace Orthanc
std::string s(c);
std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
- if (utf8.size() > ORTHANC_MAXIMUM_TAG_LENGTH)
+ if (maxStringLength != 0 &&
+ utf8.size() > maxStringLength)
{
return new DicomValue; // Create a NULL value
}
@@ -723,7 +718,7 @@ namespace Orthanc
if (element.isLeaf())
{
- std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, flags, encoding));
+ std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, flags, maxStringLength, encoding));
LeafValueToJson(target, *v, format, flags, maxStringLength);
}
else
@@ -805,10 +800,11 @@ namespace Orthanc
DcmDataset& dataset,
DicomToJsonFormat format,
DicomToJsonFlags flags,
- unsigned int maxStringLength)
+ unsigned int maxStringLength,
+ Encoding defaultEncoding)
{
target = Json::objectValue;
- DatasetToJson(target, dataset, format, flags, maxStringLength, DetectEncoding(dataset));
+ DatasetToJson(target, dataset, format, flags, maxStringLength, DetectEncoding(dataset, defaultEncoding));
}
@@ -1077,28 +1073,115 @@ namespace Orthanc
}
- ValueRepresentation FromDcmtkBridge::GetValueRepresentation(const DicomTag& tag)
+ ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag)
{
DcmTag t(tag.GetGroup(), tag.GetElement());
- switch (t.getEVR())
+ return Convert(t.getEVR());
+ }
+
+ ValueRepresentation FromDcmtkBridge::Convert(const DcmEVR vr)
+ {
+ switch (vr)
{
- case EVR_PN:
- return ValueRepresentation_PatientName;
+ case EVR_AE:
+ return ValueRepresentation_ApplicationEntity;
+
+ case EVR_AS:
+ return ValueRepresentation_AgeString;
+
+ case EVR_AT:
+ return ValueRepresentation_AttributeTag;
+
+ case EVR_CS:
+ return ValueRepresentation_CodeString;
case EVR_DA:
return ValueRepresentation_Date;
+ case EVR_DS:
+ return ValueRepresentation_DecimalString;
+
case EVR_DT:
return ValueRepresentation_DateTime;
- case EVR_TM:
- return ValueRepresentation_Time;
+ case EVR_FL:
+ return ValueRepresentation_FloatingPointSingle;
+
+ case EVR_FD:
+ return ValueRepresentation_FloatingPointDouble;
+
+ case EVR_IS:
+ return ValueRepresentation_IntegerString;
+
+ case EVR_LO:
+ return ValueRepresentation_LongString;
+
+ case EVR_LT:
+ return ValueRepresentation_LongText;
+
+ case EVR_OB:
+ return ValueRepresentation_OtherByte;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case EVR_OD:
+ return ValueRepresentation_OtherDouble;*/
+
+ case EVR_OF:
+ return ValueRepresentation_OtherFloat;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case EVR_OL:
+ return ValueRepresentation_OtherLong;*/
+
+ case EVR_OW:
+ return ValueRepresentation_OtherWord;
+
+ case EVR_PN:
+ return ValueRepresentation_PersonName;
+
+ case EVR_SH:
+ return ValueRepresentation_ShortString;
+
+ case EVR_SL:
+ return ValueRepresentation_SignedLong;
case EVR_SQ:
return ValueRepresentation_Sequence;
+ case EVR_SS:
+ return ValueRepresentation_SignedShort;
+
+ case EVR_ST:
+ return ValueRepresentation_ShortText;
+
+ case EVR_TM:
+ return ValueRepresentation_Time;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case EVR_UC:
+ return ValueRepresentation_UnlimitedCharacters;*/
+
+ case EVR_UI:
+ return ValueRepresentation_UniqueIdentifier;
+
+ case EVR_UL:
+ return ValueRepresentation_UnsignedLong;
+
+ case EVR_UN:
+ return ValueRepresentation_Unknown;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case EVR_UR:
+ return ValueRepresentation_UniversalResource;*/
+
+ case EVR_US:
+ return ValueRepresentation_UnsignedShort;
+
+ case EVR_UT:
+ return ValueRepresentation_UnlimitedText;
+
default:
- return ValueRepresentation_Other;
+ return ValueRepresentation_NotSupported;
}
}
@@ -1272,7 +1355,11 @@ namespace Orthanc
boost::starts_with(utf8Value, "data:application/octet-stream;base64,"))
{
std::string mime;
- Toolbox::DecodeDataUriScheme(mime, binary, utf8Value);
+ if (!Toolbox::DecodeDataUriScheme(mime, binary, utf8Value))
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
decoded = &binary;
}
else if (dicomEncoding != Encoding_Utf8)
@@ -1481,89 +1568,201 @@ namespace Orthanc
}
- DcmEVR FromDcmtkBridge::ParseValueRepresentation(const std::string& s)
+ DcmPixelSequence* FromDcmtkBridge::GetPixelSequence(DcmDataset& dataset)
{
- if (s == "AE")
- return EVR_AE;
+ DcmElement *element = NULL;
+ if (!dataset.findAndGetElement(DCM_PixelData, element).good())
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
- if (s == "AS")
- return EVR_AS;
+ DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
+ DcmPixelSequence* pixelSequence = NULL;
+ if (!pixelData.getEncapsulatedRepresentation
+ (dataset.getOriginalXfer(), NULL, pixelSequence).good())
+ {
+ return NULL;
+ }
+ else
+ {
+ return pixelSequence;
+ }
+ }
- if (s == "AT")
- return EVR_AT;
- if (s == "CS")
- return EVR_CS;
+ Encoding FromDcmtkBridge::ExtractEncoding(const Json::Value& json,
+ Encoding defaultEncoding)
+ {
+ if (json.type() != Json::objectValue)
+ {
+ throw OrthancException(ErrorCode_BadParameterType);
+ }
- if (s == "DA")
- return EVR_DA;
+ Encoding encoding = defaultEncoding;
- if (s == "DS")
- return EVR_DS;
+ const Json::Value::Members tags = json.getMemberNames();
+
+ // Look for SpecificCharacterSet (0008,0005) in the JSON file
+ for (size_t i = 0; i < tags.size(); i++)
+ {
+ DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
+ if (tag == DICOM_TAG_SPECIFIC_CHARACTER_SET)
+ {
+ const Json::Value& value = json[tags[i]];
+ if (value.type() != Json::stringValue ||
+ !GetDicomEncoding(encoding, value.asCString()))
+ {
+ LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value;
+ throw OrthancException(ErrorCode_BadRequest);
+ }
+ }
+ }
- if (s == "DT")
- return EVR_DT;
+ return encoding;
+ }
- if (s == "FD")
- return EVR_FD;
- if (s == "FL")
- return EVR_FL;
+ static void SetString(DcmDataset& target,
+ const DcmTag& tag,
+ const std::string& value)
+ {
+ if (!target.putAndInsertString(tag, value.c_str()).good())
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+ }
- if (s == "IS")
- return EVR_IS;
- if (s == "LO")
- return EVR_LO;
+ DcmDataset* FromDcmtkBridge::FromJson(const Json::Value& json, // Encoded using UTF-8
+ bool generateIdentifiers,
+ bool decodeDataUriScheme,
+ Encoding defaultEncoding)
+ {
+ std::auto_ptr<DcmDataset> result(new DcmDataset);
+ Encoding encoding = ExtractEncoding(json, defaultEncoding);
- if (s == "LT")
- return EVR_LT;
+ SetString(*result, DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(encoding));
- if (s == "OB")
- return EVR_OB;
+ const Json::Value::Members tags = json.getMemberNames();
+
+ bool hasPatientId = false;
+ bool hasStudyInstanceUid = false;
+ bool hasSeriesInstanceUid = false;
+ bool hasSopInstanceUid = false;
- if (s == "OF")
- return EVR_OF;
+ for (size_t i = 0; i < tags.size(); i++)
+ {
+ DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
+ const Json::Value& value = json[tags[i]];
- if (s == "OW")
- return EVR_OW;
+ if (tag == DICOM_TAG_PATIENT_ID)
+ {
+ hasPatientId = true;
+ }
+ else if (tag == DICOM_TAG_STUDY_INSTANCE_UID)
+ {
+ hasStudyInstanceUid = true;
+ }
+ else if (tag == DICOM_TAG_SERIES_INSTANCE_UID)
+ {
+ hasSeriesInstanceUid = true;
+ }
+ else if (tag == DICOM_TAG_SOP_INSTANCE_UID)
+ {
+ hasSopInstanceUid = true;
+ }
- if (s == "PN")
- return EVR_PN;
+ if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
+ {
+ std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding));
+ const DcmTagKey& tag = element->getTag();
- if (s == "SH")
- return EVR_SH;
+ result->findAndDeleteElement(tag);
- if (s == "SL")
- return EVR_SL;
+ DcmElement* tmp = element.release();
+ if (!result->insert(tmp, false, false).good())
+ {
+ delete tmp;
+ throw OrthancException(ErrorCode_InternalError);
+ }
+ }
+ }
- if (s == "SQ")
- return EVR_SQ;
+ if (!hasPatientId &&
+ generateIdentifiers)
+ {
+ SetString(*result, DCM_PatientID, GenerateUniqueIdentifier(ResourceType_Patient));
+ }
- if (s == "SS")
- return EVR_SS;
+ if (!hasStudyInstanceUid &&
+ generateIdentifiers)
+ {
+ SetString(*result, DCM_StudyInstanceUID, GenerateUniqueIdentifier(ResourceType_Study));
+ }
- if (s == "ST")
- return EVR_ST;
+ if (!hasSeriesInstanceUid &&
+ generateIdentifiers)
+ {
+ SetString(*result, DCM_SeriesInstanceUID, GenerateUniqueIdentifier(ResourceType_Series));
+ }
- if (s == "TM")
- return EVR_TM;
+ if (!hasSopInstanceUid &&
+ generateIdentifiers)
+ {
+ SetString(*result, DCM_SOPInstanceUID, GenerateUniqueIdentifier(ResourceType_Instance));
+ }
+
+ return result.release();
+ }
- if (s == "UI")
- return EVR_UI;
- if (s == "UL")
- return EVR_UL;
+ DcmFileFormat* FromDcmtkBridge::LoadFromMemoryBuffer(const void* buffer,
+ size_t size)
+ {
+ DcmInputBufferStream is;
+ if (size > 0)
+ {
+ is.setBuffer(buffer, size);
+ }
+ is.setEos();
- if (s == "UN")
- return EVR_UN;
+ std::auto_ptr<DcmFileFormat> result(new DcmFileFormat);
- if (s == "US")
- return EVR_US;
+ result->transferInit();
+ if (!result->read(is).good())
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
- if (s == "UT")
- return EVR_UT;
+ result->loadAllDataIntoMemory();
+ result->transferEnd();
- throw OrthancException(ErrorCode_ParameterOutOfRange);
+ return result.release();
+ }
+
+
+ void FromDcmtkBridge::FromJson(DicomMap& target,
+ const Json::Value& source)
+ {
+ if (source.type() != Json::objectValue)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ target.Clear();
+
+ Json::Value::Members members = source.getMemberNames();
+
+ for (size_t i = 0; i < members.size(); i++)
+ {
+ const Json::Value& value = source[members[i]];
+
+ if (value.type() != Json::stringValue)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ target.SetValue(ParseTag(members[i]), value.asString(), false);
+ }
}
}
diff --git a/OrthancServer/FromDcmtkBridge.h b/OrthancServer/FromDcmtkBridge.h
index ca238f5..eff3b81 100644
--- a/OrthancServer/FromDcmtkBridge.h
+++ b/OrthancServer/FromDcmtkBridge.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -38,6 +38,8 @@
#include <dcmtk/dcmdata/dcdatset.h>
#include <dcmtk/dcmdata/dcmetinf.h>
+#include <dcmtk/dcmdata/dcpixseq.h>
+#include <dcmtk/dcmdata/dcfilefo.h>
#include <json/json.h>
namespace Orthanc
@@ -48,14 +50,18 @@ namespace Orthanc
static void InitializeDictionary();
static void RegisterDictionaryTag(const DicomTag& tag,
- const DcmEVR& vr,
+ ValueRepresentation vr,
const std::string& name,
unsigned int minMultiplicity,
unsigned int maxMultiplicity);
- static Encoding DetectEncoding(DcmDataset& dataset);
+ static Encoding DetectEncoding(DcmItem& dataset,
+ Encoding defaultEncoding);
- static void Convert(DicomMap& target, DcmDataset& dataset);
+ static void Convert(DicomMap& target,
+ DcmItem& dataset,
+ unsigned int maxStringLength,
+ Encoding defaultEncoding);
static DicomTag Convert(const DcmTag& tag);
@@ -65,6 +71,7 @@ namespace Orthanc
static DicomValue* ConvertLeafElement(DcmElement& element,
DicomToJsonFlags flags,
+ unsigned int maxStringLength,
Encoding encoding);
static void ToJson(Json::Value& parent,
@@ -78,7 +85,8 @@ namespace Orthanc
DcmDataset& dataset,
DicomToJsonFormat format,
DicomToJsonFlags flags,
- unsigned int maxStringLength);
+ unsigned int maxStringLength,
+ Encoding defaultEncoding);
static void ToJson(Json::Value& target,
DcmMetaInfo& header,
@@ -123,7 +131,9 @@ namespace Orthanc
static bool SaveToMemoryBuffer(std::string& buffer,
DcmDataset& dataSet);
- static ValueRepresentation GetValueRepresentation(const DicomTag& tag);
+ static ValueRepresentation Convert(DcmEVR vr);
+
+ static ValueRepresentation LookupValueRepresentation(const DicomTag& tag);
static DcmElement* CreateElementForTag(const DicomTag& tag);
@@ -134,10 +144,24 @@ namespace Orthanc
Encoding dicomEncoding);
static DcmElement* FromJson(const DicomTag& tag,
- const Json::Value& element, // Encoding using UTF-8
+ const Json::Value& element, // Encoded using UTF-8
bool decodeDataUriScheme,
Encoding dicomEncoding);
- static DcmEVR ParseValueRepresentation(const std::string& s);
+ static DcmPixelSequence* GetPixelSequence(DcmDataset& dataset);
+
+ static Encoding ExtractEncoding(const Json::Value& json,
+ Encoding defaultEncoding);
+
+ static DcmDataset* FromJson(const Json::Value& json, // Encoded using UTF-8
+ bool generateIdentifiers,
+ bool decodeDataUriScheme,
+ Encoding defaultEncoding);
+
+ static DcmFileFormat* LoadFromMemoryBuffer(const void* buffer,
+ size_t size);
+
+ static void FromJson(DicomMap& values,
+ const Json::Value& result);
};
}
diff --git a/OrthancServer/IDatabaseListener.h b/OrthancServer/IDatabaseListener.h
index 46a6c0b..d29bbd5 100644
--- a/OrthancServer/IDatabaseListener.h
+++ b/OrthancServer/IDatabaseListener.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/IDatabaseWrapper.h b/OrthancServer/IDatabaseWrapper.h
index 902a8de..0892a3d 100644
--- a/OrthancServer/IDatabaseWrapper.h
+++ b/OrthancServer/IDatabaseWrapper.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/IDicomImageDecoder.h b/OrthancServer/IDicomImageDecoder.h
index 5677776..ef891a4 100644
--- a/OrthancServer/IDicomImageDecoder.h
+++ b/OrthancServer/IDicomImageDecoder.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -38,8 +38,6 @@
namespace Orthanc
{
- class ParsedDicomFile;
-
class IDicomImageDecoder : public boost::noncopyable
{
public:
@@ -47,7 +45,8 @@ namespace Orthanc
{
}
- virtual ImageAccessor* Decode(ParsedDicomFile& dicom,
+ virtual ImageAccessor* Decode(const void* dicom,
+ size_t size,
unsigned int frame) = 0;
};
}
diff --git a/OrthancServer/IServerListener.h b/OrthancServer/IServerListener.h
index f3513aa..185037b 100644
--- a/OrthancServer/IServerListener.h
+++ b/OrthancServer/IServerListener.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Internals/CommandDispatcher.cpp b/OrthancServer/Internals/CommandDispatcher.cpp
index bc090e2..7b56db5 100644
--- a/OrthancServer/Internals/CommandDispatcher.cpp
+++ b/OrthancServer/Internals/CommandDispatcher.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Internals/CommandDispatcher.h b/OrthancServer/Internals/CommandDispatcher.h
index ea82f17..35dd970 100644
--- a/OrthancServer/Internals/CommandDispatcher.h
+++ b/OrthancServer/Internals/CommandDispatcher.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Internals/DicomFrameIndex.cpp b/OrthancServer/Internals/DicomFrameIndex.cpp
new file mode 100644
index 0000000..c157656
--- /dev/null
+++ b/OrthancServer/Internals/DicomFrameIndex.cpp
@@ -0,0 +1,440 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 "../PrecompiledHeadersServer.h"
+#include "DicomFrameIndex.h"
+
+#include "../../Core/OrthancException.h"
+#include "../../Core/DicomFormat/DicomImageInformation.h"
+#include "../FromDcmtkBridge.h"
+#include "../OrthancInitialization.h"
+#include "../../Core/Endianness.h"
+#include "DicomImageDecoder.h"
+
+#include <boost/lexical_cast.hpp>
+
+#include <dcmtk/dcmdata/dcdeftag.h>
+#include <dcmtk/dcmdata/dcpxitem.h>
+#include <dcmtk/dcmdata/dcpixseq.h>
+
+namespace Orthanc
+{
+ class DicomFrameIndex::FragmentIndex : public DicomFrameIndex::IIndex
+ {
+ private:
+ DcmPixelSequence* pixelSequence_;
+ std::vector<DcmPixelItem*> startFragment_;
+ std::vector<unsigned int> countFragments_;
+ std::vector<unsigned int> frameSize_;
+
+ void GetOffsetTable(std::vector<uint32_t>& table)
+ {
+ DcmPixelItem* item = NULL;
+ if (!pixelSequence_->getItem(item, 0).good() ||
+ item == NULL)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ uint32_t length = item->getLength();
+ if (length == 0)
+ {
+ table.clear();
+ return;
+ }
+
+ if (length % 4 != 0)
+ {
+ // Error: Each fragment is index with 4 bytes (uint32_t)
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ uint8_t* content = NULL;
+ if (!item->getUint8Array(content).good() ||
+ content == NULL)
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ table.resize(length / 4);
+
+ // The offset table is always in little endian in the DICOM
+ // file. Swap it to host endianness if needed.
+ const uint32_t* offset = reinterpret_cast<const uint32_t*>(content);
+ for (size_t i = 0; i < table.size(); i++, offset++)
+ {
+ table[i] = le32toh(*offset);
+ }
+ }
+
+
+ public:
+ FragmentIndex(DcmPixelSequence* pixelSequence,
+ unsigned int countFrames) :
+ pixelSequence_(pixelSequence)
+ {
+ assert(pixelSequence != NULL);
+
+ startFragment_.resize(countFrames);
+ countFragments_.resize(countFrames);
+ frameSize_.resize(countFrames);
+
+ // The first fragment corresponds to the offset table
+ unsigned int countFragments = static_cast<unsigned int>(pixelSequence_->card());
+ if (countFragments < countFrames + 1)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ if (countFragments == countFrames + 1)
+ {
+ // Simple case: There is one fragment per frame.
+
+ DcmObject* fragment = pixelSequence_->nextInContainer(NULL); // Skip the offset table
+ if (fragment == NULL)
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ for (unsigned int i = 0; i < countFrames; i++)
+ {
+ fragment = pixelSequence_->nextInContainer(fragment);
+ startFragment_[i] = dynamic_cast<DcmPixelItem*>(fragment);
+ frameSize_[i] = fragment->getLength();
+ countFragments_[i] = 1;
+ }
+
+ return;
+ }
+
+ // Parse the offset table
+ std::vector<uint32_t> offsetOfFrame;
+ GetOffsetTable(offsetOfFrame);
+
+ if (offsetOfFrame.size() != countFrames ||
+ offsetOfFrame[0] != 0)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+
+ // Loop over the fragments (ignoring the offset table). This is
+ // an alternative, faster implementation to DCMTK's
+ // "DcmCodec::determineStartFragment()".
+ DcmObject* fragment = pixelSequence_->nextInContainer(NULL);
+ if (fragment == NULL)
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ fragment = pixelSequence_->nextInContainer(fragment); // Skip the offset table
+ if (fragment == NULL)
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ uint32_t offset = 0;
+ unsigned int currentFrame = 0;
+ startFragment_[0] = dynamic_cast<DcmPixelItem*>(fragment);
+
+ unsigned int currentFragment = 1;
+ while (fragment != NULL)
+ {
+ if (currentFrame + 1 < countFrames &&
+ offset == offsetOfFrame[currentFrame + 1])
+ {
+ currentFrame += 1;
+ startFragment_[currentFrame] = dynamic_cast<DcmPixelItem*>(fragment);
+ }
+
+ frameSize_[currentFrame] += fragment->getLength();
+ countFragments_[currentFrame]++;
+
+ // 8 bytes = overhead for the item tag and length field
+ offset += fragment->getLength() + 8;
+
+ currentFragment++;
+ fragment = pixelSequence_->nextInContainer(fragment);
+ }
+
+ if (currentFragment != countFragments ||
+ currentFrame + 1 != countFrames ||
+ fragment != NULL)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ assert(startFragment_.size() == countFragments_.size() &&
+ startFragment_.size() == frameSize_.size());
+ }
+
+
+ virtual void GetRawFrame(std::string& frame,
+ unsigned int index) const
+ {
+ if (index >= startFragment_.size())
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ frame.resize(frameSize_[index]);
+ if (frame.size() == 0)
+ {
+ return;
+ }
+
+ uint8_t* target = reinterpret_cast<uint8_t*>(&frame[0]);
+
+ size_t offset = 0;
+ DcmPixelItem* fragment = startFragment_[index];
+ for (unsigned int i = 0; i < countFragments_[index]; i++)
+ {
+ uint8_t* content = NULL;
+ if (!fragment->getUint8Array(content).good() ||
+ content == NULL)
+ {
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ assert(offset + fragment->getLength() <= frame.size());
+
+ memcpy(target + offset, content, fragment->getLength());
+ offset += fragment->getLength();
+
+ fragment = dynamic_cast<DcmPixelItem*>(pixelSequence_->nextInContainer(fragment));
+ }
+ }
+ };
+
+
+
+ class DicomFrameIndex::UncompressedIndex : public DicomFrameIndex::IIndex
+ {
+ private:
+ uint8_t* pixelData_;
+ size_t frameSize_;
+
+ public:
+ UncompressedIndex(DcmDataset& dataset,
+ unsigned int countFrames,
+ size_t frameSize) :
+ pixelData_(NULL),
+ frameSize_(frameSize)
+ {
+ size_t size = 0;
+
+ DcmElement* e;
+ if (dataset.findAndGetElement(DCM_PixelData, e).good() &&
+ e != NULL)
+ {
+ size = e->getLength();
+
+ if (size > 0)
+ {
+ pixelData_ = NULL;
+ if (!e->getUint8Array(pixelData_).good() ||
+ pixelData_ == NULL)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+ }
+
+ if (size < frameSize_ * countFrames)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ virtual void GetRawFrame(std::string& frame,
+ unsigned int index) const
+ {
+ frame.resize(frameSize_);
+ if (frameSize_ > 0)
+ {
+ memcpy(&frame[0], pixelData_ + index * frameSize_, frameSize_);
+ }
+ }
+ };
+
+
+ class DicomFrameIndex::PsmctRle1Index : public DicomFrameIndex::IIndex
+ {
+ private:
+ std::string pixelData_;
+ size_t frameSize_;
+
+ public:
+ PsmctRle1Index(DcmDataset& dataset,
+ unsigned int countFrames,
+ size_t frameSize) :
+ frameSize_(frameSize)
+ {
+ if (!DicomImageDecoder::DecodePsmctRle1(pixelData_, dataset) ||
+ pixelData_.size() < frameSize * countFrames)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ virtual void GetRawFrame(std::string& frame,
+ unsigned int index) const
+ {
+ frame.resize(frameSize_);
+ if (frameSize_ > 0)
+ {
+ memcpy(&frame[0], reinterpret_cast<const uint8_t*>(&pixelData_[0]) + index * frameSize_, frameSize_);
+ }
+ }
+ };
+
+
+
+ bool DicomFrameIndex::IsVideo(DcmFileFormat& dicom)
+ {
+ // Retrieve the transfer syntax from the DICOM header
+ const char* value = NULL;
+ if (!dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() ||
+ value == NULL)
+ {
+ return false;
+ }
+
+ const std::string transferSyntax(value);
+
+ // Video standards supported in DICOM 2016a
+ // http://dicom.nema.org/medical/dicom/2016a/output/html/part05.html
+ if (transferSyntax == "1.2.840.10008.1.2.4.100" || // MPEG2 MP at ML option of ISO/IEC MPEG2
+ transferSyntax == "1.2.840.10008.1.2.4.101" || // MPEG2 MP at HL option of ISO/IEC MPEG2
+ transferSyntax == "1.2.840.10008.1.2.4.102" || // MPEG-4 AVC/H.264 High Profile / Level 4.1 of ITU-T H.264
+ transferSyntax == "1.2.840.10008.1.2.4.103" || // MPEG-4 AVC/H.264 BD-compat High Profile / Level 4.1 of ITU-T H.264
+ transferSyntax == "1.2.840.10008.1.2.4.104" || // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
+ transferSyntax == "1.2.840.10008.1.2.4.105" || // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
+ transferSyntax == "1.2.840.10008.1.2.4.106") // MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2 of the ITU-T H.264
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ unsigned int DicomFrameIndex::GetFramesCount(DcmFileFormat& dicom)
+ {
+ // Assume 1 frame for video transfer syntaxes
+ if (IsVideo(dicom))
+ {
+ return 1;
+ }
+
+ const char* tmp = NULL;
+ if (!dicom.getDataset()->findAndGetString(DCM_NumberOfFrames, tmp).good() ||
+ tmp == NULL)
+ {
+ return 1;
+ }
+
+ int count = -1;
+ try
+ {
+ count = boost::lexical_cast<int>(tmp);
+ }
+ catch (boost::bad_lexical_cast&)
+ {
+ }
+
+ if (count < 0)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ else
+ {
+ return count;
+ }
+ }
+
+
+ DicomFrameIndex::DicomFrameIndex(DcmFileFormat& dicom)
+ {
+ countFrames_ = GetFramesCount(dicom);
+ if (countFrames_ == 0)
+ {
+ // The image has no frame. No index is to be built.
+ return;
+ }
+
+ DcmDataset& dataset = *dicom.getDataset();
+
+ // Test whether this image is composed of a sequence of fragments
+ DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
+ if (pixelSequence != NULL)
+ {
+ index_.reset(new FragmentIndex(pixelSequence, countFrames_));
+ return;
+ }
+
+ // Extract information about the image structure
+ DicomMap tags;
+ FromDcmtkBridge::Convert(tags, dataset, ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
+
+ DicomImageInformation information(tags);
+
+ // Access to the raw pixel data
+ if (DicomImageDecoder::IsPsmctRle1(dataset))
+ {
+ index_.reset(new PsmctRle1Index(dataset, countFrames_, information.GetFrameSize()));
+ }
+ else
+ {
+ index_.reset(new UncompressedIndex(dataset, countFrames_, information.GetFrameSize()));
+ }
+ }
+
+
+ void DicomFrameIndex::GetRawFrame(std::string& frame,
+ unsigned int index) const
+ {
+ if (index >= countFrames_)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ else if (index_.get() != NULL)
+ {
+ return index_->GetRawFrame(frame, index);
+ }
+ else
+ {
+ frame.clear();
+ }
+ }
+}
diff --git a/OrthancServer/Search/SetOfResources.h b/OrthancServer/Internals/DicomFrameIndex.h
similarity index 65%
copy from OrthancServer/Search/SetOfResources.h
copy to OrthancServer/Internals/DicomFrameIndex.h
index c8ac73a..7a15f69 100644
--- a/OrthancServer/Search/SetOfResources.h
+++ b/OrthancServer/Internals/DicomFrameIndex.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,47 +32,49 @@
#pragma once
-#include "../IDatabaseWrapper.h"
-
-#include <set>
+#include <dcmtk/dcmdata/dcdatset.h>
+#include <dcmtk/dcmdata/dcfilefo.h>
+#include <vector>
+#include <stdint.h>
#include <boost/noncopyable.hpp>
#include <memory>
namespace Orthanc
{
- class SetOfResources : public boost::noncopyable
+ class DicomFrameIndex
{
private:
- typedef std::set<int64_t> Resources;
-
- IDatabaseWrapper& database_;
- ResourceType level_;
- std::auto_ptr<Resources> resources_;
-
- public:
- SetOfResources(IDatabaseWrapper& database,
- ResourceType level) :
- database_(database),
- level_(level)
+ class IIndex : public boost::noncopyable
{
- }
+ public:
+ virtual ~IIndex()
+ {
+ }
- ResourceType GetLevel() const
- {
- return level_;
- }
+ virtual void GetRawFrame(std::string& frame,
+ unsigned int index) const = 0;
+ };
- void Intersect(const std::list<int64_t>& resources);
+ class FragmentIndex;
+ class UncompressedIndex;
+ class PsmctRle1Index;
- void GoDown();
+ std::auto_ptr<IIndex> index_;
+ unsigned int countFrames_;
- void Flatten(std::list<int64_t>& result);
-
- void Flatten(std::list<std::string>& result);
+ public:
+ DicomFrameIndex(DcmFileFormat& dicom);
- void Clear()
+ unsigned int GetFramesCount() const
{
- resources_.reset(NULL);
+ return countFrames_;
}
+
+ void GetRawFrame(std::string& frame,
+ unsigned int index) const;
+
+ static bool IsVideo(DcmFileFormat& dicom);
+
+ static unsigned int GetFramesCount(DcmFileFormat& dicom);
};
}
diff --git a/OrthancServer/Internals/DicomImageDecoder.cpp b/OrthancServer/Internals/DicomImageDecoder.cpp
index 8784f0c..a78bea8 100644
--- a/OrthancServer/Internals/DicomImageDecoder.cpp
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -76,29 +76,49 @@
=========================================================================*/
-
-#include "../PrecompiledHeadersServer.h"
-#include "DicomImageDecoder.h"
-
#include "../../Core/Logging.h"
#include "../../Core/OrthancException.h"
#include "../../Core/Images/Image.h"
#include "../../Core/Images/ImageProcessing.h"
+#include "../../Core/Images/PngWriter.h"
+#include "../../Core/Images/JpegWriter.h"
#include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
#include "../ToDcmtkBridge.h"
#include "../FromDcmtkBridge.h"
#include "../ParsedDicomFile.h"
+#include "../OrthancInitialization.h"
#include <boost/lexical_cast.hpp>
#include <dcmtk/dcmdata/dcfilefo.h>
+#include <dcmtk/dcmdata/dcrleccd.h>
+#include <dcmtk/dcmdata/dcrlecp.h>
#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
-#include <dcmtk/dcmjpls/djcodecd.h>
-#include <dcmtk/dcmjpls/djcparam.h>
-#include <dcmtk/dcmjpeg/djrplol.h>
+# include <dcmtk/dcmjpls/djcodecd.h>
+# include <dcmtk/dcmjpls/djcparam.h>
+# include <dcmtk/dcmjpeg/djrplol.h>
#endif
+#if ORTHANC_JPEG_ENABLED == 1
+# include <dcmtk/dcmjpeg/djcodecd.h>
+# include <dcmtk/dcmjpeg/djcparam.h>
+# include <dcmtk/dcmjpeg/djdecbas.h>
+# include <dcmtk/dcmjpeg/djdecext.h>
+# include <dcmtk/dcmjpeg/djdeclol.h>
+# include <dcmtk/dcmjpeg/djdecpro.h>
+# include <dcmtk/dcmjpeg/djdecsps.h>
+# include <dcmtk/dcmjpeg/djdecsv1.h>
+#endif
+
+#if DCMTK_VERSION_NUMBER <= 360
+# define EXS_JPEGProcess1 EXS_JPEGProcess1TransferSyntax
+# define EXS_JPEGProcess2_4 EXS_JPEGProcess2_4TransferSyntax
+# define EXS_JPEGProcess6_8 EXS_JPEGProcess6_8TransferSyntax
+# define EXS_JPEGProcess10_12 EXS_JPEGProcess10_12TransferSyntax
+# define EXS_JPEGProcess14 EXS_JPEGProcess14TransferSyntax
+# define EXS_JPEGProcess14SV1 EXS_JPEGProcess14SV1TransferSyntax
+#endif
namespace Orthanc
{
@@ -106,15 +126,7 @@ namespace Orthanc
static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011);
- static bool IsJpegLossless(const DcmDataset& dataset)
- {
- // http://support.dcmtk.org/docs/dcxfer_8h-source.html
- return (dataset.getOriginalXfer() == EXS_JPEGLSLossless ||
- dataset.getOriginalXfer() == EXS_JPEGLSLossy);
- }
-
-
- static bool IsPsmctRle1(DcmDataset& dataset)
+ bool DicomImageDecoder::IsPsmctRle1(DcmDataset& dataset)
{
DcmElement* e;
char* c;
@@ -137,8 +149,8 @@ namespace Orthanc
}
- static bool DecodePsmctRle1(std::string& output,
- DcmDataset& dataset)
+ bool DicomImageDecoder::DecodePsmctRle1(std::string& output,
+ DcmDataset& dataset)
{
// Check whether the DICOM instance contains an image encoded with
// the PMSCT_RLE1 scheme.
@@ -238,7 +250,8 @@ namespace Orthanc
// See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
DicomMap m;
- FromDcmtkBridge::Convert(m, dataset);
+ FromDcmtkBridge::Convert(m, dataset, ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
/**
* Create an accessor to the raw values of the DICOM image.
@@ -306,15 +319,17 @@ namespace Orthanc
};
- ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset)
+ ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset,
+ bool ignorePhotometricInterpretation)
{
DicomMap m;
- FromDcmtkBridge::Convert(m, dataset);
+ FromDcmtkBridge::Convert(m, dataset, ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
DicomImageInformation info(m);
PixelFormat format;
- if (!info.ExtractPixelFormat(format))
+ if (!info.ExtractPixelFormat(format, ignorePhotometricInterpretation))
{
LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored()
<< "bpp, " << info.GetChannelCount() << " channels, "
@@ -329,17 +344,6 @@ namespace Orthanc
}
- bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset)
- {
- // http://support.dcmtk.org/docs/dcxfer_8h-source.html
- return (dataset.getOriginalXfer() == EXS_Unknown ||
- dataset.getOriginalXfer() == EXS_LittleEndianImplicit ||
- dataset.getOriginalXfer() == EXS_BigEndianImplicit ||
- dataset.getOriginalXfer() == EXS_LittleEndianExplicit ||
- dataset.getOriginalXfer() == EXS_BigEndianExplicit);
- }
-
-
template <typename PixelType>
static void CopyPixels(ImageAccessor& target,
const DicomIntegerPixelAccessor& source)
@@ -376,18 +380,6 @@ namespace Orthanc
ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset,
unsigned int frame)
{
- if (!IsUncompressedImage(dataset))
- {
- throw OrthancException(ErrorCode_BadParameterType);
- }
-
- return DecodeUncompressedImageInternal(dataset, frame);
- }
-
-
- ImageAccessor* DicomImageDecoder::DecodeUncompressedImageInternal(DcmDataset& dataset,
- unsigned int frame)
- {
ImageSource source;
source.Setup(dataset, frame);
@@ -396,7 +388,7 @@ namespace Orthanc
* Resize the target image.
**/
- std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
+ std::auto_ptr<ImageAccessor> target(CreateImage(dataset, false));
if (source.GetWidth() != target->GetWidth() ||
source.GetHeight() != target->GetHeight())
@@ -415,7 +407,7 @@ namespace Orthanc
bool fastVersionSuccess = false;
PixelFormat sourceFormat;
if (!info.IsPlanar() &&
- info.ExtractPixelFormat(sourceFormat))
+ info.ExtractPixelFormat(sourceFormat, false))
{
try
{
@@ -474,104 +466,184 @@ namespace Orthanc
}
-#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
- ImageAccessor* DicomImageDecoder::DecodeJpegLossless(DcmDataset& dataset,
- unsigned int frame)
+ ImageAccessor* DicomImageDecoder::ApplyCodec(const DcmCodec& codec,
+ const DcmCodecParameter& parameters,
+ DcmDataset& dataset,
+ unsigned int frame)
{
- if (!IsJpegLossless(dataset))
- {
- throw OrthancException(ErrorCode_BadParameterType);
- }
-
- DcmElement *element = NULL;
- if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), element).good())
- {
- throw OrthancException(ErrorCode_BadFileFormat);
- }
-
- DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
- DcmPixelSequence* pixelSequence = NULL;
- if (!pixelData.getEncapsulatedRepresentation
- (dataset.getOriginalXfer(), NULL, pixelSequence).good())
+ DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
+ if (pixelSequence == NULL)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
- std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
-
- /**
- * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
- * are exactly the same, except for the "supportedTransferSyntax()"
- * virtual function.
- * http://support.dcmtk.org/docs/classDJLSDecoderBase.html
- **/
-
- DJLSLosslessDecoder decoder; DJLSCodecParameter parameters;
- //DJLSNearLosslessDecoder decoder; DJLSCodecParameter parameters;
+ std::auto_ptr<ImageAccessor> target(CreateImage(dataset, true));
Uint32 startFragment = 0; // Default
OFString decompressedColorModel; // Out
DJ_RPLossless representationParameter;
- OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters,
- &dataset, frame, startFragment, target->GetBuffer(),
- target->GetSize(), decompressedColorModel);
+ OFCondition c = codec.decodeFrame(&representationParameter,
+ pixelSequence, ¶meters,
+ &dataset, frame, startFragment, target->GetBuffer(),
+ target->GetSize(), decompressedColorModel);
- if (!c.good())
+ if (c.good())
{
- throw OrthancException(ErrorCode_InternalError);
+ return target.release();
+ }
+ else
+ {
+ LOG(ERROR) << "Cannot decode an image";
+ throw OrthancException(ErrorCode_BadFileFormat);
}
-
- return target.release();
}
-#endif
-
-
ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom,
unsigned int frame)
{
DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
+ E_TransferSyntax syntax = dataset.getOriginalXfer();
- if (IsUncompressedImage(dataset))
+ /**
+ * Deal with uncompressed, raw images.
+ * http://support.dcmtk.org/docs/dcxfer_8h-source.html
+ **/
+ if (syntax == EXS_Unknown ||
+ syntax == EXS_LittleEndianImplicit ||
+ syntax == EXS_BigEndianImplicit ||
+ syntax == EXS_LittleEndianExplicit ||
+ syntax == EXS_BigEndianExplicit)
{
return DecodeUncompressedImage(dataset, frame);
}
+
#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
- if (IsJpegLossless(dataset))
+ /**
+ * Deal with JPEG-LS images.
+ **/
+
+ if (syntax == EXS_JPEGLSLossless ||
+ syntax == EXS_JPEGLSLossy)
{
- LOG(INFO) << "Decoding a JPEG-LS image";
- return DecodeJpegLossless(dataset, frame);
+ DJLSCodecParameter parameters;
+ std::auto_ptr<DJLSDecoderBase> decoder;
+
+ switch (syntax)
+ {
+ case EXS_JPEGLSLossless:
+ LOG(INFO) << "Decoding a JPEG-LS lossless DICOM image";
+ decoder.reset(new DJLSLosslessDecoder);
+ break;
+
+ case EXS_JPEGLSLossy:
+ LOG(INFO) << "Decoding a JPEG-LS near-lossless DICOM image";
+ decoder.reset(new DJLSNearLosslessDecoder);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ return ApplyCodec(*decoder, parameters, dataset, frame);
}
#endif
#if ORTHANC_JPEG_ENABLED == 1
- // TODO Implement this part to speed up JPEG decompression
+ /**
+ * Deal with JPEG images.
+ **/
+
+ if (syntax == EXS_JPEGProcess1 || // DJDecoderBaseline
+ syntax == EXS_JPEGProcess2_4 || // DJDecoderExtended
+ syntax == EXS_JPEGProcess6_8 || // DJDecoderSpectralSelection (retired)
+ syntax == EXS_JPEGProcess10_12 || // DJDecoderProgressive (retired)
+ syntax == EXS_JPEGProcess14 || // DJDecoderLossless
+ syntax == EXS_JPEGProcess14SV1) // DJDecoderP14SV1
+ {
+ // http://support.dcmtk.org/docs-snapshot/djutils_8h.html#a2a9695e5b6b0f5c45a64c7f072c1eb9d
+ DJCodecParameter parameters(
+ ECC_lossyYCbCr, // Mode for color conversion for compression, Unused for decompression
+ EDC_photometricInterpretation, // Perform color space conversion from YCbCr to RGB if DICOM photometric interpretation indicates YCbCr
+ EUC_default, // Mode for UID creation, unused for decompression
+ EPC_default); // Automatically determine whether color-by-plane is required from the SOP Class UID and decompressed photometric interpretation
+ std::auto_ptr<DJCodecDecoder> decoder;
+
+ switch (syntax)
+ {
+ case EXS_JPEGProcess1:
+ LOG(INFO) << "Decoding a JPEG baseline (process 1) DICOM image";
+ decoder.reset(new DJDecoderBaseline);
+ break;
+
+ case EXS_JPEGProcess2_4 :
+ LOG(INFO) << "Decoding a JPEG baseline (processes 2 and 4) DICOM image";
+ decoder.reset(new DJDecoderExtended);
+ break;
+
+ case EXS_JPEGProcess6_8: // Retired
+ LOG(INFO) << "Decoding a JPEG spectral section, nonhierarchical (processes 6 and 8) DICOM image";
+ decoder.reset(new DJDecoderSpectralSelection);
+ break;
+
+ case EXS_JPEGProcess10_12: // Retired
+ LOG(INFO) << "Decoding a JPEG full progression, nonhierarchical (processes 10 and 12) DICOM image";
+ decoder.reset(new DJDecoderProgressive);
+ break;
+
+ case EXS_JPEGProcess14:
+ LOG(INFO) << "Decoding a JPEG lossless, nonhierarchical (process 14) DICOM image";
+ decoder.reset(new DJDecoderLossless);
+ break;
+
+ case EXS_JPEGProcess14SV1:
+ LOG(INFO) << "Decoding a JPEG lossless, nonhierarchical, first-order prediction (process 14 selection value 1) DICOM image";
+ decoder.reset(new DJDecoderP14SV1);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ return ApplyCodec(*decoder, parameters, dataset, frame);
+ }
#endif
+
+ if (syntax == EXS_RLELossless)
+ {
+ LOG(INFO) << "Decoding a RLE lossless DICOM image";
+ DcmRLECodecParameter parameters;
+ DcmRLECodecDecoder decoder;
+ return ApplyCodec(decoder, parameters, dataset, frame);
+ }
+
+
/**
* This DICOM image format is not natively supported by
- * Orthanc. As a last resort, try and decode it through
- * DCMTK. This will result in higher memory consumption. This is
- * actually the second example of the following page:
+ * Orthanc. As a last resort, try and decode it through DCMTK by
+ * converting its transfer syntax to Little Endian. This will
+ * result in higher memory consumption. This is actually the
+ * second example of the following page:
* http://support.dcmtk.org/docs/mod_dcmjpeg.html#Examples
**/
{
- LOG(INFO) << "Using DCMTK to decode a compressed image";
+ LOG(INFO) << "Decoding a compressed image by converting its transfer syntax to Little Endian";
std::auto_ptr<DcmDataset> converted(dynamic_cast<DcmDataset*>(dataset.clone()));
converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
if (converted->canWriteXfer(EXS_LittleEndianExplicit))
{
- return DecodeUncompressedImageInternal(*converted, frame);
+ return DecodeUncompressedImage(*converted, frame);
}
}
- return NULL;
+ LOG(ERROR) << "Cannot decode a DICOM image with the built-in decoder";
+ throw OrthancException(ErrorCode_BadFileFormat);
}
@@ -653,4 +725,77 @@ namespace Orthanc
throw OrthancException(ErrorCode_NotImplemented);
}
}
+
+
+ void DicomImageDecoder::ApplyExtractionMode(std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode)
+ {
+ if (image.get() == NULL)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ bool ok = false;
+
+ switch (mode)
+ {
+ case ImageExtractionMode_UInt8:
+ ok = TruncateDecodedImage(image, PixelFormat_Grayscale8, false);
+ break;
+
+ case ImageExtractionMode_UInt16:
+ ok = TruncateDecodedImage(image, PixelFormat_Grayscale16, false);
+ break;
+
+ case ImageExtractionMode_Int16:
+ ok = TruncateDecodedImage(image, PixelFormat_SignedGrayscale16, false);
+ break;
+
+ case ImageExtractionMode_Preview:
+ ok = PreviewDecodedImage(image);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ if (ok)
+ {
+ assert(image.get() != NULL);
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_NotImplemented);
+ }
+ }
+
+
+ void DicomImageDecoder::ExtractPngImage(std::string& result,
+ std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode)
+ {
+ ApplyExtractionMode(image, mode);
+
+ PngWriter writer;
+ writer.WriteToMemory(result, *image);
+ }
+
+
+ void DicomImageDecoder::ExtractJpegImage(std::string& result,
+ std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode,
+ uint8_t quality)
+ {
+ if (mode != ImageExtractionMode_UInt8 &&
+ mode != ImageExtractionMode_Preview)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ ApplyExtractionMode(image, mode);
+
+ JpegWriter writer;
+ writer.SetQuality(quality);
+ writer.WriteToMemory(result, *image);
+ }
}
diff --git a/OrthancServer/Internals/DicomImageDecoder.h b/OrthancServer/Internals/DicomImageDecoder.h
index 451f4f6..9c1d752 100644
--- a/OrthancServer/Internals/DicomImageDecoder.h
+++ b/OrthancServer/Internals/DicomImageDecoder.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -32,44 +32,61 @@
#pragma once
-#include <memory>
+#include "../ParsedDicomFile.h"
-#include "../IDicomImageDecoder.h"
+#include <memory>
class DcmDataset;
+class DcmCodec;
+class DcmCodecParameter;
namespace Orthanc
{
- class DicomImageDecoder : public IDicomImageDecoder
+ class DicomImageDecoder : public boost::noncopyable
{
private:
class ImageSource;
- static ImageAccessor* DecodeUncompressedImageInternal(DcmDataset& dataset,
- unsigned int frame);
-
- static bool IsPsmctRle1(DcmDataset& dataset);
-
- static ImageAccessor* CreateImage(DcmDataset& dataset);
+ DicomImageDecoder() // This is a fully abstract class, no constructor
+ {
+ }
- static bool IsUncompressedImage(const DcmDataset& dataset);
+ static ImageAccessor* CreateImage(DcmDataset& dataset,
+ bool ignorePhotometricInterpretation);
static ImageAccessor* DecodeUncompressedImage(DcmDataset& dataset,
unsigned int frame);
-#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
- static ImageAccessor* DecodeJpegLossless(DcmDataset& dataset,
- unsigned int frame);
-#endif
-
- public:
- virtual ImageAccessor *Decode(ParsedDicomFile& dicom,
- unsigned int frame);
+ static ImageAccessor* ApplyCodec(const DcmCodec& codec,
+ const DcmCodecParameter& parameters,
+ DcmDataset& dataset,
+ unsigned int frame);
static bool TruncateDecodedImage(std::auto_ptr<ImageAccessor>& image,
PixelFormat format,
bool allowColorConversion);
static bool PreviewDecodedImage(std::auto_ptr<ImageAccessor>& image);
+
+ static void ApplyExtractionMode(std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode);
+
+ public:
+ static bool IsPsmctRle1(DcmDataset& dataset);
+
+ static bool DecodePsmctRle1(std::string& output,
+ DcmDataset& dataset);
+
+ static ImageAccessor *Decode(ParsedDicomFile& dicom,
+ unsigned int frame);
+
+ static void ExtractPngImage(std::string& result,
+ std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode);
+
+ static void ExtractJpegImage(std::string& result,
+ std::auto_ptr<ImageAccessor>& image,
+ ImageExtractionMode mode,
+ uint8_t quality);
};
}
diff --git a/OrthancServer/Internals/FindScp.cpp b/OrthancServer/Internals/FindScp.cpp
index 38eb179..da96e1f 100644
--- a/OrthancServer/Internals/FindScp.cpp
+++ b/OrthancServer/Internals/FindScp.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -86,6 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../ToDcmtkBridge.h"
#include "../../Core/Logging.h"
#include "../../Core/OrthancException.h"
+#include "../OrthancInitialization.h"
#include <dcmtk/dcmdata/dcfilefo.h>
@@ -170,7 +171,8 @@ namespace Orthanc
}
DicomMap input;
- FromDcmtkBridge::Convert(input, *requestIdentifiers);
+ FromDcmtkBridge::Convert(input, *requestIdentifiers, ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
data.findHandler_->Handle(data.answers_, input, sequencesToReturn,
*data.remoteIp_, *data.remoteAet_,
*data.calledAet_);
diff --git a/OrthancServer/Internals/FindScp.h b/OrthancServer/Internals/FindScp.h
index 0fa9bbc..6e368c0 100644
--- a/OrthancServer/Internals/FindScp.h
+++ b/OrthancServer/Internals/FindScp.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Internals/MoveScp.cpp b/OrthancServer/Internals/MoveScp.cpp
index 3afd07d..1b0ba1d 100644
--- a/OrthancServer/Internals/MoveScp.cpp
+++ b/OrthancServer/Internals/MoveScp.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -86,6 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../FromDcmtkBridge.h"
#include "../ToDcmtkBridge.h"
+#include "../OrthancInitialization.h"
#include "../../Core/Logging.h"
#include "../../Core/OrthancException.h"
@@ -129,13 +130,14 @@ namespace Orthanc
if (data.lastRequest_ == NULL)
{
DicomMap input;
- FromDcmtkBridge::Convert(input, *requestIdentifiers);
+ FromDcmtkBridge::Convert(input, *requestIdentifiers, ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
try
{
data.iterator_.reset(data.handler_->Handle(data.target_, input,
*data.remoteIp_, *data.remoteAet_,
- *data.calledAet_));
+ *data.calledAet_, request->MessageID));
if (data.iterator_.get() == NULL)
{
diff --git a/OrthancServer/Internals/MoveScp.h b/OrthancServer/Internals/MoveScp.h
index 98905d4..46516b9 100644
--- a/OrthancServer/Internals/MoveScp.h
+++ b/OrthancServer/Internals/MoveScp.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Internals/StoreScp.cpp b/OrthancServer/Internals/StoreScp.cpp
index f38fe58..2f57b02 100644
--- a/OrthancServer/Internals/StoreScp.cpp
+++ b/OrthancServer/Internals/StoreScp.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -85,6 +85,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../FromDcmtkBridge.h"
#include "../ServerToolbox.h"
#include "../ToDcmtkBridge.h"
+#include "../OrthancInitialization.h"
#include "../../Core/OrthancException.h"
#include "../../Core/Logging.h"
@@ -167,11 +168,15 @@ namespace Orthanc
try
{
- FromDcmtkBridge::Convert(summary, **imageDataSet);
+ const Encoding defaultEncoding = Configuration::GetDefaultEncoding();
+ FromDcmtkBridge::Convert(summary, **imageDataSet,
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ defaultEncoding);
FromDcmtkBridge::ToJson(dicomJson, **imageDataSet,
DicomToJsonFormat_Full,
DicomToJsonFlags_Default,
- ORTHANC_MAXIMUM_TAG_LENGTH);
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ defaultEncoding);
if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, **imageDataSet))
{
diff --git a/OrthancServer/Internals/StoreScp.h b/OrthancServer/Internals/StoreScp.h
index 02f42dd..ac9af45 100644
--- a/OrthancServer/Internals/StoreScp.h
+++ b/OrthancServer/Internals/StoreScp.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/LuaScripting.cpp b/OrthancServer/LuaScripting.cpp
index 2f02a65..4d961e2 100644
--- a/OrthancServer/LuaScripting.cpp
+++ b/OrthancServer/LuaScripting.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -253,7 +253,8 @@ namespace Orthanc
LOG(INFO) << "Lua script to send resource " << parameters["Resource"].asString()
<< " to modality " << modality << " using Store-SCU";
return new StoreScuCommand(context_, localAet,
- Configuration::GetModalityUsingSymbolicName(modality), true);
+ Configuration::GetModalityUsingSymbolicName(modality),
+ true, 0 /* not a C-MOVE */);
}
if (operation == "store-peer")
@@ -262,7 +263,7 @@ namespace Orthanc
LOG(INFO) << "Lua script to send resource " << parameters["Resource"].asString()
<< " to peer " << peer << " using HTTP";
- OrthancPeerParameters parameters;
+ WebServiceParameters parameters;
Configuration::GetOrthancPeer(parameters, peer);
return new StorePeerCommand(context_, parameters, true);
}
@@ -390,7 +391,6 @@ namespace Orthanc
lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration);
lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
- lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
}
diff --git a/OrthancServer/LuaScripting.h b/OrthancServer/LuaScripting.h
index f039ce8..e355290 100644
--- a/OrthancServer/LuaScripting.h
+++ b/OrthancServer/LuaScripting.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancFindRequestHandler.cpp b/OrthancServer/OrthancFindRequestHandler.cpp
index 6844336..f451083 100644
--- a/OrthancServer/OrthancFindRequestHandler.cpp
+++ b/OrthancServer/OrthancFindRequestHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -46,10 +46,293 @@
namespace Orthanc
{
+ static void GetChildren(std::list<std::string>& target,
+ ServerIndex& index,
+ const std::list<std::string>& source)
+ {
+ target.clear();
+
+ for (std::list<std::string>::const_iterator
+ it = source.begin(); it != source.end(); ++it)
+ {
+ std::list<std::string> tmp;
+ index.GetChildren(tmp, *it);
+ target.splice(target.end(), tmp);
+ }
+ }
+
+
+ static void StoreSetOfStrings(DicomMap& result,
+ const DicomTag& tag,
+ const std::set<std::string>& values)
+ {
+ bool isFirst = true;
+
+ std::string s;
+ for (std::set<std::string>::const_iterator
+ it = values.begin(); it != values.end(); ++it)
+ {
+ if (isFirst)
+ {
+ isFirst = false;
+ }
+ else
+ {
+ s += "\\";
+ }
+
+ s += *it;
+ }
+
+ result.SetValue(tag, s, false);
+ }
+
+
+ static void ExtractTagFromMainDicomTags(std::set<std::string>& target,
+ ServerIndex& index,
+ const DicomTag& tag,
+ const std::list<std::string>& resources,
+ ResourceType level)
+ {
+ for (std::list<std::string>::const_iterator
+ it = resources.begin(); it != resources.end(); ++it)
+ {
+ DicomMap tags;
+ if (index.GetMainDicomTags(tags, *it, level, level) &&
+ tags.HasTag(tag))
+ {
+ target.insert(tags.GetValue(tag).GetContent());
+ }
+ }
+ }
+
+
+ static void ExtractTagFromInstances(std::set<std::string>& target,
+ ServerContext& context,
+ const DicomTag& tag,
+ const std::list<std::string>& instances)
+ {
+ // WARNING: This function is slow, as it reads the JSON file
+ // summarizing each instance of interest from the hard drive.
+
+ std::string formatted = tag.Format();
+
+ for (std::list<std::string>::const_iterator
+ it = instances.begin(); it != instances.end(); ++it)
+ {
+ Json::Value dicom;
+ context.ReadJson(dicom, *it);
+
+ if (dicom.isMember(formatted))
+ {
+ const Json::Value& source = dicom[formatted];
+
+ if (source.type() == Json::objectValue &&
+ source.isMember("Type") &&
+ source.isMember("Value") &&
+ source["Type"].asString() == "String" &&
+ source["Value"].type() == Json::stringValue)
+ {
+ target.insert(source["Value"].asString());
+ }
+ }
+ }
+ }
+
+
+ static void ComputePatientCounters(DicomMap& result,
+ ServerIndex& index,
+ const std::string& patient,
+ const DicomMap& query)
+ {
+ std::list<std::string> studies;
+ index.GetChildren(studies, patient);
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES,
+ boost::lexical_cast<std::string>(studies.size()), false);
+ }
+
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) &&
+ !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
+ {
+ return;
+ }
+
+ std::list<std::string> series;
+ GetChildren(series, index, studies);
+ studies.clear(); // This information is useless below
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES,
+ boost::lexical_cast<std::string>(series.size()), false);
+ }
+
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
+ {
+ return;
+ }
+
+ std::list<std::string> instances;
+ GetChildren(instances, index, series);
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES,
+ boost::lexical_cast<std::string>(instances.size()), false);
+ }
+ }
+
+
+ static void ComputeStudyCounters(DicomMap& result,
+ ServerContext& context,
+ const std::string& study,
+ const DicomMap& query)
+ {
+ ServerIndex& index = context.GetIndex();
+
+ std::list<std::string> series;
+ index.GetChildren(series, study);
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES,
+ boost::lexical_cast<std::string>(series.size()), false);
+ }
+
+ if (query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY))
+ {
+ std::set<std::string> values;
+ ExtractTagFromMainDicomTags(values, index, DICOM_TAG_MODALITY, series, ResourceType_Series);
+ StoreSetOfStrings(result, DICOM_TAG_MODALITIES_IN_STUDY, values);
+ }
+
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) &&
+ !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY))
+ {
+ return;
+ }
+
+ std::list<std::string> instances;
+ GetChildren(instances, index, series);
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES,
+ boost::lexical_cast<std::string>(instances.size()), false);
+ }
+
+ if (query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY))
+ {
+ if (Configuration::GetGlobalBoolParameter("AllowFindSopClassesInStudy", false))
+ {
+ std::set<std::string> values;
+ ExtractTagFromInstances(values, context, DICOM_TAG_SOP_CLASS_UID, instances);
+ StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values);
+ }
+ else
+ {
+ result.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, "", false);
+ LOG(WARNING) << "The handling of \"SOP Classes in Study\" (0008,0062) "
+ << "in C-FIND requests is disabled";
+ }
+ }
+ }
+
+
+ static void ComputeSeriesCounters(DicomMap& result,
+ ServerIndex& index,
+ const std::string& series,
+ const DicomMap& query)
+ {
+ std::list<std::string> instances;
+ index.GetChildren(instances, series);
+
+ if (query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES))
+ {
+ result.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES,
+ boost::lexical_cast<std::string>(instances.size()), false);
+ }
+ }
+
+
+ static DicomMap* ComputeCounters(ServerContext& context,
+ const std::string& instanceId,
+ ResourceType level,
+ const DicomMap& query)
+ {
+ switch (level)
+ {
+ case ResourceType_Patient:
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES) &&
+ !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) &&
+ !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
+ {
+ return NULL;
+ }
+
+ break;
+
+ case ResourceType_Study:
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES) &&
+ !query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) &&
+ !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY) &&
+ !query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY))
+ {
+ return NULL;
+ }
+
+ break;
+
+ case ResourceType_Series:
+ if (!query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES))
+ {
+ return NULL;
+ }
+
+ break;
+
+ default:
+ return NULL;
+ }
+
+ std::string parent;
+ if (!context.GetIndex().LookupParent(parent, instanceId, level))
+ {
+ throw OrthancException(ErrorCode_UnknownResource); // The resource was deleted in between
+ }
+
+ std::auto_ptr<DicomMap> result(new DicomMap);
+
+ switch (level)
+ {
+ case ResourceType_Patient:
+ ComputePatientCounters(*result, context.GetIndex(), parent, query);
+ break;
+
+ case ResourceType_Study:
+ ComputeStudyCounters(*result, context, parent, query);
+ break;
+
+ case ResourceType_Series:
+ ComputeSeriesCounters(*result, context.GetIndex(), parent, query);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ return result.release();
+ }
+
+
static void AddAnswer(DicomFindAnswers& answers,
const Json::Value& resource,
const DicomArray& query,
- const std::list<DicomTag>& sequencesToReturn)
+ const std::list<DicomTag>& sequencesToReturn,
+ const DicomMap* counters)
{
DicomMap result;
@@ -71,15 +354,24 @@ namespace Orthanc
if (resource.isMember(tag))
{
value = resource.get(tag, Json::arrayValue).get("Value", "").asString();
- result.SetValue(query.GetElement(i).GetTag(), value);
+ result.SetValue(query.GetElement(i).GetTag(), value, false);
}
else
{
- result.SetValue(query.GetElement(i).GetTag(), "");
+ result.SetValue(query.GetElement(i).GetTag(), "", false);
}
}
}
+ if (counters != NULL)
+ {
+ DicomArray tmp(*counters);
+ for (size_t i = 0; i < tmp.GetSize(); i++)
+ {
+ result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent(), false);
+ }
+ }
+
if (result.GetSize() == 0 &&
sequencesToReturn.empty())
{
@@ -96,8 +388,6 @@ namespace Orthanc
for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin();
tag != sequencesToReturn.end(); ++tag)
{
- std::cout << tag->Format();
-
const Json::Value& source = resource[tag->Format()];
if (source.type() == Json::objectValue &&
@@ -115,7 +405,7 @@ namespace Orthanc
content.append(item);
}
- dicom.Replace(*tag, content, false);
+ dicom.Replace(*tag, content, false, DicomReplaceMode_InsertIfAbsent);
}
}
@@ -218,7 +508,7 @@ namespace Orthanc
}
DicomTag tag(FromDcmtkBridge::ParseTag(members[i]));
- target.SetValue(tag, output[members[i]].asString());
+ target.SetValue(tag, output[members[i]].asString(), false);
}
return true;
@@ -332,12 +622,12 @@ namespace Orthanc
if (FilterQueryTag(value, level, tag, modality.GetManufacturer()))
{
- ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
+ ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag);
// DICOM specifies that searches must be case sensitive, except
// for tags with a PN value representation
bool sensitive = true;
- if (vr == ValueRepresentation_PatientName)
+ if (vr == ValueRepresentation_PersonName)
{
sensitive = caseSensitivePN;
}
@@ -379,7 +669,8 @@ namespace Orthanc
}
else
{
- AddAnswer(answers, dicom, query, sequencesToReturn);
+ std::auto_ptr<DicomMap> counters(ComputeCounters(context_, instances[i], level, input));
+ AddAnswer(answers, dicom, query, sequencesToReturn, counters.get());
}
}
}
diff --git a/OrthancServer/OrthancFindRequestHandler.h b/OrthancServer/OrthancFindRequestHandler.h
index 601e5d7..c0f0c9c 100644
--- a/OrthancServer/OrthancFindRequestHandler.h
+++ b/OrthancServer/OrthancFindRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancHttpHandler.cpp b/OrthancServer/OrthancHttpHandler.cpp
index 2004a06..28405cf 100644
--- a/OrthancServer/OrthancHttpHandler.cpp
+++ b/OrthancServer/OrthancHttpHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancHttpHandler.h b/OrthancServer/OrthancHttpHandler.h
index 66dd03d..cc7693c 100644
--- a/OrthancServer/OrthancHttpHandler.h
+++ b/OrthancServer/OrthancHttpHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancInitialization.cpp b/OrthancServer/OrthancInitialization.cpp
index a28be68..a498a67 100644
--- a/OrthancServer/OrthancInitialization.cpp
+++ b/OrthancServer/OrthancInitialization.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -42,6 +42,7 @@
#include "ServerEnumerations.h"
#include "DatabaseWrapper.h"
#include "FromDcmtkBridge.h"
+#include "ToDcmtkBridge.h"
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
@@ -69,6 +70,10 @@
#endif
+#include <dcmtk/dcmnet/dul.h>
+
+
+
namespace Orthanc
{
static boost::recursive_mutex globalMutex_;
@@ -76,6 +81,7 @@ namespace Orthanc
static boost::filesystem::path defaultDirectory_;
static std::string configurationAbsolutePath_;
static FontRegistry fontRegistry_;
+ static const char* configurationFileArg_ = NULL;
static std::string GetGlobalStringParameterInternal(const std::string& parameter,
@@ -123,7 +129,8 @@ namespace Orthanc
- static void AddFileToConfiguration(const boost::filesystem::path& path)
+ static void AddFileToConfiguration(Json::Value& target,
+ const boost::filesystem::path& path)
{
LOG(WARNING) << "Reading the configuration from: " << path;
@@ -145,30 +152,31 @@ namespace Orthanc
Toolbox::CopyJsonWithoutComments(config, tmp);
}
- if (configuration_.size() == 0)
+ if (target.size() == 0)
{
- configuration_ = config;
+ target = config;
}
else
{
Json::Value::Members members = config.getMemberNames();
for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
{
- if (configuration_.isMember(members[i]))
+ if (target.isMember(members[i]))
{
LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files";
throw OrthancException(ErrorCode_BadFileFormat);
}
else
{
- configuration_[members[i]] = config[members[i]];
+ target[members[i]] = config[members[i]];
}
}
}
}
- static void ScanFolderForConfiguration(const char* folder)
+ static void ScanFolderForConfiguration(Json::Value& target,
+ const char* folder)
{
using namespace boost::filesystem;
@@ -186,19 +194,17 @@ namespace Orthanc
if (extension == ".json")
{
- AddFileToConfiguration(it->path().string());
+ AddFileToConfiguration(target, it->path().string());
}
}
}
}
- static void ReadGlobalConfiguration(const char* configurationFile)
+ static void ReadConfiguration(Json::Value& target,
+ const char* configurationFile)
{
- // Prepare the default configuration
- defaultDirectory_ = boost::filesystem::current_path();
- configuration_ = Json::objectValue;
- configurationAbsolutePath_ = "";
+ target = Json::objectValue;
if (configurationFile)
{
@@ -210,15 +216,11 @@ namespace Orthanc
if (boost::filesystem::is_directory(configurationFile))
{
- defaultDirectory_ = boost::filesystem::path(configurationFile);
- configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string();
- ScanFolderForConfiguration(configurationFile);
+ ScanFolderForConfiguration(target, configurationFile);
}
else
{
- defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
- configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string();
- AddFileToConfiguration(configurationFile);
+ AddFileToConfiguration(target, configurationFile);
}
}
else
@@ -235,9 +237,47 @@ namespace Orthanc
boost::filesystem::path p = ORTHANC_PATH;
p /= "Resources";
p /= "Configuration.json";
- configurationAbsolutePath_ = boost::filesystem::absolute(p).string();
- AddFileToConfiguration(p);
+ AddFileToConfiguration(target, p);
+#endif
+ }
+ }
+
+
+
+ static void ReadGlobalConfiguration(const char* configurationFile)
+ {
+ // Read the content of the configuration
+ configurationFileArg_ = configurationFile;
+ ReadConfiguration(configuration_, configurationFile);
+
+ // Adapt the paths to the configurations
+ defaultDirectory_ = boost::filesystem::current_path();
+ configurationAbsolutePath_ = "";
+
+ if (configurationFile)
+ {
+ if (boost::filesystem::is_directory(configurationFile))
+ {
+ defaultDirectory_ = boost::filesystem::path(configurationFile);
+ configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string();
+ }
+ else
+ {
+ defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
+ configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string();
+ }
+ }
+ else
+ {
+#if ORTHANC_STANDALONE != 1
+ // In a non-standalone build, we use the
+ // "Resources/Configuration.json" from the Orthanc source code
+
+ boost::filesystem::path p = ORTHANC_PATH;
+ p /= "Resources";
+ p /= "Configuration.json";
+ configurationAbsolutePath_ = boost::filesystem::absolute(p).string();
#endif
}
}
@@ -250,7 +290,7 @@ namespace Orthanc
Configuration::GetListOfOrthancPeers(ids);
for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it)
{
- OrthancPeerParameters peer;
+ WebServiceParameters peer;
Configuration::GetOrthancPeer(peer, *it);
}
@@ -374,7 +414,7 @@ namespace Orthanc
}
DicomTag tag(FromDcmtkBridge::ParseTag(tags[i]));
- DcmEVR vr = FromDcmtkBridge::ParseValueRepresentation(content[0].asString());
+ ValueRepresentation vr = StringToValueRepresentation(content[0].asString(), true);
std::string name = content[1].asString();
unsigned int minMultiplicity = (content.size() >= 2) ? content[2].asUInt() : 1;
unsigned int maxMultiplicity = (content.size() >= 3) ? content[3].asUInt() : 1;
@@ -384,6 +424,48 @@ namespace Orthanc
}
+ static void ConfigurePkcs11(const Json::Value& config)
+ {
+ if (config.type() != Json::objectValue ||
+ !config.isMember("Module") ||
+ config["Module"].type() != Json::stringValue)
+ {
+ LOG(ERROR) << "No path to the PKCS#11 module (DLL or .so) is provided for HTTPS client authentication";
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ std::string pin;
+ if (config.isMember("Pin"))
+ {
+ if (config["Pin"].type() == Json::stringValue)
+ {
+ pin = config["Pin"].asString();
+ }
+ else
+ {
+ LOG(ERROR) << "The PIN number in the PKCS#11 configuration must be a string";
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ bool verbose = false;
+ if (config.isMember("Verbose"))
+ {
+ if (config["Verbose"].type() == Json::booleanValue)
+ {
+ verbose = config["Verbose"].asBool();
+ }
+ else
+ {
+ LOG(ERROR) << "The Verbose option in the PKCS#11 configuration must be a Boolean";
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ HttpClient::InitializePkcs11(config["Module"].asString(), pin, verbose);
+ }
+
+
void OrthancInitialize(const char* configurationFile)
{
@@ -395,10 +477,6 @@ namespace Orthanc
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
-
- curl_global_init(CURL_GLOBAL_ALL);
-#else
- curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL);
#endif
InitializeServerEnumerations();
@@ -407,8 +485,12 @@ namespace Orthanc
ReadGlobalConfiguration(configurationFile);
ValidateGlobalConfiguration();
- HttpClient::GlobalInitialize(GetGlobalBoolParameterInternal("HttpsVerifyPeers", true),
- GetGlobalStringParameterInternal("HttpsCACertificates", ""));
+ if (configuration_.isMember("Pkcs11"))
+ {
+ ConfigurePkcs11(configuration_["Pkcs11"]);
+ }
+
+ HttpClient::GlobalInitialize();
RegisterUserMetadata();
RegisterUserContentType();
@@ -427,6 +509,9 @@ namespace Orthanc
#endif
fontRegistry_.AddFromResource(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
+
+ /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */
+ dcmDisableGethostbyaddr.set(OFTrue);
}
@@ -446,8 +531,6 @@ namespace Orthanc
DJDecoderRegistration::cleanup();
#endif
- curl_global_cleanup();
-
#if ORTHANC_SSL_ENABLED == 1
// Finalize OpenSSL
// https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
@@ -535,7 +618,7 @@ namespace Orthanc
- void Configuration::GetOrthancPeer(OrthancPeerParameters& peer,
+ void Configuration::GetOrthancPeer(WebServiceParameters& peer,
const std::string& name)
{
boost::recursive_mutex::scoped_lock lock(globalMutex_);
@@ -846,7 +929,7 @@ namespace Orthanc
void Configuration::UpdatePeer(const std::string& symbolicName,
- const OrthancPeerParameters& peer)
+ const WebServiceParameters& peer)
{
boost::recursive_mutex::scoped_lock lock(globalMutex_);
@@ -1024,4 +1107,29 @@ namespace Orthanc
{
return fontRegistry_;
}
+
+
+ Encoding Configuration::GetDefaultEncoding()
+ {
+ std::string s = GetGlobalStringParameter("DefaultEncoding", "Latin1");
+
+ // By default, Latin1 encoding is assumed
+ return s.empty() ? Encoding_Latin1 : StringToEncoding(s.c_str());
+ }
+
+
+ bool Configuration::HasConfigurationChanged()
+ {
+ Json::Value starting;
+ GetConfiguration(starting);
+
+ Json::Value current;
+ ReadConfiguration(current, configurationFileArg_);
+
+ Json::FastWriter writer;
+ std::string a = writer.write(starting);
+ std::string b = writer.write(current);
+
+ return a != b;
+ }
}
diff --git a/OrthancServer/OrthancInitialization.h b/OrthancServer/OrthancInitialization.h
index 1bde67b..b2dc0cc 100644
--- a/OrthancServer/OrthancInitialization.h
+++ b/OrthancServer/OrthancInitialization.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -36,13 +36,15 @@
#include <set>
#include <json/json.h>
#include <stdint.h>
+
+#include "../Core/FileStorage/IStorageArea.h"
#include "../Core/HttpServer/MongooseServer.h"
+#include "../Core/Images/FontRegistry.h"
+#include "../Core/WebServiceParameters.h"
+
#include "DicomProtocol/RemoteModalityParameters.h"
-#include "ServerEnumerations.h"
-#include "OrthancPeerParameters.h"
#include "IDatabaseWrapper.h"
-#include "../Core/FileStorage/IStorageArea.h"
-#include "../Core/Images/FontRegistry.h"
+#include "ServerEnumerations.h"
namespace Orthanc
{
@@ -52,6 +54,9 @@ namespace Orthanc
class Configuration
{
+ private:
+ Configuration(); // Forbidden, this is a static class
+
public:
static std::string GetGlobalStringParameter(const std::string& parameter,
const std::string& defaultValue);
@@ -68,7 +73,7 @@ namespace Orthanc
static bool LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality,
const std::string& aet);
- static void GetOrthancPeer(OrthancPeerParameters& peer,
+ static void GetOrthancPeer(WebServiceParameters& peer,
const std::string& name);
static void GetListOfDicomModalities(std::set<std::string>& target);
@@ -100,7 +105,7 @@ namespace Orthanc
static void RemoveModality(const std::string& symbolicName);
static void UpdatePeer(const std::string& symbolicName,
- const OrthancPeerParameters& peer);
+ const WebServiceParameters& peer);
static void RemovePeer(const std::string& symbolicName);
@@ -115,5 +120,9 @@ namespace Orthanc
static void FormatConfiguration(std::string& result);
static const FontRegistry& GetFontRegistry();
+
+ static Encoding GetDefaultEncoding();
+
+ static bool HasConfigurationChanged();
};
}
diff --git a/OrthancServer/OrthancMoveRequestHandler.cpp b/OrthancServer/OrthancMoveRequestHandler.cpp
index afa1afc..240b703 100644
--- a/OrthancServer/OrthancMoveRequestHandler.cpp
+++ b/OrthancServer/OrthancMoveRequestHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -52,14 +52,17 @@ namespace Orthanc
std::vector<std::string> instances_;
size_t position_;
RemoteModalityParameters remote_;
+ uint16_t moveRequestID_;
public:
OrthancMoveRequestIterator(ServerContext& context,
const std::string& aet,
- const std::string& publicId) :
+ const std::string& publicId,
+ uint16_t moveRequestID) :
context_(context),
localAet_(context.GetDefaultLocalApplicationEntityTitle()),
- position_(0)
+ position_(0),
+ moveRequestID_(moveRequestID)
{
LOG(INFO) << "Sending resource " << publicId << " to modality \"" << aet << "\"";
@@ -95,7 +98,7 @@ namespace Orthanc
{
ReusableDicomUserConnection::Locker locker
(context_.GetReusableDicomUserConnection(), localAet_, remote_);
- locker.GetConnection().Store(dicom);
+ locker.GetConnection().Store(dicom, moveRequestID_);
}
return Status_Success;
@@ -166,7 +169,8 @@ namespace Orthanc
const DicomMap& input,
const std::string& remoteIp,
const std::string& remoteAet,
- const std::string& calledAet)
+ const std::string& calledAet,
+ uint16_t messageId)
{
LOG(WARNING) << "Move-SCU request received for AET \"" << targetAet << "\"";
@@ -184,6 +188,35 @@ namespace Orthanc
}
+#if 0
+ /**
+ * Retrieve the Message ID (0000,0110) for this C-MOVE request, if
+ * any. If present, this Message ID will be stored in the Move
+ * Originator Message ID (0000,1031) field of the C-MOVE response.
+ * http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_9.3.1
+ **/
+
+ static const DicomTag MESSAGE_ID(0x0000, 0x0110);
+ const DicomValue* messageIdTmp = input.TestAndGetValue(MESSAGE_ID);
+
+ messageId = 0;
+
+ if (messageIdTmp != NULL &&
+ !messageIdTmp->IsNull() &&
+ !messageIdTmp->IsBinary())
+ {
+ try
+ {
+ messageId = boost::lexical_cast<uint16_t>(messageIdTmp->GetContent());
+ }
+ catch (boost::bad_lexical_cast&)
+ {
+ LOG(WARNING) << "Cannot convert the Message ID (\"" << messageIdTmp ->GetContent()
+ << "\") of an incoming C-MOVE request to an integer, assuming zero";
+ }
+ }
+#endif
+
/**
* Retrieve the query level.
@@ -208,7 +241,7 @@ namespace Orthanc
LookupIdentifier(publicId, ResourceType_Study, input) ||
LookupIdentifier(publicId, ResourceType_Patient, input))
{
- return new OrthancMoveRequestIterator(context_, targetAet, publicId);
+ return new OrthancMoveRequestIterator(context_, targetAet, publicId, messageId);
}
else
{
@@ -229,7 +262,7 @@ namespace Orthanc
if (LookupIdentifier(publicId, level, input))
{
- return new OrthancMoveRequestIterator(context_, targetAet, publicId);
+ return new OrthancMoveRequestIterator(context_, targetAet, publicId, messageId);
}
else
{
diff --git a/OrthancServer/OrthancMoveRequestHandler.h b/OrthancServer/OrthancMoveRequestHandler.h
index 07de3e2..18dc817 100644
--- a/OrthancServer/OrthancMoveRequestHandler.h
+++ b/OrthancServer/OrthancMoveRequestHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -55,6 +55,7 @@ namespace Orthanc
const DicomMap& input,
const std::string& remoteIp,
const std::string& remoteAet,
- const std::string& calledAet);
+ const std::string& calledAet,
+ uint16_t messageId);
};
}
diff --git a/OrthancServer/OrthancPeerParameters.cpp b/OrthancServer/OrthancPeerParameters.cpp
deleted file mode 100644
index 3cec3a8..0000000
--- a/OrthancServer/OrthancPeerParameters.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * 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 "PrecompiledHeadersServer.h"
-#include "OrthancPeerParameters.h"
-
-#include "../Core/OrthancException.h"
-
-namespace Orthanc
-{
- OrthancPeerParameters::OrthancPeerParameters() :
- url_("http://localhost:8042/")
- {
- }
-
-
- void OrthancPeerParameters::FromJson(const Json::Value& peer)
- {
- if (!peer.isArray() ||
- (peer.size() != 1 && peer.size() != 3))
- {
- throw OrthancException(ErrorCode_BadFileFormat);
- }
-
- std::string url;
-
- try
- {
- url = peer.get(0u, "").asString();
-
- if (peer.size() == 1)
- {
- SetUsername("");
- SetPassword("");
- }
- else if (peer.size() == 3)
- {
- SetUsername(peer.get(1u, "").asString());
- SetPassword(peer.get(2u, "").asString());
- }
- else
- {
- throw OrthancException(ErrorCode_BadFileFormat);
- }
- }
- catch (...)
- {
- throw OrthancException(ErrorCode_BadFileFormat);
- }
-
- if (url.size() != 0 && url[url.size() - 1] != '/')
- {
- url += '/';
- }
-
- SetUrl(url);
- }
-
-
- void OrthancPeerParameters::ToJson(Json::Value& value) const
- {
- value = Json::arrayValue;
- value.append(GetUrl());
- value.append(GetUsername());
- value.append(GetPassword());
- }
-}
diff --git a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp
index 7894cec..7168fbd 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -480,7 +480,8 @@ namespace Orthanc
}
else
{
- dicom.Replace(tag, value);
+ // This is V1, don't try and decode data URI scheme
+ dicom.ReplacePlainString(tag, value);
}
}
}
@@ -526,7 +527,7 @@ namespace Orthanc
}
else
{
- dicom.Replace(tag, tags[name], decodeBinaryTags);
+ dicom.Replace(tag, tags[name], decodeBinaryTags, DicomReplaceMode_InsertIfAbsent);
}
}
}
@@ -542,8 +543,8 @@ namespace Orthanc
assert(content.size() > 0);
ServerContext& context = OrthancRestApi::GetContext(call);
- base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
- base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
+ base.ReplacePlainString(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
+ base.ReplacePlainString(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
std::string someInstance;
@@ -580,8 +581,8 @@ namespace Orthanc
}
dicom->EmbedContent(payload->asString());
- dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
- dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
+ dicom->ReplacePlainString(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
+ dicom->ReplacePlainString(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
StoreCreatedInstance(someInstance, call, *dicom);
}
@@ -636,8 +637,7 @@ namespace Orthanc
}
else
{
- std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
- encoding = StringToEncoding(tmp.c_str());
+ encoding = Configuration::GetDefaultEncoding();
}
dicom.SetEncoding(encoding);
@@ -731,12 +731,12 @@ namespace Orthanc
const Json::Value& tag = siblingTags[t];
if (tag["Type"] == "Null")
{
- dicom.Replace(*it, "");
+ dicom.ReplacePlainString(*it, "");
}
else if (tag["Type"] == "String")
{
std::string value = tag["Value"].asString();
- dicom.Replace(*it, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding()));
+ dicom.ReplacePlainString(*it, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding()));
}
}
}
@@ -759,26 +759,26 @@ namespace Orthanc
// Inject time-related information
std::string date, time;
Toolbox::GetNowDicom(date, time);
- dicom.Replace(DICOM_TAG_ACQUISITION_DATE, date);
- dicom.Replace(DICOM_TAG_ACQUISITION_TIME, time);
- dicom.Replace(DICOM_TAG_CONTENT_DATE, date);
- dicom.Replace(DICOM_TAG_CONTENT_TIME, time);
- dicom.Replace(DICOM_TAG_INSTANCE_CREATION_DATE, date);
- dicom.Replace(DICOM_TAG_INSTANCE_CREATION_TIME, time);
+ dicom.ReplacePlainString(DICOM_TAG_ACQUISITION_DATE, date);
+ dicom.ReplacePlainString(DICOM_TAG_ACQUISITION_TIME, time);
+ dicom.ReplacePlainString(DICOM_TAG_CONTENT_DATE, date);
+ dicom.ReplacePlainString(DICOM_TAG_CONTENT_TIME, time);
+ dicom.ReplacePlainString(DICOM_TAG_INSTANCE_CREATION_DATE, date);
+ dicom.ReplacePlainString(DICOM_TAG_INSTANCE_CREATION_TIME, time);
if (parentType == ResourceType_Patient ||
parentType == ResourceType_Study ||
parentType == ResourceType_Instance /* no parent */)
{
- dicom.Replace(DICOM_TAG_SERIES_DATE, date);
- dicom.Replace(DICOM_TAG_SERIES_TIME, time);
+ dicom.ReplacePlainString(DICOM_TAG_SERIES_DATE, date);
+ dicom.ReplacePlainString(DICOM_TAG_SERIES_TIME, time);
}
if (parentType == ResourceType_Patient ||
parentType == ResourceType_Instance /* no parent */)
{
- dicom.Replace(DICOM_TAG_STUDY_DATE, date);
- dicom.Replace(DICOM_TAG_STUDY_TIME, time);
+ dicom.ReplacePlainString(DICOM_TAG_STUDY_DATE, date);
+ dicom.ReplacePlainString(DICOM_TAG_STUDY_TIME, time);
}
diff --git a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp
index 007f591..0b4164a 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancRestApi/OrthancRestApi.h b/OrthancServer/OrthancRestApi/OrthancRestApi.h
index 07fbbba..3acd3f9 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp
index bdc8942..66c2872 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp
index f2d9a87..64db6ce 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp
index 03fa9da..3341a91 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -97,7 +97,7 @@ namespace Orthanc
for (size_t i = 0; i < members.size(); i++)
{
DicomTag t = FromDcmtkBridge::ParseTag(members[i]);
- result.SetValue(t, query[members[i]].asString());
+ result.SetValue(t, query[members[i]].asString(), false);
}
return true;
@@ -287,7 +287,7 @@ namespace Orthanc
std::string tmp;
if (source.GetTagValue(tmp, tag))
{
- target.SetValue(tag, tmp);
+ target.SetValue(tag, tmp, false);
}
}
@@ -386,7 +386,7 @@ namespace Orthanc
std::auto_ptr<QueryRetrieveHandler> handler(new QueryRetrieveHandler(context));
handler->SetModality(call.GetUriComponent("id", ""));
- handler->SetLevel(StringToResourceType(request["Level"].asString().c_str()));
+ handler->SetLevel(StringToResourceType(request["Level"].asCString()));
if (request.isMember("Query"))
{
@@ -683,10 +683,41 @@ namespace Orthanc
return;
}
+ static const char* LOCAL_AET = "LocalAet";
std::string localAet = context.GetDefaultLocalApplicationEntityTitle();
- if (request.isMember("LocalAet"))
+ if (request.type() == Json::objectValue &&
+ request.isMember(LOCAL_AET))
{
- localAet = request["LocalAet"].asString();
+ if (request[LOCAL_AET].type() == Json::stringValue)
+ {
+ localAet = request[LOCAL_AET].asString();
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ uint16_t moveOriginatorID = 0; /* By default, not a C-MOVE */
+
+ static const char* MOVE_ORIGINATOR_ID = "MoveOriginatorID";
+ if (request.type() == Json::objectValue &&
+ request.isMember(MOVE_ORIGINATOR_ID))
+ {
+ if (request[MOVE_ORIGINATOR_ID].type() != Json::intValue)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ int v = request[MOVE_ORIGINATOR_ID].asInt();
+ if (v <= 0 || v >= 65536)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ else
+ {
+ moveOriginatorID = boost::lexical_cast<uint16_t>(v);
+ }
}
RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote);
@@ -695,7 +726,8 @@ namespace Orthanc
for (std::list<std::string>::const_iterator
it = instances.begin(); it != instances.end(); ++it)
{
- job.AddCommand(new StoreScuCommand(context, localAet, p, false)).AddInput(*it);
+ job.AddCommand(new StoreScuCommand(context, localAet, p, false,
+ moveOriginatorID)).AddInput(*it);
}
job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\"");
@@ -713,6 +745,76 @@ namespace Orthanc
/***************************************************************************
+ * DICOM C-Move SCU
+ ***************************************************************************/
+
+ static void DicomMove(RestApiPostCall& call)
+ {
+ ServerContext& context = OrthancRestApi::GetContext(call);
+
+ Json::Value request;
+
+ static const char* RESOURCES = "Resources";
+ static const char* LEVEL = "Level";
+
+ if (!call.ParseJsonRequest(request) ||
+ request.type() != Json::objectValue ||
+ !request.isMember(RESOURCES) ||
+ !request.isMember(LEVEL) ||
+ request[RESOURCES].type() != Json::arrayValue ||
+ request[LEVEL].type() != Json::stringValue)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ ResourceType level = StringToResourceType(request["Level"].asCString());
+
+ static const char* LOCAL_AET = "LocalAet";
+ std::string localAet = context.GetDefaultLocalApplicationEntityTitle();
+ if (request.isMember(LOCAL_AET))
+ {
+ if (request[LOCAL_AET].type() == Json::stringValue)
+ {
+ localAet = request[LOCAL_AET].asString();
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ static const char* TARGET_AET = "TargetAet";
+ std::string targetAet = context.GetDefaultLocalApplicationEntityTitle();
+ if (request.isMember(TARGET_AET))
+ {
+ if (request[TARGET_AET].type() == Json::stringValue)
+ {
+ targetAet = request[TARGET_AET].asString();
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+
+ const RemoteModalityParameters source = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
+
+ for (Json::Value::ArrayIndex i = 0; i < request[RESOURCES].size(); i++)
+ {
+ DicomMap resource;
+ FromDcmtkBridge::FromJson(resource, request[RESOURCES][i]);
+
+ ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, source);
+ locker.GetConnection().Move(targetAet, level, resource);
+ }
+
+ // Move has succeeded
+ call.GetOutput().AnswerBuffer("{}", "application/json");
+ }
+
+
+
+ /***************************************************************************
* Orthanc Peers => Store client
***************************************************************************/
@@ -762,7 +864,7 @@ namespace Orthanc
return;
}
- OrthancPeerParameters peer;
+ WebServiceParameters peer;
Configuration::GetOrthancPeer(peer, remote);
ServerJob job;
@@ -850,7 +952,7 @@ namespace Orthanc
Json::Reader reader;
if (reader.parse(call.GetBodyData(), call.GetBodyData() + call.GetBodySize(), json))
{
- OrthancPeerParameters peer;
+ WebServiceParameters peer;
peer.FromJson(json);
Configuration::UpdatePeer(call.GetUriComponent("id", ""), peer);
call.GetOutput().AnswerBuffer("", "text/plain");
@@ -904,6 +1006,7 @@ namespace Orthanc
Register("/modalities/{id}/find-instance", DicomFindInstance);
Register("/modalities/{id}/find", DicomFind);
Register("/modalities/{id}/store", DicomStore);
+ Register("/modalities/{id}/move", DicomMove);
// For Query/Retrieve
Register("/modalities/{id}/query", DicomQuery);
diff --git a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp
index 28063f7..dad9728 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -236,28 +236,22 @@ namespace Orthanc
static void ListFrames(RestApiGetCall& call)
{
- Json::Value instance;
- if (OrthancRestApi::GetIndex(call).LookupResource(instance, call.GetUriComponent("id", ""), ResourceType_Instance))
- {
- unsigned int numberOfFrames = 1;
-
- try
- {
- Json::Value tmp = instance["MainDicomTags"]["NumberOfFrames"];
- numberOfFrames = boost::lexical_cast<unsigned int>(tmp.asString());
- }
- catch (...)
- {
- }
-
- Json::Value result = Json::arrayValue;
- for (unsigned int i = 0; i < numberOfFrames; i++)
- {
- result.append(i);
- }
+ std::string publicId = call.GetUriComponent("id", "");
- call.GetOutput().AnswerJson(result);
+ unsigned int numberOfFrames;
+
+ {
+ ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), publicId);
+ numberOfFrames = locker.GetDicom().GetFramesCount();
}
+
+ Json::Value result = Json::arrayValue;
+ for (unsigned int i = 0; i < numberOfFrames; i++)
+ {
+ result.append(i);
+ }
+
+ call.GetOutput().AnswerJson(result);
}
@@ -266,58 +260,34 @@ namespace Orthanc
class ImageToEncode
{
private:
- IDicomImageDecoder& decoder_;
- std::string format_;
- std::string encoded_;
- ParsedDicomFile& dicom_;
- unsigned int frame_;
- ImageExtractionMode mode_;
+ std::auto_ptr<ImageAccessor>& image_;
+ ImageExtractionMode mode_;
+ std::string format_;
+ std::string answer_;
public:
- ImageToEncode(IDicomImageDecoder& decoder,
- ParsedDicomFile& dicom,
- unsigned int frame,
+ ImageToEncode(std::auto_ptr<ImageAccessor>& image,
ImageExtractionMode mode) :
- decoder_(decoder),
- dicom_(dicom),
- frame_(frame),
+ image_(image),
mode_(mode)
{
}
- ParsedDicomFile& GetDicom() const
- {
- return dicom_;
- }
-
- unsigned int GetFrame() const
- {
- return frame_;
- }
-
- ImageExtractionMode GetMode() const
- {
- return mode_;
- }
-
- void SetFormat(const std::string& format)
+ void Answer(RestApiOutput& output)
{
- format_ = format;
+ output.AnswerBuffer(answer_, format_);
}
- std::string& GetTarget()
+ void EncodeUsingPng()
{
- return encoded_;
+ format_ = "image/png";
+ DicomImageDecoder::ExtractPngImage(answer_, image_, mode_);
}
- void Answer(RestApiOutput& output)
+ void EncodeUsingJpeg(uint8_t quality)
{
- output.AnswerBuffer(encoded_, format_);
- }
-
- IDicomImageDecoder& GetDecoder() const
- {
- return decoder_;
+ format_ = "image/jpeg";
+ DicomImageDecoder::ExtractJpegImage(answer_, image_, mode_, quality);
}
};
@@ -336,9 +306,7 @@ namespace Orthanc
{
assert(type == "image");
assert(subtype == "png");
- image_.GetDicom().ExtractPngImage(image_.GetTarget(), image_.GetDecoder(),
- image_.GetFrame(), image_.GetMode());
- image_.SetFormat("image/png");
+ image_.EncodeUsingPng();
}
};
@@ -377,9 +345,7 @@ namespace Orthanc
{
assert(type == "image");
assert(subtype == "jpeg");
- image_.GetDicom().ExtractJpegImage(image_.GetTarget(), image_.GetDecoder(),
- image_.GetFrame(), image_.GetMode(), quality_);
- image_.SetFormat("image/jpeg");
+ image_.EncodeUsingJpeg(quality_);
}
};
}
@@ -402,29 +368,35 @@ namespace Orthanc
return;
}
- std::string publicId = call.GetUriComponent("id", "");
- std::string dicomContent;
- context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
-
- ParsedDicomFile dicom(dicomContent);
+ std::auto_ptr<ImageAccessor> decoded;
try
{
+ std::string publicId = call.GetUriComponent("id", "");
+
#if ORTHANC_PLUGINS_ENABLED == 1
- IDicomImageDecoder& decoder = context.GetPlugins();
-#else
- DicomImageDecoder decoder; // This is Orthanc's built-in decoder
+ if (context.GetPlugins().HasCustomImageDecoder())
+ {
+ // TODO create a cache of file
+ std::string dicomContent;
+ context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
+ decoded.reset(context.GetPlugins().DecodeUnsafe(dicomContent.c_str(), dicomContent.size(), frame));
+
+ /**
+ * Note that we call "DecodeUnsafe()": We do not fallback to
+ * the builtin decoder if no installed decoder plugin is able
+ * to decode the image. This allows us to take advantage of
+ * the cache below.
+ **/
+ }
#endif
- ImageToEncode image(decoder, dicom, frame, mode);
-
- HttpContentNegociation negociation;
- EncodePng png(image); negociation.Register("image/png", png);
- EncodeJpeg jpeg(image, call); negociation.Register("image/jpeg", jpeg);
-
- if (negociation.Apply(call.GetHttpHeaders()))
+ if (decoded.get() == NULL)
{
- image.Answer(call.GetOutput());
+ // Use Orthanc's built-in decoder, using the cache to speed-up
+ // things on multi-frame images
+ ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), publicId);
+ decoded.reset(DicomImageDecoder::Decode(locker.GetDicom(), frame));
}
}
catch (OrthancException& e)
@@ -445,6 +417,17 @@ namespace Orthanc
call.GetOutput().Redirect(root + "app/images/unsupported.png");
}
}
+
+ ImageToEncode image(decoded, mode);
+
+ HttpContentNegociation negociation;
+ EncodePng png(image); negociation.Register("image/png", png);
+ EncodeJpeg jpeg(image, call); negociation.Register("image/jpeg", jpeg);
+
+ if (negociation.Apply(call.GetHttpHeaders()))
+ {
+ image.Answer(call.GetOutput());
+ }
}
@@ -471,11 +454,10 @@ namespace Orthanc
#if ORTHANC_PLUGINS_ENABLED == 1
IDicomImageDecoder& decoder = context.GetPlugins();
#else
- DicomImageDecoder decoder; // This is Orthanc's built-in decoder
+ DefaultDicomImageDecoder decoder; // This is Orthanc's built-in decoder
#endif
- ParsedDicomFile dicom(dicomContent);
- std::auto_ptr<ImageAccessor> decoded(dicom.ExtractImage(decoder, frame));
+ std::auto_ptr<ImageAccessor> decoded(decoder.Decode(dicomContent.c_str(), dicomContent.size(), frame));
std::string result;
decoded->ToMatlabString(result);
@@ -485,6 +467,33 @@ namespace Orthanc
+ static void GetRawFrame(RestApiGetCall& call)
+ {
+ std::string frameId = call.GetUriComponent("frame", "0");
+
+ unsigned int frame;
+ try
+ {
+ frame = boost::lexical_cast<unsigned int>(frameId);
+ }
+ catch (boost::bad_lexical_cast)
+ {
+ return;
+ }
+
+ std::string publicId = call.GetUriComponent("id", "");
+ std::string raw, mime;
+
+ {
+ ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), publicId);
+ locker.GetDicom().GetRawFrame(raw, mime, frame);
+ }
+
+ call.GetOutput().AnswerBuffer(raw, mime);
+ }
+
+
+
static void GetResourceStatistics(RestApiGetCall& call)
{
std::string publicId = call.GetUriComponent("id", "");
@@ -1097,7 +1106,7 @@ namespace Orthanc
size_t limit = 0;
if (request.isMember("Limit"))
{
- int tmp = request["CaseSensitive"].asInt();
+ int tmp = request["Limit"].asInt();
if (tmp < 0)
{
throw OrthancException(ErrorCode_ParameterOutOfRange);
@@ -1287,6 +1296,9 @@ namespace Orthanc
std::string dicomContent;
context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
+ // TODO Consider using "DicomMap::ParseDicomMetaInformation()" to
+ // speed up things here
+
ParsedDicomFile dicom(dicomContent);
Json::Value header;
@@ -1347,6 +1359,7 @@ namespace Orthanc
Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>);
Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage);
+ Register("/instances/{id}/frames/{frame}/raw", GetRawFrame);
Register("/instances/{id}/pdf", ExtractPdf);
Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>);
Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
diff --git a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp
index 049750f..7678674 100644
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp
+++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ParsedDicomFile.cpp b/OrthancServer/ParsedDicomFile.cpp
index 2eda1cd..27b84f5 100644
--- a/OrthancServer/ParsedDicomFile.cpp
+++ b/OrthancServer/ParsedDicomFile.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -84,12 +84,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ServerToolbox.h"
#include "FromDcmtkBridge.h"
#include "ToDcmtkBridge.h"
-#include "Internals/DicomImageDecoder.h"
-#include "../Core/DicomFormat/DicomIntegerPixelAccessor.h"
-#include "../Core/Images/JpegWriter.h"
+#include "Internals/DicomFrameIndex.h"
#include "../Core/Images/JpegReader.h"
#include "../Core/Images/PngReader.h"
-#include "../Core/Images/PngWriter.h"
#include "../Core/Logging.h"
#include "../Core/OrthancException.h"
#include "../Core/Toolbox.h"
@@ -104,7 +101,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <dcmtk/dcmdata/dcdicent.h>
#include <dcmtk/dcmdata/dcdict.h>
#include <dcmtk/dcmdata/dcfilefo.h>
-#include <dcmtk/dcmdata/dcistrmb.h>
#include <dcmtk/dcmdata/dcuid.h>
#include <dcmtk/dcmdata/dcmetinf.h>
#include <dcmtk/dcmdata/dcdeftag.h>
@@ -140,6 +136,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/algorithm/string/predicate.hpp>
+#if DCMTK_VERSION_NUMBER <= 360
+# define EXS_JPEGProcess1 EXS_JPEGProcess1TransferSyntax
+#endif
+
+
static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
@@ -149,34 +150,10 @@ namespace Orthanc
struct ParsedDicomFile::PImpl
{
std::auto_ptr<DcmFileFormat> file_;
+ std::auto_ptr<DicomFrameIndex> frameIndex_;
};
- // This method can only be called from the constructors!
- void ParsedDicomFile::Setup(const void* buffer,
- size_t size)
- {
- DcmInputBufferStream is;
- if (size > 0)
- {
- is.setBuffer(buffer, size);
- }
- is.setEos();
-
- pimpl_->file_.reset(new DcmFileFormat);
- pimpl_->file_->transferInit();
- if (!pimpl_->file_->read(is).good())
- {
- delete pimpl_; // Avoid a memory leak due to exception
- // throwing, as we are in the constructor
-
- throw OrthancException(ErrorCode_BadFileFormat);
- }
- pimpl_->file_->loadAllDataIntoMemory();
- pimpl_->file_->transferEnd();
- }
-
-
static void SendPathValueForDictionary(RestApiOutput& output,
DcmItem& dicom)
{
@@ -366,7 +343,7 @@ namespace Orthanc
DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
if (blockUri == NULL)
{
- // The user asks how many blocks are presents in this pixel data
+ // The user asks how many blocks are present in this pixel data
unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax);
Json::Value result(Json::arrayValue);
@@ -520,6 +497,8 @@ namespace Orthanc
void ParsedDicomFile::Remove(const DicomTag& tag)
{
+ InvalidateCache();
+
DcmTagKey key(tag.GetGroup(), tag.GetElement());
DcmElement* element = pimpl_->file_->getDataset()->remove(key);
if (element != NULL)
@@ -532,6 +511,8 @@ namespace Orthanc
void ParsedDicomFile::RemovePrivateTagsInternal(const std::set<DicomTag>* toKeep)
{
+ InvalidateCache();
+
DcmDataset& dataset = *pimpl_->file_->getDataset();
// Loop over the dataset to detect its private tags
@@ -595,36 +576,55 @@ namespace Orthanc
const Json::Value& value,
bool decodeDataUriScheme)
{
+ if (pimpl_->file_->getDataset()->tagExists(ToDcmtkBridge::Convert(tag)))
+ {
+ throw OrthancException(ErrorCode_AlreadyExistingTag);
+ }
+
+ if (decodeDataUriScheme &&
+ value.type() == Json::stringValue &&
+ (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT ||
+ tag == DICOM_TAG_PIXEL_DATA))
+ {
+ if (EmbedContentInternal(value.asString()))
+ {
+ return;
+ }
+ }
+
+ InvalidateCache();
std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding()));
InsertInternal(*pimpl_->file_->getDataset(), element.release());
}
- static void ReplaceInternal(DcmDataset& dicom,
- std::auto_ptr<DcmElement>& element,
- DicomReplaceMode mode)
+ static bool CanReplaceProceed(DcmDataset& dicom,
+ const DcmTagKey& tag,
+ DicomReplaceMode mode)
{
- const DcmTagKey& tag = element->getTag();
-
- if (!dicom.findAndDeleteElement(tag).good())
+ if (dicom.findAndDeleteElement(tag).good())
{
- // This field does not exist, act wrt. the specified "mode"
+ // This tag was existing, it has been deleted
+ return true;
+ }
+ else
+ {
+ // This tag was absent, act wrt. the specified "mode"
switch (mode)
{
case DicomReplaceMode_InsertIfAbsent:
- break;
+ return true;
case DicomReplaceMode_ThrowIfAbsent:
throw OrthancException(ErrorCode_InexistentItem);
case DicomReplaceMode_IgnoreIfAbsent:
- return;
+ return false;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
-
- // Either the tag was not existing, or the replace mode was set to
- // "InsertIfAbsent"
- InsertInternal(dicom, element.release());
}
@@ -645,7 +645,11 @@ namespace Orthanc
boost::starts_with(utf8Value, "data:application/octet-stream;base64,"))
{
std::string mime;
- Toolbox::DecodeDataUriScheme(mime, binary, utf8Value);
+ if (!Toolbox::DecodeDataUriScheme(mime, binary, utf8Value))
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
decoded = &binary;
}
else
@@ -669,24 +673,45 @@ namespace Orthanc
if (tag == DICOM_TAG_SOP_CLASS_UID)
{
- Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded, DicomReplaceMode_InsertIfAbsent);
+ ReplacePlainString(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded);
}
if (tag == DICOM_TAG_SOP_INSTANCE_UID)
{
- Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded, DicomReplaceMode_InsertIfAbsent);
+ ReplacePlainString(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded);
}
}
void ParsedDicomFile::Replace(const DicomTag& tag,
const std::string& utf8Value,
+ bool decodeDataUriScheme,
DicomReplaceMode mode)
{
- std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag));
- FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, false, GetEncoding());
- ReplaceInternal(*pimpl_->file_->getDataset(), element, mode);
- UpdateStorageUid(tag, utf8Value, false);
+ InvalidateCache();
+
+ DcmDataset& dicom = *pimpl_->file_->getDataset();
+ if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode))
+ {
+ // Either the tag was previously existing (and now removed), or
+ // the replace mode was set to "InsertIfAbsent"
+
+ if (decodeDataUriScheme &&
+ (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT ||
+ tag == DICOM_TAG_PIXEL_DATA))
+ {
+ if (EmbedContentInternal(utf8Value))
+ {
+ return;
+ }
+ }
+
+ std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag));
+ FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, decodeDataUriScheme, GetEncoding());
+
+ InsertInternal(dicom, element.release());
+ UpdateStorageUid(tag, utf8Value, false);
+ }
}
@@ -695,18 +720,37 @@ namespace Orthanc
bool decodeDataUriScheme,
DicomReplaceMode mode)
{
- std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding()));
- ReplaceInternal(*pimpl_->file_->getDataset(), element, mode);
+ InvalidateCache();
- if (tag == DICOM_TAG_SOP_CLASS_UID ||
- tag == DICOM_TAG_SOP_INSTANCE_UID)
+ DcmDataset& dicom = *pimpl_->file_->getDataset();
+ if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode))
{
- if (value.type() != Json::stringValue)
+ // Either the tag was previously existing (and now removed), or
+ // the replace mode was set to "InsertIfAbsent"
+
+ if (decodeDataUriScheme &&
+ value.type() == Json::stringValue &&
+ (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT ||
+ tag == DICOM_TAG_PIXEL_DATA))
{
- throw OrthancException(ErrorCode_BadParameterType);
+ if (EmbedContentInternal(value.asString()))
+ {
+ return;
+ }
}
- UpdateStorageUid(tag, value.asString(), decodeDataUriScheme);
+ InsertInternal(dicom, FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding()));
+
+ if (tag == DICOM_TAG_SOP_CLASS_UID ||
+ tag == DICOM_TAG_SOP_INSTANCE_UID)
+ {
+ if (value.type() != Json::stringValue)
+ {
+ throw OrthancException(ErrorCode_BadParameterType);
+ }
+
+ UpdateStorageUid(tag, value.asString(), decodeDataUriScheme);
+ }
}
}
@@ -765,7 +809,8 @@ namespace Orthanc
}
std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement
- (*element, DicomToJsonFlags_Default, GetEncoding()));
+ (*element, DicomToJsonFlags_Default,
+ ORTHANC_MAXIMUM_TAG_LENGTH, GetEncoding()));
if (v.get() == NULL ||
v->IsNull())
@@ -820,10 +865,10 @@ namespace Orthanc
if (createIdentifiers)
{
- Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient));
- Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study));
- Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series));
- Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
+ ReplacePlainString(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient));
+ ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study));
+ ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series));
+ ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
}
}
@@ -843,18 +888,18 @@ namespace Orthanc
ParsedDicomFile::ParsedDicomFile(const void* content,
size_t size) : pimpl_(new PImpl)
{
- Setup(content, size);
+ pimpl_->file_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(content, size));
}
ParsedDicomFile::ParsedDicomFile(const std::string& content) : pimpl_(new PImpl)
{
if (content.size() == 0)
{
- Setup(NULL, 0);
+ pimpl_->file_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(NULL, 0));
}
else
{
- Setup(&content[0], content.size());
+ pimpl_->file_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(&content[0], content.size()));
}
}
@@ -865,7 +910,7 @@ namespace Orthanc
pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone()));
// Create a new instance-level identifier
- Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
+ ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
}
@@ -899,10 +944,14 @@ namespace Orthanc
}
- void ParsedDicomFile::EmbedContent(const std::string& dataUriScheme)
+ bool ParsedDicomFile::EmbedContentInternal(const std::string& dataUriScheme)
{
std::string mime, content;
- Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme);
+ if (!Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme))
+ {
+ return false;
+ }
+
Toolbox::ToLowerCase(mime);
if (mime == "image/png" ||
@@ -919,6 +968,17 @@ namespace Orthanc
LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file: " << mime;
throw OrthancException(ErrorCode_NotImplemented);
}
+
+ return true;
+ }
+
+
+ void ParsedDicomFile::EmbedContent(const std::string& dataUriScheme)
+ {
+ if (!EmbedContentInternal(dataUriScheme))
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
}
@@ -948,12 +1008,15 @@ namespace Orthanc
{
if (accessor.GetFormat() != PixelFormat_Grayscale8 &&
accessor.GetFormat() != PixelFormat_Grayscale16 &&
+ accessor.GetFormat() != PixelFormat_SignedGrayscale16 &&
accessor.GetFormat() != PixelFormat_RGB24 &&
accessor.GetFormat() != PixelFormat_RGBA32)
{
throw OrthancException(ErrorCode_NotImplemented);
}
+ InvalidateCache();
+
if (accessor.GetFormat() == PixelFormat_RGBA32)
{
LOG(WARNING) << "Getting rid of the alpha channel when embedding a RGBA image inside DICOM";
@@ -962,35 +1025,49 @@ namespace Orthanc
// http://dicomiseasy.blogspot.be/2012/08/chapter-12-pixel-data.html
Remove(DICOM_TAG_PIXEL_DATA);
- Replace(DICOM_TAG_COLUMNS, boost::lexical_cast<std::string>(accessor.GetWidth()));
- Replace(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight()));
- Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
- Replace(DICOM_TAG_NUMBER_OF_FRAMES, "1");
- Replace(DICOM_TAG_PIXEL_REPRESENTATION, "0"); // Unsigned pixels
- Replace(DICOM_TAG_PLANAR_CONFIGURATION, "0"); // Color channels are interleaved
- Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
- Replace(DICOM_TAG_BITS_ALLOCATED, "8");
- Replace(DICOM_TAG_BITS_STORED, "8");
- Replace(DICOM_TAG_HIGH_BIT, "7");
-
- unsigned int bytesPerPixel = 1;
+ ReplacePlainString(DICOM_TAG_COLUMNS, boost::lexical_cast<std::string>(accessor.GetWidth()));
+ ReplacePlainString(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight()));
+ ReplacePlainString(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
+ ReplacePlainString(DICOM_TAG_NUMBER_OF_FRAMES, "1");
+
+ if (accessor.GetFormat() == PixelFormat_SignedGrayscale16)
+ {
+ ReplacePlainString(DICOM_TAG_PIXEL_REPRESENTATION, "1");
+ }
+ else
+ {
+ ReplacePlainString(DICOM_TAG_PIXEL_REPRESENTATION, "0"); // Unsigned pixels
+ }
+
+ ReplacePlainString(DICOM_TAG_PLANAR_CONFIGURATION, "0"); // Color channels are interleaved
+ ReplacePlainString(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
+
+ unsigned int bytesPerPixel = 0;
switch (accessor.GetFormat())
{
+ case PixelFormat_Grayscale8:
+ ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "8");
+ ReplacePlainString(DICOM_TAG_BITS_STORED, "8");
+ ReplacePlainString(DICOM_TAG_HIGH_BIT, "7");
+ bytesPerPixel = 1;
+ break;
+
case PixelFormat_RGB24:
case PixelFormat_RGBA32:
- Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB");
- Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "3");
+ ReplacePlainString(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB");
+ ReplacePlainString(DICOM_TAG_SAMPLES_PER_PIXEL, "3");
+ ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "8");
+ ReplacePlainString(DICOM_TAG_BITS_STORED, "8");
+ ReplacePlainString(DICOM_TAG_HIGH_BIT, "7");
bytesPerPixel = 3;
break;
- case PixelFormat_Grayscale8:
- break;
-
case PixelFormat_Grayscale16:
- Replace(DICOM_TAG_BITS_ALLOCATED, "16");
- Replace(DICOM_TAG_BITS_STORED, "16");
- Replace(DICOM_TAG_HIGH_BIT, "15");
+ case PixelFormat_SignedGrayscale16:
+ ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "16");
+ ReplacePlainString(DICOM_TAG_BITS_STORED, "16");
+ ReplacePlainString(DICOM_TAG_HIGH_BIT, "15");
bytesPerPixel = 2;
break;
@@ -998,6 +1075,8 @@ namespace Orthanc
throw OrthancException(ErrorCode_NotImplemented);
}
+ assert(bytesPerPixel != 0);
+
DcmTag key(DICOM_TAG_PIXEL_DATA.GetGroup(),
DICOM_TAG_PIXEL_DATA.GetElement());
@@ -1047,102 +1126,10 @@ namespace Orthanc
}
- ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder,
- unsigned int frame)
- {
- std::auto_ptr<ImageAccessor> decoded(decoder.Decode(*this, frame));
-
- if (decoded.get() == NULL)
- {
- LOG(ERROR) << "Cannot decode a DICOM image";
- throw OrthancException(ErrorCode_BadFileFormat);
- }
- else
- {
- return decoded.release();
- }
- }
-
-
- ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode)
- {
- std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame));
-
- bool ok = false;
-
- switch (mode)
- {
- case ImageExtractionMode_UInt8:
- ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale8, false);
- break;
-
- case ImageExtractionMode_UInt16:
- ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale16, false);
- break;
-
- case ImageExtractionMode_Int16:
- ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_SignedGrayscale16, false);
- break;
-
- case ImageExtractionMode_Preview:
- ok = DicomImageDecoder::PreviewDecodedImage(decoded);
- break;
-
- default:
- throw OrthancException(ErrorCode_ParameterOutOfRange);
- }
-
- if (ok)
- {
- assert(decoded.get() != NULL);
- return decoded.release();
- }
- else
- {
- throw OrthancException(ErrorCode_NotImplemented);
- }
- }
-
-
- void ParsedDicomFile::ExtractPngImage(std::string& result,
- IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode)
- {
- std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame, mode));
- assert(decoded.get() != NULL);
-
- PngWriter writer;
- writer.WriteToMemory(result, *decoded);
- }
-
-
- void ParsedDicomFile::ExtractJpegImage(std::string& result,
- IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode,
- uint8_t quality)
- {
- if (mode != ImageExtractionMode_UInt8 &&
- mode != ImageExtractionMode_Preview)
- {
- throw OrthancException(ErrorCode_ParameterOutOfRange);
- }
-
- std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame, mode));
- assert(decoded.get() != NULL);
-
- JpegWriter writer;
- writer.SetQuality(quality);
- writer.WriteToMemory(result, *decoded);
- }
-
-
Encoding ParsedDicomFile::GetEncoding() const
{
- return FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset());
+ return FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset(),
+ Configuration::GetDefaultEncoding());
}
@@ -1156,7 +1143,7 @@ namespace Orthanc
}
std::string s = GetDicomSpecificCharacterSet(encoding);
- Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent);
+ ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, s);
}
void ParsedDicomFile::ToJson(Json::Value& target,
@@ -1164,7 +1151,9 @@ namespace Orthanc
DicomToJsonFlags flags,
unsigned int maxStringLength)
{
- FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(), format, flags, maxStringLength);
+ FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(),
+ format, flags, maxStringLength,
+ Configuration::GetDefaultEncoding());
}
@@ -1191,11 +1180,13 @@ namespace Orthanc
throw OrthancException(ErrorCode_BadFileFormat);
}
- Replace(DICOM_TAG_SOP_CLASS_UID, UID_EncapsulatedPDFStorage);
- Replace(FromDcmtkBridge::Convert(DCM_Modality), "OT");
- Replace(FromDcmtkBridge::Convert(DCM_ConversionType), "WSD");
- Replace(FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument), "application/pdf");
- //Replace(FromDcmtkBridge::Convert(DCM_SeriesNumber), "1");
+ InvalidateCache();
+
+ ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_EncapsulatedPDFStorage);
+ ReplacePlainString(FromDcmtkBridge::Convert(DCM_Modality), "OT");
+ ReplacePlainString(FromDcmtkBridge::Convert(DCM_ConversionType), "WSD");
+ ReplacePlainString(FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument), "application/pdf");
+ //ReplacePlainString(FromDcmtkBridge::Convert(DCM_SeriesNumber), "1");
std::auto_ptr<DcmPolymorphOBOW> element(new DcmPolymorphOBOW(DCM_EncapsulatedDocument));
@@ -1267,39 +1258,23 @@ namespace Orthanc
void ParsedDicomFile::Convert(DicomMap& tags)
{
- FromDcmtkBridge::Convert(tags, *pimpl_->file_->getDataset());
+ FromDcmtkBridge::Convert(tags, *pimpl_->file_->getDataset(),
+ ORTHANC_MAXIMUM_TAG_LENGTH,
+ Configuration::GetDefaultEncoding());
}
ParsedDicomFile* ParsedDicomFile::CreateFromJson(const Json::Value& json,
DicomFromJsonFlags flags)
{
- std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
- Encoding encoding = StringToEncoding(tmp.c_str());
-
- Json::Value::Members tags = json.getMemberNames();
-
- for (size_t i = 0; i < tags.size(); i++)
- {
- DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
- if (tag == DICOM_TAG_SPECIFIC_CHARACTER_SET)
- {
- const Json::Value& value = json[tags[i]];
- if (value.type() != Json::stringValue ||
- !GetDicomEncoding(encoding, value.asCString()))
- {
- LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value;
- throw OrthancException(ErrorCode_BadRequest);
- }
- }
- }
-
- const bool generateIdentifiers = (flags & DicomFromJsonFlags_GenerateIdentifiers);
- const bool decodeDataUriScheme = (flags & DicomFromJsonFlags_DecodeDataUriScheme);
+ const bool generateIdentifiers = (flags & DicomFromJsonFlags_GenerateIdentifiers) ? true : false;
+ const bool decodeDataUriScheme = (flags & DicomFromJsonFlags_DecodeDataUriScheme) ? true : false;
std::auto_ptr<ParsedDicomFile> result(new ParsedDicomFile(generateIdentifiers));
- result->SetEncoding(encoding);
+ result->SetEncoding(FromDcmtkBridge::ExtractEncoding(json, Configuration::GetDefaultEncoding()));
+ const Json::Value::Members tags = json.getMemberNames();
+
for (size_t i = 0; i < tags.size(); i++)
{
DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
@@ -1319,10 +1294,52 @@ namespace Orthanc
}
else if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
{
- result->Replace(tag, value, decodeDataUriScheme);
+ result->Replace(tag, value, decodeDataUriScheme, DicomReplaceMode_InsertIfAbsent);
}
}
return result.release();
}
+
+
+ void ParsedDicomFile::GetRawFrame(std::string& target,
+ std::string& mime,
+ unsigned int frameId)
+ {
+ if (pimpl_->frameIndex_.get() == NULL)
+ {
+ pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_));
+ }
+
+ pimpl_->frameIndex_->GetRawFrame(target, frameId);
+
+ E_TransferSyntax transferSyntax = pimpl_->file_->getDataset()->getOriginalXfer();
+ switch (transferSyntax)
+ {
+ case EXS_JPEGProcess1:
+ mime = "image/jpeg";
+ break;
+
+ case EXS_JPEG2000LosslessOnly:
+ case EXS_JPEG2000:
+ mime = "image/jp2";
+ break;
+
+ default:
+ mime = "application/octet-stream";
+ break;
+ }
+ }
+
+
+ void ParsedDicomFile::InvalidateCache()
+ {
+ pimpl_->frameIndex_.reset(NULL);
+ }
+
+
+ unsigned int ParsedDicomFile::GetFramesCount() const
+ {
+ return DicomFrameIndex::GetFramesCount(*pimpl_->file_);
+ }
}
diff --git a/OrthancServer/ParsedDicomFile.h b/OrthancServer/ParsedDicomFile.h
index 27ff80e..011ff75 100644
--- a/OrthancServer/ParsedDicomFile.h
+++ b/OrthancServer/ParsedDicomFile.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -33,9 +33,9 @@
#pragma once
#include "../Core/DicomFormat/DicomInstanceHasher.h"
+#include "../Core/Images/ImageAccessor.h"
#include "../Core/IDynamicObject.h"
#include "../Core/RestApi/RestApiOutput.h"
-#include "IDicomImageDecoder.h"
#include "ServerEnumerations.h"
class DcmDataset;
@@ -51,15 +51,16 @@ namespace Orthanc
ParsedDicomFile(ParsedDicomFile& other);
- void Setup(const void* content,
- size_t size);
-
void RemovePrivateTagsInternal(const std::set<DicomTag>* toKeep);
void UpdateStorageUid(const DicomTag& tag,
const std::string& value,
bool decodeDataUriScheme);
+ void InvalidateCache();
+
+ bool EmbedContentInternal(const std::string& dataUriScheme);
+
public:
ParsedDicomFile(bool createIdentifiers); // Create a minimal DICOM instance
@@ -89,17 +90,24 @@ namespace Orthanc
void Replace(const DicomTag& tag,
const std::string& utf8Value,
- DicomReplaceMode mode = DicomReplaceMode_InsertIfAbsent);
+ bool decodeDataUriScheme,
+ DicomReplaceMode mode);
void Replace(const DicomTag& tag,
const Json::Value& value, // Assumed to be encoded with UTF-8
bool decodeDataUriScheme,
- DicomReplaceMode mode = DicomReplaceMode_InsertIfAbsent);
+ DicomReplaceMode mode);
void Insert(const DicomTag& tag,
const Json::Value& value, // Assumed to be encoded with UTF-8
bool decodeDataUriScheme);
+ void ReplacePlainString(const DicomTag& tag,
+ const std::string& utf8Value)
+ {
+ Replace(tag, utf8Value, false, DicomReplaceMode_InsertIfAbsent);
+ }
+
void RemovePrivateTags()
{
RemovePrivateTagsInternal(NULL);
@@ -126,24 +134,6 @@ namespace Orthanc
void EmbedImage(const std::string& mime,
const std::string& content);
- ImageAccessor* ExtractImage(IDicomImageDecoder& decoder,
- unsigned int frame);
-
- ImageAccessor* ExtractImage(IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode);
-
- void ExtractPngImage(std::string& result,
- IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode);
-
- void ExtractJpegImage(std::string& result,
- IDicomImageDecoder& decoder,
- unsigned int frame,
- ImageExtractionMode mode,
- uint8_t quality);
-
Encoding GetEncoding() const;
void SetEncoding(Encoding encoding);
@@ -164,6 +154,12 @@ namespace Orthanc
void Convert(DicomMap& tags);
+ void GetRawFrame(std::string& target, // OUT
+ std::string& mime, // OUT
+ unsigned int frameId); // IN
+
+ unsigned int GetFramesCount() const;
+
static ParsedDicomFile* CreateFromJson(const Json::Value& value,
DicomFromJsonFlags flags);
};
diff --git a/OrthancServer/PrecompiledHeadersServer.cpp b/OrthancServer/PrecompiledHeadersServer.cpp
index a846f70..0835253 100644
--- a/OrthancServer/PrecompiledHeadersServer.cpp
+++ b/OrthancServer/PrecompiledHeadersServer.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/PrecompiledHeadersServer.h b/OrthancServer/PrecompiledHeadersServer.h
index 794663b..49e8719 100644
--- a/OrthancServer/PrecompiledHeadersServer.h
+++ b/OrthancServer/PrecompiledHeadersServer.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/QueryRetrieveHandler.cpp b/OrthancServer/QueryRetrieveHandler.cpp
index db73135..3effdda 100644
--- a/OrthancServer/QueryRetrieveHandler.cpp
+++ b/OrthancServer/QueryRetrieveHandler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -84,7 +84,7 @@ namespace Orthanc
const std::string& value)
{
Invalidate();
- query_.SetValue(tag, value);
+ query_.SetValue(tag, value, false);
}
diff --git a/OrthancServer/QueryRetrieveHandler.h b/OrthancServer/QueryRetrieveHandler.h
index 6b1d208..0e86005 100644
--- a/OrthancServer/QueryRetrieveHandler.h
+++ b/OrthancServer/QueryRetrieveHandler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/CallSystemCommand.cpp b/OrthancServer/Scheduler/CallSystemCommand.cpp
index 7e5141d..135a788 100644
--- a/OrthancServer/Scheduler/CallSystemCommand.cpp
+++ b/OrthancServer/Scheduler/CallSystemCommand.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/CallSystemCommand.h b/OrthancServer/Scheduler/CallSystemCommand.h
index 70dbe4c..f5502b8 100644
--- a/OrthancServer/Scheduler/CallSystemCommand.h
+++ b/OrthancServer/Scheduler/CallSystemCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/DeleteInstanceCommand.cpp b/OrthancServer/Scheduler/DeleteInstanceCommand.cpp
index 609bef9..7e88a52 100644
--- a/OrthancServer/Scheduler/DeleteInstanceCommand.cpp
+++ b/OrthancServer/Scheduler/DeleteInstanceCommand.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/DeleteInstanceCommand.h b/OrthancServer/Scheduler/DeleteInstanceCommand.h
index 6c2af98..39362f7 100644
--- a/OrthancServer/Scheduler/DeleteInstanceCommand.h
+++ b/OrthancServer/Scheduler/DeleteInstanceCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/IServerCommand.h b/OrthancServer/Scheduler/IServerCommand.h
index 5c522a1..df33cd6 100644
--- a/OrthancServer/Scheduler/IServerCommand.h
+++ b/OrthancServer/Scheduler/IServerCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ModifyInstanceCommand.cpp b/OrthancServer/Scheduler/ModifyInstanceCommand.cpp
index 29609cd..5592680 100644
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.cpp
+++ b/OrthancServer/Scheduler/ModifyInstanceCommand.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ModifyInstanceCommand.h b/OrthancServer/Scheduler/ModifyInstanceCommand.h
index 92ecdac..a94f014 100644
--- a/OrthancServer/Scheduler/ModifyInstanceCommand.h
+++ b/OrthancServer/Scheduler/ModifyInstanceCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerCommandInstance.cpp b/OrthancServer/Scheduler/ServerCommandInstance.cpp
index 3017015..17f2042 100644
--- a/OrthancServer/Scheduler/ServerCommandInstance.cpp
+++ b/OrthancServer/Scheduler/ServerCommandInstance.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerCommandInstance.h b/OrthancServer/Scheduler/ServerCommandInstance.h
index 86d1a73..ed20938 100644
--- a/OrthancServer/Scheduler/ServerCommandInstance.h
+++ b/OrthancServer/Scheduler/ServerCommandInstance.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerJob.cpp b/OrthancServer/Scheduler/ServerJob.cpp
index 4e6ced8..c5ec396 100644
--- a/OrthancServer/Scheduler/ServerJob.cpp
+++ b/OrthancServer/Scheduler/ServerJob.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerJob.h b/OrthancServer/Scheduler/ServerJob.h
index beedfaa..a798923 100644
--- a/OrthancServer/Scheduler/ServerJob.h
+++ b/OrthancServer/Scheduler/ServerJob.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerScheduler.cpp b/OrthancServer/Scheduler/ServerScheduler.cpp
index 96b2ccc..45831a3 100644
--- a/OrthancServer/Scheduler/ServerScheduler.cpp
+++ b/OrthancServer/Scheduler/ServerScheduler.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/ServerScheduler.h b/OrthancServer/Scheduler/ServerScheduler.h
index cfaaa5a..341f829 100644
--- a/OrthancServer/Scheduler/ServerScheduler.h
+++ b/OrthancServer/Scheduler/ServerScheduler.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Scheduler/StorePeerCommand.cpp b/OrthancServer/Scheduler/StorePeerCommand.cpp
index e1fe9ce..ab9f677 100644
--- a/OrthancServer/Scheduler/StorePeerCommand.cpp
+++ b/OrthancServer/Scheduler/StorePeerCommand.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -39,7 +39,7 @@
namespace Orthanc
{
StorePeerCommand::StorePeerCommand(ServerContext& context,
- const OrthancPeerParameters& peer,
+ const WebServiceParameters& peer,
bool ignoreExceptions) :
context_(context),
peer_(peer),
@@ -51,16 +51,7 @@ namespace Orthanc
const ListOfStrings& inputs)
{
// Configure the HTTP client
- HttpClient client;
- client.SetProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
- if (peer_.GetUsername().size() != 0 &&
- peer_.GetPassword().size() != 0)
- {
- client.SetCredentials(peer_.GetUsername().c_str(),
- peer_.GetPassword().c_str());
- }
-
- client.SetUrl(peer_.GetUrl() + "instances");
+ HttpClient client(peer_, "instances");
client.SetMethod(HttpMethod_Post);
for (ListOfStrings::const_iterator
diff --git a/OrthancServer/Scheduler/StorePeerCommand.h b/OrthancServer/Scheduler/StorePeerCommand.h
index b3365e4..7f2c80b 100644
--- a/OrthancServer/Scheduler/StorePeerCommand.h
+++ b/OrthancServer/Scheduler/StorePeerCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -42,12 +42,12 @@ namespace Orthanc
{
private:
ServerContext& context_;
- OrthancPeerParameters peer_;
+ WebServiceParameters peer_;
bool ignoreExceptions_;
public:
StorePeerCommand(ServerContext& context,
- const OrthancPeerParameters& peer,
+ const WebServiceParameters& peer,
bool ignoreExceptions);
virtual bool Apply(ListOfStrings& outputs,
diff --git a/OrthancServer/Scheduler/StoreScuCommand.cpp b/OrthancServer/Scheduler/StoreScuCommand.cpp
index 9b3b0c7..7404064 100644
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp
+++ b/OrthancServer/Scheduler/StoreScuCommand.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -40,11 +40,13 @@ namespace Orthanc
StoreScuCommand::StoreScuCommand(ServerContext& context,
const std::string& localAet,
const RemoteModalityParameters& modality,
- bool ignoreExceptions) :
+ bool ignoreExceptions,
+ uint16_t moveOriginatorID) :
context_(context),
modality_(modality),
ignoreExceptions_(ignoreExceptions),
- localAet_(localAet)
+ localAet_(localAet),
+ moveOriginatorID_(moveOriginatorID)
{
}
@@ -63,7 +65,8 @@ namespace Orthanc
{
std::string dicom;
context_.ReadFile(dicom, *it, FileContentType_Dicom);
- locker.GetConnection().Store(dicom);
+
+ locker.GetConnection().Store(dicom, moveOriginatorID_);
// Only chain with other commands if this command succeeds
outputs.push_back(*it);
diff --git a/OrthancServer/Scheduler/StoreScuCommand.h b/OrthancServer/Scheduler/StoreScuCommand.h
index 6dd22ea..1070c35 100644
--- a/OrthancServer/Scheduler/StoreScuCommand.h
+++ b/OrthancServer/Scheduler/StoreScuCommand.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -44,12 +44,15 @@ namespace Orthanc
RemoteModalityParameters modality_;
bool ignoreExceptions_;
std::string localAet_;
+ uint16_t moveOriginatorID_;
public:
StoreScuCommand(ServerContext& context,
const std::string& localAet,
const RemoteModalityParameters& modality,
- bool ignoreExceptions);
+ bool ignoreExceptions,
+ uint16_t moveOriginatorID /* only makes sense if this
+ command results from a C-MOVE */);
virtual bool Apply(ListOfStrings& outputs,
const ListOfStrings& inputs);
diff --git a/OrthancServer/Search/HierarchicalMatcher.cpp b/OrthancServer/Search/HierarchicalMatcher.cpp
index f54e396..daefa4c 100644
--- a/OrthancServer/Search/HierarchicalMatcher.cpp
+++ b/OrthancServer/Search/HierarchicalMatcher.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -92,7 +92,7 @@ namespace Orthanc
continue;
}
- ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
+ ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag);
if (constraints_.find(tag) != constraints_.end() ||
sequences_.find(tag) != sequences_.end())
@@ -122,7 +122,8 @@ namespace Orthanc
else
{
std::auto_ptr<DicomValue> value(FromDcmtkBridge::ConvertLeafElement
- (*element, DicomToJsonFlags_None, encoding));
+ (*element, DicomToJsonFlags_None,
+ ORTHANC_MAXIMUM_TAG_LENGTH, encoding));
if (value->IsBinary())
{
@@ -146,7 +147,7 @@ namespace Orthanc
// DICOM specifies that searches must be case sensitive, except
// for tags with a PN value representation
bool sensitive = true;
- if (vr == ValueRepresentation_PatientName)
+ if (vr == ValueRepresentation_PersonName)
{
sensitive = caseSensitivePN;
}
@@ -221,7 +222,8 @@ namespace Orthanc
}
std::auto_ptr<DicomValue> value(FromDcmtkBridge::ConvertLeafElement
- (*element, DicomToJsonFlags_None, encoding));
+ (*element, DicomToJsonFlags_None,
+ ORTHANC_MAXIMUM_TAG_LENGTH, encoding));
if (value->IsNull() ||
value->IsBinary() ||
diff --git a/OrthancServer/Search/HierarchicalMatcher.h b/OrthancServer/Search/HierarchicalMatcher.h
index 3f1795d..e646c31 100644
--- a/OrthancServer/Search/HierarchicalMatcher.h
+++ b/OrthancServer/Search/HierarchicalMatcher.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/IFindConstraint.cpp b/OrthancServer/Search/IFindConstraint.cpp
index 38595a4..ffec897 100644
--- a/OrthancServer/Search/IFindConstraint.cpp
+++ b/OrthancServer/Search/IFindConstraint.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -47,7 +47,7 @@ namespace Orthanc
const std::string& dicomQuery,
bool caseSensitive)
{
- ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
+ ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag);
if (vr == ValueRepresentation_Sequence)
{
diff --git a/OrthancServer/Search/IFindConstraint.h b/OrthancServer/Search/IFindConstraint.h
index c581191..d182685 100644
--- a/OrthancServer/Search/IFindConstraint.h
+++ b/OrthancServer/Search/IFindConstraint.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/ListConstraint.cpp b/OrthancServer/Search/ListConstraint.cpp
index 1dcfa6d..46f9b9a 100644
--- a/OrthancServer/Search/ListConstraint.cpp
+++ b/OrthancServer/Search/ListConstraint.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/ListConstraint.h b/OrthancServer/Search/ListConstraint.h
index 5a2bc02..cbdc556 100644
--- a/OrthancServer/Search/ListConstraint.h
+++ b/OrthancServer/Search/ListConstraint.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/LookupIdentifierQuery.cpp b/OrthancServer/Search/LookupIdentifierQuery.cpp
index 0e3d75b..b56f95f 100644
--- a/OrthancServer/Search/LookupIdentifierQuery.cpp
+++ b/OrthancServer/Search/LookupIdentifierQuery.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/LookupIdentifierQuery.h b/OrthancServer/Search/LookupIdentifierQuery.h
index b595375..fa8d9c3 100644
--- a/OrthancServer/Search/LookupIdentifierQuery.h
+++ b/OrthancServer/Search/LookupIdentifierQuery.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/LookupResource.cpp b/OrthancServer/Search/LookupResource.cpp
index f2ca2a6..2c62a1a 100644
--- a/OrthancServer/Search/LookupResource.cpp
+++ b/OrthancServer/Search/LookupResource.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/LookupResource.h b/OrthancServer/Search/LookupResource.h
index 66f89d8..71390b8 100644
--- a/OrthancServer/Search/LookupResource.h
+++ b/OrthancServer/Search/LookupResource.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/RangeConstraint.cpp b/OrthancServer/Search/RangeConstraint.cpp
index f629f16..b90021d 100644
--- a/OrthancServer/Search/RangeConstraint.cpp
+++ b/OrthancServer/Search/RangeConstraint.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/RangeConstraint.h b/OrthancServer/Search/RangeConstraint.h
index 858bf3a..02e0240 100644
--- a/OrthancServer/Search/RangeConstraint.h
+++ b/OrthancServer/Search/RangeConstraint.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/SetOfResources.cpp b/OrthancServer/Search/SetOfResources.cpp
index 4297da3..995bb20 100644
--- a/OrthancServer/Search/SetOfResources.cpp
+++ b/OrthancServer/Search/SetOfResources.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/SetOfResources.h b/OrthancServer/Search/SetOfResources.h
index c8ac73a..f4a833f 100644
--- a/OrthancServer/Search/SetOfResources.h
+++ b/OrthancServer/Search/SetOfResources.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/ValueConstraint.cpp b/OrthancServer/Search/ValueConstraint.cpp
index 06b4fd5..ecb3d62 100644
--- a/OrthancServer/Search/ValueConstraint.cpp
+++ b/OrthancServer/Search/ValueConstraint.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/ValueConstraint.h b/OrthancServer/Search/ValueConstraint.h
index 8c69836..6cee8e1 100644
--- a/OrthancServer/Search/ValueConstraint.h
+++ b/OrthancServer/Search/ValueConstraint.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/WildcardConstraint.cpp b/OrthancServer/Search/WildcardConstraint.cpp
index b5e867e..f3a4654 100644
--- a/OrthancServer/Search/WildcardConstraint.cpp
+++ b/OrthancServer/Search/WildcardConstraint.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/Search/WildcardConstraint.h b/OrthancServer/Search/WildcardConstraint.h
index 353dbc8..b3eda69 100644
--- a/OrthancServer/Search/WildcardConstraint.h
+++ b/OrthancServer/Search/WildcardConstraint.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerContext.cpp b/OrthancServer/ServerContext.cpp
index cf86ee6..e183296 100644
--- a/OrthancServer/ServerContext.cpp
+++ b/OrthancServer/ServerContext.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerContext.h b/OrthancServer/ServerContext.h
index 6babece..95d320b 100644
--- a/OrthancServer/ServerContext.h
+++ b/OrthancServer/ServerContext.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerEnumerations.cpp b/OrthancServer/ServerEnumerations.cpp
index f255af3..8e3fdf3 100644
--- a/OrthancServer/ServerEnumerations.cpp
+++ b/OrthancServer/ServerEnumerations.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerEnumerations.h b/OrthancServer/ServerEnumerations.h
index 24467cb..8765680 100644
--- a/OrthancServer/ServerEnumerations.h
+++ b/OrthancServer/ServerEnumerations.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -95,16 +95,6 @@ namespace Orthanc
TransferSyntax_Rle
};
- enum ValueRepresentation
- {
- ValueRepresentation_Other,
- ValueRepresentation_PatientName,
- ValueRepresentation_Date,
- ValueRepresentation_DateTime,
- ValueRepresentation_Time,
- ValueRepresentation_Sequence
- };
-
enum DicomToJsonFormat
{
DicomToJsonFormat_Full,
diff --git a/OrthancServer/ServerIndex.cpp b/OrthancServer/ServerIndex.cpp
index 511d1b9..5cf4702 100644
--- a/OrthancServer/ServerIndex.cpp
+++ b/OrthancServer/ServerIndex.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -2177,4 +2177,36 @@ namespace Orthanc
instances[pos] = db_.GetPublicId(instance);
}
}
+
+
+ bool ServerIndex::LookupParent(std::string& target,
+ const std::string& publicId,
+ ResourceType parentType)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ ResourceType type;
+ int64_t id;
+ if (!db_.LookupResource(id, type, publicId))
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
+ }
+
+ while (type != parentType)
+ {
+ int64_t parentId;
+
+ if (type == ResourceType_Patient || // Cannot further go up in hierarchy
+ !db_.LookupParent(parentId, id))
+ {
+ return false;
+ }
+
+ id = parentId;
+ type = GetParentResourceType(type);
+ }
+
+ target = db_.GetPublicId(id);
+ return true;
+ }
}
diff --git a/OrthancServer/ServerIndex.h b/OrthancServer/ServerIndex.h
index d716452..dece92d 100644
--- a/OrthancServer/ServerIndex.h
+++ b/OrthancServer/ServerIndex.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -264,5 +264,9 @@ namespace Orthanc
void FindCandidates(std::vector<std::string>& resources,
std::vector<std::string>& instances,
const ::Orthanc::LookupResource& lookup);
+
+ bool LookupParent(std::string& target,
+ const std::string& publicId,
+ ResourceType parentType);
};
}
diff --git a/OrthancServer/ServerIndexChange.h b/OrthancServer/ServerIndexChange.h
index 91e20ea..d779840 100644
--- a/OrthancServer/ServerIndexChange.h
+++ b/OrthancServer/ServerIndexChange.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerToolbox.cpp b/OrthancServer/ServerToolbox.cpp
index afd7532..0d7a877 100644
--- a/OrthancServer/ServerToolbox.cpp
+++ b/OrthancServer/ServerToolbox.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/ServerToolbox.h b/OrthancServer/ServerToolbox.h
index daa96d4..891f38a 100644
--- a/OrthancServer/ServerToolbox.h
+++ b/OrthancServer/ServerToolbox.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/SliceOrdering.cpp b/OrthancServer/SliceOrdering.cpp
index 0b7266f..76c03a1 100644
--- a/OrthancServer/SliceOrdering.cpp
+++ b/OrthancServer/SliceOrdering.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/OrthancServer/SliceOrdering.h b/OrthancServer/SliceOrdering.h
index ea57a07..13d1693 100644
--- a/OrthancServer/SliceOrdering.h
+++ b/OrthancServer/SliceOrdering.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -42,7 +42,7 @@ namespace Orthanc
typedef float Vector[3];
struct Instance;
- struct PositionComparator;
+ class PositionComparator;
ServerIndex& index_;
std::string seriesId_;
diff --git a/OrthancServer/ToDcmtkBridge.cpp b/OrthancServer/ToDcmtkBridge.cpp
index 2dabc01..708293a 100644
--- a/OrthancServer/ToDcmtkBridge.cpp
+++ b/OrthancServer/ToDcmtkBridge.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -36,15 +36,11 @@
#include <memory>
#include <dcmtk/dcmnet/diutil.h>
+#include "../Core/OrthancException.h"
+
namespace Orthanc
{
- DcmTagKey ToDcmtkBridge::Convert(const DicomTag& tag)
- {
- return DcmTagKey(tag.GetGroup(), tag.GetElement());
- }
-
-
DcmDataset* ToDcmtkBridge::Convert(const DicomMap& map)
{
std::auto_ptr<DcmDataset> result(new DcmDataset);
@@ -61,4 +57,111 @@ namespace Orthanc
return result.release();
}
+
+
+ DcmEVR ToDcmtkBridge::Convert(ValueRepresentation vr)
+ {
+ switch (vr)
+ {
+ case ValueRepresentation_ApplicationEntity:
+ return EVR_AE;
+
+ case ValueRepresentation_AgeString:
+ return EVR_AS;
+
+ case ValueRepresentation_AttributeTag:
+ return EVR_AT;
+
+ case ValueRepresentation_CodeString:
+ return EVR_CS;
+
+ case ValueRepresentation_Date:
+ return EVR_DA;
+
+ case ValueRepresentation_DecimalString:
+ return EVR_DS;
+
+ case ValueRepresentation_DateTime:
+ return EVR_DT;
+
+ case ValueRepresentation_FloatingPointSingle:
+ return EVR_FL;
+
+ case ValueRepresentation_FloatingPointDouble:
+ return EVR_FD;
+
+ case ValueRepresentation_IntegerString:
+ return EVR_IS;
+
+ case ValueRepresentation_LongString:
+ return EVR_LO;
+
+ case ValueRepresentation_LongText:
+ return EVR_LT;
+
+ case ValueRepresentation_OtherByte:
+ return EVR_OB;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case ValueRepresentation_OtherDouble:
+ return EVR_OD;*/
+
+ case ValueRepresentation_OtherFloat:
+ return EVR_OF;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case ValueRepresentation_OtherLong:
+ return EVR_OL;*/
+
+ case ValueRepresentation_OtherWord:
+ return EVR_OW;
+
+ case ValueRepresentation_PersonName:
+ return EVR_PN;
+
+ case ValueRepresentation_ShortString:
+ return EVR_SH;
+
+ case ValueRepresentation_SignedLong:
+ return EVR_SL;
+
+ case ValueRepresentation_Sequence:
+ return EVR_SQ;
+
+ case ValueRepresentation_SignedShort:
+ return EVR_SS;
+
+ case ValueRepresentation_ShortText:
+ return EVR_ST;
+
+ case ValueRepresentation_Time:
+ return EVR_TM;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case ValueRepresentation_UnlimitedCharacters:
+ return EVR_UC;*/
+
+ case ValueRepresentation_UniqueIdentifier:
+ return EVR_UI;
+
+ case ValueRepresentation_UnsignedLong:
+ return EVR_UL;
+
+ case ValueRepresentation_Unknown:
+ return EVR_UN;
+
+ // Not supported as of DCMTK 3.6.0
+ /*case ValueRepresentation_UniversalResource:
+ return EVR_UR;*/
+
+ case ValueRepresentation_UnsignedShort:
+ return EVR_US;
+
+ case ValueRepresentation_UnlimitedText:
+ return EVR_UT;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
}
diff --git a/OrthancServer/ToDcmtkBridge.h b/OrthancServer/ToDcmtkBridge.h
index f2b0096..fff8bd4 100644
--- a/OrthancServer/ToDcmtkBridge.h
+++ b/OrthancServer/ToDcmtkBridge.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -40,8 +40,13 @@ namespace Orthanc
class ToDcmtkBridge
{
public:
- static DcmTagKey Convert(const DicomTag& tag);
+ static DcmTagKey Convert(const DicomTag& tag)
+ {
+ return DcmTagKey(tag.GetGroup(), tag.GetElement());
+ }
static DcmDataset* Convert(const DicomMap& map);
+
+ static DcmEVR Convert(ValueRepresentation vr);
};
}
diff --git a/OrthancServer/main.cpp b/OrthancServer/main.cpp
index 8c6d613..fc27b4a 100644
--- a/OrthancServer/main.cpp
+++ b/OrthancServer/main.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -33,7 +33,6 @@
#include "PrecompiledHeadersServer.h"
#include "OrthancRestApi/OrthancRestApi.h"
-#include <fstream>
#include <boost/algorithm/string/predicate.hpp>
#include "../Core/Logging.h"
@@ -278,17 +277,28 @@ class MyIncomingHttpRequestFilter : public IIncomingHttpRequestFilter
{
private:
ServerContext& context_;
+ OrthancPlugins* plugins_;
public:
- MyIncomingHttpRequestFilter(ServerContext& context) : context_(context)
+ MyIncomingHttpRequestFilter(ServerContext& context,
+ OrthancPlugins* plugins) :
+ context_(context),
+ plugins_(plugins)
{
}
virtual bool IsAllowed(HttpMethod method,
const char* uri,
const char* ip,
- const char* username) const
+ const char* username,
+ const IHttpHandler::Arguments& httpHeaders) const
{
+ if (plugins_ != NULL &&
+ !plugins_->IsAllowed(method, uri, ip, username, httpHeaders))
+ {
+ return false;
+ }
+
static const char* HTTP_FILTER = "IncomingHttpRequestFilter";
LuaScripting::Locker locker(context_.GetLua());
@@ -323,6 +333,7 @@ public:
call.PushString(uri);
call.PushString(ip);
call.PushString(username);
+ call.PushStringMap(httpHeaders);
if (!call.ExecutePredicate())
{
@@ -431,7 +442,9 @@ static void PrintHelp(const char* path)
<< "Command-line options:" << std::endl
<< " --help\t\tdisplay this help and exit" << std::endl
<< " --logdir=[dir]\tdirectory where to store the log files" << std::endl
- << "\t\t\t(if not used, the logs are dumped to stderr)" << std::endl
+ << "\t\t\t(by default, the log is dumped to stderr)" << std::endl
+ << " --logfile=[file]\tfile where to store the log of Orthanc" << std::endl
+ << "\t\t\t(by default, the log is dumped to stderr)" << std::endl
<< " --config=[file]\tcreate a sample configuration file and exit" << std::endl
<< " --errors\t\tprint the supported error codes and exit" << std::endl
<< " --verbose\t\tbe verbose in logs" << std::endl
@@ -456,7 +469,7 @@ static void PrintVersion(const char* path)
{
std::cout
<< path << " " << ORTHANC_VERSION << std::endl
- << "Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics Department, University Hospital of Liege (Belgium)" << std::endl
+ << "Copyright (C) 2012-2016 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
@@ -541,8 +554,8 @@ static void PrintErrors(const char* path)
PrintErrorCode(ErrorCode_DirectoryOverFile, "The directory to be created is already occupied by a regular file");
PrintErrorCode(ErrorCode_FileStorageCannotWrite, "Unable to create a subdirectory or a file in the file storage");
PrintErrorCode(ErrorCode_DirectoryExpected, "The specified path does not point to a directory");
- PrintErrorCode(ErrorCode_HttpPortInUse, "The TCP port of the HTTP server is already in use");
- PrintErrorCode(ErrorCode_DicomPortInUse, "The TCP port of the DICOM server is already in use");
+ PrintErrorCode(ErrorCode_HttpPortInUse, "The TCP port of the HTTP server is privileged or already in use");
+ PrintErrorCode(ErrorCode_DicomPortInUse, "The TCP port of the DICOM server is privileged or already in use");
PrintErrorCode(ErrorCode_BadHttpStatusInRest, "This HTTP status is not allowed in a REST API");
PrintErrorCode(ErrorCode_RegularFileExpected, "The specified path does not point to a regular file");
PrintErrorCode(ErrorCode_PathToExecutable, "Unable to get the path to the executable");
@@ -580,6 +593,7 @@ static void PrintErrors(const char* path)
PrintErrorCode(ErrorCode_SslDisabled, "Orthanc has been built without SSL support");
PrintErrorCode(ErrorCode_CannotOrderSlices, "Unable to order the slices of the series");
PrintErrorCode(ErrorCode_NoWorklistHandler, "No request handler factory for DICOM C-Find Modality SCP");
+ PrintErrorCode(ErrorCode_AlreadyExistingTag, "Cannot override the value of a tag that already exists");
}
std::cout << std::endl;
@@ -638,8 +652,37 @@ static bool WaitForExit(ServerContext& context,
context.GetLua().Execute("Initialize");
- Toolbox::ServerBarrier(restApi.LeaveBarrierFlag());
- bool restart = restApi.IsResetRequestReceived();
+ bool restart;
+
+ for (;;)
+ {
+ ServerBarrierEvent event = Toolbox::ServerBarrier(restApi.LeaveBarrierFlag());
+ restart = restApi.IsResetRequestReceived();
+
+ if (!restart &&
+ event == ServerBarrierEvent_Reload)
+ {
+ // Handling of SIGHUP
+
+ if (Configuration::HasConfigurationChanged())
+ {
+ LOG(WARNING) << "A SIGHUP signal has been received, resetting Orthanc";
+ Logging::Flush();
+ restart = true;
+ break;
+ }
+ else
+ {
+ LOG(WARNING) << "A SIGHUP signal has been received, but is ignored as the configuration has not changed";
+ Logging::Flush();
+ continue;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
context.GetLua().Execute("Finalize");
@@ -677,7 +720,7 @@ static bool StartHttpServer(ServerContext& context,
// HTTP server
- MyIncomingHttpRequestFilter httpFilter(context);
+ MyIncomingHttpRequestFilter httpFilter(context, plugins);
MongooseServer httpServer;
httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
@@ -703,6 +746,13 @@ static bool StartHttpServer(ServerContext& context,
httpServer.Register(context.GetHttpHandler());
+ if (httpServer.GetPortNumber() < 1024)
+ {
+ LOG(WARNING) << "The HTTP port is privileged ("
+ << httpServer.GetPortNumber() << " is below 1024), "
+ << "make sure you run Orthanc as root/administrator";
+ }
+
httpServer.Start();
LOG(WARNING) << "HTTP server listening on port: " << httpServer.GetPortNumber();
@@ -736,10 +786,22 @@ static bool StartDicomServer(ServerContext& context,
dicomServer.SetFindRequestHandlerFactory(serverFactory);
#if ORTHANC_PLUGINS_ENABLED == 1
- if (plugins &&
- plugins->HasWorklistHandler())
+ if (plugins != NULL)
{
- dicomServer.SetWorklistRequestHandlerFactory(*plugins);
+ if (plugins->HasWorklistHandler())
+ {
+ dicomServer.SetWorklistRequestHandlerFactory(*plugins);
+ }
+
+ if (plugins->HasFindHandler())
+ {
+ dicomServer.SetFindRequestHandlerFactory(*plugins);
+ }
+
+ if (plugins->HasMoveHandler())
+ {
+ dicomServer.SetMoveRequestHandlerFactory(*plugins);
+ }
}
#endif
@@ -747,11 +809,18 @@ static bool StartDicomServer(ServerContext& context,
dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"));
dicomServer.SetApplicationEntityFilter(dicomFilter);
+ if (dicomServer.GetPortNumber() < 1024)
+ {
+ LOG(WARNING) << "The DICOM port is privileged ("
+ << dicomServer.GetPortNumber() << " is below 1024), "
+ << "make sure you run Orthanc as root/administrator";
+ }
+
dicomServer.Start();
LOG(WARNING) << "DICOM server listening with AET " << dicomServer.GetApplicationEntityTitle()
<< " on port: " << dicomServer.GetPortNumber();
- bool restart;
+ bool restart = false;
ErrorCode error = ErrorCode_Success;
try
@@ -865,7 +934,10 @@ static bool ConfigureServerContext(IDatabaseWrapper& database,
{
ServerContext context(database, storageArea);
+ HttpClient::ConfigureSsl(Configuration::GetGlobalBoolParameter("HttpsVerifyPeers", true),
+ Configuration::GetGlobalStringParameter("HttpsCACertificates", ""));
HttpClient::SetDefaultTimeout(Configuration::GetGlobalIntegerParameter("HttpTimeout", 0));
+ HttpClient::SetDefaultProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
@@ -898,7 +970,7 @@ static bool ConfigureServerContext(IDatabaseWrapper& database,
}
#endif
- bool restart;
+ bool restart = false;
ErrorCode error = ErrorCode_Success;
try
@@ -915,6 +987,7 @@ static bool ConfigureServerContext(IDatabaseWrapper& database,
#if ORTHANC_PLUGINS_ENABLED == 1
if (plugins)
{
+ plugins->ResetServerContext();
context.ResetPlugins();
}
#endif
@@ -1039,6 +1112,8 @@ int main(int argc, char* argv[])
{
// Use the first argument that does not start with a "-" as
// the configuration file
+
+ // TODO WHAT IS THE ENCODING?
configurationFile = argv[i];
}
}
@@ -1067,6 +1142,7 @@ int main(int argc, char* argv[])
}
else if (boost::starts_with(argument, "--logdir="))
{
+ // TODO WHAT IS THE ENCODING?
std::string directory = argument.substr(9);
try
@@ -1080,12 +1156,29 @@ int main(int argc, char* argv[])
return -1;
}
}
+ else if (boost::starts_with(argument, "--logfile="))
+ {
+ // TODO WHAT IS THE ENCODING?
+ std::string file = argument.substr(10);
+
+ try
+ {
+ Logging::SetTargetFile(file);
+ }
+ catch (OrthancException&)
+ {
+ LOG(ERROR) << "Cannot write to the specified log file ("
+ << file << "), aborting.";
+ return -1;
+ }
+ }
else if (argument == "--upgrade")
{
allowDatabaseUpgrade = true;
}
else if (boost::starts_with(argument, "--config="))
{
+ // TODO WHAT IS THE ENCODING?
std::string configurationSample;
GetFileResource(configurationSample, EmbeddedResources::CONFIGURATION_SAMPLE);
@@ -1136,27 +1229,12 @@ int main(int argc, char* argv[])
{
OrthancInitialize(configurationFile);
- if (0)
- {
- // TODO REMOVE THIS TEST
- DicomUserConnection c;
- c.SetRemoteHost("localhost");
- c.SetRemotePort(4243);
- c.SetRemoteApplicationEntityTitle("ORTHANCTEST");
- c.Open();
- ParsedDicomFile f(false);
- f.Replace(DICOM_TAG_PATIENT_NAME, "M*");
- DicomFindAnswers a;
- c.FindWorklist(a, f);
- Json::Value j;
- a.ToJson(j, true);
- std::cout << j;
- }
-
bool restart = StartOrthanc(argc, argv, allowDatabaseUpgrade);
if (restart)
{
OrthancFinalize();
+ LOG(WARNING) << "Logging system is resetting";
+ Logging::Reset();
}
else
{
diff --git a/Plugins/Engine/IPluginServiceProvider.h b/Plugins/Engine/IPluginServiceProvider.h
index 5ec981e..e6fde58 100644
--- a/Plugins/Engine/IPluginServiceProvider.h
+++ b/Plugins/Engine/IPluginServiceProvider.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Engine/OrthancPluginDatabase.cpp b/Plugins/Engine/OrthancPluginDatabase.cpp
index dc37964..f10ab99 100644
--- a/Plugins/Engine/OrthancPluginDatabase.cpp
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -986,7 +986,7 @@ namespace Orthanc
{
const OrthancPluginDicomTag& tag = *reinterpret_cast<const OrthancPluginDicomTag*>(answer.valueGeneric);
assert(answerDicomMap_ != NULL);
- answerDicomMap_->SetValue(tag.group, tag.element, std::string(tag.value));
+ answerDicomMap_->SetValue(tag.group, tag.element, std::string(tag.value), false);
break;
}
diff --git a/Plugins/Engine/OrthancPluginDatabase.h b/Plugins/Engine/OrthancPluginDatabase.h
index 5dc9f17..3d77948 100644
--- a/Plugins/Engine/OrthancPluginDatabase.h
+++ b/Plugins/Engine/OrthancPluginDatabase.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Engine/OrthancPlugins.cpp b/Plugins/Engine/OrthancPlugins.cpp
index 32e9682..a492bbf 100644
--- a/Plugins/Engine/OrthancPlugins.cpp
+++ b/Plugins/Engine/OrthancPlugins.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -39,6 +39,7 @@
#include "../../Core/ChunkedBuffer.h"
+#include "../../Core/DicomFormat/DicomArray.h"
#include "../../Core/HttpServer/HttpToolbox.h"
#include "../../Core/Logging.h"
#include "../../Core/OrthancException.h"
@@ -58,6 +59,7 @@
#include "../../Core/Images/JpegReader.h"
#include "../../Core/Images/JpegWriter.h"
#include "../../Core/Images/ImageProcessing.h"
+#include "../../OrthancServer/DefaultDicomImageDecoder.h"
#include "PluginsEnumerations.h"
#include <boost/regex.hpp>
@@ -106,6 +108,27 @@ namespace Orthanc
}
+ static char* CopyString(const std::string& str)
+ {
+ char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
+ if (result == NULL)
+ {
+ throw OrthancException(ErrorCode_NotEnoughMemory);
+ }
+
+ if (str.size() == 0)
+ {
+ result[0] = '\0';
+ }
+ else
+ {
+ memcpy(result, &str[0], str.size() + 1);
+ }
+
+ return result;
+ }
+
+
namespace
{
class PluginStorageArea : public IStorageArea
@@ -227,8 +250,14 @@ namespace Orthanc
}
- struct OrthancPlugins::PImpl
+ class OrthancPlugins::PImpl
{
+ private:
+ boost::mutex contextMutex_;
+ ServerContext* context_;
+
+
+ public:
class RestCallback : public boost::noncopyable
{
private:
@@ -278,26 +307,66 @@ namespace Orthanc
};
+ class ServerContextLock
+ {
+ private:
+ boost::mutex::scoped_lock lock_;
+ ServerContext* context_;
+
+ public:
+ ServerContextLock(PImpl& that) :
+ lock_(that.contextMutex_),
+ context_(that.context_)
+ {
+ if (context_ == NULL)
+ {
+ throw OrthancException(ErrorCode_DatabaseNotInitialized);
+ }
+ }
+
+ ServerContext& GetContext()
+ {
+ assert(context_ != NULL);
+ return *context_;
+ }
+ };
+
+
+ void SetServerContext(ServerContext* context)
+ {
+ boost::mutex::scoped_lock(contextMutex_);
+ context_ = context;
+ }
+
+
typedef std::pair<std::string, _OrthancPluginProperty> Property;
typedef std::list<RestCallback*> RestCallbacks;
typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks;
typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks;
+ typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters;
+ typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks;
typedef std::map<Property, std::string> Properties;
PluginsManager manager_;
- ServerContext* context_;
+
RestCallbacks restCallbacks_;
OnStoredCallbacks onStoredCallbacks_;
OnChangeCallbacks onChangeCallbacks_;
+ OrthancPluginFindCallback findCallback_;
OrthancPluginWorklistCallback worklistCallback_;
- OrthancPluginDecodeImageCallback decodeImageCallback_;
+ DecodeImageCallbacks decodeImageCallbacks_;
+ _OrthancPluginMoveCallback moveCallbacks_;
+ IncomingHttpRequestFilters incomingHttpRequestFilters_;
std::auto_ptr<StorageAreaFactory> storageArea_;
+
boost::recursive_mutex restCallbackMutex_;
boost::recursive_mutex storedCallbackMutex_;
boost::recursive_mutex changeCallbackMutex_;
+ boost::mutex findCallbackMutex_;
boost::mutex worklistCallbackMutex_;
boost::mutex decodeImageCallbackMutex_;
boost::recursive_mutex invokeServiceMutex_;
+
Properties properties_;
int argc_;
char** argv_;
@@ -306,11 +375,12 @@ namespace Orthanc
PImpl() :
context_(NULL),
+ findCallback_(NULL),
worklistCallback_(NULL),
- decodeImageCallback_(NULL),
argc_(1),
argv_(NULL)
{
+ memset(&moveCallbacks_, 0, sizeof(moveCallbacks_));
}
};
@@ -342,12 +412,13 @@ namespace Orthanc
const std::string& calledAet)
{
bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false);
- matcher_.reset(new HierarchicalMatcher(query, caseSensitivePN));
- currentQuery_ = &query;
{
boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_);
+ matcher_.reset(new HierarchicalMatcher(query, caseSensitivePN));
+ currentQuery_ = &query;
+
if (that_.pimpl_->worklistCallback_)
{
OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_
@@ -363,14 +434,18 @@ namespace Orthanc
throw OrthancException(static_cast<ErrorCode>(error));
}
}
- }
- Reset();
+ Reset();
+ }
}
void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
{
- assert(currentQuery_ != NULL);
+ if (currentQuery_ == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
std::string dicom;
currentQuery_->SaveToMemoryBuffer(dicom);
CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
@@ -379,7 +454,11 @@ namespace Orthanc
bool IsMatch(const void* dicom,
size_t size) const
{
- assert(matcher_.get() != NULL);
+ if (matcher_.get() == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
ParsedDicomFile f(dicom, size);
return matcher_->Match(f);
}
@@ -388,7 +467,11 @@ namespace Orthanc
const void* dicom,
size_t size) const
{
- assert(matcher_.get() != NULL);
+ if (matcher_.get() == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
ParsedDicomFile f(dicom, size);
std::auto_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
@@ -396,25 +479,256 @@ namespace Orthanc
};
- static char* CopyString(const std::string& str)
+ class OrthancPlugins::FindHandler : public IFindRequestHandler
{
- char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
- if (result == NULL)
+ private:
+ OrthancPlugins& that_;
+ std::auto_ptr<DicomArray> currentQuery_;
+
+ void Reset()
{
- throw OrthancException(ErrorCode_NotEnoughMemory);
+ currentQuery_.reset(NULL);
}
- if (str.size() == 0)
+ public:
+ FindHandler(OrthancPlugins& that) : that_(that)
{
- result[0] = '\0';
+ Reset();
}
- else
+
+ virtual void Handle(DicomFindAnswers& answers,
+ const DicomMap& input,
+ const std::list<DicomTag>& sequencesToReturn,
+ const std::string& remoteIp,
+ const std::string& remoteAet,
+ const std::string& calledAet)
{
- memcpy(result, &str[0], str.size() + 1);
+ DicomMap tmp;
+ tmp.Assign(input);
+
+ for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin();
+ it != sequencesToReturn.end(); ++it)
+ {
+ if (!input.HasTag(*it))
+ {
+ tmp.SetValue(*it, "", false);
+ }
+ }
+
+ {
+ boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_);
+ currentQuery_.reset(new DicomArray(tmp));
+
+ if (that_.pimpl_->findCallback_)
+ {
+ OrthancPluginErrorCode error = that_.pimpl_->findCallback_
+ (reinterpret_cast<OrthancPluginFindAnswers*>(&answers),
+ reinterpret_cast<const OrthancPluginFindQuery*>(this),
+ remoteAet.c_str(),
+ calledAet.c_str());
+
+ if (error != OrthancPluginErrorCode_Success)
+ {
+ Reset();
+ that_.GetErrorDictionary().LogError(error, true);
+ throw OrthancException(static_cast<ErrorCode>(error));
+ }
+ }
+
+ Reset();
+ }
}
- return result;
- }
+ void Invoke(_OrthancPluginService service,
+ const _OrthancPluginFindOperation& operation) const
+ {
+ if (currentQuery_.get() == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
+ switch (service)
+ {
+ case _OrthancPluginService_GetFindQuerySize:
+ *operation.resultUint32 = currentQuery_->GetSize();
+ break;
+
+ case _OrthancPluginService_GetFindQueryTag:
+ {
+ const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
+ *operation.resultGroup = tag.GetGroup();
+ *operation.resultElement = tag.GetElement();
+ break;
+ }
+
+ case _OrthancPluginService_GetFindQueryTagName:
+ {
+ const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
+ *operation.resultString = CopyString(FromDcmtkBridge::GetName(tag));
+ break;
+ }
+
+ case _OrthancPluginService_GetFindQueryValue:
+ {
+ *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent());
+ break;
+ }
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
+ }
+ }
+ };
+
+
+
+ class OrthancPlugins::MoveHandler : public IMoveRequestHandler
+ {
+ private:
+ class Driver : public IMoveRequestIterator
+ {
+ private:
+ void* driver_;
+ unsigned int count_;
+ unsigned int pos_;
+ OrthancPluginApplyMove apply_;
+ OrthancPluginFreeMove free_;
+
+ public:
+ Driver(void* driver,
+ unsigned int count,
+ OrthancPluginApplyMove apply,
+ OrthancPluginFreeMove free) :
+ driver_(driver),
+ count_(count),
+ pos_(0),
+ apply_(apply),
+ free_(free)
+ {
+ if (driver_ == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+ }
+
+ virtual ~Driver()
+ {
+ if (driver_ != NULL)
+ {
+ free_(driver_);
+ driver_ = NULL;
+ }
+ }
+
+ virtual unsigned int GetSubOperationCount() const
+ {
+ return count_;
+ }
+
+ virtual Status DoNext()
+ {
+ if (pos_ >= count_)
+ {
+ throw OrthancException(ErrorCode_BadSequenceOfCalls);
+ }
+ else
+ {
+ OrthancPluginErrorCode error = apply_(driver_);
+ if (error != OrthancPluginErrorCode_Success)
+ {
+ LOG(ERROR) << "Error while doing C-Move from plugin: " << EnumerationToString(static_cast<ErrorCode>(error));
+ return Status_Failure;
+ }
+ else
+ {
+ pos_++;
+ return Status_Success;
+ }
+ }
+ }
+ };
+
+
+ _OrthancPluginMoveCallback params_;
+
+
+ static std::string ReadTag(const DicomMap& input,
+ const DicomTag& tag)
+ {
+ const DicomValue* value = input.TestAndGetValue(tag);
+ if (value != NULL &&
+ !value->IsBinary() &&
+ !value->IsNull())
+ {
+ return value->GetContent();
+ }
+ else
+ {
+ return std::string();
+ }
+ }
+
+
+
+ public:
+ MoveHandler(OrthancPlugins& that)
+ {
+ boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_);
+ params_ = that.pimpl_->moveCallbacks_;
+
+ if (params_.callback == NULL ||
+ params_.getMoveSize == NULL ||
+ params_.applyMove == NULL ||
+ params_.freeMove == NULL)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+ }
+
+ virtual IMoveRequestIterator* Handle(const std::string& targetAet,
+ const DicomMap& input,
+ const std::string& remoteIp,
+ const std::string& remoteAet,
+ const std::string& calledAet,
+ uint16_t messageId)
+ {
+ std::string levelString = ReadTag(input, DICOM_TAG_QUERY_RETRIEVE_LEVEL);
+ std::string patientId = ReadTag(input, DICOM_TAG_PATIENT_ID);
+ std::string accessionNumber = ReadTag(input, DICOM_TAG_ACCESSION_NUMBER);
+ std::string studyInstanceUid = ReadTag(input, DICOM_TAG_STUDY_INSTANCE_UID);
+ std::string seriesInstanceUid = ReadTag(input, DICOM_TAG_SERIES_INSTANCE_UID);
+ std::string sopInstanceUid = ReadTag(input, DICOM_TAG_SOP_INSTANCE_UID);
+
+ OrthancPluginResourceType level = OrthancPluginResourceType_None;
+
+ if (!levelString.empty())
+ {
+ level = Plugins::Convert(StringToResourceType(levelString.c_str()));
+ }
+
+ void* driver = params_.callback(level,
+ patientId.empty() ? NULL : patientId.c_str(),
+ accessionNumber.empty() ? NULL : accessionNumber.c_str(),
+ studyInstanceUid.empty() ? NULL : studyInstanceUid.c_str(),
+ seriesInstanceUid.empty() ? NULL : seriesInstanceUid.c_str(),
+ sopInstanceUid.empty() ? NULL : sopInstanceUid.c_str(),
+ remoteAet.c_str(),
+ calledAet.c_str(),
+ targetAet.c_str(),
+ messageId);
+
+ if (driver == NULL)
+ {
+ LOG(ERROR) << "Plugin cannot create a driver for an incoming C-MOVE request";
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
+ unsigned int size = params_.getMoveSize(driver);
+
+ return new Driver(driver, size, params_.applyMove, params_.freeMove);
+ }
+ };
+
OrthancPlugins::OrthancPlugins()
@@ -457,10 +771,15 @@ namespace Orthanc
void OrthancPlugins::SetServerContext(ServerContext& context)
{
- pimpl_->context_ = &context;
+ pimpl_->SetServerContext(&context);
}
+ void OrthancPlugins::ResetServerContext()
+ {
+ pimpl_->SetServerContext(NULL);
+ }
+
OrthancPlugins::~OrthancPlugins()
{
@@ -738,26 +1057,67 @@ namespace Orthanc
}
- void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
+ void OrthancPlugins::RegisterFindCallback(const void* parameters)
{
- const _OrthancPluginDecodeImageCallback& p =
- *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
+ const _OrthancPluginFindCallback& p =
+ *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters);
+
+ boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
+
+ if (pimpl_->findCallback_ != NULL)
+ {
+ LOG(ERROR) << "Can only register one plugin to handle C-FIND requests";
+ throw OrthancException(ErrorCode_Plugin);
+ }
+ else
+ {
+ LOG(INFO) << "Plugin has registered a callback to handle C-FIND requests";
+ pimpl_->findCallback_ = p.callback;
+ }
+ }
- boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
- if (pimpl_->decodeImageCallback_ != NULL)
+ void OrthancPlugins::RegisterMoveCallback(const void* parameters)
+ {
+ // invokeServiceMutex_ is assumed to be locked
+
+ const _OrthancPluginMoveCallback& p =
+ *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters);
+
+ if (pimpl_->moveCallbacks_.callback != NULL)
{
- LOG(ERROR) << "Can only register one plugin to handle the decompression of DICOM images";
+ LOG(ERROR) << "Can only register one plugin to handle C-MOVE requests";
throw OrthancException(ErrorCode_Plugin);
}
else
{
- LOG(INFO) << "Plugin has registered a callback to decode DICOM images";
- pimpl_->decodeImageCallback_ = p.callback;
+ LOG(INFO) << "Plugin has registered a callback to handle C-MOVE requests";
+ pimpl_->moveCallbacks_ = p;
}
}
+ void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
+ {
+ const _OrthancPluginDecodeImageCallback& p =
+ *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
+
+ boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+
+ pimpl_->decodeImageCallbacks_.push_back(p.callback);
+ LOG(INFO) << "Plugin has registered a callback to decode DICOM images ("
+ << pimpl_->decodeImageCallbacks_.size() << " decoder(s) now active)";
+ }
+
+
+ void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters)
+ {
+ const _OrthancPluginIncomingHttpRequestFilter& p =
+ *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(parameters);
+
+ LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests";
+ pimpl_->incomingHttpRequestFilters_.push_back(p.callback);
+ }
void OrthancPlugins::AnswerBuffer(const void* parameters)
@@ -909,15 +1269,6 @@ namespace Orthanc
}
- void OrthancPlugins::CheckContextAvailable()
- {
- if (!pimpl_->context_)
- {
- throw OrthancException(ErrorCode_DatabaseNotInitialized);
- }
- }
-
-
void OrthancPlugins::GetDicomForInstance(const void* parameters)
{
const _OrthancPluginGetDicomForInstance& p =
@@ -925,8 +1276,10 @@ namespace Orthanc
std::string dicom;
- CheckContextAvailable();
- pimpl_->context_->ReadFile(dicom, p.instanceId, FileContentType_Dicom);
+ {
+ PImpl::ServerContextLock lock(*pimpl_);
+ lock.GetContext().ReadFile(dicom, p.instanceId, FileContentType_Dicom);
+ }
CopyToMemoryBuffer(*p.target, dicom);
}
@@ -941,17 +1294,21 @@ namespace Orthanc
LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
<< (afterPlugins ? " (after plugins)" : " (built-in API)");
- CheckContextAvailable();
- IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ IHttpHandler* handler;
+
+ {
+ PImpl::ServerContextLock lock(*pimpl_);
+ handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ }
std::string result;
- if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri))
+ if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri))
{
CopyToMemoryBuffer(*p.target, result);
}
else
{
- throw OrthancException(ErrorCode_BadRequest);
+ throw OrthancException(ErrorCode_UnknownResource);
}
}
@@ -968,20 +1325,26 @@ namespace Orthanc
for (uint32_t i = 0; i < p.headersCount; i++)
{
- headers[p.headersKeys[i]] = p.headersValues[i];
+ std::string name(p.headersKeys[i]);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ headers[name] = p.headersValues[i];
}
- CheckContextAvailable();
- IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
+ IHttpHandler* handler;
+ {
+ PImpl::ServerContextLock lock(*pimpl_);
+ handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
+ }
+
std::string result;
- if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri, headers))
+ if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, headers))
{
CopyToMemoryBuffer(*p.target, result);
}
else
{
- throw OrthancException(ErrorCode_BadRequest);
+ throw OrthancException(ErrorCode_UnknownResource);
}
}
@@ -996,19 +1359,23 @@ namespace Orthanc
LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
<< " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
- CheckContextAvailable();
- IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ IHttpHandler* handler;
+ {
+ PImpl::ServerContextLock lock(*pimpl_);
+ handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ }
+
std::string result;
if (isPost ?
- HttpToolbox::SimplePost(result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
- HttpToolbox::SimplePut (result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
+ HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
+ HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
{
CopyToMemoryBuffer(*p.target, result);
}
else
{
- throw OrthancException(ErrorCode_BadRequest);
+ throw OrthancException(ErrorCode_UnknownResource);
}
}
@@ -1020,12 +1387,16 @@ namespace Orthanc
LOG(INFO) << "Plugin making REST DELETE call on URI " << uri
<< (afterPlugins ? " (after plugins)" : " (built-in API)");
- CheckContextAvailable();
- IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ IHttpHandler* handler;
- if (!HttpToolbox::SimpleDelete(handler, RequestOrigin_Plugins, uri))
{
- throw OrthancException(ErrorCode_BadRequest);
+ PImpl::ServerContextLock lock(*pimpl_);
+ handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
+ }
+
+ if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri))
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
}
}
@@ -1076,14 +1447,16 @@ namespace Orthanc
throw OrthancException(ErrorCode_InternalError);
}
- CheckContextAvailable();
-
std::list<std::string> result;
- pimpl_->context_->GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
- if (result.size() == 1)
{
- *p.result = CopyString(result.front());
+ PImpl::ServerContextLock lock(*pimpl_);
+ lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
+ }
+
+ if (result.size() == 1)
+ {
+ *p.result = CopyString(result.front());
}
else
{
@@ -1309,8 +1682,7 @@ namespace Orthanc
case OrthancPluginImageFormat_Dicom:
{
- ParsedDicomFile dicom(p.data, p.size);
- image.reset(Decode(dicom, 0));
+ image.reset(Decode(p.data, p.size, 0));
break;
}
@@ -1328,12 +1700,15 @@ namespace Orthanc
std::string compressed;
+ ImageAccessor accessor;
+ accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
+
switch (p.imageFormat)
{
case OrthancPluginImageFormat_Png:
{
PngWriter writer;
- writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer);
+ writer.WriteToMemory(compressed, accessor);
break;
}
@@ -1341,7 +1716,7 @@ namespace Orthanc
{
JpegWriter writer;
writer.SetQuality(p.quality);
- writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer);
+ writer.WriteToMemory(compressed, accessor);
break;
}
@@ -1400,6 +1775,111 @@ namespace Orthanc
}
+ void OrthancPlugins::CallHttpClient2(const void* parameters)
+ {
+ const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters);
+
+ HttpClient client;
+ client.SetUrl(p.url);
+ client.SetConvertHeadersToLowerCase(false);
+
+ if (p.timeout != 0)
+ {
+ client.SetTimeout(p.timeout);
+ }
+
+ if (p.username != NULL &&
+ p.password != NULL)
+ {
+ client.SetCredentials(p.username, p.password);
+ }
+
+ if (p.certificateFile != NULL)
+ {
+ std::string certificate(p.certificateFile);
+ std::string key, password;
+
+ if (p.certificateKeyFile)
+ {
+ key.assign(p.certificateKeyFile);
+ }
+
+ if (p.certificateKeyPassword)
+ {
+ password.assign(p.certificateKeyPassword);
+ }
+
+ client.SetClientCertificate(certificate, key, password);
+ }
+
+ client.SetPkcs11Enabled(p.pkcs11 ? true : false);
+
+ for (uint32_t i = 0; i < p.headersCount; i++)
+ {
+ if (p.headersKeys[i] == NULL ||
+ p.headersValues[i] == NULL)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ client.AddHeader(p.headersKeys[i], p.headersValues[i]);
+ }
+
+ switch (p.method)
+ {
+ case OrthancPluginHttpMethod_Get:
+ client.SetMethod(HttpMethod_Get);
+ break;
+
+ case OrthancPluginHttpMethod_Post:
+ client.SetMethod(HttpMethod_Post);
+ client.GetBody().assign(p.body, p.bodySize);
+ break;
+
+ case OrthancPluginHttpMethod_Put:
+ client.SetMethod(HttpMethod_Put);
+ client.GetBody().assign(p.body, p.bodySize);
+ break;
+
+ case OrthancPluginHttpMethod_Delete:
+ client.SetMethod(HttpMethod_Delete);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ std::string body;
+ HttpClient::HttpHeaders headers;
+
+ client.ApplyAndThrowException(body, headers);
+
+ // The HTTP request has succeeded
+ *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
+
+ // Copy the HTTP headers of the answer, if the plugin requested them
+ if (p.answerHeaders != NULL)
+ {
+ Json::Value json = Json::objectValue;
+
+ for (HttpClient::HttpHeaders::const_iterator
+ it = headers.begin(); it != headers.end(); ++it)
+ {
+ json[it->first] = it->second;
+ }
+
+ std::string s = json.toStyledString();
+ CopyToMemoryBuffer(*p.answerHeaders, s);
+ }
+
+ // Copy the body of the answer if it makes sense
+ if (p.method != OrthancPluginHttpMethod_Delete)
+ {
+ CopyToMemoryBuffer(*p.answerBody, body);
+ }
+ }
+
+
void OrthancPlugins::ConvertPixelFormat(const void* parameters)
{
const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters);
@@ -1465,7 +1945,12 @@ namespace Orthanc
}
std::string content;
- pimpl_->context_->ReadFile(content, p.instanceId, FileContentType_Dicom);
+
+ {
+ PImpl::ServerContextLock lock(*pimpl_);
+ lock.GetContext().ReadFile(content, p.instanceId, FileContentType_Dicom);
+ }
+
dicom.reset(new ParsedDicomFile(content));
}
@@ -1563,8 +2048,7 @@ namespace Orthanc
case _OrthancPluginService_DecodeDicomImage:
{
- ParsedDicomFile dicom(p.constBuffer, p.bufferSize);
- result.reset(Decode(dicom, p.frameIndex));
+ result.reset(Decode(p.constBuffer, p.bufferSize, p.frameIndex));
break;
}
@@ -1670,7 +2154,7 @@ namespace Orthanc
{
p.target->group = entry->getKey().getGroup();
p.target->element = entry->getKey().getElement();
- p.target->vr = Plugins::Convert(entry->getEVR());
+ p.target->vr = Plugins::Convert(FromDcmtkBridge::Convert(entry->getEVR()));
p.target->minMultiplicity = static_cast<uint32_t>(entry->getVMMin());
p.target->maxMultiplicity = (entry->getVMMax() == DcmVariableVM ? 0 : static_cast<uint32_t>(entry->getVMMax()));
}
@@ -1678,25 +2162,11 @@ namespace Orthanc
- bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
- _OrthancPluginService service,
- const void* parameters)
+ bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin,
+ _OrthancPluginService service,
+ const void* parameters)
{
- VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
-
- if (service == _OrthancPluginService_DatabaseAnswer)
- {
- // This case solves a deadlock at (*) reported by James Webster
- // on 2015-10-27 that was present in versions of Orthanc <=
- // 0.9.4 and related to database plugins implementing a custom
- // index. The problem was that locking the database is already
- // ensured by the "ServerIndex" class if the invoked service is
- // "DatabaseAnswer".
- DatabaseAnswer(parameters);
- return true;
- }
-
- boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
+ // Services that can be run without mutual exclusion
switch (service)
{
@@ -1734,30 +2204,6 @@ namespace Orthanc
BufferCompression(parameters);
return true;
- case _OrthancPluginService_RegisterRestCallback:
- RegisterRestCallback(parameters, true);
- return true;
-
- case _OrthancPluginService_RegisterRestCallbackNoLock:
- RegisterRestCallback(parameters, false);
- return true;
-
- case _OrthancPluginService_RegisterOnStoredInstanceCallback:
- RegisterOnStoredInstanceCallback(parameters);
- return true;
-
- case _OrthancPluginService_RegisterOnChangeCallback:
- RegisterOnChangeCallback(parameters);
- return true;
-
- case _OrthancPluginService_RegisterWorklistCallback:
- RegisterWorklistCallback(parameters);
- return true;
-
- case _OrthancPluginService_RegisterDecodeImageCallback:
- RegisterDecodeImageCallback(parameters);
- return true;
-
case _OrthancPluginService_AnswerBuffer:
AnswerBuffer(parameters);
return true;
@@ -1857,32 +2303,6 @@ namespace Orthanc
AccessDicomInstance(service, parameters);
return true;
- case _OrthancPluginService_RegisterStorageArea:
- {
- LOG(INFO) << "Plugin has registered a custom storage area";
- const _OrthancPluginRegisterStorageArea& p =
- *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
-
- if (pimpl_->storageArea_.get() == NULL)
- {
- pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
- }
- else
- {
- throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
- }
-
- 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 =
@@ -1893,96 +2313,28 @@ namespace Orthanc
}
else
{
- CheckContextAvailable();
- pimpl_->context_->GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
+ PImpl::ServerContextLock lock(*pimpl_);
+ lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
return true;
}
}
case _OrthancPluginService_GetGlobalProperty:
{
- CheckContextAvailable();
-
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;
- }
- }
-
- case _OrthancPluginService_RegisterDatabaseBackend:
- {
- LOG(INFO) << "Plugin has registered a custom database back-end";
-
- const _OrthancPluginRegisterDatabaseBackend& p =
- *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
-
- if (pimpl_->database_.get() == NULL)
- {
- pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
- *p.backend, NULL, 0, p.payload));
- }
- else
- {
- throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
- }
-
- *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
-
- return true;
- }
-
- case _OrthancPluginService_RegisterDatabaseBackendV2:
- {
- LOG(INFO) << "Plugin has registered a custom database back-end";
- const _OrthancPluginRegisterDatabaseBackendV2& p =
- *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
+ std::string result;
- if (pimpl_->database_.get() == NULL)
- {
- pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
- *p.backend, p.extensions,
- p.extensionsSize, p.payload));
- }
- else
{
- throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+ PImpl::ServerContextLock lock(*pimpl_);
+ result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
}
- *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
-
+ *(p.result) = CopyString(result);
return true;
}
- case _OrthancPluginService_DatabaseAnswer:
- throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
-
case _OrthancPluginService_GetExpectedDatabaseVersion:
{
const _OrthancPluginReturnSingleValue& p =
@@ -2097,6 +2449,10 @@ namespace Orthanc
CallHttpClient(parameters);
return true;
+ case _OrthancPluginService_CallHttpClient2:
+ CallHttpClient2(parameters);
+ return true;
+
case _OrthancPluginService_ConvertPixelFormat:
ConvertPixelFormat(parameters);
return true;
@@ -2122,7 +2478,7 @@ namespace Orthanc
const _OrthancPluginStorageAreaCreate& p =
*reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters);
IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
- storage.Create(p.uuid, p.content, p.size, Plugins::Convert(p.type));
+ storage.Create(p.uuid, p.content, static_cast<size_t>(p.size), Plugins::Convert(p.type));
return true;
}
@@ -2146,79 +2502,71 @@ namespace Orthanc
return true;
}
- case _OrthancPluginService_RegisterErrorCode:
+ case _OrthancPluginService_DicomBufferToJson:
+ case _OrthancPluginService_DicomInstanceToJson:
+ ApplyDicomToJson(service, parameters);
+ return true;
+
+ case _OrthancPluginService_CreateDicom:
+ ApplyCreateDicom(service, parameters);
+ return true;
+
+ case _OrthancPluginService_WorklistAddAnswer:
{
- const _OrthancPluginRegisterErrorCode& p =
- *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
- *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
+ const _OrthancPluginWorklistAnswersOperation& p =
+ *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
+ reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
return true;
}
- case _OrthancPluginService_RegisterDictionaryTag:
+ case _OrthancPluginService_WorklistMarkIncomplete:
{
- const _OrthancPluginRegisterDictionaryTag& p =
- *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
- Plugins::Convert(p.vr), p.name,
- p.minMultiplicity, p.maxMultiplicity);
+ const _OrthancPluginWorklistAnswersOperation& p =
+ *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
+ reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
return true;
}
- case _OrthancPluginService_ReconstructMainDicomTags:
+ case _OrthancPluginService_WorklistIsMatch:
{
- const _OrthancPluginReconstructMainDicomTags& p =
- *reinterpret_cast<const _OrthancPluginReconstructMainDicomTags*>(parameters);
-
- if (pimpl_->database_.get() == NULL)
- {
- LOG(ERROR) << "The service ReconstructMainDicomTags can only be invoked by custom database plugins";
- throw OrthancException(ErrorCode_DatabasePlugin);
- }
-
- IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
- Toolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level));
-
+ const _OrthancPluginWorklistQueryOperation& p =
+ *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
+ *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
return true;
}
- case _OrthancPluginService_DicomBufferToJson:
- case _OrthancPluginService_DicomInstanceToJson:
- ApplyDicomToJson(service, parameters);
- return true;
-
- case _OrthancPluginService_CreateDicom:
- ApplyCreateDicom(service, parameters);
- return true;
-
- case _OrthancPluginService_WorklistAddAnswer:
+ case _OrthancPluginService_WorklistGetDicomQuery:
{
- const _OrthancPluginWorklistAnswersOperation& p =
- *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
- reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
+ const _OrthancPluginWorklistQueryOperation& p =
+ *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
+ reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
return true;
}
- case _OrthancPluginService_WorklistMarkIncomplete:
+ case _OrthancPluginService_FindAddAnswer:
{
- const _OrthancPluginWorklistAnswersOperation& p =
- *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
- reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
+ const _OrthancPluginFindOperation& p =
+ *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+ reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
return true;
}
- case _OrthancPluginService_WorklistIsMatch:
+ case _OrthancPluginService_FindMarkIncomplete:
{
- const _OrthancPluginWorklistQueryOperation& p =
- *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
- *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
+ const _OrthancPluginFindOperation& p =
+ *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+ reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
return true;
}
- case _OrthancPluginService_WorklistGetDicomQuery:
+ case _OrthancPluginService_GetFindQuerySize:
+ case _OrthancPluginService_GetFindQueryTag:
+ case _OrthancPluginService_GetFindQueryTagName:
+ case _OrthancPluginService_GetFindQueryValue:
{
- const _OrthancPluginWorklistQueryOperation& p =
- *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
- reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
+ const _OrthancPluginFindOperation& p =
+ *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+ reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
return true;
}
@@ -2237,6 +2585,200 @@ namespace Orthanc
ApplyLookupDictionary(parameters);
return true;
+ case _OrthancPluginService_GenerateUuid:
+ {
+ *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
+ CopyString(Toolbox::GenerateUuid());
+ return true;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+
+
+ bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin,
+ _OrthancPluginService service,
+ const void* parameters)
+ {
+ // Services that must be run in mutual exclusion. Guideline:
+ // Whenever "pimpl_" is directly accessed by the service, it
+ // should be listed here.
+
+ switch (service)
+ {
+ case _OrthancPluginService_RegisterRestCallback:
+ RegisterRestCallback(parameters, true);
+ return true;
+
+ case _OrthancPluginService_RegisterRestCallbackNoLock:
+ RegisterRestCallback(parameters, false);
+ return true;
+
+ case _OrthancPluginService_RegisterOnStoredInstanceCallback:
+ RegisterOnStoredInstanceCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterOnChangeCallback:
+ RegisterOnChangeCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterWorklistCallback:
+ RegisterWorklistCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterFindCallback:
+ RegisterFindCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterMoveCallback:
+ RegisterMoveCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterDecodeImageCallback:
+ RegisterDecodeImageCallback(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
+ RegisterIncomingHttpRequestFilter(parameters);
+ return true;
+
+ case _OrthancPluginService_RegisterStorageArea:
+ {
+ LOG(INFO) << "Plugin has registered a custom storage area";
+ const _OrthancPluginRegisterStorageArea& p =
+ *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
+
+ if (pimpl_->storageArea_.get() == NULL)
+ {
+ pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
+ }
+
+ 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_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;
+ }
+ }
+
+ case _OrthancPluginService_RegisterDatabaseBackend:
+ {
+ LOG(INFO) << "Plugin has registered a custom database back-end";
+
+ const _OrthancPluginRegisterDatabaseBackend& p =
+ *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
+
+ if (pimpl_->database_.get() == NULL)
+ {
+ pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
+ *p.backend, NULL, 0, p.payload));
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+ }
+
+ *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
+
+ return true;
+ }
+
+ case _OrthancPluginService_RegisterDatabaseBackendV2:
+ {
+ LOG(INFO) << "Plugin has registered a custom database back-end";
+
+ const _OrthancPluginRegisterDatabaseBackendV2& p =
+ *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
+
+ if (pimpl_->database_.get() == NULL)
+ {
+ pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
+ *p.backend, p.extensions,
+ p.extensionsSize, p.payload));
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+ }
+
+ *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
+
+ return true;
+ }
+
+ case _OrthancPluginService_DatabaseAnswer:
+ throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
+
+ case _OrthancPluginService_RegisterErrorCode:
+ {
+ const _OrthancPluginRegisterErrorCode& p =
+ *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
+ *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
+ return true;
+ }
+
+ case _OrthancPluginService_RegisterDictionaryTag:
+ {
+ const _OrthancPluginRegisterDictionaryTag& p =
+ *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
+ Plugins::Convert(p.vr), p.name,
+ p.minMultiplicity, p.maxMultiplicity);
+ return true;
+ }
+
+ case _OrthancPluginService_ReconstructMainDicomTags:
+ {
+ const _OrthancPluginReconstructMainDicomTags& p =
+ *reinterpret_cast<const _OrthancPluginReconstructMainDicomTags*>(parameters);
+
+ if (pimpl_->database_.get() == NULL)
+ {
+ LOG(ERROR) << "The service ReconstructMainDicomTags can only be invoked by custom database plugins";
+ throw OrthancException(ErrorCode_DatabasePlugin);
+ }
+
+ IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
+ Toolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level));
+
+ return true;
+ }
+
default:
{
// This service is unknown to the Orthanc plugin engine
@@ -2246,13 +2788,48 @@ namespace Orthanc
}
+
+ bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
+ _OrthancPluginService service,
+ const void* parameters)
+ {
+ VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
+
+ if (service == _OrthancPluginService_DatabaseAnswer)
+ {
+ // This case solves a deadlock at (*) reported by James Webster
+ // on 2015-10-27 that was present in versions of Orthanc <=
+ // 0.9.4 and related to database plugins implementing a custom
+ // index. The problem was that locking the database is already
+ // ensured by the "ServerIndex" class if the invoked service is
+ // "DatabaseAnswer".
+ DatabaseAnswer(parameters);
+ return true;
+ }
+
+ if (InvokeSafeService(plugin, service, parameters))
+ {
+ // The invoked service does not require locking
+ return true;
+ }
+ else
+ {
+ // The invoked service requires locking
+ boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
+ return InvokeProtectedService(plugin, service, parameters);
+ }
+ }
+
+
bool OrthancPlugins::HasStorageArea() const
{
+ boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
return pimpl_->storageArea_.get() != NULL;
}
bool OrthancPlugins::HasDatabaseBackend() const
{
+ boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
return pimpl_->database_.get() != NULL;
}
@@ -2376,28 +2953,133 @@ namespace Orthanc
}
- ImageAccessor* OrthancPlugins::Decode(ParsedDicomFile& dicom,
- unsigned int frame)
+ IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler()
+ {
+ if (HasFindHandler())
+ {
+ return new FindHandler(*this);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+
+ bool OrthancPlugins::HasFindHandler()
+ {
+ boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
+ return pimpl_->findCallback_ != NULL;
+ }
+
+
+ IMoveRequestHandler* OrthancPlugins::ConstructMoveRequestHandler()
{
+ if (HasMoveHandler())
+ {
+ return new MoveHandler(*this);
+ }
+ else
{
- boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
- if (pimpl_->decodeImageCallback_ != NULL)
+ return NULL;
+ }
+ }
+
+
+ bool OrthancPlugins::HasMoveHandler()
+ {
+ boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
+ return pimpl_->moveCallbacks_.callback != NULL;
+ }
+
+
+ bool OrthancPlugins::HasCustomImageDecoder()
+ {
+ boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+ return !pimpl_->decodeImageCallbacks_.empty();
+ }
+
+
+ ImageAccessor* OrthancPlugins::DecodeUnsafe(const void* dicom,
+ size_t size,
+ unsigned int frame)
+ {
+ boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+
+ for (PImpl::DecodeImageCallbacks::const_iterator
+ decoder = pimpl_->decodeImageCallbacks_.begin();
+ decoder != pimpl_->decodeImageCallbacks_.end(); ++decoder)
+ {
+ OrthancPluginImage* pluginImage = NULL;
+ if ((*decoder) (&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success &&
+ pluginImage != NULL)
{
- std::string s;
- dicom.SaveToMemoryBuffer(s);
+ return reinterpret_cast<ImageAccessor*>(pluginImage);
+ }
+ }
+
+ return NULL;
+ }
- OrthancPluginImage* pluginImage = NULL;
- if (pimpl_->decodeImageCallback_(&pluginImage, s.c_str(), s.size(), frame) == OrthancPluginErrorCode_Success &&
- pluginImage != NULL)
- {
- return reinterpret_cast<ImageAccessor*>(pluginImage);
- }
- LOG(WARNING) << "The custom image decoder cannot handle an image, fallback to the built-in decoder";
+ ImageAccessor* OrthancPlugins::Decode(const void* dicom,
+ size_t size,
+ unsigned int frame)
+ {
+ ImageAccessor* result = DecodeUnsafe(dicom, size, frame);
+
+ if (result != NULL)
+ {
+ return result;
+ }
+ else
+ {
+ LOG(INFO) << "The installed image decoding plugins cannot handle an image, fallback to the built-in decoder";
+ DefaultDicomImageDecoder defaultDecoder;
+ return defaultDecoder.Decode(dicom, size, frame);
+ }
+ }
+
+
+ bool OrthancPlugins::IsAllowed(HttpMethod method,
+ const char* uri,
+ const char* ip,
+ const char* username,
+ const IHttpHandler::Arguments& httpHeaders) const
+ {
+ std::vector<const char*> httpKeys(httpHeaders.size());
+ std::vector<const char*> httpValues(httpHeaders.size());
+
+ size_t pos = 0;
+ for (IHttpHandler::Arguments::const_iterator
+ it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++)
+ {
+ httpKeys[pos] = it->first.c_str();
+ httpValues[pos] = it->second.c_str();
+ }
+
+ OrthancPluginHttpMethod cMethod = Plugins::Convert(method);
+ const char** cHttpKeys = (httpKeys.size() == 0 ? NULL : &httpKeys[0]);
+ const char** cHttpValues = (httpValues.size() == 0 ? NULL : &httpValues[0]);
+
+ for (PImpl::IncomingHttpRequestFilters::const_iterator
+ filter = pimpl_->incomingHttpRequestFilters_.begin();
+ filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter)
+ {
+ int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(), cHttpKeys, cHttpValues);
+
+ if (allowed != 0 &&
+ allowed != 1)
+ {
+ throw OrthancException(ErrorCode_Plugin);
+ }
+
+ if (allowed == 0)
+ {
+ return false;
}
}
- DicomImageDecoder defaultDecoder;
- return defaultDecoder.Decode(dicom, frame);
+ return true;
}
}
diff --git a/Plugins/Engine/OrthancPlugins.h b/Plugins/Engine/OrthancPlugins.h
index e535a67..e191c04 100644
--- a/Plugins/Engine/OrthancPlugins.h
+++ b/Plugins/Engine/OrthancPlugins.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -49,9 +49,12 @@ namespace Orthanc
#include "../../Core/FileStorage/IStorageArea.h"
#include "../../Core/HttpServer/IHttpHandler.h"
+#include "../../Core/HttpServer/IIncomingHttpRequestFilter.h"
#include "../../OrthancServer/IServerListener.h"
#include "../../OrthancServer/IDicomImageDecoder.h"
#include "../../OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h"
+#include "../../OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h"
+#include "../../OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h"
#include "OrthancPluginDatabase.h"
#include "PluginsManager.h"
@@ -67,15 +70,18 @@ namespace Orthanc
public IPluginServiceProvider,
public IServerListener,
public IWorklistRequestHandlerFactory,
- public IDicomImageDecoder
+ public IDicomImageDecoder,
+ public IIncomingHttpRequestFilter,
+ public IFindRequestHandlerFactory,
+ public IMoveRequestHandlerFactory
{
private:
- struct PImpl;
+ class PImpl;
boost::shared_ptr<PImpl> pimpl_;
class WorklistHandler;
-
- void CheckContextAvailable();
+ class FindHandler;
+ class MoveHandler;
void RegisterRestCallback(const void* parameters,
bool lock);
@@ -86,8 +92,14 @@ namespace Orthanc
void RegisterWorklistCallback(const void* parameters);
+ void RegisterFindCallback(const void* parameters);
+
+ void RegisterMoveCallback(const void* parameters);
+
void RegisterDecodeImageCallback(const void* parameters);
+ void RegisterIncomingHttpRequestFilter(const void* parameters);
+
void AnswerBuffer(const void* parameters);
void Redirect(const void* parameters);
@@ -135,6 +147,8 @@ namespace Orthanc
void CallHttpClient(const void* parameters);
+ void CallHttpClient2(const void* parameters);
+
void GetFontInfo(const void* parameters);
void DrawText(const void* parameters);
@@ -163,6 +177,14 @@ namespace Orthanc
OrthancPluginResourceType resourceType,
const char* resource);
+ bool InvokeSafeService(SharedLibrary& plugin,
+ _OrthancPluginService service,
+ const void* parameters);
+
+ bool InvokeProtectedService(SharedLibrary& plugin,
+ _OrthancPluginService service,
+ const void* parameters);
+
public:
OrthancPlugins();
@@ -170,6 +192,8 @@ namespace Orthanc
void SetServerContext(ServerContext& context);
+ void ResetServerContext();
+
virtual bool Handle(HttpOutput& output,
RequestOrigin origin,
const char* remoteIp,
@@ -234,8 +258,32 @@ namespace Orthanc
virtual IWorklistRequestHandler* ConstructWorklistRequestHandler();
- virtual ImageAccessor* Decode(ParsedDicomFile& dicom,
+ bool HasCustomImageDecoder();
+
+ // Contrarily to "Decode()", this method does not fallback to the
+ // builtin image decoder, if no installed custom decoder can
+ // handle the image (it returns NULL in this case).
+ ImageAccessor* DecodeUnsafe(const void* dicom,
+ size_t size,
+ unsigned int frame);
+
+ virtual ImageAccessor* Decode(const void* dicom,
+ size_t size,
unsigned int frame);
+
+ virtual bool IsAllowed(HttpMethod method,
+ const char* uri,
+ const char* ip,
+ const char* username,
+ const IHttpHandler::Arguments& httpHeaders) const;
+
+ bool HasFindHandler();
+
+ virtual IFindRequestHandler* ConstructFindRequestHandler();
+
+ bool HasMoveHandler();
+
+ virtual IMoveRequestHandler* ConstructMoveRequestHandler();
};
}
diff --git a/Plugins/Engine/PluginsEnumerations.cpp b/Plugins/Engine/PluginsEnumerations.cpp
index 7295a33..2d2a47c 100644
--- a/Plugins/Engine/PluginsEnumerations.cpp
+++ b/Plugins/Engine/PluginsEnumerations.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -304,185 +304,223 @@ namespace Orthanc
}
-#if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0
- DcmEVR Convert(OrthancPluginValueRepresentation vr)
+ OrthancPluginHttpMethod Convert(HttpMethod method)
+ {
+ switch (method)
+ {
+ case HttpMethod_Get:
+ return OrthancPluginHttpMethod_Get;
+
+ case HttpMethod_Post:
+ return OrthancPluginHttpMethod_Post;
+
+ case HttpMethod_Put:
+ return OrthancPluginHttpMethod_Put;
+
+ case HttpMethod_Delete:
+ return OrthancPluginHttpMethod_Delete;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ ValueRepresentation Convert(OrthancPluginValueRepresentation vr)
{
switch (vr)
{
case OrthancPluginValueRepresentation_AE:
- return EVR_AE;
+ return ValueRepresentation_ApplicationEntity;
case OrthancPluginValueRepresentation_AS:
- return EVR_AS;
+ return ValueRepresentation_AgeString;
case OrthancPluginValueRepresentation_AT:
- return EVR_AT;
+ return ValueRepresentation_AttributeTag;
case OrthancPluginValueRepresentation_CS:
- return EVR_CS;
+ return ValueRepresentation_CodeString;
case OrthancPluginValueRepresentation_DA:
- return EVR_DA;
+ return ValueRepresentation_Date;
case OrthancPluginValueRepresentation_DS:
- return EVR_DS;
+ return ValueRepresentation_DecimalString;
case OrthancPluginValueRepresentation_DT:
- return EVR_DT;
+ return ValueRepresentation_DateTime;
case OrthancPluginValueRepresentation_FD:
- return EVR_FD;
+ return ValueRepresentation_FloatingPointDouble;
case OrthancPluginValueRepresentation_FL:
- return EVR_FL;
+ return ValueRepresentation_FloatingPointSingle;
case OrthancPluginValueRepresentation_IS:
- return EVR_IS;
+ return ValueRepresentation_IntegerString;
case OrthancPluginValueRepresentation_LO:
- return EVR_LO;
+ return ValueRepresentation_LongString;
case OrthancPluginValueRepresentation_LT:
- return EVR_LT;
+ return ValueRepresentation_LongText;
case OrthancPluginValueRepresentation_OB:
- return EVR_OB;
+ return ValueRepresentation_OtherByte;
case OrthancPluginValueRepresentation_OF:
- return EVR_OF;
+ return ValueRepresentation_OtherFloat;
case OrthancPluginValueRepresentation_OW:
- return EVR_OW;
+ return ValueRepresentation_OtherWord;
case OrthancPluginValueRepresentation_PN:
- return EVR_PN;
+ return ValueRepresentation_PersonName;
case OrthancPluginValueRepresentation_SH:
- return EVR_SH;
+ return ValueRepresentation_ShortString;
case OrthancPluginValueRepresentation_SL:
- return EVR_SL;
+ return ValueRepresentation_SignedLong;
case OrthancPluginValueRepresentation_SQ:
- return EVR_SQ;
+ return ValueRepresentation_Sequence;
case OrthancPluginValueRepresentation_SS:
- return EVR_SS;
+ return ValueRepresentation_SignedShort;
case OrthancPluginValueRepresentation_ST:
- return EVR_ST;
+ return ValueRepresentation_ShortText;
case OrthancPluginValueRepresentation_TM:
- return EVR_TM;
+ return ValueRepresentation_Time;
case OrthancPluginValueRepresentation_UI:
- return EVR_UI;
+ return ValueRepresentation_UniqueIdentifier;
case OrthancPluginValueRepresentation_UL:
- return EVR_UL;
+ return ValueRepresentation_UnsignedLong;
case OrthancPluginValueRepresentation_UN:
- return EVR_UN;
+ return ValueRepresentation_Unknown;
case OrthancPluginValueRepresentation_US:
- return EVR_US;
+ return ValueRepresentation_UnsignedShort;
case OrthancPluginValueRepresentation_UT:
- return EVR_UT;
+ return ValueRepresentation_UnlimitedText;
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
+
+ /*
+ Not supported as of DCMTK 3.6.0:
+ return ValueRepresentation_OtherDouble
+ return ValueRepresentation_OtherLong
+ return ValueRepresentation_UniversalResource
+ return ValueRepresentation_UnlimitedCharacters
+ */
}
}
- OrthancPluginValueRepresentation Convert(DcmEVR vr)
+ OrthancPluginValueRepresentation Convert(ValueRepresentation vr)
{
switch (vr)
{
- case EVR_AE:
+ case ValueRepresentation_ApplicationEntity:
return OrthancPluginValueRepresentation_AE;
- case EVR_AS:
+ case ValueRepresentation_AgeString:
return OrthancPluginValueRepresentation_AS;
- case EVR_AT:
+ case ValueRepresentation_AttributeTag:
return OrthancPluginValueRepresentation_AT;
- case EVR_CS:
+ case ValueRepresentation_CodeString:
return OrthancPluginValueRepresentation_CS;
- case EVR_DA:
+ case ValueRepresentation_Date:
return OrthancPluginValueRepresentation_DA;
- case EVR_DS:
+ case ValueRepresentation_DecimalString:
return OrthancPluginValueRepresentation_DS;
- case EVR_DT:
+ case ValueRepresentation_DateTime:
return OrthancPluginValueRepresentation_DT;
- case EVR_FD:
+ case ValueRepresentation_FloatingPointDouble:
return OrthancPluginValueRepresentation_FD;
- case EVR_FL:
+ case ValueRepresentation_FloatingPointSingle:
return OrthancPluginValueRepresentation_FL;
- case EVR_IS:
+ case ValueRepresentation_IntegerString:
return OrthancPluginValueRepresentation_IS;
- case EVR_LO:
+ case ValueRepresentation_LongString:
return OrthancPluginValueRepresentation_LO;
- case EVR_LT:
+ case ValueRepresentation_LongText:
return OrthancPluginValueRepresentation_LT;
- case EVR_OB:
+ case ValueRepresentation_OtherByte:
return OrthancPluginValueRepresentation_OB;
- case EVR_OF:
+ case ValueRepresentation_OtherFloat:
return OrthancPluginValueRepresentation_OF;
- case EVR_OW:
+ case ValueRepresentation_OtherWord:
return OrthancPluginValueRepresentation_OW;
- case EVR_PN:
+ case ValueRepresentation_PersonName:
return OrthancPluginValueRepresentation_PN;
- case EVR_SH:
+ case ValueRepresentation_ShortString:
return OrthancPluginValueRepresentation_SH;
- case EVR_SL:
+ case ValueRepresentation_SignedLong:
return OrthancPluginValueRepresentation_SL;
- case EVR_SQ:
+ case ValueRepresentation_Sequence:
return OrthancPluginValueRepresentation_SQ;
- case EVR_SS:
+ case ValueRepresentation_SignedShort:
return OrthancPluginValueRepresentation_SS;
- case EVR_ST:
+ case ValueRepresentation_ShortText:
return OrthancPluginValueRepresentation_ST;
- case EVR_TM:
+ case ValueRepresentation_Time:
return OrthancPluginValueRepresentation_TM;
- case EVR_UI:
+ case ValueRepresentation_UniqueIdentifier:
return OrthancPluginValueRepresentation_UI;
- case EVR_UL:
+ case ValueRepresentation_UnsignedLong:
return OrthancPluginValueRepresentation_UL;
- case EVR_US:
+ case ValueRepresentation_UnsignedShort:
return OrthancPluginValueRepresentation_US;
- case EVR_UT:
+ case ValueRepresentation_UnlimitedText:
return OrthancPluginValueRepresentation_UT;
- case EVR_UN:
- default:
+ case ValueRepresentation_Unknown:
return OrthancPluginValueRepresentation_UN; // Unknown
+
+ // These VR are not supported as of DCMTK 3.6.0, so they are
+ // mapped to "UN" (unknown) VR in the plugins
+ case ValueRepresentation_OtherDouble:
+ case ValueRepresentation_OtherLong:
+ case ValueRepresentation_UniversalResource:
+ case ValueRepresentation_UnlimitedCharacters:
+ return OrthancPluginValueRepresentation_UN;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
-#endif
}
}
diff --git a/Plugins/Engine/PluginsEnumerations.h b/Plugins/Engine/PluginsEnumerations.h
index f5ffd91..bb42773 100644
--- a/Plugins/Engine/PluginsEnumerations.h
+++ b/Plugins/Engine/PluginsEnumerations.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -37,10 +37,6 @@
#include "../Include/orthanc/OrthancCPlugin.h"
#include "../../OrthancServer/ServerEnumerations.h"
-#if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0
-#include <dcmtk/dcmdata/dcvr.h>
-#endif
-
namespace Orthanc
{
namespace Plugins
@@ -67,11 +63,11 @@ namespace Orthanc
OrthancPluginInstanceOrigin Convert(RequestOrigin origin);
-#if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0
- DcmEVR Convert(OrthancPluginValueRepresentation vr);
+ OrthancPluginHttpMethod Convert(HttpMethod method);
- OrthancPluginValueRepresentation Convert(DcmEVR vr);
-#endif
+ ValueRepresentation Convert(OrthancPluginValueRepresentation vr);
+
+ OrthancPluginValueRepresentation Convert(ValueRepresentation vr);
}
}
diff --git a/Plugins/Engine/PluginsErrorDictionary.cpp b/Plugins/Engine/PluginsErrorDictionary.cpp
index 73665be..aba9a86 100644
--- a/Plugins/Engine/PluginsErrorDictionary.cpp
+++ b/Plugins/Engine/PluginsErrorDictionary.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Engine/PluginsErrorDictionary.h b/Plugins/Engine/PluginsErrorDictionary.h
index d848b77..018156d 100644
--- a/Plugins/Engine/PluginsErrorDictionary.h
+++ b/Plugins/Engine/PluginsErrorDictionary.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Engine/PluginsManager.cpp b/Plugins/Engine/PluginsManager.cpp
index 83e19bf..7a8659e 100644
--- a/Plugins/Engine/PluginsManager.cpp
+++ b/Plugins/Engine/PluginsManager.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -48,7 +48,7 @@
#ifdef WIN32
#define PLUGIN_EXTENSION ".dll"
-#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
#define PLUGIN_EXTENSION ".so"
#elif defined(__APPLE__) && defined(__MACH__)
#define PLUGIN_EXTENSION ".dylib"
@@ -191,7 +191,11 @@ namespace Orthanc
catch (OrthancException& e)
{
// This service provider has failed
- LOG(ERROR) << "Exception while invoking plugin service " << service << ": " << e.What();
+ if (e.GetErrorCode() != ErrorCode_UnknownResource) // This error code is valid in plugins
+ {
+ LOG(ERROR) << "Exception while invoking plugin service " << service << ": " << e.What();
+ }
+
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
}
diff --git a/Plugins/Engine/PluginsManager.h b/Plugins/Engine/PluginsManager.h
index 11dd8e1..de9f45f 100644
--- a/Plugins/Engine/PluginsManager.h
+++ b/Plugins/Engine/PluginsManager.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Engine/SharedLibrary.cpp b/Plugins/Engine/SharedLibrary.cpp
index a3f590e..7fefc49 100644
--- a/Plugins/Engine/SharedLibrary.cpp
+++ b/Plugins/Engine/SharedLibrary.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -45,7 +45,7 @@
#if defined(_WIN32)
#include <windows.h>
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
#include <dlfcn.h>
#else
#error Support your platform here
@@ -65,7 +65,7 @@ namespace Orthanc
throw OrthancException(ErrorCode_SharedLibrary);
}
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
handle_ = ::dlopen(path_.c_str(), RTLD_NOW);
if (handle_ == NULL)
{
@@ -91,7 +91,7 @@ namespace Orthanc
{
#if defined(_WIN32)
::FreeLibrary((HMODULE)handle_);
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
::dlclose(handle_);
#else
#error Support your platform here
@@ -109,7 +109,7 @@ namespace Orthanc
#if defined(_WIN32)
return ::GetProcAddress((HMODULE)handle_, name.c_str());
-#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
return ::dlsym(handle_, name.c_str());
#else
#error Support your platform here
diff --git a/Plugins/Engine/SharedLibrary.h b/Plugins/Engine/SharedLibrary.h
index 8523b20..5a7913a 100644
--- a/Plugins/Engine/SharedLibrary.h
+++ b/Plugins/Engine/SharedLibrary.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h
index b11078a..36cc8b7 100644
--- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h
+++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h
@@ -4,7 +4,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Include/orthanc/OrthancCPlugin.h b/Plugins/Include/orthanc/OrthancCPlugin.h
index 36fc61b..cb653d9 100644
--- a/Plugins/Include/orthanc/OrthancCPlugin.h
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h
@@ -18,8 +18,11 @@
* - Possibly register its callback for changes to the DICOM store using ::OrthancPluginRegisterOnChangeCallback().
* - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea().
* - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV2().
+ * - Possibly register a handler for C-Find SCP using OrthancPluginRegisterFindCallback().
* - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback().
+ * - Possibly register a handler for C-Move SCP using OrthancPluginRegisterMoveCallback().
* - Possibly register a custom decoder for DICOM images using OrthancPluginRegisterDecodeImageCallback().
+ * - Possibly register a callback to filter incoming HTTP requests using OrthancPluginRegisterIncomingHttpRequestFilter().
* -# <tt>void OrthancPluginFinalize()</tt>:
* This function is invoked by Orthanc during its shutdown. The plugin
* must free all its memory.
@@ -51,8 +54,8 @@
* @defgroup Callbacks Callbacks
* @brief Functions to register and manage callbacks by the plugins.
*
- * @defgroup Worklists Worklists
- * @brief Functions to register and manage worklists.
+ * @defgroup DicomCallbaks DicomCallbaks
+ * @brief Functions to register and manage DICOM callbacks (worklists, C-Find, C-MOVE).
*
* @defgroup Orthanc Orthanc
* @brief Functions to access the content of the Orthanc server.
@@ -69,7 +72,7 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -113,7 +116,7 @@
#endif
#define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1
-#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 0
+#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 1
#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 0
@@ -238,8 +241,8 @@ extern "C"
OrthancPluginErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */,
OrthancPluginErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */,
OrthancPluginErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */,
- OrthancPluginErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is already in use */,
- OrthancPluginErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is already in use */,
+ OrthancPluginErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use */,
+ OrthancPluginErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is privileged or already in use */,
OrthancPluginErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */,
OrthancPluginErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */,
OrthancPluginErrorCode_PathToExecutable = 2007 /*!< Unable to get the path to the executable */,
@@ -277,6 +280,7 @@ extern "C"
OrthancPluginErrorCode_SslDisabled = 2039 /*!< Orthanc has been built without SSL support */,
OrthancPluginErrorCode_CannotOrderSlices = 2040 /*!< Unable to order the slices of the series */,
OrthancPluginErrorCode_NoWorklistHandler = 2041 /*!< No request handler factory for DICOM C-Find Modality SCP */,
+ OrthancPluginErrorCode_AlreadyExistingTag = 2042 /*!< Cannot override the value of a tag that already exists */,
_OrthancPluginErrorCode_INTERNAL = 0x7fffffff
} OrthancPluginErrorCode;
@@ -401,6 +405,8 @@ extern "C"
_OrthancPluginService_ComputeMd5 = 24,
_OrthancPluginService_ComputeSha1 = 25,
_OrthancPluginService_LookupDictionary = 26,
+ _OrthancPluginService_CallHttpClient2 = 27,
+ _OrthancPluginService_GenerateUuid = 28,
/* Registration of callbacks */
_OrthancPluginService_RegisterRestCallback = 1000,
@@ -410,6 +416,9 @@ extern "C"
_OrthancPluginService_RegisterRestCallbackNoLock = 1004,
_OrthancPluginService_RegisterWorklistCallback = 1005,
_OrthancPluginService_RegisterDecodeImageCallback = 1006,
+ _OrthancPluginService_RegisterIncomingHttpRequestFilter = 1007,
+ _OrthancPluginService_RegisterFindCallback = 1008,
+ _OrthancPluginService_RegisterMoveCallback = 1009,
/* Sending answers to REST calls */
_OrthancPluginService_AnswerBuffer = 2000,
@@ -479,11 +488,17 @@ extern "C"
_OrthancPluginService_CreateImageAccessor = 6013,
_OrthancPluginService_DecodeDicomImage = 6014,
- /* Primitives for handling worklists */
+ /* Primitives for handling C-Find, C-Move and worklists */
_OrthancPluginService_WorklistAddAnswer = 7000,
_OrthancPluginService_WorklistMarkIncomplete = 7001,
_OrthancPluginService_WorklistIsMatch = 7002,
_OrthancPluginService_WorklistGetDicomQuery = 7003,
+ _OrthancPluginService_FindAddAnswer = 7004,
+ _OrthancPluginService_FindMarkIncomplete = 7005,
+ _OrthancPluginService_GetFindQuerySize = 7006,
+ _OrthancPluginService_GetFindQueryTag = 7007,
+ _OrthancPluginService_GetFindQueryTagName = 7008,
+ _OrthancPluginService_GetFindQueryValue = 7009,
_OrthancPluginService_INTERNAL = 0x7fffffff
} _OrthancPluginService;
@@ -807,22 +822,38 @@ extern "C"
/**
- * @brief Opaque structure to an object that represents a C-Find query.
- * @ingroup Worklists
+ * @brief Opaque structure to an object that represents a C-Find query for worklists.
+ * @ingroup DicomCallbacks
**/
typedef struct _OrthancPluginWorklistQuery_t OrthancPluginWorklistQuery;
/**
- * @brief Opaque structure to an object that represents the answers to a C-Find query.
- * @ingroup Worklists
+ * @brief Opaque structure to an object that represents the answers to a C-Find query for worklists.
+ * @ingroup DicomCallbacks
**/
typedef struct _OrthancPluginWorklistAnswers_t OrthancPluginWorklistAnswers;
/**
+ * @brief Opaque structure to an object that represents a C-Find query.
+ * @ingroup DicomCallbacks
+ **/
+ typedef struct _OrthancPluginFindQuery_t OrthancPluginFindQuery;
+
+
+
+ /**
+ * @brief Opaque structure to an object that represents the answers to a C-Find query for worklists.
+ * @ingroup DicomCallbacks
+ **/
+ typedef struct _OrthancPluginFindAnswers_t OrthancPluginFindAnswers;
+
+
+
+ /**
* @brief Signature of a callback function that answers to a REST request.
* @ingroup Callbacks
**/
@@ -930,27 +961,165 @@ extern "C"
/**
- * @brief Callback to handle the C-Find SCP requests received by Orthanc.
+ * @brief Callback to handle the C-Find SCP requests for worklists.
*
* Signature of a callback function that is triggered when Orthanc
* receives a C-Find SCP request against modality worklists.
*
* @param answers The target structure where answers must be stored.
* @param query The worklist query.
- * @param remoteAet The Application Entity Title (AET) of the modality from which the request originates.
+ * @param issuerAet The Application Entity Title (AET) of the modality from which the request originates.
* @param calledAet The Application Entity Title (AET) of the modality that is called by the request.
* @return 0 if success, other value if error.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
**/
typedef OrthancPluginErrorCode (*OrthancPluginWorklistCallback) (
OrthancPluginWorklistAnswers* answers,
const OrthancPluginWorklistQuery* query,
- const char* remoteAet,
+ const char* issuerAet,
const char* calledAet);
/**
+ * @brief Callback to filter incoming HTTP requests received by Orthanc.
+ *
+ * Signature of a callback function that is triggered whenever
+ * Orthanc receives an HTTP/REST request, and that answers whether
+ * this request should be allowed. If the callback returns "0"
+ * ("false"), the server answers with HTTP status code 403
+ * (Forbidden).
+ *
+ * @param method The HTTP method used by the request.
+ * @param uri The URI of interest.
+ * @param ip The IP address of the HTTP client.
+ * @param headersCount The number of HTTP headers.
+ * @param headersKeys The keys of the HTTP headers (always converted to low-case).
+ * @param headersValues The values of the HTTP headers.
+ * @return 0 if forbidden access, 1 if allowed access, -1 if error.
+ * @ingroup Callback
+ **/
+ typedef int32_t (*OrthancPluginIncomingHttpRequestFilter) (
+ OrthancPluginHttpMethod method,
+ const char* uri,
+ const char* ip,
+ uint32_t headersCount,
+ const char* const* headersKeys,
+ const char* const* headersValues);
+
+
+
+ /**
+ * @brief Callback to handle incoming C-Find SCP requests.
+ *
+ * Signature of a callback function that is triggered whenever
+ * Orthanc receives a C-Find SCP request not concerning modality
+ * worklists.
+ *
+ * @param answers The target structure where answers must be stored.
+ * @param query The worklist query.
+ * @param issuerAet The Application Entity Title (AET) of the modality from which the request originates.
+ * @param calledAet The Application Entity Title (AET) of the modality that is called by the request.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ typedef OrthancPluginErrorCode (*OrthancPluginFindCallback) (
+ OrthancPluginFindAnswers* answers,
+ const OrthancPluginFindQuery* query,
+ const char* issuerAet,
+ const char* calledAet);
+
+
+
+ /**
+ * @brief Callback to handle incoming C-Move SCP requests.
+ *
+ * Signature of a callback function that is triggered whenever
+ * Orthanc receives a C-Move SCP request. The callback receives the
+ * type of the resource of interest (study, series, instance...)
+ * together with the DICOM tags containing its identifiers. In turn,
+ * the plugin must create a driver object that will be responsible
+ * for driving the successive move suboperations.
+ *
+ * @param resourceType The type of the resource of interest. Note
+ * that this might be set to ResourceType_None if the
+ * QueryRetrieveLevel (0008,0052) tag was not provided by the
+ * issuer.
+ * @param patientId Content of the PatientID (0x0010, 0x0020) tag of the resource of interest. Might be NULL.
+ * @param accessionNumber Content of the AccessionNumber (0x0008, 0x0050) tag. Might be NULL.
+ * @param studyInstanceUid Content of the StudyInstanceUID (0x0020, 0x000d) tag. Might be NULL.
+ * @param seriesInstanceUid Content of the SeriesInstanceUID (0x0020, 0x000e) tag. Might be NULL.
+ * @param sopInstanceUid Content of the SOPInstanceUID (0x0008, 0x0018) tag. Might be NULL.
+ * @param issuerAet The Application Entity Title (AET) of the
+ * modality from which the request originates.
+ * @param sourceAet The Application Entity Title (AET) of the
+ * modality that should send its DICOM files to another modality.
+ * @param targetAet The Application Entity Title (AET) of the
+ * modality that should receive the DICOM files.
+ *
+ * @return The NULL value if the plugin cannot deal with this query,
+ * or a pointer to the driver object that is responsible for
+ * handling the successive move suboperations.
+ *
+ * @note If targetAet equals sourceAet, this is actually a query/retrieve operation.
+ * @ingroup DicomCallbacks
+ **/
+ typedef void* (*OrthancPluginMoveCallback) (
+ OrthancPluginResourceType resourceType,
+ const char* patientId,
+ const char* accessionNumber,
+ const char* studyInstanceUid,
+ const char* seriesInstanceUid,
+ const char* sopInstanceUid,
+ const char* issuerAet,
+ const char* sourceAet,
+ const char* targetAet,
+ uint16_t moveOriginatorId);
+
+
+ /**
+ * @brief Callback to read the size of a C-Move driver.
+ *
+ * Signature of a callback function that returns the number of
+ * C-Move suboperations that are to be achieved by the given C-Move
+ * driver. This driver is the return value of a previous call to the
+ * OrthancPluginMoveCallback() callback.
+ *
+ * @param moveDriver The C-Move driver of interest.
+ * @return The number of suboperations.
+ **/
+ typedef uint32_t (*OrthancPluginGetMoveSize) (void* moveDriver);
+
+
+ /**
+ * @brief Callback to apply one C-Move suboperation.
+ *
+ * Signature of a callback function that applies the next C-Move
+ * suboperation that os to be achieved by the given C-Move
+ * driver. This driver is the return value of a previous call to the
+ * OrthancPluginMoveCallback() callback.
+ *
+ * @param moveDriver The C-Move driver of interest.
+ * @return 0 if success, or the error code if failure.
+ **/
+ typedef OrthancPluginErrorCode (*OrthancPluginApplyMove) (void* moveDriver);
+
+
+ /**
+ * @brief Callback to free one C-Move driver.
+ *
+ * Signature of a callback function that releases the resources
+ * allocated by the given C-Move driver. This driver is the return
+ * value of a previous call to the OrthancPluginMoveCallback()
+ * callback.
+ *
+ * @param moveDriver The C-Move driver of interest.
+ **/
+ typedef void (*OrthancPluginFreeMove) (void* moveDriver);
+
+
+
+ /**
* @brief Data structure that contains information about the Orthanc core.
**/
typedef struct _OrthancPluginContext_t
@@ -1400,6 +1569,7 @@ extern "C"
* @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
* @param uri The URI in the built-in Orthanc API.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiGetAfterPlugins
* @ingroup Orthanc
**/
@@ -1429,6 +1599,7 @@ extern "C"
* @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
* @param uri The URI in the built-in Orthanc API.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiGet
* @ingroup Orthanc
**/
@@ -1465,6 +1636,7 @@ extern "C"
* @param body The body of the POST request.
* @param bodySize The size of the body.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiPostAfterPlugins
* @ingroup Orthanc
**/
@@ -1499,6 +1671,7 @@ extern "C"
* @param body The body of the POST request.
* @param bodySize The size of the body.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiPost
* @ingroup Orthanc
**/
@@ -1527,6 +1700,7 @@ extern "C"
* @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, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiDeleteAfterPlugins
* @ingroup Orthanc
**/
@@ -1549,6 +1723,7 @@ extern "C"
* @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, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiDelete
* @ingroup Orthanc
**/
@@ -1573,6 +1748,7 @@ extern "C"
* @param body The body of the PUT request.
* @param bodySize The size of the body.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiPutAfterPlugins
* @ingroup Orthanc
**/
@@ -1608,6 +1784,7 @@ extern "C"
* @param body The body of the PUT request.
* @param bodySize The size of the body.
* @return 0 if success, or the error code if failure.
+ * @note If the resource is not existing (error 404), the error code will be OrthancPluginErrorCode_UnknownResource.
* @see OrthancPluginRestApiPut
* @ingroup Orthanc
**/
@@ -2397,7 +2574,7 @@ extern "C"
* Orthanc, you should make these calls in a separate thread (with
* the events passing through a message queue). Otherwise, this
* could result in deadlocks in the presence of other plugins or Lua
- * script.
+ * scripts.
*
* @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
* @param callback The callback function.
@@ -3991,7 +4168,7 @@ extern "C"
{
char** result;
const char* instanceId;
- const char* buffer;
+ const void* buffer;
uint32_t size;
OrthancPluginDicomToJsonFormat format;
OrthancPluginDicomToJsonFlags flags;
@@ -4020,7 +4197,7 @@ extern "C"
**/
ORTHANC_PLUGIN_INLINE char* OrthancPluginDicomBufferToJson(
OrthancPluginContext* context,
- const char* buffer,
+ const void* buffer,
uint32_t size,
OrthancPluginDicomToJsonFormat format,
OrthancPluginDicomToJsonFlags flags,
@@ -4117,8 +4294,8 @@ extern "C"
* @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer().
* @param uri The URI in the built-in Orthanc API.
* @param headersCount The number of HTTP headers.
- * @param headersKeys Array containing the keys of the HTTP headers.
- * @param headersValues Array containing the values of the HTTP headers.
+ * @param headersKeys Array containing the keys of the HTTP headers (can be <tt>NULL</tt> if no header).
+ * @param headersValues Array containing the values of the HTTP headers (can be <tt>NULL</tt> if no header).
* @param afterPlugins If 0, the built-in API of Orthanc is used.
* If 1, the API is tainted by the plugins.
* @return 0 if success, or the error code if failure.
@@ -4161,7 +4338,7 @@ extern "C"
* @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
* @param callback The callback.
* @return 0 if success, other value if error.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
**/
ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterWorklistCallback(
OrthancPluginContext* context,
@@ -4196,7 +4373,8 @@ extern "C"
* @param dicom The worklist to answer, encoded as a DICOM file.
* @param size The size of the DICOM file.
* @return 0 if success, other value if error.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
+ * @see OrthancPluginCreateDicom()
**/
ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginWorklistAddAnswer(
OrthancPluginContext* context,
@@ -4226,7 +4404,7 @@ extern "C"
* @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
* @param answers The set of answers.
* @return 0 if success, other value if error.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
**/
ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginWorklistMarkIncomplete(
OrthancPluginContext* context,
@@ -4264,7 +4442,7 @@ extern "C"
* @param dicom The worklist to answer, encoded as a DICOM file.
* @param size The size of the DICOM file.
* @return 1 if the worklist matches the query, 0 otherwise.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
**/
ORTHANC_PLUGIN_INLINE int32_t OrthancPluginWorklistIsMatch(
OrthancPluginContext* context,
@@ -4303,7 +4481,7 @@ extern "C"
* @param target Memory buffer where to store the DICOM file. It must be freed with OrthancPluginFreeMemoryBuffer().
* @param query The worklist query, as received by the callback.
* @return 0 if success, other value if error.
- * @ingroup Worklists
+ * @ingroup DicomCallbacks
**/
ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginWorklistGetDicomQuery(
OrthancPluginContext* context,
@@ -4731,6 +4909,453 @@ extern "C"
}
+ typedef struct
+ {
+ OrthancPluginIncomingHttpRequestFilter callback;
+ } _OrthancPluginIncomingHttpRequestFilter;
+
+ /**
+ * @brief Register a callback to filter incoming HTTP requests.
+ *
+ * This function registers a custom callback to filter incoming HTTP/REST
+ * requests received by the HTTP server of Orthanc.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param callback The callback.
+ * @return 0 if success, other value if error.
+ * @ingroup Callbacks
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterIncomingHttpRequestFilter(
+ OrthancPluginContext* context,
+ OrthancPluginIncomingHttpRequestFilter callback)
+ {
+ _OrthancPluginIncomingHttpRequestFilter params;
+ params.callback = callback;
+
+ return context->InvokeService(context, _OrthancPluginService_RegisterIncomingHttpRequestFilter, ¶ms);
+ }
+
+
+
+ typedef struct
+ {
+ OrthancPluginMemoryBuffer* answerBody;
+ OrthancPluginMemoryBuffer* answerHeaders;
+ uint16_t* httpStatus;
+ OrthancPluginHttpMethod method;
+ const char* url;
+ uint32_t headersCount;
+ const char* const* headersKeys;
+ const char* const* headersValues;
+ const char* body;
+ uint32_t bodySize;
+ const char* username;
+ const char* password;
+ uint32_t timeout;
+ const char* certificateFile;
+ const char* certificateKeyFile;
+ const char* certificateKeyPassword;
+ uint8_t pkcs11;
+ } _OrthancPluginCallHttpClient2;
+
+
+
+ /**
+ * @brief Issue a HTTP call with full flexibility.
+ *
+ * Make a HTTP call to the given URL. The result to the query is
+ * stored into a newly allocated memory buffer. The HTTP request
+ * will be done accordingly to the global configuration of Orthanc
+ * (in particular, the options "HttpProxy", "HttpTimeout",
+ * "HttpsVerifyPeers", "HttpsCACertificates", and "Pkcs11" will be
+ * taken into account).
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param answerBody The target memory buffer (out argument).
+ * It must be freed with OrthancPluginFreeMemoryBuffer().
+ * @param answerHeaders The target memory buffer for the HTTP headers in the answers (out argument).
+ * The answer headers are formatted as a JSON object (associative array).
+ * The buffer must be freed with OrthancPluginFreeMemoryBuffer().
+ * This argument can be set to NULL if the plugin has no interest in the HTTP headers.
+ * @param httpStatus The HTTP status after the execution of the request (out argument).
+ * @param method HTTP method to be used.
+ * @param url The URL of interest.
+ * @param headersCount The number of HTTP headers.
+ * @param headersKeys Array containing the keys of the HTTP headers (can be <tt>NULL</tt> if no header).
+ * @param headersValues Array containing the values of the HTTP headers (can be <tt>NULL</tt> if no header).
+ * @param username The username (can be <tt>NULL</tt> if no password protection).
+ * @param password The password (can be <tt>NULL</tt> if no password protection).
+ * @param body The body of the POST request.
+ * @param bodySize The size of the body.
+ * @param timeout Timeout in seconds (0 for default timeout).
+ * @param certificateFile Path to the client certificate for HTTPS, in PEM format
+ * (can be <tt>NULL</tt> if no client certificate or if not using HTTPS).
+ * @param certificateKeyFile Path to the key of the client certificate for HTTPS, in PEM format
+ * (can be <tt>NULL</tt> if no client certificate or if not using HTTPS).
+ * @param certificateKeyPassword Password to unlock the key of the client certificate
+ * (can be <tt>NULL</tt> if no client certificate or if not using HTTPS).
+ * @param pkcs11 Enable PKCS#11 client authentication for hardware security modules and smart cards.
+ * @return 0 if success, or the error code if failure.
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpClient(
+ OrthancPluginContext* context,
+ OrthancPluginMemoryBuffer* answerBody,
+ OrthancPluginMemoryBuffer* answerHeaders,
+ uint16_t* httpStatus,
+ OrthancPluginHttpMethod method,
+ const char* url,
+ uint32_t headersCount,
+ const char* const* headersKeys,
+ const char* const* headersValues,
+ const char* body,
+ uint32_t bodySize,
+ const char* username,
+ const char* password,
+ uint32_t timeout,
+ const char* certificateFile,
+ const char* certificateKeyFile,
+ const char* certificateKeyPassword,
+ uint8_t pkcs11)
+ {
+ _OrthancPluginCallHttpClient2 params;
+ memset(¶ms, 0, sizeof(params));
+
+ params.answerBody = answerBody;
+ params.answerHeaders = answerHeaders;
+ params.httpStatus = httpStatus;
+ params.method = method;
+ params.url = url;
+ params.headersCount = headersCount;
+ params.headersKeys = headersKeys;
+ params.headersValues = headersValues;
+ params.body = body;
+ params.bodySize = bodySize;
+ params.username = username;
+ params.password = password;
+ params.timeout = timeout;
+ params.certificateFile = certificateFile;
+ params.certificateKeyFile = certificateKeyFile;
+ params.certificateKeyPassword = certificateKeyPassword;
+ params.pkcs11 = pkcs11;
+
+ return context->InvokeService(context, _OrthancPluginService_CallHttpClient2, ¶ms);
+ }
+
+
+ /**
+ * @brief Generate an UUID.
+ *
+ * Generate a random GUID/UUID (globally unique identifier).
+ *
+ * @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 UUID. This string must be freed by OrthancPluginFreeString().
+ * @ingroup Toolbox
+ **/
+ ORTHANC_PLUGIN_INLINE char* OrthancPluginGenerateUuid(
+ OrthancPluginContext* context)
+ {
+ char* result;
+
+ _OrthancPluginRetrieveDynamicString params;
+ params.result = &result;
+ params.argument = NULL;
+
+ if (context->InvokeService(context, _OrthancPluginService_GenerateUuid, ¶ms) != OrthancPluginErrorCode_Success)
+ {
+ /* Error */
+ return NULL;
+ }
+ else
+ {
+ return result;
+ }
+ }
+
+
+
+
+ typedef struct
+ {
+ OrthancPluginFindCallback callback;
+ } _OrthancPluginFindCallback;
+
+ /**
+ * @brief Register a callback to handle C-Find requests.
+ *
+ * This function registers a callback to handle C-Find SCP requests
+ * that are not related to modality worklists.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param callback The callback.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterFindCallback(
+ OrthancPluginContext* context,
+ OrthancPluginFindCallback callback)
+ {
+ _OrthancPluginFindCallback params;
+ params.callback = callback;
+
+ return context->InvokeService(context, _OrthancPluginService_RegisterFindCallback, ¶ms);
+ }
+
+
+ typedef struct
+ {
+ OrthancPluginFindAnswers *answers;
+ const OrthancPluginFindQuery *query;
+ const void *dicom;
+ uint32_t size;
+ uint32_t index;
+ uint32_t *resultUint32;
+ uint16_t *resultGroup;
+ uint16_t *resultElement;
+ char **resultString;
+ } _OrthancPluginFindOperation;
+
+ /**
+ * @brief Add one answer to some C-Find request.
+ *
+ * This function adds one answer (encoded as a DICOM file) to the
+ * set of answers corresponding to some C-Find SCP request that is
+ * not related to modality worklists.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param answers The set of answers.
+ * @param dicom The answer to be added, encoded as a DICOM file.
+ * @param size The size of the DICOM file.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ * @see OrthancPluginCreateDicom()
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginFindAddAnswer(
+ OrthancPluginContext* context,
+ OrthancPluginFindAnswers* answers,
+ const void* dicom,
+ uint32_t size)
+ {
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.answers = answers;
+ params.dicom = dicom;
+ params.size = size;
+
+ return context->InvokeService(context, _OrthancPluginService_FindAddAnswer, ¶ms);
+ }
+
+
+ /**
+ * @brief Mark the set of C-Find answers as incomplete.
+ *
+ * This function marks as incomplete the set of answers
+ * corresponding to some C-Find SCP request that is not related to
+ * modality worklists. This must be used if canceling the handling
+ * of a request when too many answers are to be returned.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param answers The set of answers.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginFindMarkIncomplete(
+ OrthancPluginContext* context,
+ OrthancPluginFindAnswers* answers)
+ {
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.answers = answers;
+
+ return context->InvokeService(context, _OrthancPluginService_FindMarkIncomplete, ¶ms);
+ }
+
+
+
+ /**
+ * @brief Get the number of tags in a C-Find query.
+ *
+ * This function returns the number of tags that are contained in
+ * the given C-Find query.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param query The C-Find query.
+ * @return The number of tags.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE uint32_t OrthancPluginGetFindQuerySize(
+ OrthancPluginContext* context,
+ const OrthancPluginFindQuery* query)
+ {
+ uint32_t count = 0;
+
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.query = query;
+ params.resultUint32 = &count;
+
+ if (context->InvokeService(context, _OrthancPluginService_GetFindQuerySize, ¶ms) != OrthancPluginErrorCode_Success)
+ {
+ /* Error */
+ return 0;
+ }
+ else
+ {
+ return count;
+ }
+ }
+
+
+ /**
+ * @brief Get one tag in a C-Find query.
+ *
+ * This function returns the group and the element of one DICOM tag
+ * in the given C-Find query.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param group The group of the tag (output).
+ * @param element The element of the tag (output).
+ * @param query The C-Find query.
+ * @param index The index of the tag of interest.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginGetFindQueryTag(
+ OrthancPluginContext* context,
+ uint16_t* group,
+ uint16_t* element,
+ const OrthancPluginFindQuery* query,
+ uint32_t index)
+ {
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.query = query;
+ params.index = index;
+ params.resultGroup = group;
+ params.resultElement = element;
+
+ return context->InvokeService(context, _OrthancPluginService_GetFindQueryTag, ¶ms);
+ }
+
+
+ /**
+ * @brief Get the symbolic name of one tag in a C-Find query.
+ *
+ * This function returns the symbolic name of one DICOM tag in the
+ * given C-Find query.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param query The C-Find query.
+ * @param index The index of the tag of interest.
+ * @return The NULL value in case of error, or a string containing the name of the tag.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE char* OrthancPluginGetFindQueryTagName(
+ OrthancPluginContext* context,
+ const OrthancPluginFindQuery* query,
+ uint32_t index)
+ {
+ char* result;
+
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.query = query;
+ params.index = index;
+ params.resultString = &result;
+
+ if (context->InvokeService(context, _OrthancPluginService_GetFindQueryTagName, ¶ms) != OrthancPluginErrorCode_Success)
+ {
+ /* Error */
+ return NULL;
+ }
+ else
+ {
+ return result;
+ }
+ }
+
+
+ /**
+ * @brief Get the value associated with one tag in a C-Find query.
+ *
+ * This function returns the value associated with one tag in the
+ * given C-Find query.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param query The C-Find query.
+ * @param index The index of the tag of interest.
+ * @return The NULL value in case of error, or a string containing the value of the tag.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE char* OrthancPluginGetFindQueryValue(
+ OrthancPluginContext* context,
+ const OrthancPluginFindQuery* query,
+ uint32_t index)
+ {
+ char* result;
+
+ _OrthancPluginFindOperation params;
+ memset(¶ms, 0, sizeof(params));
+ params.query = query;
+ params.index = index;
+ params.resultString = &result;
+
+ if (context->InvokeService(context, _OrthancPluginService_GetFindQueryValue, ¶ms) != OrthancPluginErrorCode_Success)
+ {
+ /* Error */
+ return NULL;
+ }
+ else
+ {
+ return result;
+ }
+ }
+
+
+
+
+ typedef struct
+ {
+ OrthancPluginMoveCallback callback;
+ OrthancPluginGetMoveSize getMoveSize;
+ OrthancPluginApplyMove applyMove;
+ OrthancPluginFreeMove freeMove;
+ } _OrthancPluginMoveCallback;
+
+ /**
+ * @brief Register a callback to handle C-Move requests.
+ *
+ * This function registers a callback to handle C-Move SCP requests.
+ *
+ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+ * @param callback The main callback.
+ * @param getMoveSize Callback to read the number of C-Move suboperations.
+ * @param applyMove Callback to apply one C-Move suboperations.
+ * @param freeMove Callback to free the C-Move driver.
+ * @return 0 if success, other value if error.
+ * @ingroup DicomCallbacks
+ **/
+ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterMoveCallback(
+ OrthancPluginContext* context,
+ OrthancPluginMoveCallback callback,
+ OrthancPluginGetMoveSize getMoveSize,
+ OrthancPluginApplyMove applyMove,
+ OrthancPluginFreeMove freeMove)
+ {
+ _OrthancPluginMoveCallback params;
+ params.callback = callback;
+ params.getMoveSize = getMoveSize;
+ params.applyMove = applyMove;
+ params.freeMove = freeMove;
+
+ return context->InvokeService(context, _OrthancPluginService_RegisterMoveCallback, ¶ms);
+ }
+
+
+
+
#ifdef __cplusplus
}
#endif
diff --git a/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h b/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h
index 415fa84..dd8b840 100644
--- a/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h
+++ b/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/AutomatedJpeg2kCompression/Plugin.cpp b/Plugins/Samples/AutomatedJpeg2kCompression/Plugin.cpp
index 299dd77..44752e6 100644
--- a/Plugins/Samples/AutomatedJpeg2kCompression/Plugin.cpp
+++ b/Plugins/Samples/AutomatedJpeg2kCompression/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/Basic/Plugin.c b/Plugins/Samples/Basic/Plugin.c
index 5b6ee35..36b30bb 100644
--- a/Plugins/Samples/Basic/Plugin.c
+++ b/Plugins/Samples/Basic/Plugin.c
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -322,6 +322,38 @@ ORTHANC_PLUGINS_API OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeT
}
+ORTHANC_PLUGINS_API int32_t FilterIncomingHttpRequest(OrthancPluginHttpMethod method,
+ const char* uri,
+ const char* ip,
+ uint32_t headersCount,
+ const char* const* headersKeys,
+ const char* const* headersValues)
+{
+ uint32_t i;
+
+ if (headersCount > 0)
+ {
+ OrthancPluginLogInfo(context, "HTTP headers of an incoming REST request:");
+ for (i = 0; i < headersCount; i++)
+ {
+ char info[1024];
+ sprintf(info, " %s: %s", headersKeys[i], headersValues[i]);
+ OrthancPluginLogInfo(context, info);
+ }
+ }
+
+ if (method == OrthancPluginHttpMethod_Get ||
+ method == OrthancPluginHttpMethod_Post)
+ {
+ return 1; /* Allowed */
+ }
+ else
+ {
+ return 0; /* Only allow GET and POST requests */
+ }
+}
+
+
ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
{
OrthancPluginMemoryBuffer tmp;
@@ -384,8 +416,8 @@ ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
OrthancPluginRegisterRestCallback(context, "/plugin/create", CallbackCreateDicom);
OrthancPluginRegisterOnStoredInstanceCallback(context, OnStoredCallback);
-
OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback);
+ OrthancPluginRegisterIncomingHttpRequestFilter(context, FilterIncomingHttpRequest);
/* Declare several properties of the plugin */
OrthancPluginSetRootUri(context, "/plugin/hello");
diff --git a/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
new file mode 100644
index 0000000..6a7dbc0
--- /dev/null
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
@@ -0,0 +1,829 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancPluginCppWrapper.h"
+
+#include <json/reader.h>
+
+
+namespace OrthancPlugins
+{
+ const char* PluginException::GetErrorDescription(OrthancPluginContext* context) const
+ {
+ const char* description = OrthancPluginGetErrorDescription(context, code_);
+ if (description)
+ {
+ return description;
+ }
+ else
+ {
+ return "No description available";
+ }
+ }
+
+
+ MemoryBuffer::MemoryBuffer(OrthancPluginContext* context) :
+ context_(context)
+ {
+ buffer_.data = NULL;
+ buffer_.size = 0;
+ }
+
+
+ void MemoryBuffer::Clear()
+ {
+ if (buffer_.data != NULL)
+ {
+ OrthancPluginFreeMemoryBuffer(context_, &buffer_);
+ buffer_.data = NULL;
+ buffer_.size = 0;
+ }
+ }
+
+
+ void MemoryBuffer::Assign(OrthancPluginMemoryBuffer& other)
+ {
+ Clear();
+
+ buffer_.data = other.data;
+ buffer_.size = other.size;
+
+ other.data = NULL;
+ other.size = 0;
+ }
+
+
+ void MemoryBuffer::ToString(std::string& target) const
+ {
+ if (buffer_.size == 0)
+ {
+ target.clear();
+ }
+ else
+ {
+ target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
+ }
+ }
+
+
+ void MemoryBuffer::ToJson(Json::Value& target) const
+ {
+ if (buffer_.data == NULL ||
+ buffer_.size == 0)
+ {
+ throw PluginException(OrthancPluginErrorCode_InternalError);
+ }
+
+ const char* tmp = reinterpret_cast<const char*>(buffer_.data);
+
+ Json::Reader reader;
+ if (!reader.parse(tmp, tmp + buffer_.size, target))
+ {
+ OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+ }
+
+
+ bool MemoryBuffer::RestApiGet(const std::string& uri,
+ bool applyPlugins)
+ {
+ Clear();
+
+ OrthancPluginErrorCode error;
+
+ if (applyPlugins)
+ {
+ error = OrthancPluginRestApiGetAfterPlugins(context_, &buffer_, uri.c_str());
+ }
+ else
+ {
+ error = OrthancPluginRestApiGet(context_, &buffer_, uri.c_str());
+ }
+
+ if (error == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (error == OrthancPluginErrorCode_UnknownResource ||
+ error == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
+ }
+ else
+ {
+ throw PluginException(error);
+ }
+ }
+
+
+ bool MemoryBuffer::RestApiPost(const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
+ {
+ Clear();
+
+ OrthancPluginErrorCode error;
+
+ if (applyPlugins)
+ {
+ error = OrthancPluginRestApiPostAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize);
+ }
+ else
+ {
+ error = OrthancPluginRestApiPost(context_, &buffer_, uri.c_str(), body, bodySize);
+ }
+
+ if (error == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (error == OrthancPluginErrorCode_UnknownResource ||
+ error == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
+ }
+ else
+ {
+ throw PluginException(error);
+ }
+ }
+
+
+ bool MemoryBuffer::RestApiPut(const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
+ {
+ Clear();
+
+ OrthancPluginErrorCode error;
+
+ if (applyPlugins)
+ {
+ error = OrthancPluginRestApiPutAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize);
+ }
+ else
+ {
+ error = OrthancPluginRestApiPut(context_, &buffer_, uri.c_str(), body, bodySize);
+ }
+
+ if (error == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (error == OrthancPluginErrorCode_UnknownResource ||
+ error == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
+ }
+ else
+ {
+ throw PluginException(error);
+ }
+ }
+
+
+ OrthancString::OrthancString(OrthancPluginContext* context,
+ char* str) :
+ context_(context),
+ str_(str)
+ {
+ }
+
+
+ void OrthancString::Clear()
+ {
+ if (str_ != NULL)
+ {
+ OrthancPluginFreeString(context_, str_);
+ str_ = NULL;
+ }
+ }
+
+
+ void OrthancString::ToString(std::string& target) const
+ {
+ if (str_ == NULL)
+ {
+ target.clear();
+ }
+ else
+ {
+ target.assign(str_);
+ }
+ }
+
+
+ void OrthancString::ToJson(Json::Value& target) const
+ {
+ if (str_ == NULL)
+ {
+ OrthancPluginLogError(context_, "Cannot convert an empty memory buffer to JSON");
+ throw PluginException(OrthancPluginErrorCode_InternalError);
+ }
+
+ Json::Reader reader;
+ if (!reader.parse(str_, target))
+ {
+ OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+ }
+
+
+ OrthancConfiguration::OrthancConfiguration(OrthancPluginContext* context) :
+ context_(context)
+ {
+ OrthancString str(context, OrthancPluginGetConfiguration(context));
+
+ if (str.GetContent() == NULL)
+ {
+ OrthancPluginLogError(context, "Cannot access the Orthanc configuration");
+ throw PluginException(OrthancPluginErrorCode_InternalError);
+ }
+
+ str.ToJson(configuration_);
+
+ if (configuration_.type() != Json::objectValue)
+ {
+ OrthancPluginLogError(context, "Unable to read the Orthanc configuration");
+ throw PluginException(OrthancPluginErrorCode_InternalError);
+ }
+ }
+
+
+ OrthancPluginContext* OrthancConfiguration::GetContext() const
+ {
+ if (context_ == NULL)
+ {
+ throw PluginException(OrthancPluginErrorCode_Plugin);
+ }
+ else
+ {
+ return context_;
+ }
+ }
+
+
+ std::string OrthancConfiguration::GetPath(const std::string& key) const
+ {
+ if (path_.empty())
+ {
+ return key;
+ }
+ else
+ {
+ return path_ + "." + key;
+ }
+ }
+
+
+ void OrthancConfiguration::GetSection(OrthancConfiguration& target,
+ const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ target.context_ = context_;
+ target.path_ = GetPath(key);
+
+ if (!configuration_.isMember(key))
+ {
+ target.configuration_ = Json::objectValue;
+ }
+ else
+ {
+ if (configuration_[key].type() != Json::objectValue)
+ {
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration section \"" + target.path_ + "\" is not an associative array as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+
+ target.configuration_ = configuration_[key];
+ }
+ }
+
+
+ bool OrthancConfiguration::LookupStringValue(std::string& target,
+ const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ if (!configuration_.isMember(key))
+ {
+ return false;
+ }
+
+ if (configuration_[key].type() != Json::stringValue)
+ {
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration option \"" + GetPath(key) + "\" is not a string as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+
+ target = configuration_[key].asString();
+ return true;
+ }
+
+
+ bool OrthancConfiguration::LookupIntegerValue(int& target,
+ const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ if (!configuration_.isMember(key))
+ {
+ return false;
+ }
+
+ switch (configuration_[key].type())
+ {
+ case Json::intValue:
+ target = configuration_[key].asInt();
+ return true;
+
+ case Json::uintValue:
+ target = configuration_[key].asUInt();
+ return true;
+
+ default:
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration option \"" + GetPath(key) + "\" is not an integer as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+ }
+
+
+ bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
+ const std::string& key) const
+ {
+ int tmp;
+ if (!LookupIntegerValue(tmp, key))
+ {
+ return false;
+ }
+
+ if (tmp < 0)
+ {
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration option \"" + GetPath(key) + "\" is not a positive integer as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+ else
+ {
+ target = static_cast<unsigned int>(tmp);
+ return true;
+ }
+ }
+
+
+ bool OrthancConfiguration::LookupBooleanValue(bool& target,
+ const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ if (!configuration_.isMember(key))
+ {
+ return false;
+ }
+
+ if (configuration_[key].type() != Json::booleanValue)
+ {
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration option \"" + GetPath(key) + "\" is not a Boolean as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+
+ target = configuration_[key].asBool();
+ return true;
+ }
+
+
+ bool OrthancConfiguration::LookupFloatValue(float& target,
+ const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ if (!configuration_.isMember(key))
+ {
+ return false;
+ }
+
+ switch (configuration_[key].type())
+ {
+ case Json::realValue:
+ target = configuration_[key].asFloat();
+ return true;
+
+ case Json::intValue:
+ target = configuration_[key].asInt();
+ return true;
+
+ case Json::uintValue:
+ target = configuration_[key].asUInt();
+ return true;
+
+ default:
+ if (context_ != NULL)
+ {
+ std::string s = "The configuration option \"" + GetPath(key) + "\" is not an integer as expected";
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ }
+ }
+
+
+ std::string OrthancConfiguration::GetStringValue(const std::string& key,
+ const std::string& defaultValue) const
+ {
+ std::string tmp;
+ if (LookupStringValue(tmp, key))
+ {
+ return tmp;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ int OrthancConfiguration::GetIntegerValue(const std::string& key,
+ int defaultValue) const
+ {
+ int tmp;
+ if (LookupIntegerValue(tmp, key))
+ {
+ return tmp;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
+ unsigned int defaultValue) const
+ {
+ unsigned int tmp;
+ if (LookupUnsignedIntegerValue(tmp, key))
+ {
+ return tmp;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ bool OrthancConfiguration::GetBooleanValue(const std::string& key,
+ bool defaultValue) const
+ {
+ bool tmp;
+ if (LookupBooleanValue(tmp, key))
+ {
+ return tmp;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ float OrthancConfiguration::GetFloatValue(const std::string& key,
+ float defaultValue) const
+ {
+ float tmp;
+ if (LookupFloatValue(tmp, key))
+ {
+ return tmp;
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ void OrthancImage::Clear()
+ {
+ if (image_ != NULL)
+ {
+ OrthancPluginFreeImage(context_, image_);
+ image_ = NULL;
+ }
+ }
+
+
+ void OrthancImage::CheckImageAvailable()
+ {
+ if (image_ == NULL)
+ {
+ OrthancPluginLogError(context_, "Trying to access a NULL image");
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ OrthancImage::OrthancImage(OrthancPluginContext* context) :
+ context_(context),
+ image_(NULL)
+ {
+ if (context == NULL)
+ {
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ OrthancImage::OrthancImage(OrthancPluginContext* context,
+ OrthancPluginImage* image) :
+ context_(context),
+ image_(image)
+ {
+ if (context == NULL)
+ {
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ OrthancImage::OrthancImage(OrthancPluginContext* context,
+ OrthancPluginPixelFormat format,
+ uint32_t width,
+ uint32_t height) :
+ context_(context)
+ {
+ if (context == NULL)
+ {
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ else
+ {
+ image_ = OrthancPluginCreateImage(context, format, width, height);
+ }
+ }
+
+
+ void OrthancImage::UncompressPngImage(const void* data,
+ size_t size)
+ {
+ Clear();
+ image_ = OrthancPluginUncompressImage(context_, data, size, OrthancPluginImageFormat_Png);
+ if (image_ == NULL)
+ {
+ OrthancPluginLogError(context_, "Cannot uncompress a PNG image");
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ void OrthancImage::UncompressJpegImage(const void* data,
+ size_t size)
+ {
+ Clear();
+ image_ = OrthancPluginUncompressImage(context_, data, size, OrthancPluginImageFormat_Jpeg);
+ if (image_ == NULL)
+ {
+ OrthancPluginLogError(context_, "Cannot uncompress a JPEG image");
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ void OrthancImage::DecodeDicomImage(const void* data,
+ size_t size,
+ unsigned int frame)
+ {
+ Clear();
+ image_ = OrthancPluginDecodeDicomImage(context_, data, size, frame);
+ if (image_ == NULL)
+ {
+ OrthancPluginLogError(context_, "Cannot uncompress a DICOM image");
+ throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ OrthancPluginPixelFormat OrthancImage::GetPixelFormat()
+ {
+ CheckImageAvailable();
+ return OrthancPluginGetImagePixelFormat(context_, image_);
+ }
+
+
+ unsigned int OrthancImage::GetWidth()
+ {
+ CheckImageAvailable();
+ return OrthancPluginGetImageWidth(context_, image_);
+ }
+
+
+ unsigned int OrthancImage::GetHeight()
+ {
+ CheckImageAvailable();
+ return OrthancPluginGetImageHeight(context_, image_);
+ }
+
+
+ unsigned int OrthancImage::GetPitch()
+ {
+ CheckImageAvailable();
+ return OrthancPluginGetImagePitch(context_, image_);
+ }
+
+
+ const void* OrthancImage::GetBuffer()
+ {
+ CheckImageAvailable();
+ return OrthancPluginGetImageBuffer(context_, image_);
+ }
+
+
+ void OrthancImage::CompressPngImage(MemoryBuffer& target)
+ {
+ CheckImageAvailable();
+
+ OrthancPluginMemoryBuffer tmp;
+ OrthancPluginCompressPngImage(context_, &tmp, GetPixelFormat(),
+ GetWidth(), GetHeight(), GetPitch(), GetBuffer());
+
+ target.Assign(tmp);
+ }
+
+
+ void OrthancImage::CompressJpegImage(MemoryBuffer& target,
+ uint8_t quality)
+ {
+ CheckImageAvailable();
+
+ OrthancPluginMemoryBuffer tmp;
+ OrthancPluginCompressJpegImage(context_, &tmp, GetPixelFormat(),
+ GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
+
+ target.Assign(tmp);
+ }
+
+
+ void OrthancImage::AnswerPngImage(OrthancPluginRestOutput* output)
+ {
+ CheckImageAvailable();
+ OrthancPluginCompressAndAnswerPngImage(context_, output, GetPixelFormat(),
+ GetWidth(), GetHeight(), GetPitch(), GetBuffer());
+ }
+
+
+ void OrthancImage::AnswerJpegImage(OrthancPluginRestOutput* output,
+ uint8_t quality)
+ {
+ CheckImageAvailable();
+ OrthancPluginCompressAndAnswerJpegImage(context_, output, GetPixelFormat(),
+ GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
+ }
+
+
+ bool RestApiGetJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins)
+ {
+ MemoryBuffer answer(context);
+ if (!answer.RestApiGet(uri, applyPlugins))
+ {
+ return false;
+ }
+ else
+ {
+ answer.ToJson(result);
+ return true;
+ }
+ }
+
+
+ bool RestApiPostJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
+ {
+ MemoryBuffer answer(context);
+ if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
+ {
+ return false;
+ }
+ else
+ {
+ answer.ToJson(result);
+ return true;
+ }
+ }
+
+
+ bool RestApiPutJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
+ {
+ MemoryBuffer answer(context);
+ if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
+ {
+ return false;
+ }
+ else
+ {
+ answer.ToJson(result);
+ return true;
+ }
+ }
+
+
+ bool RestApiDelete(OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins)
+ {
+ OrthancPluginErrorCode error;
+
+ if (applyPlugins)
+ {
+ error = OrthancPluginRestApiDeleteAfterPlugins(context, uri.c_str());
+ }
+ else
+ {
+ error = OrthancPluginRestApiDelete(context, uri.c_str());
+ }
+
+ if (error == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (error == OrthancPluginErrorCode_UnknownResource ||
+ error == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
+ }
+ else
+ {
+ throw PluginException(error);
+ }
+ }
+}
+
diff --git a/Plugins/Samples/Common/OrthancPluginCppWrapper.h b/Plugins/Samples/Common/OrthancPluginCppWrapper.h
new file mode 100644
index 0000000..2b9966f
--- /dev/null
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h
@@ -0,0 +1,384 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, 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 <orthanc/OrthancCPlugin.h>
+#include <boost/noncopyable.hpp>
+#include <boost/lexical_cast.hpp>
+#include <json/value.h>
+
+#if HAS_ORTHANC_EXCEPTION == 1
+# include <OrthancException.h>
+#endif
+
+
+namespace OrthancPlugins
+{
+ typedef void (*RestCallback) (OrthancPluginRestOutput* output,
+ const char* url,
+ const OrthancPluginHttpRequest* request);
+
+
+ class PluginException
+ {
+ private:
+ OrthancPluginErrorCode code_;
+
+ public:
+ PluginException(OrthancPluginErrorCode code) : code_(code)
+ {
+ }
+
+ OrthancPluginErrorCode GetErrorCode() const
+ {
+ return code_;
+ }
+
+ const char* GetErrorDescription(OrthancPluginContext* context) const;
+ };
+
+
+ class MemoryBuffer : public boost::noncopyable
+ {
+ private:
+ OrthancPluginContext* context_;
+ OrthancPluginMemoryBuffer buffer_;
+
+ public:
+ MemoryBuffer(OrthancPluginContext* context);
+
+ ~MemoryBuffer()
+ {
+ Clear();
+ }
+
+ OrthancPluginMemoryBuffer* operator*()
+ {
+ return &buffer_;
+ }
+
+ // This transfers ownership
+ void Assign(OrthancPluginMemoryBuffer& other);
+
+ const char* GetData() const
+ {
+ if (buffer_.size > 0)
+ {
+ return reinterpret_cast<const char*>(buffer_.data);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ size_t GetSize() const
+ {
+ return buffer_.size;
+ }
+
+ void Clear();
+
+ void ToString(std::string& target) const;
+
+ void ToJson(Json::Value& target) const;
+
+ bool RestApiGet(const std::string& uri,
+ bool applyPlugins);
+
+ bool RestApiPost(const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ bool RestApiPut(const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ bool RestApiPost(const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
+ {
+ return RestApiPost(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
+ }
+
+ bool RestApiPut(const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
+ {
+ return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
+ }
+ };
+
+
+ class OrthancString : public boost::noncopyable
+ {
+ private:
+ OrthancPluginContext* context_;
+ char* str_;
+
+ public:
+ OrthancString(OrthancPluginContext* context,
+ char* str);
+
+ ~OrthancString()
+ {
+ Clear();
+ }
+
+ void Clear();
+
+ const char* GetContent() const
+ {
+ return str_;
+ }
+
+ void ToString(std::string& target) const;
+
+ void ToJson(Json::Value& target) const;
+ };
+
+
+ class OrthancConfiguration : public boost::noncopyable
+ {
+ private:
+ OrthancPluginContext* context_;
+ Json::Value configuration_;
+ std::string path_;
+
+ std::string GetPath(const std::string& key) const;
+
+ public:
+ OrthancConfiguration() : context_(NULL)
+ {
+ }
+
+ OrthancConfiguration(OrthancPluginContext* context);
+
+ OrthancPluginContext* GetContext() const;
+
+ const Json::Value& GetJson() const
+ {
+ return configuration_;
+ }
+
+ void GetSection(OrthancConfiguration& target,
+ const std::string& key) const;
+
+ bool LookupStringValue(std::string& target,
+ const std::string& key) const;
+
+ bool LookupIntegerValue(int& target,
+ const std::string& key) const;
+
+ bool LookupUnsignedIntegerValue(unsigned int& target,
+ const std::string& key) const;
+
+ bool LookupBooleanValue(bool& target,
+ const std::string& key) const;
+
+ bool LookupFloatValue(float& target,
+ const std::string& key) const;
+
+ std::string GetStringValue(const std::string& key,
+ const std::string& defaultValue) const;
+
+ int GetIntegerValue(const std::string& key,
+ int defaultValue) const;
+
+ unsigned int GetUnsignedIntegerValue(const std::string& key,
+ unsigned int defaultValue) const;
+
+ bool GetBooleanValue(const std::string& key,
+ bool defaultValue) const;
+
+ float GetFloatValue(const std::string& key,
+ float defaultValue) const;
+ };
+
+ class OrthancImage
+ {
+ private:
+ OrthancPluginContext* context_;
+ OrthancPluginImage* image_;
+
+ void Clear();
+
+ void CheckImageAvailable();
+
+ public:
+ OrthancImage(OrthancPluginContext* context);
+
+ OrthancImage(OrthancPluginContext* context,
+ OrthancPluginImage* image);
+
+ OrthancImage(OrthancPluginContext* context,
+ OrthancPluginPixelFormat format,
+ uint32_t width,
+ uint32_t height);
+
+ ~OrthancImage()
+ {
+ Clear();
+ }
+
+ void UncompressPngImage(const void* data,
+ size_t size);
+
+ void UncompressJpegImage(const void* data,
+ size_t size);
+
+ void DecodeDicomImage(const void* data,
+ size_t size,
+ unsigned int frame);
+
+ OrthancPluginPixelFormat GetPixelFormat();
+
+ unsigned int GetWidth();
+
+ unsigned int GetHeight();
+
+ unsigned int GetPitch();
+
+ const void* GetBuffer();
+
+ void CompressPngImage(MemoryBuffer& target);
+
+ void CompressJpegImage(MemoryBuffer& target,
+ uint8_t quality);
+
+ void AnswerPngImage(OrthancPluginRestOutput* output);
+
+ void AnswerJpegImage(OrthancPluginRestOutput* output,
+ uint8_t quality);
+ };
+
+
+ bool RestApiGetJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins);
+
+ bool RestApiPostJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ bool RestApiPutJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ inline bool RestApiPostJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
+ {
+ return RestApiPostJson(result, context, uri, body.empty() ? NULL : body.c_str(),
+ body.size(), applyPlugins);
+ }
+
+ bool RestApiDelete(OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins);
+
+ inline bool RestApiPutJson(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
+ {
+ return RestApiPutJson(result, context, uri, body.empty() ? NULL : body.c_str(),
+ body.size(), applyPlugins);
+ }
+
+ bool RestApiDelete(OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins);
+
+
+ namespace Internals
+ {
+ template <RestCallback Callback>
+ OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
+ const char* url,
+ const OrthancPluginHttpRequest* request)
+ {
+ try
+ {
+ Callback(output, url, request);
+ return OrthancPluginErrorCode_Success;
+ }
+ catch (OrthancPlugins::PluginException& e)
+ {
+ return e.GetErrorCode();
+ }
+#if HAS_ORTHANC_EXCEPTION == 1
+ catch (Orthanc::OrthancException& e)
+ {
+ return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+ }
+#endif
+ catch (boost::bad_lexical_cast& e)
+ {
+ return OrthancPluginErrorCode_BadFileFormat;
+ }
+ catch (...)
+ {
+ return OrthancPluginErrorCode_Plugin;
+ }
+ }
+ }
+
+
+ template <RestCallback Callback>
+ void RegisterRestCallback(OrthancPluginContext* context,
+ const std::string& uri,
+ bool isThreadSafe)
+ {
+ if (isThreadSafe)
+ {
+ OrthancPluginRegisterRestCallbackNoLock(context, uri.c_str(), Internals::Protect<Callback>);
+ }
+ else
+ {
+ OrthancPluginRegisterRestCallback(context, uri.c_str(), Internals::Protect<Callback>);
+ }
+ }
+}
diff --git a/Plugins/Samples/CustomImageDecoder/Plugin.cpp b/Plugins/Samples/CustomImageDecoder/Plugin.cpp
index 98097d5..5dc10ef 100644
--- a/Plugins/Samples/CustomImageDecoder/Plugin.cpp
+++ b/Plugins/Samples/CustomImageDecoder/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/DatabasePlugin/Database.cpp b/Plugins/Samples/DatabasePlugin/Database.cpp
index 25da386..4c48944 100644
--- a/Plugins/Samples/DatabasePlugin/Database.cpp
+++ b/Plugins/Samples/DatabasePlugin/Database.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/DatabasePlugin/Database.h b/Plugins/Samples/DatabasePlugin/Database.h
index 94a9bf5..26632e6 100644
--- a/Plugins/Samples/DatabasePlugin/Database.h
+++ b/Plugins/Samples/DatabasePlugin/Database.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/DatabasePlugin/Plugin.cpp b/Plugins/Samples/DatabasePlugin/Plugin.cpp
index 072ad12..88509d2 100644
--- a/Plugins/Samples/DatabasePlugin/Plugin.cpp
+++ b/Plugins/Samples/DatabasePlugin/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp
index 0492fee..6f608e2 100644
--- a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp
+++ b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h
index 2a7b15a..f606305 100644
--- a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h
+++ b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp
index 00587c1..892a175 100644
--- a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp
+++ b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -112,7 +112,9 @@ namespace OrthancPlugins
else
{
if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
- image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
+ image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB &&
+ (image.GetTransferSyntax() != gdcm::TransferSyntax::JPEG2000Lossless ||
+ image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::YBR_RCT))
{
photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
photometric_->SetInput(image);
@@ -188,7 +190,8 @@ namespace OrthancPlugins
}
}
else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
- image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
+ (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB ||
+ image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::YBR_RCT))
{
switch (image.GetPixelFormat())
{
diff --git a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h
index d494a87..815b794 100644
--- a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h
+++ b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp b/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp
index 57c6595..12900dd 100644
--- a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp
+++ b/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h b/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h
index 8f5578e..c3f7619 100644
--- a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h
+++ b/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/GdcmDecoder/Plugin.cpp b/Plugins/Samples/GdcmDecoder/Plugin.cpp
index e936d69..2665e46 100644
--- a/Plugins/Samples/GdcmDecoder/Plugin.cpp
+++ b/Plugins/Samples/GdcmDecoder/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/ModalityWorklists/Plugin.cpp b/Plugins/Samples/ModalityWorklists/Plugin.cpp
index 4840f31..f68fc29 100644
--- a/Plugins/Samples/ModalityWorklists/Plugin.cpp
+++ b/Plugins/Samples/ModalityWorklists/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -124,6 +124,8 @@ OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers* answers,
const char* remoteAet,
const char* calledAet)
{
+ namespace fs = boost::filesystem;
+
Json::Value json;
if (!GetQueryDicom(json, query))
@@ -137,16 +139,19 @@ OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers* answers,
OrthancPluginLogInfo(context_, msg.c_str());
}
- boost::filesystem::path source(folder_);
- boost::filesystem::directory_iterator end;
+ fs::path source(folder_);
+ fs::directory_iterator end;
try
{
- for (boost::filesystem::directory_iterator it(source); it != end; ++it)
+ for (fs::directory_iterator it(source); it != end; ++it)
{
- if (is_regular_file(it->status()))
+ fs::file_type type(it->status().type());
+
+ if (type == fs::regular_file ||
+ type == fs::reparse_file) // cf. BitBucket issue #11
{
- std::string extension = boost::filesystem::extension(it->path());
+ std::string extension = fs::extension(it->path());
ToLowerCase(extension);
if (extension == ".wl")
@@ -161,7 +166,7 @@ OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers* answers,
}
}
}
- catch (boost::filesystem::filesystem_error&)
+ catch (fs::filesystem_error&)
{
std::string description = std::string("Inexistent folder while scanning for worklists: ") + source.string();
OrthancPluginLogError(context_, description.c_str());
diff --git a/Plugins/Samples/ServeFolders/Plugin.cpp b/Plugins/Samples/ServeFolders/Plugin.cpp
index 80a83d9..6772c83 100644
--- a/Plugins/Samples/ServeFolders/Plugin.cpp
+++ b/Plugins/Samples/ServeFolders/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -198,7 +198,10 @@ static OrthancPluginErrorCode FolderCallback(OrthancPluginRestOutput* output,
for (fs::directory_iterator it(parent) ; it != end; ++it)
{
- if (fs::is_regular_file(it->status()))
+ fs::file_type type = it->status().type();
+
+ if (type == fs::regular_file ||
+ type == fs::reparse_file) // cf. BitBucket issue #11
{
std::string f = it->path().filename().string();
s += " <li><a href=\"" + f + "\">" + f + "</a></li>\n";
diff --git a/Plugins/Samples/StorageArea/Plugin.cpp b/Plugins/Samples/StorageArea/Plugin.cpp
index 472c4e0..1d201c1 100644
--- a/Plugins/Samples/StorageArea/Plugin.cpp
+++ b/Plugins/Samples/StorageArea/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/WebSkeleton/Configuration.h b/Plugins/Samples/WebSkeleton/Configuration.h
index b08071e..c0bdb9e 100644
--- a/Plugins/Samples/WebSkeleton/Configuration.h
+++ b/Plugins/Samples/WebSkeleton/Configuration.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py b/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py
index b551cc3..04c1206 100644
--- a/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py
+++ b/Plugins/Samples/WebSkeleton/Framework/EmbedResources.py
@@ -1,5 +1,5 @@
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/WebSkeleton/Framework/Framework.cmake b/Plugins/Samples/WebSkeleton/Framework/Framework.cmake
index 76a71f5..5aa4baf 100644
--- a/Plugins/Samples/WebSkeleton/Framework/Framework.cmake
+++ b/Plugins/Samples/WebSkeleton/Framework/Framework.cmake
@@ -1,5 +1,5 @@
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp b/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp
index 473186f..c1ff3d9 100644
--- a/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp
+++ b/Plugins/Samples/WebSkeleton/Framework/Plugin.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Resources/CMake/BoostConfiguration.cmake b/Resources/CMake/BoostConfiguration.cmake
index eb42dde..20c50df 100644
--- a/Resources/CMake/BoostConfiguration.cmake
+++ b/Resources/CMake/BoostConfiguration.cmake
@@ -8,7 +8,7 @@ else()
#set(Boost_USE_STATIC_LIBS ON)
find_package(Boost
- COMPONENTS filesystem thread system date_time regex locale)
+ COMPONENTS filesystem thread system date_time regex locale ${ORTHANC_BOOST_COMPONENTS})
if (NOT Boost_FOUND)
message(FATAL_ERROR "Unable to locate Boost on this system")
@@ -39,10 +39,10 @@ endif()
if (BOOST_STATIC)
- # Parameters for Boost 1.59.0
- set(BOOST_NAME boost_1_59_0)
- set(BOOST_BCP_SUFFIX bcpdigest-0.9.5)
- set(BOOST_MD5 "08abb7cdbea0b380f9ab0d5cce476f12")
+ # Parameters for Boost 1.60.0
+ set(BOOST_NAME boost_1_60_0)
+ set(BOOST_BCP_SUFFIX bcpdigest-1.0.1)
+ set(BOOST_MD5 "a789f8ec2056ad1c2d5f0cb64687cc7b")
set(BOOST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src")
set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
@@ -61,6 +61,8 @@ if (BOOST_STATIC)
)
add_definitions(
-DBOOST_LOCALE_WITH_ICONV=1
+ -DBOOST_LOCALE_NO_WINAPI_BACKEND=1
+ -DBOOST_LOCALE_NO_STD_BACKEND=1
)
if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
@@ -86,6 +88,10 @@ if (BOOST_STATIC)
add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
endif()
+ add_definitions(
+ -DBOOST_LOCALE_NO_POSIX_BACKEND=1
+ -DBOOST_LOCALE_NO_STD_BACKEND=1
+ )
else()
message(FATAL_ERROR "Support your platform here")
endif()
@@ -109,6 +115,55 @@ if (BOOST_STATIC)
${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
)
+ if (USE_BOOST_LOCALE_BACKENDS)
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
+ list(APPEND BOOST_SOURCES
+ ${BOOST_SOURCES_DIR}/libs/locale/src/posix/codecvt.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/posix/collate.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/posix/converter.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/posix/numeric.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/posix/posix_backend.cpp
+ )
+ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ list(APPEND BOOST_SOURCES
+ ${BOOST_SOURCES_DIR}/libs/locale/src/win32/collate.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/win32/converter.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/win32/lcid.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/win32/numeric.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/win32/win_backend.cpp
+ )
+ else()
+ message(FATAL_ERROR "Support your platform here")
+ endif()
+
+ list(APPEND BOOST_SOURCES
+ ${BOOST_REGEX_SOURCES}
+ ${BOOST_SOURCES_DIR}/libs/date_time/src/gregorian/greg_month.cpp
+ ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
+
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/codecvt_error_category.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/operations.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/path.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/path_traits.cpp
+
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/generator.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/date_time.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/formatting.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/ids.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/localization_backend.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/message.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/shared/mo_lambda.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/util/codecvt_converter.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/util/default_locale.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/util/gregorian.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/util/info.cpp
+ ${BOOST_SOURCES_DIR}/libs/locale/src/util/locale_data.cpp
+ )
+ endif()
+
add_definitions(
# Static build of Boost
-DBOOST_ALL_NO_LIB
diff --git a/Resources/CMake/BoostConfiguration.sh b/Resources/CMake/BoostConfiguration.sh
index 621ea27..92c98b4 100755
--- a/Resources/CMake/BoostConfiguration.sh
+++ b/Resources/CMake/BoostConfiguration.sh
@@ -14,22 +14,23 @@ set -u
## - Orthanc between 0.6.2 and 0.7.3: Boost 1.54.0
## - Orthanc between 0.7.4 and 0.9.1: Boost 1.55.0
## - Orthanc between 0.9.2 and 0.9.4: Boost 1.58.0
-## - Orthanc >= 0.9.5: Boost 1.59.0
+## - Orthanc between 0.9.5 and 1.0.0: Boost 1.59.0
+## - Orthanc >= 1.0.1: Boost 1.60.0
-rm -rf /tmp/boost_1_59_0
-rm -rf /tmp/bcp/boost_1_59_0
+rm -rf /tmp/boost_1_60_0
+rm -rf /tmp/bcp/boost_1_60_0
cd /tmp
-echo "Uncompressing the sources of Boost 1.59.0..."
-tar xfz ./boost_1_59_0.tar.gz
+echo "Uncompressing the sources of Boost 1.60.0..."
+tar xfz ./boost_1_60_0.tar.gz
echo "Generating the subset..."
-mkdir -p /tmp/bcp/boost_1_59_0
-bcp --boost=/tmp/boost_1_59_0 thread system locale date_time filesystem math/special_functions algorithm uuid atomic iostreams /tmp/bcp/boost_1_59_0
+mkdir -p /tmp/bcp/boost_1_60_0
+bcp --boost=/tmp/boost_1_60_0 thread system locale date_time filesystem math/special_functions algorithm uuid atomic iostreams program_options numeric/ublas /tmp/bcp/boost_1_60_0
cd /tmp/bcp
echo "Compressing the subset..."
-tar cfz boost_1_59_0_bcpdigest-0.9.5.tar.gz boost_1_59_0
-ls -l boost_1_59_0_bcpdigest-0.9.5.tar.gz
-md5sum boost_1_59_0_bcpdigest-0.9.5.tar.gz
-readlink -f boost_1_59_0_bcpdigest-0.9.5.tar.gz
+tar cfz boost_1_60_0_bcpdigest-1.0.1.tar.gz boost_1_60_0
+ls -l boost_1_60_0_bcpdigest-1.0.1.tar.gz
+md5sum boost_1_60_0_bcpdigest-1.0.1.tar.gz
+readlink -f boost_1_60_0_bcpdigest-1.0.1.tar.gz
diff --git a/Resources/CMake/Compiler.cmake b/Resources/CMake/Compiler.cmake
index a82efc7..52f4fab 100644
--- a/Resources/CMake/Compiler.cmake
+++ b/Resources/CMake/Compiler.cmake
@@ -10,7 +10,7 @@ if (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long -Wno-implicit-function-declaration")
# --std=c99 makes libcurl not to compile
# -pedantic gives a lot of warnings on OpenSSL
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wno-long-long -Wno-variadic-macros")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros")
if (CMAKE_CROSSCOMPILING)
# http://stackoverflow.com/a/3543845/881731
@@ -50,7 +50,12 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--version-script=${ORTHANC_ROOT}/Plugins/Samples/Common/VersionScript.map")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
+
+ if (NOT DEFINED ENABLE_PLUGINS_VERSION_SCRIPT OR
+ ENABLE_PLUGINS_VERSION_SCRIPT)
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${ORTHANC_ROOT}/Plugins/Samples/Common/VersionScript.map")
+ endif()
# Remove the "-rdynamic" option
# http://www.mail-archive.com/cmake@cmake.org/msg08837.html
@@ -68,6 +73,11 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
link_libraries(dl)
endif()
+ CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
+ if (NOT HAVE_UUID_H)
+ message(FATAL_ERROR "Please install the uuid-dev package")
+ endif()
+
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if (MSVC)
message("MSVC compiler version = " ${MSVC_VERSION} "\n")
@@ -92,6 +102,11 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
link_libraries(rpcrt4 ws2_32)
if (CMAKE_COMPILER_IS_GNUCXX)
+ # Some additional C/C++ compiler flags for MinGW
+ SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable")
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}")
+
# This is a patch for MinGW64
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
@@ -115,6 +130,11 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
)
link_libraries(iconv)
+ CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
+ if (NOT HAVE_UUID_H)
+ message(FATAL_ERROR "Please install the uuid-dev package")
+ endif()
+
endif()
@@ -134,17 +154,6 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
endif()
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
- CHECK_INCLUDE_FILES(rpc.h HAVE_UUID_H)
-else()
- CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H)
-endif()
-
-if (NOT HAVE_UUID_H)
- message(FATAL_ERROR "Please install the uuid-dev package")
-endif()
-
-
if (STATIC_BUILD)
add_definitions(-DORTHANC_STATIC=1)
else()
diff --git a/Resources/CMake/DcmtkConfiguration.cmake b/Resources/CMake/DcmtkConfiguration.cmake
index 4bc5ce2..0e22007 100644
--- a/Resources/CMake/DcmtkConfiguration.cmake
+++ b/Resources/CMake/DcmtkConfiguration.cmake
@@ -1,9 +1,31 @@
+if (NOT DEFINED ENABLE_DCMTK_NETWORKING)
+ set(ENABLE_DCMTK_NETWORKING ON)
+endif()
+
if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
- SET(DCMTK_VERSION_NUMBER 360)
- SET(DCMTK_PACKAGE_VERSION "3.6.0")
- SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
- SET(DCMTK_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip")
- SET(DCMTK_MD5 "219ad631b82031806147e4abbfba4fa4")
+ if (USE_DCMTK_361)
+ SET(DCMTK_VERSION_NUMBER 361)
+ SET(DCMTK_PACKAGE_VERSION "3.6.1")
+ SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.1_20160216)
+ SET(DCMTK_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.1_20160216.tar.gz")
+ SET(DCMTK_MD5 "273c8a544b9fe09b8a4fb4eb51df8e52")
+ SET(DCMTK_PATCH_SPEED "${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.1-speed.patch")
+
+ macro(DCMTK_UNSET)
+ endmacro()
+
+ set(DCMTK_BINARY_DIR ${DCMTK_SOURCES_DIR}/)
+ set(DCMTK_CMAKE_INCLUDE ${DCMTK_SOURCES_DIR}/)
+ add_definitions(-DDCMTK_INSIDE_LOG4CPLUS=1)
+ else()
+ SET(DCMTK_VERSION_NUMBER 360)
+ SET(DCMTK_PACKAGE_VERSION "3.6.0")
+ SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
+ SET(DCMTK_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip")
+ SET(DCMTK_MD5 "219ad631b82031806147e4abbfba4fa4")
+ SET(DCMTK_PATCH_SPEED "${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.0-speed.patch")
+ SET(DCMTK_PATCH_MINGW64 "${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.0-mingw64.patch")
+ endif()
if (IS_DIRECTORY "${DCMTK_SOURCES_DIR}")
set(FirstRun OFF)
@@ -40,10 +62,20 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
${DCMTK_SOURCES_DIR}/CMake/osconfig.h.in
${DCMTK_SOURCES_DIR}/config/include/dcmtk/config/osconfig.h)
- AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmnet/libsrc DCMTK_SOURCES)
+ if (USE_DCMTK_361)
+ # This step must be after the generation of "osconfig.h"
+ INSPECT_FUNDAMENTAL_ARITHMETIC_TYPES()
+ endif()
+
AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmdata/libsrc DCMTK_SOURCES)
AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/ofstd/libsrc DCMTK_SOURCES)
+ if (ENABLE_DCMTK_NETWORKING)
+ AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmnet/libsrc DCMTK_SOURCES)
+ include_directories(
+ ${DCMTK_SOURCES_DIR}/dcmnet/include
+ )
+ endif()
if (ENABLE_JPEG)
AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc DCMTK_SOURCES)
@@ -59,6 +91,16 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
)
list(REMOVE_ITEM DCMTK_SOURCES
${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/ddpiimpl.cc
+
+ # Disable support for encoding JPEG (modification in Orthanc 1.0.1)
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djcodece.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsv1.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencbas.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencpro.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djenclol.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencode.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencext.cc
+ ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djencsps.cc
)
endif()
@@ -73,6 +115,9 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
)
list(REMOVE_ITEM DCMTK_SOURCES
${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djcodece.cc
+
+ # Disable support for encoding JPEG-LS (modification in Orthanc 1.0.1)
+ ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djencode.cc
)
list(APPEND DCMTK_SOURCES
${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djrplol.cc
@@ -87,12 +132,13 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
list(REMOVE_ITEM DCMTK_SOURCES
+ ${DCMTK_SOURCES_DIR}/oflog/libsrc/clfsap.cc
${DCMTK_SOURCES_DIR}/oflog/libsrc/windebap.cc
${DCMTK_SOURCES_DIR}/oflog/libsrc/winsock.cc
)
execute_process(
- COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-linux-speed.patch
+ COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${DCMTK_PATCH_SPEED}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
@@ -106,10 +152,10 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
${DCMTK_SOURCES_DIR}/oflog/libsrc/unixsock.cc
)
- if (CMAKE_COMPILER_IS_GNUCXX)
+ if (CMAKE_COMPILER_IS_GNUCXX AND DCMTK_PATCH_MINGW64)
# This is a patch for MinGW64
execute_process(
- COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-mingw64.patch
+ COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${DCMTK_PATCH_MINGW64}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
@@ -122,7 +168,7 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
# This patch improves speed, even for Windows
execute_process(
COMMAND ${PATCH_EXECUTABLE} -p0 -N
- INPUT_FILE ${ORTHANC_ROOT}/Resources/Patches/dcmtk-linux-speed.patch
+ INPUT_FILE ${DCMTK_PATCH_SPEED}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
@@ -135,7 +181,7 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
list(REMOVE_ITEM DCMTK_SOURCES
${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdictbi.cc
${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdeftag.cc
- ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdictbi.cc
+ #${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdictbi.cc
)
#set_source_files_properties(${DCMTK_SOURCES}
@@ -152,7 +198,6 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
include_directories(
#${DCMTK_SOURCES_DIR}
${DCMTK_SOURCES_DIR}/config/include
- ${DCMTK_SOURCES_DIR}/dcmnet/include
${DCMTK_SOURCES_DIR}/ofstd/include
${DCMTK_SOURCES_DIR}/oflog/include
${DCMTK_SOURCES_DIR}/dcmdata/include
@@ -180,16 +225,16 @@ else()
include(FindDCMTK)
list(APPEND DCMTK_LIBRARIES "${tmp}")
- include_directories(${DCMTK_INCLUDE_DIR})
+ include_directories(${DCMTK_INCLUDE_DIRS})
add_definitions(
-DHAVE_CONFIG_H=1
)
- if (EXISTS "${DCMTK_DIR}/config/cfunix.h")
- set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/cfunix.h")
- elseif (EXISTS "${DCMTK_DIR}/config/osconfig.h") # This is for Arch Linux
- set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/osconfig.h")
+ if (EXISTS "${DCMTK_config_INCLUDE_DIR}/cfunix.h")
+ set(DCMTK_CONFIGURATION_FILE "${DCMTK_config_INCLUDE_DIR}/cfunix.h")
+ elseif (EXISTS "${DCMTK_config_INCLUDE_DIR}/osconfig.h") # This is for Arch Linux
+ set(DCMTK_CONFIGURATION_FILE "${DCMTK_config_INCLUDE_DIR}/osconfig.h")
else()
message(FATAL_ERROR "Please install libdcmtk*-dev")
endif()
diff --git a/Resources/CMake/DownloadPackage.cmake b/Resources/CMake/DownloadPackage.cmake
index 00647be..492a352 100644
--- a/Resources/CMake/DownloadPackage.cmake
+++ b/Resources/CMake/DownloadPackage.cmake
@@ -83,7 +83,9 @@ macro(DownloadPackage MD5 Url TargetDirectory)
# How to silently extract files using 7-zip
# http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly
- if (("${TMP_EXTENSION}" STREQUAL "gz") OR ("${TMP_EXTENSION}" STREQUAL "tgz"))
+ if (("${TMP_EXTENSION}" STREQUAL "gz") OR
+ ("${TMP_EXTENSION}" STREQUAL "tgz") OR
+ ("${TMP_EXTENSION}" STREQUAL "xz"))
execute_process(
COMMAND ${ZIP_EXECUTABLE} e -y ${TMP_PATH}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
@@ -97,8 +99,10 @@ macro(DownloadPackage MD5 Url TargetDirectory)
if ("${TMP_EXTENSION}" STREQUAL "tgz")
string(REGEX REPLACE ".tgz$" ".tar" TMP_FILENAME2 "${TMP_FILENAME}")
- else()
+ elseif ("${TMP_EXTENSION}" STREQUAL "gz")
string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}")
+ elseif ("${TMP_EXTENSION}" STREQUAL "xz")
+ string(REGEX REPLACE ".xz" "" TMP_FILENAME2 "${TMP_FILENAME}")
endif()
execute_process(
@@ -138,6 +142,12 @@ macro(DownloadPackage MD5 Url TargetDirectory)
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
+ elseif ("${TMP_EXTENSION}" STREQUAL "xz")
+ execute_process(
+ COMMAND sh -c "${TAR_EXECUTABLE} xf ${TMP_PATH}"
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ RESULT_VARIABLE Failure
+ )
else()
message(FATAL_ERROR "Unknown package format.")
endif()
diff --git a/Resources/CMake/GoogleLogConfiguration.cmake b/Resources/CMake/GoogleLogConfiguration.cmake
deleted file mode 100644
index b2d2870..0000000
--- a/Resources/CMake/GoogleLogConfiguration.cmake
+++ /dev/null
@@ -1,197 +0,0 @@
-if (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_LOG)
- SET(GOOGLE_LOG_SOURCES_DIR ${CMAKE_BINARY_DIR}/glog-0.3.2)
- SET(GOOGLE_LOG_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/glog-0.3.2.tar.gz")
- SET(GOOGLE_LOG_MD5 "897fbff90d91ea2b6d6e78c8cea641cc")
-
- if (IS_DIRECTORY "${GOOGLE_LOG_SOURCES_DIR}")
- set(FirstRun OFF)
- else()
- set(FirstRun ON)
- endif()
-
- DownloadPackage(${GOOGLE_LOG_MD5} ${GOOGLE_LOG_URL} "${GOOGLE_LOG_SOURCES_DIR}")
-
- # Glog 0.3.3 fails to build with old versions of MinGW, such as the
- # one installed on our Continuous Integration Server that runs
- # Debian Squeeze. We thus stick to Glog 0.3.2 for the time being.
-
- #SET(GOOGLE_LOG_SOURCES_DIR ${CMAKE_BINARY_DIR}/glog-0.3.3)
- #DownloadPackage(
- # "a6fd2c22f8996846e34c763422717c18"
- # "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/glog-0.3.3.tar.gz"
- # "${GOOGLE_LOG_SOURCES_DIR}")
-
-
- set(GOOGLE_LOG_HEADERS
- ${GOOGLE_LOG_SOURCES_DIR}/src/glog/logging.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/glog/raw_logging.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/glog/stl_logging.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/glog/vlog_is_on.h
- )
-
- set(ac_google_namespace google)
- set(ac_google_start_namespace "namespace google {")
- set(ac_google_end_namespace "}")
-
- if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
- set(ac_cv_have_unistd_h 1)
- set(ac_cv_have_stdint_h 1)
- set(ac_cv_have_systypes_h 0)
- set(ac_cv_have_inttypes_h 0)
- set(ac_cv_have_libgflags 0)
- set(ac_cv_have_uint16_t 1)
- set(ac_cv_have_u_int16_t 0)
- set(ac_cv_have___uint16 0)
- set(ac_cv_cxx_using_operator 1)
- set(ac_cv_have___builtin_expect 1)
- else()
- set(ac_cv_have_unistd_h 0)
- set(ac_cv_have_stdint_h 0)
- set(ac_cv_have_systypes_h 0)
- set(ac_cv_have_inttypes_h 0)
- set(ac_cv_have_libgflags 0)
- set(ac_cv_have_uint16_t 0)
- set(ac_cv_have_u_int16_t 0)
- set(ac_cv_have___uint16 1)
- set(ac_cv_cxx_using_operator 1)
- set(ac_cv_have___builtin_expect 0)
- endif()
-
- foreach (f ${GOOGLE_LOG_HEADERS})
- configure_file(${f}.in ${f})
- endforeach()
-
- include_directories(
- ${GOOGLE_LOG_SOURCES_DIR}/src
- )
-
- if (CMAKE_COMPILER_IS_GNUCXX)
- if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
- execute_process(
- COMMAND ${PATCH_EXECUTABLE} -N utilities.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-utilities-lsb.diff
- WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src
- RESULT_VARIABLE Failure
- )
- else()
- execute_process(
- COMMAND ${PATCH_EXECUTABLE} -N utilities.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-utilities.diff
- WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src
- RESULT_VARIABLE Failure
- )
- endif()
-
- if (Failure AND FirstRun)
- message(FATAL_ERROR "Error while patching a file")
- endif()
-
- # Patches for MinGW
- execute_process(
- #COMMAND ${PATCH_EXECUTABLE} -N port.h -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-h.diff
- COMMAND ${PATCH_EXECUTABLE} -N port.h -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-h-v2.diff
- WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src/windows
- RESULT_VARIABLE Failure
- )
-
- if (Failure AND FirstRun)
- message(FATAL_ERROR "Error while patching a file")
- endif()
-
- execute_process(
- COMMAND ${PATCH_EXECUTABLE} -N port.cc -i ${ORTHANC_ROOT}/Resources/Patches/glog-port-cc.diff
- WORKING_DIRECTORY ${GOOGLE_LOG_SOURCES_DIR}/src/windows
- RESULT_VARIABLE Failure
- )
-
- if (Failure AND FirstRun)
- message(FATAL_ERROR "Error while patching a file")
- endif()
-
- elseif (MSVC)
- # https://code.google.com/p/google-glog/issues/detail?id=117
- configure_file(
- ${ORTHANC_ROOT}/Resources/Patches/glog-visual-studio-port.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/windows/port.h
- COPYONLY)
-
- endif()
-
-
- if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
- if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
- # Install the specific configuration for LSB SDK
- configure_file(
- ${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfigurationLSB.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/config.h
- COPYONLY)
- elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
- # Install the specific configuration for Mac OS
- configure_file(
- ${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfigurationDarwin.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/config.h
- COPYONLY)
- else()
- configure_file(
- ${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfiguration.h
- ${GOOGLE_LOG_SOURCES_DIR}/src/config.h
- COPYONLY)
- endif()
-
- set(GOOGLE_LOG_SOURCES
- ${GOOGLE_LOG_SOURCES_DIR}/src/demangle.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/logging.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/raw_logging.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/signalhandler.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/symbolize.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/utilities.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/vlog_is_on.cc
- )
-
- elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
- include_directories(
- ${GOOGLE_LOG_SOURCES_DIR}/src/windows
- )
-
- set(GOOGLE_LOG_SOURCES
- ${GOOGLE_LOG_SOURCES_DIR}/src/windows/port.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/logging.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/raw_logging.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/utilities.cc
- ${GOOGLE_LOG_SOURCES_DIR}/src/vlog_is_on.cc
- )
-
- add_definitions(
- -DGLOG_NO_ABBREVIATED_SEVERITIES=1
- -DNO_FRAME_POINTER=1
- -DGOOGLE_GLOG_DLL_DECL=
- )
-
- if (CMAKE_COMPILER_IS_GNUCXX)
- # This is a patch for MinGW64
- add_definitions(-D_TIME_H__S=1)
- endif()
- endif()
-
-else()
- CHECK_INCLUDE_FILE_CXX(glog/logging.h HAVE_GOOGLE_LOG_H)
- if (NOT HAVE_GOOGLE_LOG_H)
- message(FATAL_ERROR "Please install the libgoogle-glog-dev package")
- endif()
-
- link_libraries(glog)
-endif()
-
-
-CHECK_INCLUDE_FILES(sec_api/string_s.h HAVE_SECURE_STRING_EXTENSIONS)
-if (HAVE_SECURE_STRING_EXTENSIONS)
- add_definitions(-DHAVE_SECURE_STRING_EXTENSIONS=1)
-else()
- add_definitions(-DHAVE_SECURE_STRING_EXTENSIONS=0)
-endif()
-
-add_definitions(-DORTHANC_ENABLE_GOOGLE_LOG=1)
diff --git a/Resources/CMake/GoogleLogConfiguration.h b/Resources/CMake/GoogleLogConfiguration.h
deleted file mode 100644
index 432f4da..0000000
--- a/Resources/CMake/GoogleLogConfiguration.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* src/config.h. Generated from config.h.in by configure. */
-/* src/config.h.in. Generated from configure.ac by autoheader. */
-
-/* Namespace for Google classes */
-#define GOOGLE_NAMESPACE google
-
-/* Define if you have the `dladdr' function */
-/* #undef HAVE_DLADDR */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you have the <execinfo.h> header file. */
-#define HAVE_EXECINFO_H 1
-
-/* Define if you have the `fcntl' function */
-#define HAVE_FCNTL 1
-
-/* Define to 1 if you have the <glob.h> header file. */
-#define HAVE_GLOB_H 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `pthread' library (-lpthread). */
-#define HAVE_LIBPTHREAD 1
-
-/* Define to 1 if you have the <libunwind.h> header file. */
-/* #undef HAVE_LIBUNWIND_H */
-
-/* define if you have google gflags library */
-/* #undef HAVE_LIB_GFLAGS */
-
-/* define if you have google gmock library */
-/* #undef HAVE_LIB_GMOCK */
-
-/* define if you have google gtest library */
-/* #undef HAVE_LIB_GTEST */
-
-/* define if you have libunwind */
-/* #undef HAVE_LIB_UNWIND */
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* define if the compiler implements namespaces */
-#define HAVE_NAMESPACES 1
-
-/* Define if you have POSIX threads libraries and header files. */
-#define HAVE_PTHREAD 1
-
-/* Define to 1 if you have the <pwd.h> header file. */
-#define HAVE_PWD_H 1
-
-/* define if the compiler implements pthread_rwlock_* */
-#define HAVE_RWLOCK 1
-
-/* Define if you have the `sigaltstack' function */
-#define HAVE_SIGALTSTACK 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <syscall.h> header file. */
-#define HAVE_SYSCALL_H 1
-
-/* Define to 1 if you have the <syslog.h> header file. */
-#define HAVE_SYSLOG_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/syscall.h> header file. */
-#define HAVE_SYS_SYSCALL_H 1
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#define HAVE_SYS_TIME_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <sys/ucontext.h> header file. */
-#define HAVE_SYS_UCONTEXT_H 1
-
-/* Define to 1 if you have the <sys/utsname.h> header file. */
-#define HAVE_SYS_UTSNAME_H 1
-
-/* Define to 1 if you have the <ucontext.h> header file. */
-#define HAVE_UCONTEXT_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* define if the compiler supports using expression for operator */
-#define HAVE_USING_OPERATOR 1
-
-/* define if your compiler has __attribute__ */
-#define HAVE___ATTRIBUTE__ 1
-
-/* define if your compiler has __builtin_expect */
-#define HAVE___BUILTIN_EXPECT 1
-
-/* define if your compiler has __sync_val_compare_and_swap */
-#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "glog"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "opensource at google.com"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "glog"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "glog 0.3.2"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "glog"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "0.3.2"
-
-/* How to access the PC from a struct ucontext */
-/*#include <ucontext.h>
-#include <sys/ucontext.h>
-#ifdef REG_RIP
-#define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_RIP]
-#else
-#undef PC_FROM_UCONTEXT
-#endif*/
-
-// This is required for older versions of Linux
-#undef PC_FROM_UCONTEXT
-
-/* Define to necessary symbol if this constant uses a non-standard name on
- your system. */
-/* #undef PTHREAD_CREATE_JOINABLE */
-
-/* The size of `void *', as computed by sizeof. */
-#define SIZEOF_VOID_P 8
-
-/* Define to 1 if you have the ANSI C header files. */
-/* #undef STDC_HEADERS */
-
-/* the namespace where STL code like vector<> is defined */
-#define STL_NAMESPACE std
-
-/* location of source code */
-#define TEST_SRC_DIR "."
-
-/* Version number of package */
-#define VERSION "0.3.2"
-
-/* Stops putting the code inside the Google namespace */
-#define _END_GOOGLE_NAMESPACE_ }
-
-/* Puts following code inside the Google namespace */
-#define _START_GOOGLE_NAMESPACE_ namespace google {
diff --git a/Resources/CMake/GoogleLogConfigurationDarwin.h b/Resources/CMake/GoogleLogConfigurationDarwin.h
deleted file mode 100644
index aba3c28..0000000
--- a/Resources/CMake/GoogleLogConfigurationDarwin.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* src/config.h. Generated from config.h.in by configure. */
-/* src/config.h.in. Generated from configure.ac by autoheader. */
-
-/* Namespace for Google classes */
-#define GOOGLE_NAMESPACE google
-
-/* Define if you have the `dladdr' function */
-/* #undef HAVE_DLADDR */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you have the <execinfo.h> header file. */
-#define HAVE_EXECINFO_H 1
-
-/* Define if you have the `fcntl' function */
-#define HAVE_FCNTL 1
-
-/* Define to 1 if you have the <glob.h> header file. */
-#define HAVE_GLOB_H 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `pthread' library (-lpthread). */
-#define HAVE_LIBPTHREAD 1
-
-/* Define to 1 if you have the <libunwind.h> header file. */
-/* #undef HAVE_LIBUNWIND_H */
-
-/* define if you have google gflags library */
-/* #undef HAVE_LIB_GFLAGS */
-
-/* define if you have google gmock library */
-/* #undef HAVE_LIB_GMOCK */
-
-/* define if you have google gtest library */
-/* #undef HAVE_LIB_GTEST */
-
-/* define if you have libunwind */
-/* #undef HAVE_LIB_UNWIND */
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* define if the compiler implements namespaces */
-#define HAVE_NAMESPACES 1
-
-/* Define if you have POSIX threads libraries and header files. */
-#define HAVE_PTHREAD 1
-
-/* Define to 1 if you have the <pwd.h> header file. */
-#define HAVE_PWD_H 1
-
-/* define if the compiler implements pthread_rwlock_* */
-#define HAVE_RWLOCK 1
-
-/* Define if you have the `sigaltstack' function */
-#define HAVE_SIGALTSTACK 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <syscall.h> header file. */
-/* #undef HAVE_SYSCALL_H */
-
-/* Define to 1 if you have the <syslog.h> header file. */
-#define HAVE_SYSLOG_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-/* #define HAVE_SYS_STAT_H 1 */
-
-/* Define to 1 if you have the <sys/syscall.h> header file. */
-#define HAVE_SYS_SYSCALL_H 1
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#define HAVE_SYS_TIME_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-/* #define HAVE_SYS_TYPES_H 1 */
-
-/* Define to 1 if you have the <sys/ucontext.h> header file. */
-/* #define HAVE_SYS_UCONTEXT_H 1 */
-
-/* Define to 1 if you have the <sys/utsname.h> header file. */
-#define HAVE_SYS_UTSNAME_H 1
-
-/* Define to 1 if you have the <ucontext.h> header file. */
-#define HAVE_UCONTEXT_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* define if the compiler supports using expression for operator */
-#define HAVE_USING_OPERATOR 1
-
-/* define if your compiler has __attribute__ */
-#define HAVE___ATTRIBUTE__ 1
-
-/* define if your compiler has __builtin_expect */
-#define HAVE___BUILTIN_EXPECT 1
-
-/* define if your compiler has __sync_val_compare_and_swap */
-#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "glog"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "opensource at google.com"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "glog"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "glog 0.3.2"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "glog"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "0.3.2"
-
-/* How to access the PC from a struct ucontext */
-/*#include <ucontext.h>
-#include <sys/ucontext.h>
-#ifdef REG_RIP
-#define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_RIP]
-#else
-#undef PC_FROM_UCONTEXT
-#endif*/
-
-// This is required for older versions of Linux
-#undef PC_FROM_UCONTEXT
-
-/* Define to necessary symbol if this constant uses a non-standard name on
- your system. */
-/* #undef PTHREAD_CREATE_JOINABLE */
-
-/* The size of `void *', as computed by sizeof. */
-#define SIZEOF_VOID_P 8
-
-/* Define to 1 if you have the ANSI C header files. */
-/* #undef STDC_HEADERS */
-
-/* the namespace where STL code like vector<> is defined */
-#define STL_NAMESPACE std
-
-/* location of source code */
-#define TEST_SRC_DIR "."
-
-/* Version number of package */
-#define VERSION "0.3.2"
-
-/* Stops putting the code inside the Google namespace */
-#define _END_GOOGLE_NAMESPACE_ }
-
-/* Puts following code inside the Google namespace */
-#define _START_GOOGLE_NAMESPACE_ namespace google {
diff --git a/Resources/CMake/GoogleLogConfigurationLSB.h b/Resources/CMake/GoogleLogConfigurationLSB.h
deleted file mode 100644
index a1557dc..0000000
--- a/Resources/CMake/GoogleLogConfigurationLSB.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* src/config.h. Generated from config.h.in by configure. */
-/* src/config.h.in. Generated from configure.ac by autoheader. */
-
-/* Namespace for Google classes */
-#define GOOGLE_NAMESPACE google
-
-/* Define if you have the `dladdr' function */
-/* #undef HAVE_DLADDR */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you have the <execinfo.h> header file. */
-#define HAVE_EXECINFO_H 1
-
-/* Define if you have the `fcntl' function */
-#define HAVE_FCNTL 1
-
-/* Define to 1 if you have the <glob.h> header file. */
-#define HAVE_GLOB_H 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `pthread' library (-lpthread). */
-#define HAVE_LIBPTHREAD 1
-
-/* Define to 1 if you have the <libunwind.h> header file. */
-/* #undef HAVE_LIBUNWIND_H */
-
-/* define if you have google gflags library */
-/* #undef HAVE_LIB_GFLAGS */
-
-/* define if you have google gmock library */
-/* #undef HAVE_LIB_GMOCK */
-
-/* define if you have google gtest library */
-/* #undef HAVE_LIB_GTEST */
-
-/* define if you have libunwind */
-/* #undef HAVE_LIB_UNWIND */
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* define if the compiler implements namespaces */
-#define HAVE_NAMESPACES 1
-
-/* Define if you have POSIX threads libraries and header files. */
-#define HAVE_PTHREAD 1
-
-/* Define to 1 if you have the <pwd.h> header file. */
-#define HAVE_PWD_H 1
-
-/* define if the compiler implements pthread_rwlock_* */
-#define HAVE_RWLOCK 1
-
-/* Define if you have the `sigaltstack' function */
-#define HAVE_SIGALTSTACK 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <syscall.h> header file. */
-/* #undef HAVE_SYSCALL_H */
-
-/* Define to 1 if you have the <syslog.h> header file. */
-#define HAVE_SYSLOG_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/syscall.h> header file. */
-/* #undef HAVE_SYS_SYSCALL_H */
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#define HAVE_SYS_TIME_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <sys/ucontext.h> header file. */
-/* #define HAVE_SYS_UCONTEXT_H 1 */
-
-/* Define to 1 if you have the <sys/utsname.h> header file. */
-#define HAVE_SYS_UTSNAME_H 1
-
-/* Define to 1 if you have the <ucontext.h> header file. */
-/* #define HAVE_UCONTEXT_H 1 */
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* define if the compiler supports using expression for operator */
-#define HAVE_USING_OPERATOR 1
-
-/* define if your compiler has __attribute__ */
-#define HAVE___ATTRIBUTE__ 1
-
-/* define if your compiler has __builtin_expect */
-#define HAVE___BUILTIN_EXPECT 1
-
-/* define if your compiler has __sync_val_compare_and_swap */
-#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "glog"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "opensource at google.com"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "glog"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "glog 0.3.2"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "glog"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "0.3.2"
-
-/* How to access the PC from a struct ucontext */
-/*#include <ucontext.h>
-#include <sys/ucontext.h>
-#ifdef REG_RIP
-#define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_RIP]
-#else
-#undef PC_FROM_UCONTEXT
-#endif*/
-
-// This is required for older versions of Linux
-#undef PC_FROM_UCONTEXT
-
-/* Define to necessary symbol if this constant uses a non-standard name on
- your system. */
-/* #undef PTHREAD_CREATE_JOINABLE */
-
-/* The size of `void *', as computed by sizeof. */
-#define SIZEOF_VOID_P 8
-
-/* Define to 1 if you have the ANSI C header files. */
-/* #undef STDC_HEADERS */
-
-/* the namespace where STL code like vector<> is defined */
-#define STL_NAMESPACE std
-
-/* location of source code */
-#define TEST_SRC_DIR "."
-
-/* Version number of package */
-#define VERSION "0.3.2"
-
-/* Stops putting the code inside the Google namespace */
-#define _END_GOOGLE_NAMESPACE_ }
-
-/* Puts following code inside the Google namespace */
-#define _START_GOOGLE_NAMESPACE_ namespace google {
diff --git a/Resources/CMake/JsonCppConfiguration.cmake b/Resources/CMake/JsonCppConfiguration.cmake
index 7c6d8a1..bad61b8 100644
--- a/Resources/CMake/JsonCppConfiguration.cmake
+++ b/Resources/CMake/JsonCppConfiguration.cmake
@@ -32,4 +32,29 @@ else()
message(FATAL_ERROR "Please install the libjsoncpp-dev package")
endif()
+ # Switch to the C++11 standard if the version of JsonCpp is 1.y.z
+ if (EXISTS ${JSONCPP_INCLUDE_DIR}/json/version.h)
+ file(STRINGS
+ "${JSONCPP_INCLUDE_DIR}/json/version.h"
+ JSONCPP_VERSION_MAJOR1 REGEX
+ ".*define JSONCPP_VERSION_MAJOR.*")
+
+ if (NOT JSONCPP_VERSION_MAJOR1)
+ message(FATAL_ERROR "Unable to extract the major version of JsonCpp")
+ endif()
+
+ string(REGEX REPLACE
+ ".*JSONCPP_VERSION_MAJOR.*([0-9]+)$" "\\1"
+ JSONCPP_VERSION_MAJOR ${JSONCPP_VERSION_MAJOR1})
+ message("JsonCpp major version: ${JSONCPP_VERSION_MAJOR}")
+
+ if (CMAKE_COMPILER_IS_GNUCXX AND
+ JSONCPP_VERSION_MAJOR GREATER 0)
+ message("Switching to C++11 standard, as version of JsonCpp is >= 1.0.0")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated-declarations")
+ endif()
+ else()
+ message("Unable to detect the major version of JsonCpp, assuming < 1.0.0")
+ endif()
+
endif()
diff --git a/Resources/CMake/LibCurlConfiguration.cmake b/Resources/CMake/LibCurlConfiguration.cmake
index d924a58..d10912a 100644
--- a/Resources/CMake/LibCurlConfiguration.cmake
+++ b/Resources/CMake/LibCurlConfiguration.cmake
@@ -35,6 +35,7 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_CURL)
add_definitions(
#-DHAVE_LIBSSL=1
-DUSE_OPENSSL=1
+ -DHAVE_OPENSSL_ENGINE_H=1
-DUSE_SSLEAY=1
)
endif()
diff --git a/Resources/CMake/LibP11Configuration.cmake b/Resources/CMake/LibP11Configuration.cmake
new file mode 100644
index 0000000..509bee6
--- /dev/null
+++ b/Resources/CMake/LibP11Configuration.cmake
@@ -0,0 +1,69 @@
+if (STATIC_BUILD OR NOT USE_SYSTEM_LIBP11)
+ SET(LIBP11_SOURCES_DIR ${CMAKE_BINARY_DIR}/libp11-0.4.0)
+ SET(LIBP11_URL "www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/beid/libp11-0.4.0.tar.gz")
+ SET(LIBP11_MD5 "00b3e41db5be840d822bda12f3ab2ca7")
+
+ if (IS_DIRECTORY "${LIBP11_SOURCES_DIR}")
+ set(FirstRun OFF)
+ else()
+ set(FirstRun ON)
+ endif()
+
+ DownloadPackage(${LIBP11_MD5} ${LIBP11_URL} "${LIBP11_SOURCES_DIR}")
+
+ # Apply the patches
+ execute_process(
+ COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Patches/libp11-0.4.0.patch
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ RESULT_VARIABLE Failure
+ )
+
+ if (Failure AND FirstRun)
+ message(FATAL_ERROR "Error while patching libp11")
+ endif()
+
+ # This command MUST be after applying the patch
+ file(COPY
+ ${LIBP11_SOURCES_DIR}/src/engine.h
+ ${LIBP11_SOURCES_DIR}/src/libp11.h
+ DESTINATION ${AUTOGENERATED_DIR}/libp11)
+
+ set(LIBP11_SOURCES
+ #${LIBP11_SOURCES_DIR}/src/eng_front.c
+ ${LIBP11_SOURCES_DIR}/src/eng_back.c
+ ${LIBP11_SOURCES_DIR}/src/eng_parse.c
+ ${LIBP11_SOURCES_DIR}/src/libpkcs11.c
+ ${LIBP11_SOURCES_DIR}/src/p11_attr.c
+ ${LIBP11_SOURCES_DIR}/src/p11_cert.c
+ ${LIBP11_SOURCES_DIR}/src/p11_ec.c
+ ${LIBP11_SOURCES_DIR}/src/p11_err.c
+ ${LIBP11_SOURCES_DIR}/src/p11_front.c
+ ${LIBP11_SOURCES_DIR}/src/p11_key.c
+ ${LIBP11_SOURCES_DIR}/src/p11_load.c
+ ${LIBP11_SOURCES_DIR}/src/p11_misc.c
+ ${LIBP11_SOURCES_DIR}/src/p11_rsa.c
+ ${LIBP11_SOURCES_DIR}/src/p11_slot.c
+ )
+
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+ list(APPEND LIBP11_SOURCES
+ ${LIBP11_SOURCES_DIR}/src/atfork.c
+ )
+ endif()
+
+else()
+ check_include_file_cxx(libp11.h HAVE_LIBP11_H)
+ if (NOT HAVE_LIBP11_H)
+ message(FATAL_ERROR "Please install the libp11-dev package")
+ endif()
+
+ check_library_exists(p11 PKCS11_login "" HAVE_LIBP11_LIB)
+ if (NOT HAVE_LIBP11_LIB)
+ message(FATAL_ERROR "Please install the libp11-dev package")
+ endif()
+
+ link_libraries(p11)
+endif()
diff --git a/Resources/CMake/OpenSslConfiguration.cmake b/Resources/CMake/OpenSslConfiguration.cmake
index 43405d7..6e7a995 100644
--- a/Resources/CMake/OpenSslConfiguration.cmake
+++ b/Resources/CMake/OpenSslConfiguration.cmake
@@ -25,9 +25,6 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_OPENSSL)
-DOPENSSL_NO_BF
-DOPENSSL_NO_CAMELLIA
-DOPENSSL_NO_CAST
- -DOPENSSL_NO_EC
- -DOPENSSL_NO_ECDH
- -DOPENSSL_NO_ECDSA
-DOPENSSL_NO_EC_NISTP_64_GCC_128
-DOPENSSL_NO_GMP
-DOPENSSL_NO_GOST
@@ -99,6 +96,21 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_OPENSSL)
${OPENSSL_SOURCES_DIR}/ssl
)
+ if (ENABLE_PKCS11)
+ list(APPEND OPENSSL_SOURCES_SUBDIRS
+ # EC, ECDH and ECDSA are necessary for PKCS11
+ ${OPENSSL_SOURCES_DIR}/crypto/ec
+ ${OPENSSL_SOURCES_DIR}/crypto/ecdh
+ ${OPENSSL_SOURCES_DIR}/crypto/ecdsa
+ )
+ else()
+ add_definitions(
+ -DOPENSSL_NO_EC
+ -DOPENSSL_NO_ECDH
+ -DOPENSSL_NO_ECDSA
+ )
+ endif()
+
foreach(d ${OPENSSL_SOURCES_SUBDIRS})
AUX_SOURCE_DIRECTORY(${d} OPENSSL_SOURCES)
endforeach()
@@ -173,8 +185,18 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_OPENSSL)
${OPENSSL_SOURCES_DIR}/crypto/dh/dhtest.c
${OPENSSL_SOURCES_DIR}/crypto/pqueue/pq_test.c
${OPENSSL_SOURCES_DIR}/crypto/des/ncbc_enc.c
+
+ ${OPENSSL_SOURCES_DIR}/crypto/evp/evp_extra_test.c
+ ${OPENSSL_SOURCES_DIR}/crypto/evp/verify_extra_test.c
+ ${OPENSSL_SOURCES_DIR}/crypto/x509/verify_extra_test.c
+ ${OPENSSL_SOURCES_DIR}/crypto/x509v3/v3prin.c
+ ${OPENSSL_SOURCES_DIR}/crypto/x509v3/v3nametest.c
+ ${OPENSSL_SOURCES_DIR}/crypto/ssl/heartbeat_test.c
+ ${OPENSSL_SOURCES_DIR}/crypto/constant_time_test.c
+ ${OPENSSL_SOURCES_DIR}/crypto/ec/ecp_nistz256_table.c
)
+
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
set_source_files_properties(
${OPENSSL_SOURCES}
diff --git a/Resources/Configuration.json b/Resources/Configuration.json
index 024347f..6ba93c6 100644
--- a/Resources/Configuration.json
+++ b/Resources/Configuration.json
@@ -118,7 +118,8 @@
// Whether or not SSL is enabled
"SslEnabled" : false,
- // Path to the SSL certificate (meaningful only if SSL is enabled)
+ // Path to the SSL certificate in the PEM format (meaningful only if
+ // SSL is enabled)
"SslCertificate" : "certificate.pem",
// Whether or not the password protection is enabled
@@ -144,7 +145,7 @@
* store (shipped in the DCMTK distribution) started by the
* command line "storescp 2000".
**/
- // "sample" : [ "STORESCP", "localhost", 2000 ]
+ // "sample" : [ "STORESCP", "127.0.0.1", 2000 ]
/**
* A fourth parameter is available to enable patches for a
@@ -164,8 +165,24 @@
* followed by the username/password pair (if the password
* protection is enabled on the peer).
**/
- // "peer" : [ "http://localhost:8043/", "alice", "alicePassword" ]
- // "peer2" : [ "http://localhost:8044/" ]
+ // "peer" : [ "http://127.0.0.1:8043/", "alice", "alicePassword" ]
+ // "peer2" : [ "http://127.0.0.1:8044/" ]
+
+ /**
+ * This is another, more advanced format to define Orthanc
+ * peers. It notably allows to specify a HTTPS client certificate
+ * in the PEM format (as in the "--cert" option of curl), or to
+ * enable PKCS#11 authentication for smart cards.
+ **/
+ // "peer" : {
+ // "Url" : "http://127.0.0.1:8043/",
+ // "Username" : "alice",
+ // "Password" : "alicePassword",
+ // "CertificateFile" : "client.crt",
+ // "CertificateKeyFile" : "client.key",
+ // "CertificateKeyPassword" : "certpass",
+ // "Pkcs11" : false
+ // }
},
// Parameters of the HTTP proxy to be used by Orthanc. If set to the
@@ -177,7 +194,10 @@
// Set the timeout for HTTP requests issued by Orthanc (in seconds).
"HttpTimeout" : 10,
- // Enable the verification of the peers during HTTPS requests.
+ // Enable the verification of the peers during HTTPS requests. This
+ // option must be set to "false" if using self-signed certificates.
+ // Pay attention that setting this option to "false" results in
+ // security risks!
// Reference: http://curl.haxx.se/docs/sslcerts.html
"HttpsVerifyPeers" : true,
@@ -270,7 +290,23 @@
// (such as PatientName). By default, the search is
// case-insensitive, which does not follow the DICOM standard.
"CaseSensitivePN" : false,
+
+ // Configure PKCS#11 to use hardware security modules (HSM) and
+ // smart cards when carrying on HTTPS client authentication.
+ /**
+ "Pkcs11" : {
+ "Module" : "/usr/local/lib/libbeidpkcs11.so",
+ "Module" : "C:/Windows/System32/beidpkcs11.dll",
+ "Pin" : "1234",
+ "Verbose" : true
+ }
+ **/
+ // If set to "true", Orthanc will handle "SOP Classes in Study"
+ // (0008,0062) in C-FIND requests. This option is turned off by
+ // default, as it requires intensive accesses to the hard drive.
+ "AllowFindSopClassesInStudy" : false,
+
// Register a new tag in the dictionary of DICOM tags that are known
// to Orthanc. Each line must contain the tag (formatted as 2
// hexadecimal numbers), the value representation (2 upcase
diff --git a/Resources/DicomConformanceStatement.py b/Resources/DicomConformanceStatement.py
index 43a0792..c9f8ee8 100755
--- a/Resources/DicomConformanceStatement.py
+++ b/Resources/DicomConformanceStatement.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/EmbedResources.py b/Resources/EmbedResources.py
index d71ad22..d914d15 100755
--- a/Resources/EmbedResources.py
+++ b/Resources/EmbedResources.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/ErrorCodes.json b/Resources/ErrorCodes.json
index 77758c7..f6ac2a7 100644
--- a/Resources/ErrorCodes.json
+++ b/Resources/ErrorCodes.json
@@ -327,12 +327,12 @@
{
"Code": 2003,
"Name": "HttpPortInUse",
- "Description": "The TCP port of the HTTP server is already in use"
+ "Description": "The TCP port of the HTTP server is privileged or already in use"
},
{
"Code": 2004,
"Name": "DicomPortInUse",
- "Description": "The TCP port of the DICOM server is already in use"
+ "Description": "The TCP port of the DICOM server is privileged or already in use"
},
{
"Code": 2005,
@@ -518,5 +518,10 @@
"Code": 2041,
"Name": "NoWorklistHandler",
"Description": "No request handler factory for DICOM C-Find Modality SCP"
+ },
+ {
+ "Code": 2042,
+ "Name": "AlreadyExistingTag",
+ "Description": "Cannot override the value of a tag that already exists"
}
]
diff --git a/Resources/Fonts/GenerateFont.py b/Resources/Fonts/GenerateFont.py
index 6ff4e6b..e49f332 100755
--- a/Resources/Fonts/GenerateFont.py
+++ b/Resources/Fonts/GenerateFont.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/GenerateErrorCodes.py b/Resources/GenerateErrorCodes.py
index f0979e4..863c283 100755
--- a/Resources/GenerateErrorCodes.py
+++ b/Resources/GenerateErrorCodes.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/OrthancLogo.png b/Resources/OrthancLogo.png
index dbdfe9d..abf5915 100644
Binary files a/Resources/OrthancLogo.png and b/Resources/OrthancLogo.png differ
diff --git a/Resources/OrthancLogoDocumentation.png b/Resources/OrthancLogoDocumentation.png
index 743fc19..4cf57af 100644
Binary files a/Resources/OrthancLogoDocumentation.png and b/Resources/OrthancLogoDocumentation.png differ
diff --git a/Resources/Patches/dcmtk-mingw64.patch b/Resources/Patches/dcmtk-3.6.0-mingw64.patch
similarity index 100%
rename from Resources/Patches/dcmtk-mingw64.patch
rename to Resources/Patches/dcmtk-3.6.0-mingw64.patch
diff --git a/Resources/Patches/dcmtk-3.6.0-speed.patch b/Resources/Patches/dcmtk-3.6.0-speed.patch
new file mode 100644
index 0000000..185dfe9
--- /dev/null
+++ b/Resources/Patches/dcmtk-3.6.0-speed.patch
@@ -0,0 +1,44 @@
+diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc dcmtk-3.6.0/dcmnet/libsrc/dul.cc
+--- dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc 2016-04-05 14:30:18.254459281 +0200
++++ dcmtk-3.6.0/dcmnet/libsrc/dul.cc 2016-04-05 14:32:07.246463713 +0200
+@@ -1770,7 +1770,7 @@
+ // send number of socket handle in child process over anonymous pipe
+ DWORD bytesWritten;
+ char buf[20];
+- sprintf(buf, "%i", OFreinterpret_cast(int, childSocketHandle));
++ sprintf(buf, "%i", OFstatic_cast(int, OFreinterpret_cast(size_t, childSocketHandle)));
+ if (!WriteFile(hChildStdInWriteDup, buf, strlen(buf) + 1, &bytesWritten, NULL))
+ {
+ CloseHandle(hChildStdInWriteDup);
+@@ -1780,7 +1780,7 @@
+ // return OF_ok status code DULC_FORKEDCHILD with descriptive text
+ OFOStringStream stream;
+ stream << "New child process started with pid " << OFstatic_cast(int, pi.dwProcessId)
+- << ", socketHandle " << OFreinterpret_cast(int, childSocketHandle) << OFStringStream_ends;
++ << ", socketHandle " << OFstatic_cast(int, OFreinterpret_cast(size_t, childSocketHandle)) << OFStringStream_ends;
+ OFSTRINGSTREAM_GETOFSTRING(stream, msg)
+ return makeDcmnetCondition(DULC_FORKEDCHILD, OF_ok, msg.c_str());
+ }
+@@ -1840,7 +1840,7 @@
+ }
+ #endif
+ #endif
+- setTCPBufferLength(sock);
++ //setTCPBufferLength(sock);
+
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+ /*
+Only in dcmtk-3.6.0/dcmnet/libsrc: dul.cc~
+diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc
+--- dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc 2016-04-05 14:30:18.250459281 +0200
++++ dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc 2016-04-05 14:32:20.566464254 +0200
+@@ -2417,7 +2417,7 @@
+ return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
+ }
+ #endif
+- setTCPBufferLength(s);
++ //setTCPBufferLength(s);
+
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+ /*
+Only in dcmtk-3.6.0/dcmnet/libsrc: dulfsm.cc~
diff --git a/Resources/Patches/dcmtk-3.6.1-speed.patch b/Resources/Patches/dcmtk-3.6.1-speed.patch
new file mode 100644
index 0000000..97b4a25
--- /dev/null
+++ b/Resources/Patches/dcmtk-3.6.1-speed.patch
@@ -0,0 +1,26 @@
+diff -urEb dcmtk-3.6.1_20160216.orig/dcmnet/libsrc/dul.cc dcmtk-3.6.1_20160216/dcmnet/libsrc/dul.cc
+--- dcmtk-3.6.1_20160216.orig/dcmnet/libsrc/dul.cc 2016-04-05 12:56:28.962230391 +0200
++++ dcmtk-3.6.1_20160216/dcmnet/libsrc/dul.cc 2016-04-05 12:57:15.814232296 +0200
+@@ -1841,7 +1841,7 @@
+ return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
+ }
+ #endif
+- setTCPBufferLength(sock);
++ //setTCPBufferLength(sock);
+
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+ /*
+Only in dcmtk-3.6.1_20160216/dcmnet/libsrc: dul.cc~
+diff -urEb dcmtk-3.6.1_20160216.orig/dcmnet/libsrc/dulfsm.cc dcmtk-3.6.1_20160216/dcmnet/libsrc/dulfsm.cc
+--- dcmtk-3.6.1_20160216.orig/dcmnet/libsrc/dulfsm.cc 2016-04-05 12:56:28.962230391 +0200
++++ dcmtk-3.6.1_20160216/dcmnet/libsrc/dulfsm.cc 2016-04-05 12:57:31.946232952 +0200
+@@ -2431,7 +2431,7 @@
+ return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
+ }
+ #endif
+- setTCPBufferLength(s);
++ //setTCPBufferLength(s);
+
+ #ifndef DONT_DISABLE_NAGLE_ALGORITHM
+ /*
+Only in dcmtk-3.6.1_20160216/dcmnet/libsrc: dulfsm.cc~
diff --git a/Resources/Patches/dcmtk-linux-speed.patch b/Resources/Patches/dcmtk-linux-speed.patch
deleted file mode 100644
index 3b4857a..0000000
--- a/Resources/Patches/dcmtk-linux-speed.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc dcmtk-3.6.0/dcmnet/libsrc/dul.cc
---- dcmtk-3.6.0.orig/dcmnet/libsrc/dul.cc 2010-12-01 09:26:36.000000000 +0100
-+++ dcmtk-3.6.0/dcmnet/libsrc/dul.cc 2015-05-15 17:03:50.762451757 +0200
-@@ -1840,7 +1840,7 @@
- }
- #endif
- #endif
-- setTCPBufferLength(sock);
-+ //setTCPBufferLength(sock);
-
- #ifndef DONT_DISABLE_NAGLE_ALGORITHM
- /*
-diff -urEb dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc
---- dcmtk-3.6.0.orig/dcmnet/libsrc/dulfsm.cc 2010-12-01 09:26:36.000000000 +0100
-+++ dcmtk-3.6.0/dcmnet/libsrc/dulfsm.cc 2015-05-15 17:03:55.570451952 +0200
-@@ -2417,7 +2417,7 @@
- return makeDcmnetCondition(DULC_TCPINITERROR, OF_error, msg.c_str());
- }
- #endif
-- setTCPBufferLength(s);
-+ //setTCPBufferLength(s);
-
- #ifndef DONT_DISABLE_NAGLE_ALGORITHM
- /*
diff --git a/Resources/Patches/dcmtk-mingw64.txt b/Resources/Patches/dcmtk-mingw64.txt
deleted file mode 100644
index f5b44d7..0000000
--- a/Resources/Patches/dcmtk-mingw64.txt
+++ /dev/null
@@ -1 +0,0 @@
-diff -urEb dcmtk-3.6.0.orig/ dcmtk-3.6.0 > ../Resources/Patches/dcmtk-mingw64.patch
diff --git a/Resources/Patches/dcmtk.txt b/Resources/Patches/dcmtk.txt
new file mode 100644
index 0000000..851fa4e
--- /dev/null
+++ b/Resources/Patches/dcmtk.txt
@@ -0,0 +1 @@
+diff -urEb dcmtk-3.6.0.orig/ dcmtk-3.6.0
diff --git a/Resources/Patches/glog-port-cc.diff b/Resources/Patches/glog-port-cc.diff
deleted file mode 100644
index b9e8f53..0000000
--- a/Resources/Patches/glog-port-cc.diff
+++ /dev/null
@@ -1,15 +0,0 @@
---- i/glog-0.3.2/src/windows/port.cc 2011-08-30 09:56:13.000000000 +0200
-+++ port.cc 2012-10-03 17:11:54.000000000 +0200
-@@ -55,6 +55,7 @@
- return _vsnprintf(str, size-1, format, ap);
- }
-
-+#if !defined(__MINGW32__)
- int snprintf(char *str, size_t size, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
-@@ -62,3 +63,4 @@
- va_end(ap);
- return r;
- }
-+#endif
diff --git a/Resources/Patches/glog-port-h-v2.diff b/Resources/Patches/glog-port-h-v2.diff
deleted file mode 100644
index 10ed828..0000000
--- a/Resources/Patches/glog-port-h-v2.diff
+++ /dev/null
@@ -1,52 +0,0 @@
-124,130c124,146
-< // ----------------------------------- THREADS
-< typedef DWORD pthread_t;
-< typedef DWORD pthread_key_t;
-< typedef LONG pthread_once_t;
-< enum { PTHREAD_ONCE_INIT = 0 }; // important that this be 0! for SpinLock
-< #define pthread_self GetCurrentThreadId
-< #define pthread_equal(pthread_t_1, pthread_t_2) ((pthread_t_1)==(pthread_t_2))
----
-> // ----------------------------------- SECURE STRINGS
->
-> #if HAVE_SECURE_STRING_EXTENSIONS == 0
-> // Emulation of "localtime_s" and "strerror_s" for old versions of MinGW
-> inline int localtime_s(tm * _tm, const time_t * time)
-> {
-> tm * posix_local_time_struct = localtime(time);
-> if (posix_local_time_struct == NULL)
-> {
-> return 1;
-> }
->
-> *_tm = *posix_local_time_struct;
->
-> return 0;
-> }
->
-> inline char* strerror_s(char* buf, size_t buflen, int errnum)
-> {
-> const char* str = strerror(errnum);
-> return strncpy(buf, str, buflen - 1);
-> }
-> #endif
-131a148,149
->
-> #if !defined(__MINGW32__) || HAVE_SECURE_STRING_EXTENSIONS == 0
-135a154,155
-> #endif
->
-140a161,173
->
->
-> // ----------------------------------- THREADS
->
-> #if !defined(__MINGW32__) || HAVE_WIN_PTHREAD == 0
-> typedef DWORD pthread_t;
-> typedef DWORD pthread_key_t;
-> typedef LONG pthread_once_t;
-> enum { PTHREAD_ONCE_INIT = 0 }; // important that this be 0! for SpinLock
-> #define pthread_self GetCurrentThreadId
-> #define pthread_equal(pthread_t_1, pthread_t_2) ((pthread_t_1)==(pthread_t_2))
-> #endif
->
diff --git a/Resources/Patches/glog-port-h.diff b/Resources/Patches/glog-port-h.diff
deleted file mode 100644
index f7970d8..0000000
--- a/Resources/Patches/glog-port-h.diff
+++ /dev/null
@@ -1,30 +0,0 @@
---- /tmp/m/glog-0.3.2/src/windows/port.h 2012-10-03 12:54:10.958149861 +0200
-+++ port.h 2012-10-03 16:19:56.721837994 +0200
-@@ -129,6 +129,27 @@
- #define pthread_self GetCurrentThreadId
- #define pthread_equal(pthread_t_1, pthread_t_2) ((pthread_t_1)==(pthread_t_2))
-
-+#if defined(__MINGW32__)
-+inline int localtime_s(tm * _tm, const time_t * time)
-+{
-+ tm * posix_local_time_struct = localtime(time);
-+ if (posix_local_time_struct == NULL)
-+ {
-+ return 1;
-+ }
-+
-+ *_tm = *posix_local_time_struct;
-+
-+ return 0;
-+}
-+
-+inline char* strerror_s(char* buf, size_t buflen, int errnum)
-+{
-+ const char* str = strerror(errnum);
-+ return strncpy(buf, str, buflen - 1);
-+}
-+#endif
-+
- inline struct tm* localtime_r(const time_t* timep, struct tm* result) {
- localtime_s(result, timep);
- return result;
diff --git a/Resources/Patches/glog-utilities-lsb.diff b/Resources/Patches/glog-utilities-lsb.diff
deleted file mode 100644
index e2b8540..0000000
--- a/Resources/Patches/glog-utilities-lsb.diff
+++ /dev/null
@@ -1,52 +0,0 @@
---- utilities.cc.orig 2012-01-12 09:40:21.000000000 +0100
-+++ utilities.cc 2013-09-23 17:37:35.033275313 +0200
-@@ -233,40 +233,7 @@
- }
-
- pid_t GetTID() {
-- // On Linux and FreeBSD, we try to use gettid().
--#if defined OS_LINUX || defined OS_FREEBSD || defined OS_MACOSX
--#ifndef __NR_gettid
--#ifdef OS_MACOSX
--#define __NR_gettid SYS_gettid
--#elif ! defined __i386__
--#error "Must define __NR_gettid for non-x86 platforms"
--#else
--#define __NR_gettid 224
--#endif
--#endif
-- static bool lacks_gettid = false;
-- if (!lacks_gettid) {
-- pid_t tid = syscall(__NR_gettid);
-- if (tid != -1) {
-- return tid;
-- }
-- // Technically, this variable has to be volatile, but there is a small
-- // performance penalty in accessing volatile variables and there should
-- // not be any serious adverse effect if a thread does not immediately see
-- // the value change to "true".
-- lacks_gettid = true;
-- }
--#endif // OS_LINUX || OS_FREEBSD
--
-- // If gettid() could not be used, we use one of the following.
--#if defined OS_LINUX
-- return getpid(); // Linux: getpid returns thread ID when gettid is absent
--#elif defined OS_WINDOWS || defined OS_CYGWIN
-- return GetCurrentThreadId();
--#else
-- // If none of the techniques above worked, we use pthread_self().
- return (pid_t)(uintptr_t)pthread_self();
--#endif
- }
-
- const char* const_basename(const char* filepath) {
-@@ -295,7 +262,7 @@
- g_my_user_name = "invalid-user";
- }
- }
--REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer());
-+REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer())
-
- #ifdef HAVE_STACKTRACE
- void DumpStackTraceToString(string* stacktrace) {
diff --git a/Resources/Patches/glog-utilities.diff b/Resources/Patches/glog-utilities.diff
deleted file mode 100644
index b0438e5..0000000
--- a/Resources/Patches/glog-utilities.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- /tmp/m/glog-0.3.2/src/utilities.cc 2012-01-12 09:40:21.000000000 +0100
-+++ utilities.cc 2012-10-03 16:04:19.745861665 +0200
-@@ -295,7 +295,7 @@
- g_my_user_name = "invalid-user";
- }
- }
-+REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer())
--REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer());
-
- #ifdef HAVE_STACKTRACE
- void DumpStackTraceToString(string* stacktrace) {
diff --git a/Resources/Patches/glog-visual-studio-port.h b/Resources/Patches/glog-visual-studio-port.h
deleted file mode 100644
index bd7868b..0000000
--- a/Resources/Patches/glog-visual-studio-port.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/* Copyright (c) 2008, Google Inc.
- * 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. 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.
- *
- * ---
- * Author: Craig Silverstein
- * Copied from google-perftools and modified by Shinichiro Hamaji
- *
- * These are some portability typedefs and defines to make it a bit
- * easier to compile this code under VC++.
- *
- * Several of these are taken from glib:
- * http://developer.gnome.org/doc/API/glib/glib-windows-compatability-functions.html
- */
-
-#ifndef CTEMPLATE_WINDOWS_PORT_H_
-#define CTEMPLATE_WINDOWS_PORT_H_
-
-#include "config.h"
-
-#ifdef _WIN32
-
-#define WIN32_LEAN_AND_MEAN /* We always want minimal includes */
-#include <windows.h>
-#include <winsock.h> /* for gethostname */
-#include <io.h> /* because we so often use open/close/etc */
-#include <direct.h> /* for _getcwd() */
-#include <process.h> /* for _getpid() */
-#include <stdio.h> /* read in vsnprintf decl. before redifining it */
-#include <stdarg.h> /* template_dictionary.cc uses va_copy */
-#include <string.h> /* for _strnicmp(), strerror_s() */
-#include <time.h> /* for localtime_s() */
-/* Note: the C++ #includes are all together at the bottom. This file is
- * used by both C and C++ code, so we put all the C++ together.
- */
-
-// Fix by Sebastien Jodogne for Visual Studio 2013
-// https://code.google.com/p/google-glog/issues/detail?id=212
-#if defined(_MSC_VER) && (_MSC_VER >= 1800)
-#include <algorithm>
-#endif
-
-/* 4244: otherwise we get problems when substracting two size_t's to an int
- * 4251: it's complaining about a private struct I've chosen not to dllexport
- * 4355: we use this in a constructor, but we do it safely
- * 4715: for some reason VC++ stopped realizing you can't return after abort()
- * 4800: we know we're casting ints/char*'s to bools, and we're ok with that
- * 4996: Yes, we're ok using "unsafe" functions like fopen() and strerror()
- */
-#pragma warning(disable:4244 4251 4355 4715 4800 4996)
-
-/* file I/O */
-#define PATH_MAX 1024
-#define access _access
-#define getcwd _getcwd
-#define open _open
-#define read _read
-#define write _write
-#define lseek _lseek
-#define close _close
-#define popen _popen
-#define pclose _pclose
-#define R_OK 04 /* read-only (for access()) */
-#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
-#ifndef __MINGW32__
-enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
-#endif
-#define S_IRUSR S_IREAD
-#define S_IWUSR S_IWRITE
-
-/* Not quite as lightweight as a hard-link, but more than good enough for us. */
-#define link(oldpath, newpath) CopyFileA(oldpath, newpath, false)
-
-#define strcasecmp _stricmp
-#define strncasecmp _strnicmp
-
-/* In windows-land, hash<> is called hash_compare<> (from xhash.h) */
-#if _MSC_VER < 1700
-#define hash hash_compare
-#endif
-
-/* Sleep is in ms, on windows */
-#define sleep(secs) Sleep((secs) * 1000)
-
-/* We can't just use _vsnprintf and _snprintf as drop-in-replacements,
- * because they don't always NUL-terminate. :-( We also can't use the
- * name vsnprintf, since windows defines that (but not snprintf (!)).
- */
-extern int snprintf(char *str, size_t size,
- const char *format, ...);
-extern int safe_vsnprintf(char *str, size_t size,
- const char *format, va_list ap);
-#define vsnprintf(str, size, format, ap) safe_vsnprintf(str, size, format, ap)
-#define va_copy(dst, src) (dst) = (src)
-
-/* Windows doesn't support specifying the number of buckets as a
- * hash_map constructor arg, so we leave this blank.
- */
-#define CTEMPLATE_SMALL_HASHTABLE
-
-#define DEFAULT_TEMPLATE_ROOTDIR ".."
-
-// ----------------------------------- SYSTEM/PROCESS
-typedef int pid_t;
-#define getpid _getpid
-
-// ----------------------------------- THREADS
-typedef DWORD pthread_t;
-typedef DWORD pthread_key_t;
-typedef LONG pthread_once_t;
-enum { PTHREAD_ONCE_INIT = 0 }; // important that this be 0! for SpinLock
-#define pthread_self GetCurrentThreadId
-#define pthread_equal(pthread_t_1, pthread_t_2) ((pthread_t_1)==(pthread_t_2))
-
-#if defined(__MINGW32__)
-inline int localtime_s(tm * _tm, const time_t * time)
-{
- tm * posix_local_time_struct = localtime(time);
- if (posix_local_time_struct == NULL)
- {
- return 1;
- }
-
- *_tm = *posix_local_time_struct;
-
- return 0;
-}
-
-inline char* strerror_s(char* buf, size_t buflen, int errnum)
-{
- const char* str = strerror(errnum);
- return strncpy(buf, str, buflen - 1);
-}
-#endif
-
-inline struct tm* localtime_r(const time_t* timep, struct tm* result) {
- localtime_s(result, timep);
- return result;
-}
-
-inline char* strerror_r(int errnum, char* buf, size_t buflen) {
- strerror_s(buf, buflen, errnum);
- return buf;
-}
-
-#ifndef __cplusplus
-/* I don't see how to get inlining for C code in MSVC. Ah well. */
-#define inline
-#endif
-
-#endif /* _WIN32 */
-
-#endif /* CTEMPLATE_WINDOWS_PORT_H_ */
diff --git a/Resources/Patches/libp11-0.4.0.patch b/Resources/Patches/libp11-0.4.0.patch
new file mode 100644
index 0000000..496389e
--- /dev/null
+++ b/Resources/Patches/libp11-0.4.0.patch
@@ -0,0 +1,36 @@
+diff -urEb libp11-0.4.0.orig/src/atfork.c libp11-0.4.0/src/atfork.c
+--- libp11-0.4.0.orig/src/atfork.c 2016-06-20 13:38:43.845575107 +0200
++++ libp11-0.4.0/src/atfork.c 2016-06-20 13:46:52.969575591 +0200
+@@ -25,7 +25,7 @@
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+-#include <atfork.h>
++#include "atfork.h"
+
+ #ifdef __sun
+ # pragma fini(lib_deinit)
+diff -urEb libp11-0.4.0.orig/src/engine.h libp11-0.4.0/src/engine.h
+--- libp11-0.4.0.orig/src/engine.h 2016-06-20 13:38:43.845575107 +0200
++++ libp11-0.4.0/src/engine.h 2016-06-20 13:46:27.421575566 +0200
+@@ -29,7 +29,7 @@
+ #define _ENGINE_PKCS11_H
+
+ #ifndef _WIN32
+-#include "config.h"
++//#include "config.h"
+ #endif
+
+ #include "libp11.h"
+diff -urEb libp11-0.4.0.orig/src/libp11-int.h libp11-0.4.0/src/libp11-int.h
+--- libp11-0.4.0.orig/src/libp11-int.h 2016-06-20 13:38:43.845575107 +0200
++++ libp11-0.4.0/src/libp11-int.h 2016-06-20 13:46:27.421575566 +0200
+@@ -20,7 +20,7 @@
+ #define _LIBP11_INT_H
+
+ #ifndef _WIN32
+-#include "config.h"
++//#include "config.h"
+ #endif
+
+ #include "libp11.h"
diff --git a/Resources/Patches/log4cplus-patch.diff b/Resources/Patches/log4cplus-patch.diff
deleted file mode 100644
index 1ae5074..0000000
--- a/Resources/Patches/log4cplus-patch.diff
+++ /dev/null
@@ -1,10 +0,0 @@
---- log4cplus-1.0.4.1/src/factory.cxx 2010-05-28 11:06:51.000000000 +0200
-+++ factory.cxx 2012-10-02 11:43:31.808439489 +0200
-@@ -35,7 +35,7 @@
- # if defined (LOG4CPLUS_HAVE_WIN32_CONSOLE)
- # include <log4cplus/win32consoleappender.h>
- # endif
--# include <log4cplus/Win32DebugAppender.h>
-+# include <log4cplus/win32debugappender.h>
- #endif
-
diff --git a/Resources/Patches/mongoose-3.1-patch.diff b/Resources/Patches/mongoose-3.1-patch.diff
index eb62dc8..13923a4 100644
--- a/Resources/Patches/mongoose-3.1-patch.diff
+++ b/Resources/Patches/mongoose-3.1-patch.diff
@@ -42,7 +42,7 @@
// Wait until mg_fini() stops
while (ctx->stop_flag != 2) {
-+#if defined(__linux)
++#if defined(__linux__)
+ usleep(100000);
+#elif defined(_WIN32)
+ Sleep(100);
diff --git a/Resources/RetrieveCACertificates.py b/Resources/RetrieveCACertificates.py
index 42882ec..5408c0b 100755
--- a/Resources/RetrieveCACertificates.py
+++ b/Resources/RetrieveCACertificates.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py b/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py
index 1403fe7..adb3262 100755
--- a/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py
+++ b/Resources/Samples/ImportDicomFiles/ImportDicomFiles.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -33,7 +33,7 @@ API.
Usage: %s [hostname] [HTTP port] [path]
Usage: %s [hostname] [HTTP port] [path] [username] [password]
-For instance: %s localhost 8042 .
+For instance: %s 127.0.0.1 8042 .
""" % (sys.argv[0], sys.argv[0], sys.argv[0]))
exit(-1)
diff --git a/Resources/Samples/Lua/AutoroutingModification.lua b/Resources/Samples/Lua/AutoroutingModification.lua
index 49dbb84..cd0eb26 100644
--- a/Resources/Samples/Lua/AutoroutingModification.lua
+++ b/Resources/Samples/Lua/AutoroutingModification.lua
@@ -1,8 +1,7 @@
-function OnStoredInstance(instanceId, tags, metadata)
- -- Ignore the instances that result from a modification to avoid
- -- infinite loops
- if (metadata['ModifiedFrom'] == nil and
- metadata['AnonymizedFrom'] == nil) then
+function OnStoredInstance(instanceId, tags, metadata, origin)
+ -- Ignore the instances that result from the present Lua script to
+ -- avoid infinite loops
+ if origin['RequestOrigin'] ~= 'Lua' then
-- The tags to be replaced
local replace = {}
@@ -12,10 +11,21 @@ function OnStoredInstance(instanceId, tags, metadata)
-- The tags to be removed
local remove = { 'MilitaryRank' }
- -- Modify the instance, send it, then delete the modified instance
- Delete(SendToModality(ModifyInstance(instanceId, replace, remove, true), 'sample'))
+ -- Modify the instance
+ local command = {}
+ command['Replace'] = replace
+ command['Remove'] = remove
+ local modifiedFile = RestApiPost('/instances/' .. instanceId .. '/modify', DumpJson(command, true))
- -- Delete the original instance
- Delete(instanceId)
+ -- Upload the modified instance to the Orthanc database so that
+ -- it can be sent by Orthanc to other modalities
+ local modifiedId = ParseJson(RestApiPost('/instances/', modifiedFile)) ['ID']
+
+ -- Send the modified instance to another modality
+ RestApiPost('/modalities/sample/store', modifiedId)
+
+ -- Delete the original and the modified instances
+ RestApiDelete('/instances/' .. instanceId)
+ RestApiDelete('/instances/' .. modifiedId)
end
end
diff --git a/Resources/Samples/Lua/CallWebService.js b/Resources/Samples/Lua/CallWebService.js
index 416230f..84f3130 100644
--- a/Resources/Samples/Lua/CallWebService.js
+++ b/Resources/Samples/Lua/CallWebService.js
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Resources/Samples/Lua/CallWebService.lua b/Resources/Samples/Lua/CallWebService.lua
index 24b465d..9674805 100644
--- a/Resources/Samples/Lua/CallWebService.lua
+++ b/Resources/Samples/Lua/CallWebService.lua
@@ -17,7 +17,7 @@ function OnStoredInstance(instanceId, tags, metadata)
info['PatientID'] = tags['PatientID']
-- Send the POST request
- local answer = HttpPost('http://localhost:8000/', JSON:encode(info))
+ local answer = HttpPost('http://127.0.0.1:8000/', JSON:encode(info))
-- The answer equals "ERROR" in case of an error
print('Web service called, answer received: ' .. answer)
diff --git a/Resources/Samples/Python/AnonymizeAllPatients.py b/Resources/Samples/Python/AnonymizeAllPatients.py
index 4b99c0b..b83bcb2 100755
--- a/Resources/Samples/Python/AnonymizeAllPatients.py
+++ b/Resources/Samples/Python/AnonymizeAllPatients.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -20,7 +20,7 @@
-URL = 'http://localhost:8042'
+URL = 'http://127.0.0.1:8042'
#
# This sample code will anonymize all the patients that are stored in
diff --git a/Resources/Samples/Python/AnonymizeAllPatients.py b/Resources/Samples/Python/ArchiveAllPatients.py
similarity index 51%
copy from Resources/Samples/Python/AnonymizeAllPatients.py
copy to Resources/Samples/Python/ArchiveAllPatients.py
index 4b99c0b..6c4be5c 100755
--- a/Resources/Samples/Python/AnonymizeAllPatients.py
+++ b/Resources/Samples/Python/ArchiveAllPatients.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -20,26 +20,28 @@
-URL = 'http://localhost:8042'
-
-#
-# This sample code will anonymize all the patients that are stored in
-# Orthanc.
-#
-
+import os
+import os.path
import sys
import RestToolbox
-# Loop over the patients
-for patient in RestToolbox.DoGet('%s/patients' % URL):
+def PrintHelp():
+ print('Download one ZIP archive for all the patients stored in Orthanc\n')
+ print('Usage: %s <URL> <Target>\n' % sys.argv[0])
+ print('Example: %s http://127.0.0.1:8042/ /tmp/Archive.zip\n' % sys.argv[0])
+ exit(-1)
+
+if len(sys.argv) != 3:
+ PrintHelp()
+
+URL = sys.argv[1]
+TARGET = sys.argv[2]
+
+patients = RestToolbox.DoGet('%s/patients' % URL)
- # Ignore patients whose name starts with "Anonymized", as it is
- # the result of a previous anonymization
- infos = RestToolbox.DoGet('%s/patients/%s' % (URL, patient))
- name = infos['MainDicomTags']['PatientName'].lower()
- if not name.startswith('anonymized'):
+print('Downloading ZIP...')
+zipContent = RestToolbox.DoPost('%s/tools/create-archive' % URL, patients)
- # Trigger the anonymization
- RestToolbox.DoPost('%s/patients/%s/anonymize' % (URL, patient),
- { 'Keep' : [ 'SeriesDescription',
- 'StudyDescription' ] })
+# Write the ZIP archive at the proper location
+with open(TARGET, 'wb') as f:
+ f.write(zipContent)
diff --git a/Resources/Samples/Python/ArchiveStudiesInTimeRange.py b/Resources/Samples/Python/ArchiveStudiesInTimeRange.py
index b97b4ac..0da7a21 100755
--- a/Resources/Samples/Python/ArchiveStudiesInTimeRange.py
+++ b/Resources/Samples/Python/ArchiveStudiesInTimeRange.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -29,7 +29,7 @@ def PrintHelp():
print('Download ZIP archives for all the studies generated '
'during a given time range (according to the StudyDate tag)\n')
print('Usage: %s <URL> <StartDate> <EndDate> <TargetFolder>\n' % sys.argv[0])
- print('Example: %s http://localhost:8042/ 20150101 20151231 /tmp/\n' % sys.argv[0])
+ print('Example: %s http://127.0.0.1:8042/ 20150101 20151231 /tmp/\n' % sys.argv[0])
exit(-1)
def CheckIsDate(date):
diff --git a/Resources/Samples/Python/AutoClassify.py b/Resources/Samples/Python/AutoClassify.py
index 353b734..1efd731 100755
--- a/Resources/Samples/Python/AutoClassify.py
+++ b/Resources/Samples/Python/AutoClassify.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -30,7 +30,7 @@ parser = argparse.ArgumentParser(
description = 'Automated classification of DICOM files from Orthanc.',
formatter_class = argparse.ArgumentDefaultsHelpFormatter)
-parser.add_argument('--host', default = 'localhost',
+parser.add_argument('--host', default = '127.0.0.1',
help = 'The host address that runs Orthanc')
parser.add_argument('--port', type = int, default = '8042',
help = 'The port number to which Orthanc is listening for the REST API')
diff --git a/Resources/Samples/Python/ChangesLoop.py b/Resources/Samples/Python/ChangesLoop.py
index 1c8428c..8030ccc 100755
--- a/Resources/Samples/Python/ChangesLoop.py
+++ b/Resources/Samples/Python/ChangesLoop.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -34,7 +34,7 @@ Sample script that continuously monitors the arrival of new DICOM
images into Orthanc (through the Changes API).
Usage: %s [hostname] [HTTP port]
-For instance: %s localhost 8042
+For instance: %s 127.0.0.1 8042
""" % (sys.argv[0], sys.argv[0]))
exit(-1)
diff --git a/Resources/Samples/Python/ContinuousPatientAnonymization.py b/Resources/Samples/Python/ContinuousPatientAnonymization.py
index 0142528..ae60ea7 100755
--- a/Resources/Samples/Python/ContinuousPatientAnonymization.py
+++ b/Resources/Samples/Python/ContinuousPatientAnonymization.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -37,7 +37,7 @@ been received for this patient for a sufficient amount of time - cf.
the configuration option "StableAge").
Usage: %s [hostname] [HTTP port]
-For instance: %s localhost 8042
+For instance: %s 127.0.0.1 8042
""" % (sys.argv[0], sys.argv[0]))
exit(-1)
diff --git a/Resources/Samples/Python/DownloadAnonymized.py b/Resources/Samples/Python/DownloadAnonymized.py
index c680255..721721e 100755
--- a/Resources/Samples/Python/DownloadAnonymized.py
+++ b/Resources/Samples/Python/DownloadAnonymized.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -20,7 +20,7 @@
-URL = 'http://localhost:8042'
+URL = 'http://127.0.0.1:8042'
#
# This sample code will download a ZIP file for each patient that has
diff --git a/Resources/Samples/Python/HighPerformanceAutoRouting.py b/Resources/Samples/Python/HighPerformanceAutoRouting.py
index 00c3393..3a00f8b 100755
--- a/Resources/Samples/Python/HighPerformanceAutoRouting.py
+++ b/Resources/Samples/Python/HighPerformanceAutoRouting.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -20,7 +20,7 @@
-URL = 'http://localhost:8042'
+URL = 'http://127.0.0.1:8042'
TARGET = 'sample'
diff --git a/Resources/Samples/Python/ManualModification.py b/Resources/Samples/Python/ManualModification.py
new file mode 100755
index 0000000..780de72
--- /dev/null
+++ b/Resources/Samples/Python/ManualModification.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, 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.
+#
+# 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/>.
+
+
+
+# This sample shows how to carry on a manual modification of DICOM
+# tags spread accross various levels (Patient/Study/Series/Instance)
+# that would normally forbidden as such by the REST API of Orthanc to
+# avoid breaking the DICOM hierarchy. This sample can be useful for
+# more complex anonymization/modification scenarios, or for optimizing
+# the disk usage (the original and the modified instances never
+# coexist).
+
+from RestToolbox import *
+
+URL = 'http://127.0.0.1:8042'
+STUDY = '27f7126f-4f66fb14-03f4081b-f9341db2-53925988'
+
+identifiers = {}
+
+for instance in DoGet('%s/studies/%s/instances' % (URL, STUDY)):
+ # Setup the parameters of the modification
+ replace = {
+ "PatientID" : "Hello",
+ "PatientName" : "Modified",
+ "StationName" : "TEST",
+ }
+
+ # Get the original UIDs of the instance
+ seriesUID = DoGet('%s/instances/%s/content/SeriesInstanceUID' % (URL, instance['ID']))
+ if seriesUID in identifiers:
+ replace['SeriesInstanceUID'] = identifiers[seriesUID]
+
+ studyUID = DoGet('%s/instances/%s/content/StudyInstanceUID' % (URL, instance['ID']))
+ if studyUID in identifiers:
+ replace['StudyInstanceUID'] = identifiers[studyUID]
+
+ # Manually modify the instance
+ print('Modifying instance %s' % instance['ID'])
+ modified = DoPost('%s/instances/%s/modify' % (URL, instance['ID']),
+ { "Replace" : replace })
+
+ # Remove the original instance
+ DoDelete('%s/instances/%s' % (URL, instance['ID']))
+
+ # Add the modified instance
+ modifiedId = DoPost('%s/instances' % URL, modified)['ID']
+
+ # Register the modified UIDs
+ identifiers[seriesUID] = DoGet('%s/instances/%s/content/SeriesInstanceUID' % (URL, modifiedId))
+ identifiers[studyUID] = DoGet('%s/instances/%s/content/StudyInstanceUID' % (URL, modifiedId))
diff --git a/Resources/Samples/Python/Replicate.py b/Resources/Samples/Python/Replicate.py
index f36a89d..e1134ac 100755
--- a/Resources/Samples/Python/Replicate.py
+++ b/Resources/Samples/Python/Replicate.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
@@ -33,7 +33,7 @@ Script to copy the content of one Orthanc server to another Orthanc
server through their REST API.
Usage: %s [SourceURI] [TargetURI]
-For instance: %s http://orthanc:password@localhost:8042/ http://localhost:8043/
+For instance: %s http://orthanc:password@127.0.0.1:8042/ http://127.0.0.1:8043/
""" % (sys.argv[0], sys.argv[0]))
exit(-1)
diff --git a/Resources/Samples/Python/RestToolbox.py b/Resources/Samples/Python/RestToolbox.py
index 3d5c063..fba0ad3 100644
--- a/Resources/Samples/Python/RestToolbox.py
+++ b/Resources/Samples/Python/RestToolbox.py
@@ -1,5 +1,5 @@
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/Resources/Samples/Tools/CMakeLists.txt b/Resources/Samples/Tools/CMakeLists.txt
index d093d95..ce4cb36 100644
--- a/Resources/Samples/Tools/CMakeLists.txt
+++ b/Resources/Samples/Tools/CMakeLists.txt
@@ -20,12 +20,12 @@ include(${ORTHANC_ROOT}/Resources/CMake/Compiler.cmake)
include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
include(${ORTHANC_ROOT}/Resources/CMake/ZlibConfiguration.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/GoogleLogConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
add_library(CommonLibraries
${BOOST_SOURCES}
- ${THIRD_PARTY_SOURCES}
- ${ORTHANC_ROOT}/Core/OrthancException.cpp
+ ${JSONCPP_SOURCES}
+ ${ORTHANC_ROOT}/Core/Enumerations.cpp
${ORTHANC_ROOT}/Core/Toolbox.cpp
${ORTHANC_ROOT}/Core/Uuid.cpp
${ORTHANC_ROOT}/Resources/ThirdParty/md5/md5.c
@@ -34,8 +34,9 @@ add_library(CommonLibraries
add_executable(RecoverCompressedFile
RecoverCompressedFile.cpp
- ${ORTHANC_ROOT}/Core/Compression/BufferCompressor.cpp
+ ${ORTHANC_ROOT}/Core/Compression/DeflateBaseCompressor.cpp
${ORTHANC_ROOT}/Core/Compression/ZlibCompressor.cpp
+ ${ZLIB_SOURCES}
)
-target_link_libraries(RecoverCompressedFile CommonLibraries GoogleLog)
+target_link_libraries(RecoverCompressedFile CommonLibraries)
diff --git a/Resources/Samples/Tools/RecoverCompressedFile.cpp b/Resources/Samples/Tools/RecoverCompressedFile.cpp
index 8231852..b99e4e3 100644
--- a/Resources/Samples/Tools/RecoverCompressedFile.cpp
+++ b/Resources/Samples/Tools/RecoverCompressedFile.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -47,7 +47,9 @@ int main(int argc, const char* argv[])
Orthanc::ZlibCompressor compressor;
std::string uncompressed;
- compressor.Uncompress(uncompressed, content);
+ compressor.Uncompress(uncompressed,
+ content.empty() ? NULL : content.c_str(),
+ content.size());
fprintf(stderr, "Writing the uncompressed data...\n");
fflush(stderr);
diff --git a/Resources/Samples/WebApplications/DrawingDicomizer.js b/Resources/Samples/WebApplications/DrawingDicomizer.js
index b89cce7..2b80e52 100644
--- a/Resources/Samples/WebApplications/DrawingDicomizer.js
+++ b/Resources/Samples/WebApplications/DrawingDicomizer.js
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js b/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js
index 2e44e66..49c1f6e 100644
--- a/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js
+++ b/Resources/Samples/WebApplications/DrawingDicomizer/orthanc.js
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Resources/Samples/WebApplications/NodeToolbox.js b/Resources/Samples/WebApplications/NodeToolbox.js
index 798b559..93fb267 100644
--- a/Resources/Samples/WebApplications/NodeToolbox.js
+++ b/Resources/Samples/WebApplications/NodeToolbox.js
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/Resources/WindowsResources.py b/Resources/WindowsResources.py
index 649a453..c56733b 100755
--- a/Resources/WindowsResources.py
+++ b/Resources/WindowsResources.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
#
# This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/DicomMapTests.cpp b/UnitTestsSources/DicomMapTests.cpp
index 851ae75..d3a4474 100644
--- a/UnitTestsSources/DicomMapTests.cpp
+++ b/UnitTestsSources/DicomMapTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -90,7 +90,7 @@ TEST(DicomMap, Tags)
ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_NAME));
ASSERT_FALSE(m.HasTag(0x0010, 0x0010));
- m.SetValue(0x0010, 0x0010, "PatientName");
+ m.SetValue(0x0010, 0x0010, "PatientName", false);
ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_NAME));
ASSERT_TRUE(m.HasTag(0x0010, 0x0010));
@@ -99,9 +99,9 @@ TEST(DicomMap, Tags)
ASSERT_EQ(DICOM_TAG_PATIENT_NAME, *s.begin());
ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_ID));
- m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID");
+ m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID", false);
ASSERT_TRUE(m.HasTag(0x0010, 0x0020));
- m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2");
+ m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2", false);
ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).GetContent());
m.GetTags(s);
@@ -117,7 +117,7 @@ TEST(DicomMap, Tags)
std::auto_ptr<DicomMap> mm(m.Clone());
ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).GetContent());
- m.SetValue(DICOM_TAG_PATIENT_ID, "Hello");
+ m.SetValue(DICOM_TAG_PATIENT_ID, "Hello", false);
ASSERT_THROW(mm->GetValue(DICOM_TAG_PATIENT_ID), OrthancException);
mm->CopyTagIfExists(m, DICOM_TAG_PATIENT_ID);
ASSERT_EQ("Hello", mm->GetValue(DICOM_TAG_PATIENT_ID).GetContent());
diff --git a/UnitTestsSources/FileStorageTests.cpp b/UnitTestsSources/FileStorageTests.cpp
index c90c971..6c9ef75 100644
--- a/UnitTestsSources/FileStorageTests.cpp
+++ b/UnitTestsSources/FileStorageTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/FromDcmtkTests.cpp b/UnitTestsSources/FromDcmtkTests.cpp
index 47a54fb..f35bdda 100644
--- a/UnitTestsSources/FromDcmtkTests.cpp
+++ b/UnitTestsSources/FromDcmtkTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -34,6 +34,7 @@
#include "gtest/gtest.h"
#include "../OrthancServer/FromDcmtkBridge.h"
+#include "../OrthancServer/ToDcmtkBridge.h"
#include "../OrthancServer/OrthancInitialization.h"
#include "../OrthancServer/DicomModification.h"
#include "../OrthancServer/ServerToolbox.h"
@@ -41,9 +42,14 @@
#include "../Core/Images/ImageBuffer.h"
#include "../Core/Images/PngReader.h"
#include "../Core/Images/PngWriter.h"
+#include "../Core/Images/Image.h"
+#include "../Core/Images/ImageProcessing.h"
#include "../Core/Uuid.h"
+#include "../Core/Endianness.h"
#include "../Resources/EncodingTests.h"
#include "../OrthancServer/DicomProtocol/DicomFindAnswers.h"
+#include "../OrthancServer/Internals/DicomImageDecoder.h"
+#include "../Plugins/Engine/PluginsEnumerations.h"
#include <dcmtk/dcmdata/dcelem.h>
@@ -72,8 +78,8 @@ TEST(DicomModification, Basic)
DicomModification m;
m.SetupAnonymization();
//m.SetLevel(DicomRootLevel_Study);
- //m.Replace(DICOM_TAG_PATIENT_ID, "coucou");
- //m.Replace(DICOM_TAG_PATIENT_NAME, "coucou");
+ //m.ReplacePlainString(DICOM_TAG_PATIENT_ID, "coucou");
+ //m.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
ParsedDicomFile o(true);
o.SaveToFile("UnitTestsResults/anon.dcm");
@@ -84,7 +90,7 @@ TEST(DicomModification, Basic)
sprintf(b, "UnitTestsResults/anon%06d.dcm", i);
std::auto_ptr<ParsedDicomFile> f(o.Clone());
if (i > 4)
- o.Replace(DICOM_TAG_SERIES_INSTANCE_UID, "coucou");
+ o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou");
m.Apply(*f);
f->SaveToFile(b);
}
@@ -104,21 +110,21 @@ TEST(DicomModification, Anonymization)
std::string s;
ParsedDicomFile o(true);
- o.Replace(DICOM_TAG_PATIENT_NAME, "coucou");
+ o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
ASSERT_FALSE(o.GetTagValue(s, privateTag));
o.Insert(privateTag, "private tag", false);
ASSERT_TRUE(o.GetTagValue(s, privateTag));
ASSERT_STREQ("private tag", s.c_str());
ASSERT_FALSE(o.GetTagValue(s, privateTag2));
- ASSERT_THROW(o.Replace(privateTag2, "hello", DicomReplaceMode_ThrowIfAbsent), OrthancException);
+ ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent), OrthancException);
ASSERT_FALSE(o.GetTagValue(s, privateTag2));
- o.Replace(privateTag2, "hello", DicomReplaceMode_IgnoreIfAbsent);
+ o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent);
ASSERT_FALSE(o.GetTagValue(s, privateTag2));
- o.Replace(privateTag2, "hello", DicomReplaceMode_InsertIfAbsent);
+ o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent);
ASSERT_TRUE(o.GetTagValue(s, privateTag2));
ASSERT_STREQ("hello", s.c_str());
- o.Replace(privateTag2, "hello world");
+ o.ReplacePlainString(privateTag2, "hello world");
ASSERT_TRUE(o.GetTagValue(s, privateTag2));
ASSERT_STREQ("hello world", s.c_str());
@@ -150,7 +156,7 @@ TEST(DicomModification, Png)
std::string s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
std::string m, cc;
- Toolbox::DecodeDataUriScheme(m, cc, s);
+ ASSERT_TRUE(Toolbox::DecodeDataUriScheme(m, cc, s));
ASSERT_EQ("image/png", m);
@@ -173,7 +179,7 @@ TEST(DicomModification, Png)
// Check box in Graylevel8
s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII=";
o.EmbedContent(s);
- //o.Replace(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
+ //o.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
o.SaveToFile("UnitTestsResults/png3.dcm");
@@ -216,12 +222,13 @@ TEST(FromDcmtkBridge, Encodings1)
TEST(FromDcmtkBridge, Enumerations)
{
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
Encoding e;
ASSERT_FALSE(GetDicomEncoding(e, ""));
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 6")); ASSERT_EQ(Encoding_Utf8, e);
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-2
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-2
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 100")); ASSERT_EQ(Encoding_Latin1, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 101")); ASSERT_EQ(Encoding_Latin2, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 109")); ASSERT_EQ(Encoding_Latin3, e);
@@ -231,11 +238,11 @@ TEST(FromDcmtkBridge, Enumerations)
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 126")); ASSERT_EQ(Encoding_Greek, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 148")); ASSERT_EQ(Encoding_Latin5, e);
- ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 13")); ASSERT_EQ(Encoding_Japanese, e);
+ ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 13")); ASSERT_EQ(Encoding_Japanese, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 166")); ASSERT_EQ(Encoding_Thai, e);
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-3
- ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 6")); ASSERT_EQ(Encoding_Utf8, e);
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-3
+ ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 6")); ASSERT_EQ(Encoding_Utf8, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 100")); ASSERT_EQ(Encoding_Latin1, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 101")); ASSERT_EQ(Encoding_Latin2, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 109")); ASSERT_EQ(Encoding_Latin3, e);
@@ -245,17 +252,17 @@ TEST(FromDcmtkBridge, Enumerations)
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 126")); ASSERT_EQ(Encoding_Greek, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 148")); ASSERT_EQ(Encoding_Latin5, e);
- ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 13")); ASSERT_EQ(Encoding_Japanese, e);
+ ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 13")); ASSERT_EQ(Encoding_Japanese, e);
ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 166")); ASSERT_EQ(Encoding_Thai, e);
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-4
- ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 87")); //ASSERT_EQ(Encoding_JapaneseKanji, e);
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-4
+ ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 87")); //ASSERT_EQ(Encoding_JapaneseKanji, e);
ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 159")); //ASSERT_EQ(Encoding_JapaneseKanjiSupplementary, e);
ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 149")); //ASSERT_EQ(Encoding_Korean, e);
- // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-5
+ // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-5
ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 192")); ASSERT_EQ(Encoding_Utf8, e);
- ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e);
+ ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e);
}
@@ -294,16 +301,59 @@ TEST(FromDcmtkBridge, Encodings3)
TEST(FromDcmtkBridge, ValueRepresentation)
{
- ASSERT_EQ(ValueRepresentation_PatientName,
- FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_NAME));
+ ASSERT_EQ(ValueRepresentation_PersonName,
+ FromDcmtkBridge::LookupValueRepresentation(DICOM_TAG_PATIENT_NAME));
ASSERT_EQ(ValueRepresentation_Date,
- FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
+ FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
ASSERT_EQ(ValueRepresentation_Time,
- FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */));
+ FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */));
ASSERT_EQ(ValueRepresentation_DateTime,
- FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */));
- ASSERT_EQ(ValueRepresentation_Other,
- FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_ID));
+ FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */));
+ ASSERT_EQ(ValueRepresentation_NotSupported,
+ FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0001, 0x0001) /* some private tag */));
+}
+
+
+TEST(FromDcmtkBridge, ValueRepresentationConversions)
+{
+ ASSERT_EQ(1, ValueRepresentation_ApplicationEntity);
+ ASSERT_EQ(1, OrthancPluginValueRepresentation_AE);
+
+ for (int i = ValueRepresentation_ApplicationEntity;
+ i <= ValueRepresentation_NotSupported; i++)
+ {
+ ValueRepresentation vr = static_cast<ValueRepresentation>(i);
+
+ if (vr == ValueRepresentation_NotSupported)
+ {
+ ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
+ ASSERT_THROW(Plugins::Convert(vr), OrthancException);
+ }
+ else if (vr == ValueRepresentation_OtherDouble ||
+ vr == ValueRepresentation_OtherLong ||
+ vr == ValueRepresentation_UniversalResource ||
+ vr == ValueRepresentation_UnlimitedCharacters)
+ {
+ // These VR are not supported as of DCMTK 3.6.0
+ ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
+ ASSERT_EQ(OrthancPluginValueRepresentation_UN, Plugins::Convert(vr));
+ }
+ else
+ {
+ ASSERT_EQ(vr, FromDcmtkBridge::Convert(ToDcmtkBridge::Convert(vr)));
+
+ OrthancPluginValueRepresentation plugins = Plugins::Convert(vr);
+ ASSERT_EQ(vr, Plugins::Convert(plugins));
+ }
+ }
+
+ for (int i = OrthancPluginValueRepresentation_AE;
+ i <= OrthancPluginValueRepresentation_UT; i++)
+ {
+ OrthancPluginValueRepresentation plugins = static_cast<OrthancPluginValueRepresentation>(i);
+ ValueRepresentation orthanc = Plugins::Convert(plugins);
+ ASSERT_EQ(plugins, Plugins::Convert(orthanc));
+ }
}
@@ -377,12 +427,12 @@ TEST(FromDcmtkBridge, FromJson)
Json::Value b;
FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii);
ASSERT_EQ(Json::arrayValue, b["0008,1110"].type());
- ASSERT_EQ(2, b["0008,1110"].size());
+ ASSERT_EQ(2u, b["0008,1110"].size());
Json::Value::ArrayIndex i = (b["0008,1110"][0]["0010,0010"].asString() == "Hello") ? 0 : 1;
- ASSERT_EQ(3, b["0008,1110"][i].size());
- ASSERT_EQ(2, b["0008,1110"][1 - i].size());
+ ASSERT_EQ(3u, b["0008,1110"][i].size());
+ ASSERT_EQ(2u, b["0008,1110"][1 - i].size());
ASSERT_EQ(b["0008,1110"][i]["0010,0010"].asString(), "Hello");
ASSERT_EQ(b["0008,1110"][i]["0010,0020"].asString(), "World");
ASSERT_EQ(b["0008,1110"][i]["0008,1030"].asString(), "Toto");
@@ -411,21 +461,22 @@ TEST(ParsedDicomFile, InsertReplaceStrings)
f.Insert(DICOM_TAG_PATIENT_NAME, "World", false);
ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false), OrthancException); // Already existing tag
- f.Replace(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*)
- f.Replace(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**)
+ f.ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*)
+ f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**)
std::string s;
- ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_ThrowIfAbsent), OrthancException);
- f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_IgnoreIfAbsent);
+ ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"),
+ false, DicomReplaceMode_ThrowIfAbsent), OrthancException);
+ f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_IgnoreIfAbsent);
ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
- f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_InsertIfAbsent);
+ f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_InsertIfAbsent);
ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
ASSERT_EQ(s, "Accession");
- f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession2", DicomReplaceMode_IgnoreIfAbsent);
+ f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession2"), false, DicomReplaceMode_IgnoreIfAbsent);
ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
ASSERT_EQ(s, "Accession2");
- f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession3", DicomReplaceMode_ThrowIfAbsent);
+ f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession3"), false, DicomReplaceMode_ThrowIfAbsent);
ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
ASSERT_EQ(s, "Accession3");
@@ -481,8 +532,8 @@ TEST(ParsedDicomFile, InsertReplaceJson)
}
a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64
- f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false); // (*)
- f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true); // (**)
+ f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false, DicomReplaceMode_InsertIfAbsent); // (*)
+ f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true, DicomReplaceMode_InsertIfAbsent); // (**)
std::string s;
ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID));
@@ -513,7 +564,7 @@ TEST(ParsedDicomFile, JsonEncoding)
}
Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i]);
- f.Replace(DICOM_TAG_PATIENT_NAME, s, false);
+ f.Replace(DICOM_TAG_PATIENT_NAME, s, false, DicomReplaceMode_InsertIfAbsent);
Json::Value v;
f.ToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
@@ -525,8 +576,8 @@ TEST(ParsedDicomFile, JsonEncoding)
TEST(ParsedDicomFile, ToJsonFlags1)
{
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), EVR_PN, "MyPrivateTag", 1, 1);
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), EVR_PN, "Declared public tag", 1, 1);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_PersonName, "MyPrivateTag", 1, 1);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1);
ParsedDicomFile f(true);
f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false); // Even group => public tag
@@ -536,7 +587,7 @@ TEST(ParsedDicomFile, ToJsonFlags1)
Json::Value v;
f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(6, v.getMemberNames().size());
+ ASSERT_EQ(6u, v.getMemberNames().size());
ASSERT_FALSE(v.isMember("7052,1000"));
ASSERT_FALSE(v.isMember("7053,1000"));
ASSERT_TRUE(v.isMember("7050,1000"));
@@ -545,7 +596,7 @@ TEST(ParsedDicomFile, ToJsonFlags1)
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(7, v.getMemberNames().size());
+ ASSERT_EQ(7u, v.getMemberNames().size());
ASSERT_FALSE(v.isMember("7052,1000"));
ASSERT_TRUE(v.isMember("7050,1000"));
ASSERT_TRUE(v.isMember("7053,1000"));
@@ -554,20 +605,20 @@ TEST(ParsedDicomFile, ToJsonFlags1)
f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(7, v.getMemberNames().size());
+ ASSERT_EQ(7u, v.getMemberNames().size());
ASSERT_FALSE(v.isMember("7052,1000"));
ASSERT_TRUE(v.isMember("7050,1000"));
ASSERT_TRUE(v.isMember("7053,1000"));
ASSERT_EQ("Some public tag", v["7050,1000"].asString());
std::string mime, content;
ASSERT_EQ(Json::stringValue, v["7053,1000"].type());
- Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString());
+ ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString()));
ASSERT_EQ("application/octet-stream", mime);
ASSERT_EQ("Some private tag", content);
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(7, v.getMemberNames().size());
+ ASSERT_EQ(7u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7050,1000"));
ASSERT_TRUE(v.isMember("7052,1000"));
ASSERT_FALSE(v.isMember("7053,1000"));
@@ -576,19 +627,19 @@ TEST(ParsedDicomFile, ToJsonFlags1)
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(7, v.getMemberNames().size());
+ ASSERT_EQ(7u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7050,1000"));
ASSERT_TRUE(v.isMember("7052,1000"));
ASSERT_FALSE(v.isMember("7053,1000"));
ASSERT_EQ("Some public tag", v["7050,1000"].asString());
ASSERT_EQ(Json::stringValue, v["7052,1000"].type());
- Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString());
+ ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString()));
ASSERT_EQ("application/octet-stream", mime);
ASSERT_EQ("Some unknown tag", content);
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(8, v.getMemberNames().size());
+ ASSERT_EQ(8u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7050,1000"));
ASSERT_TRUE(v.isMember("7052,1000"));
ASSERT_TRUE(v.isMember("7053,1000"));
@@ -606,29 +657,29 @@ TEST(ParsedDicomFile, ToJsonFlags2)
Json::Value v;
f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(5, v.getMemberNames().size());
+ ASSERT_EQ(5u, v.getMemberNames().size());
ASSERT_FALSE(v.isMember("7fe0,0010"));
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToNull), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(6, v.getMemberNames().size());
+ ASSERT_EQ(6u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7fe0,0010"));
ASSERT_EQ(Json::nullValue, v["7fe0,0010"].type());
f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToAscii), 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(6, v.getMemberNames().size());
+ ASSERT_EQ(6u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7fe0,0010"));
ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
ASSERT_EQ("Pixels", v["7fe0,0010"].asString());
f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0);
ASSERT_EQ(Json::objectValue, v.type());
- ASSERT_EQ(6, v.getMemberNames().size());
+ ASSERT_EQ(6u, v.getMemberNames().size());
ASSERT_TRUE(v.isMember("7fe0,0010"));
ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
std::string mime, content;
- Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString());
+ ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString()));
ASSERT_EQ("application/octet-stream", mime);
ASSERT_EQ("Pixels", content);
}
@@ -640,19 +691,19 @@ TEST(DicomFindAnswers, Basic)
{
DicomMap m;
- m.SetValue(DICOM_TAG_PATIENT_ID, "hello");
+ m.SetValue(DICOM_TAG_PATIENT_ID, "hello", false);
a.Add(m);
}
{
ParsedDicomFile d(true);
- d.Replace(DICOM_TAG_PATIENT_ID, "my");
+ d.ReplacePlainString(DICOM_TAG_PATIENT_ID, "my");
a.Add(d);
}
{
DicomMap m;
- m.SetValue(DICOM_TAG_PATIENT_ID, "world");
+ m.SetValue(DICOM_TAG_PATIENT_ID, "world", false);
a.Add(m);
}
@@ -666,9 +717,9 @@ TEST(DicomFindAnswers, Basic)
TEST(ParsedDicomFile, FromJson)
{
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), EVR_OB, "MyPrivateTag", 1, 1);
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), EVR_OB, "MyPrivateTag", 1, 1);
- FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), EVR_PN, "Declared public tag", 1, 1);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1);
+ FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1);
Json::Value v;
const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1"; // CR Image Storage:
@@ -728,7 +779,7 @@ TEST(ParsedDicomFile, FromJson)
dicom->ToJson(vv, DicomToJsonFormat_Human, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0);
std::string mime, content;
- Toolbox::DecodeDataUriScheme(mime, content, vv["PixelData"].asString());
+ ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, vv["PixelData"].asString()));
ASSERT_EQ("application/octet-stream", mime);
ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size());
}
@@ -760,3 +811,227 @@ TEST(ParsedDicomFile, FromJson)
ASSERT_TRUE(vv[DICOM_TAG_PIXEL_DATA.Format()].asString().empty());
}
}
+
+
+
+TEST(TestImages, PatternGrayscale8)
+{
+ static const char* PATH = "UnitTestsResults/PatternGrayscale8.dcm";
+
+ Orthanc::Image image(Orthanc::PixelFormat_Grayscale8, 256, 256);
+
+ for (int y = 0; y < 256; y++)
+ {
+ uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
+ for (int x = 0; x < 256; x++, p++)
+ {
+ *p = y;
+ }
+ }
+
+ Orthanc::ImageAccessor r = image.GetRegion(32, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, 0);
+ r = image.GetRegion(160, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, 255);
+
+ {
+ ParsedDicomFile f(true);
+ f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+ f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
+ f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+ f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale8");
+ f.EmbedImage(image);
+
+ f.SaveToFile(PATH);
+ }
+
+ {
+ std::string s;
+ Orthanc::Toolbox::ReadFile(s, PATH);
+ Orthanc::ParsedDicomFile f(s);
+
+ std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+ ASSERT_EQ(256u, decoded->GetWidth());
+ ASSERT_EQ(256u, decoded->GetHeight());
+ ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat());
+
+ for (int y = 0; y < 256; y++)
+ {
+ const void* a = image.GetConstRow(y);
+ const void* b = decoded->GetConstRow(y);
+ ASSERT_EQ(0, memcmp(a, b, 256));
+ }
+ }
+}
+
+
+TEST(TestImages, PatternRGB)
+{
+ static const char* PATH = "UnitTestsResults/PatternRGB24.dcm";
+
+ Orthanc::Image image(Orthanc::PixelFormat_RGB24, 384, 256);
+
+ for (int y = 0; y < 256; y++)
+ {
+ uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
+ for (int x = 0; x < 128; x++, p += 3)
+ {
+ p[0] = y;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ for (int x = 128; x < 128 * 2; x++, p += 3)
+ {
+ p[0] = 0;
+ p[1] = 255 - y;
+ p[2] = 0;
+ }
+ for (int x = 128 * 2; x < 128 * 3; x++, p += 3)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = y;
+ }
+ }
+
+ {
+ ParsedDicomFile f(true);
+ f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+ f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
+ f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+ f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "RGB24");
+ f.EmbedImage(image);
+
+ f.SaveToFile(PATH);
+ }
+
+ {
+ std::string s;
+ Orthanc::Toolbox::ReadFile(s, PATH);
+ Orthanc::ParsedDicomFile f(s);
+
+ std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+ ASSERT_EQ(384u, decoded->GetWidth());
+ ASSERT_EQ(256u, decoded->GetHeight());
+ ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat());
+
+ for (int y = 0; y < 256; y++)
+ {
+ const void* a = image.GetConstRow(y);
+ const void* b = decoded->GetConstRow(y);
+ ASSERT_EQ(0, memcmp(a, b, 3 * 384));
+ }
+ }
+}
+
+
+TEST(TestImages, PatternUint16)
+{
+ static const char* PATH = "UnitTestsResults/PatternGrayscale16.dcm";
+
+ Orthanc::Image image(Orthanc::PixelFormat_Grayscale16, 256, 256);
+
+ uint16_t v = 0;
+ for (int y = 0; y < 256; y++)
+ {
+ uint16_t *p = reinterpret_cast<uint16_t*>(image.GetRow(y));
+ for (int x = 0; x < 256; x++, v++, p++)
+ {
+ *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
+ }
+ }
+
+ Orthanc::ImageAccessor r = image.GetRegion(32, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, 0);
+ r = image.GetRegion(160, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, 65535);
+
+ {
+ ParsedDicomFile f(true);
+ f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+ f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
+ f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+ f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale16");
+ f.EmbedImage(image);
+
+ f.SaveToFile(PATH);
+ }
+
+ {
+ std::string s;
+ Orthanc::Toolbox::ReadFile(s, PATH);
+ Orthanc::ParsedDicomFile f(s);
+
+ std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+ ASSERT_EQ(256u, decoded->GetWidth());
+ ASSERT_EQ(256u, decoded->GetHeight());
+ ASSERT_EQ(Orthanc::PixelFormat_Grayscale16, decoded->GetFormat());
+
+ for (int y = 0; y < 256; y++)
+ {
+ const void* a = image.GetConstRow(y);
+ const void* b = decoded->GetConstRow(y);
+ ASSERT_EQ(0, memcmp(a, b, 512));
+ }
+ }
+}
+
+
+TEST(TestImages, PatternInt16)
+{
+ static const char* PATH = "UnitTestsResults/PatternSignedGrayscale16.dcm";
+
+ Orthanc::Image image(Orthanc::PixelFormat_SignedGrayscale16, 256, 256);
+
+ int16_t v = -32768;
+ for (int y = 0; y < 256; y++)
+ {
+ int16_t *p = reinterpret_cast<int16_t*>(image.GetRow(y));
+ for (int x = 0; x < 256; x++, v++, p++)
+ {
+ *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
+ }
+ }
+
+ Orthanc::ImageAccessor r = image.GetRegion(32, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, -32768);
+ r = image.GetRegion(160, 32, 64, 192);
+ Orthanc::ImageProcessing::Set(r, 32767);
+
+ {
+ ParsedDicomFile f(true);
+ f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+ f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
+ f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
+ f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+ f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "SignedGrayscale16");
+ f.EmbedImage(image);
+
+ f.SaveToFile(PATH);
+ }
+
+ {
+ std::string s;
+ Orthanc::Toolbox::ReadFile(s, PATH);
+ Orthanc::ParsedDicomFile f(s);
+
+ std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+ ASSERT_EQ(256u, decoded->GetWidth());
+ ASSERT_EQ(256u, decoded->GetHeight());
+ ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat());
+
+ for (int y = 0; y < 256; y++)
+ {
+ const void* a = image.GetConstRow(y);
+ const void* b = decoded->GetConstRow(y);
+ ASSERT_EQ(0, memcmp(a, b, 512));
+ }
+ }
+}
diff --git a/UnitTestsSources/ImageProcessingTests.cpp b/UnitTestsSources/ImageProcessingTests.cpp
index 93a0386..8714fc1 100644
--- a/UnitTestsSources/ImageProcessingTests.cpp
+++ b/UnitTestsSources/ImageProcessingTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -44,18 +44,18 @@ TEST(DicomImageInformation, ExtractPixelFormat1)
{
// Cardiac/MR*
DicomMap m;
- m.SetValue(DICOM_TAG_ROWS, "24");
- m.SetValue(DICOM_TAG_COLUMNS, "16");
- m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
- m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
- m.SetValue(DICOM_TAG_BITS_STORED, "12");
- m.SetValue(DICOM_TAG_HIGH_BIT, "11");
- m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0");
- m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
+ m.SetValue(DICOM_TAG_ROWS, "24", false);
+ m.SetValue(DICOM_TAG_COLUMNS, "16", false);
+ m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false);
+ m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false);
+ m.SetValue(DICOM_TAG_BITS_STORED, "12", false);
+ m.SetValue(DICOM_TAG_HIGH_BIT, "11", false);
+ m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0", false);
+ m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false);
DicomImageInformation info(m);
PixelFormat format;
- ASSERT_TRUE(info.ExtractPixelFormat(format));
+ ASSERT_TRUE(info.ExtractPixelFormat(format, false));
ASSERT_EQ(PixelFormat_Grayscale16, format);
}
@@ -64,17 +64,17 @@ TEST(DicomImageInformation, ExtractPixelFormat2)
{
// Delphine CT
DicomMap m;
- m.SetValue(DICOM_TAG_ROWS, "24");
- m.SetValue(DICOM_TAG_COLUMNS, "16");
- m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
- m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
- m.SetValue(DICOM_TAG_BITS_STORED, "16");
- m.SetValue(DICOM_TAG_HIGH_BIT, "15");
- m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1");
- m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
+ m.SetValue(DICOM_TAG_ROWS, "24", false);
+ m.SetValue(DICOM_TAG_COLUMNS, "16", false);
+ m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false);
+ m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false);
+ m.SetValue(DICOM_TAG_BITS_STORED, "16", false);
+ m.SetValue(DICOM_TAG_HIGH_BIT, "15", false);
+ m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1", false);
+ m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false);
DicomImageInformation info(m);
PixelFormat format;
- ASSERT_TRUE(info.ExtractPixelFormat(format));
+ ASSERT_TRUE(info.ExtractPixelFormat(format, false));
ASSERT_EQ(PixelFormat_SignedGrayscale16, format);
}
diff --git a/UnitTestsSources/ImageTests.cpp b/UnitTestsSources/ImageTests.cpp
index 7c1f723..7fc5c77 100644
--- a/UnitTestsSources/ImageTests.cpp
+++ b/UnitTestsSources/ImageTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -66,7 +66,10 @@ TEST(PngWriter, ColorPattern)
}
}
- w.WriteToFile("UnitTestsResults/ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]);
+ Orthanc::ImageAccessor accessor;
+ accessor.AssignReadOnly(Orthanc::PixelFormat_RGB24, width, height, pitch, &image[0]);
+
+ w.WriteToFile("UnitTestsResults/ColorPattern.png", accessor);
std::string f, md5;
Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/ColorPattern.png");
@@ -91,7 +94,10 @@ TEST(PngWriter, Gray8Pattern)
}
}
- w.WriteToFile("UnitTestsResults/Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]);
+ Orthanc::ImageAccessor accessor;
+ accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale8, width, height, pitch, &image[0]);
+
+ w.WriteToFile("UnitTestsResults/Gray8Pattern.png", accessor);
std::string f, md5;
Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.png");
@@ -118,7 +124,9 @@ TEST(PngWriter, Gray16Pattern)
}
}
- w.WriteToFile("UnitTestsResults/Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+ Orthanc::ImageAccessor accessor;
+ accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]);
+ w.WriteToFile("UnitTestsResults/Gray16Pattern.png", accessor);
std::string f, md5;
Orthanc::Toolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.png");
@@ -145,8 +153,11 @@ TEST(PngWriter, EndToEnd)
}
}
+ Orthanc::ImageAccessor accessor;
+ accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]);
+
std::string s;
- w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+ w.WriteToMemory(s, accessor);
{
Orthanc::PngReader r;
@@ -225,12 +236,12 @@ TEST(JpegWriter, Basic)
{
Orthanc::JpegReader r1, r2;
r1.ReadFromFile("UnitTestsResults/hello.jpg");
- ASSERT_EQ(16, r1.GetWidth());
- ASSERT_EQ(16, r1.GetHeight());
+ ASSERT_EQ(16u, r1.GetWidth());
+ ASSERT_EQ(16u, r1.GetHeight());
r2.ReadFromMemory(s);
- ASSERT_EQ(16, r2.GetWidth());
- ASSERT_EQ(16, r2.GetHeight());
+ ASSERT_EQ(16u, r2.GetWidth());
+ ASSERT_EQ(16u, r2.GetHeight());
for (unsigned int y = 0; y < r1.GetHeight(); y++)
{
@@ -250,7 +261,7 @@ TEST(Font, Basic)
Orthanc::Image s(Orthanc::PixelFormat_RGB24, 640, 480);
memset(s.GetBuffer(), 0, s.GetPitch() * s.GetHeight());
- ASSERT_GE(1, Orthanc::Configuration::GetFontRegistry().GetSize());
+ ASSERT_GE(1u, Orthanc::Configuration::GetFontRegistry().GetSize());
Orthanc::Configuration::GetFontRegistry().GetFont(0).Draw(s, "Hello world É\n\rComment ça va ?\nq", 50, 60, 255, 0, 0);
Orthanc::PngWriter w;
diff --git a/UnitTestsSources/JpegLosslessTests.cpp b/UnitTestsSources/JpegLosslessTests.cpp
index 4408eab..a50fb6f 100644
--- a/UnitTestsSources/JpegLosslessTests.cpp
+++ b/UnitTestsSources/JpegLosslessTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/LuaTests.cpp b/UnitTestsSources/LuaTests.cpp
index 727645a..1453250 100644
--- a/UnitTestsSources/LuaTests.cpp
+++ b/UnitTestsSources/LuaTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -259,9 +259,9 @@ TEST(Lua, ReturnJson)
ASSERT_EQ(Json::stringValue, v["List"][0]["a"].type());
ASSERT_EQ(Json::stringValue, v["List"][0]["b"].type());
ASSERT_EQ(Json::stringValue, v["List"][0]["c"].type());
- ASSERT_EQ("42", v["List"][0]["a"].asString());
- ASSERT_EQ("44.37", v["List"][0]["b"].asString());
- ASSERT_EQ("-43", v["List"][0]["c"].asString());
+ ASSERT_FLOAT_EQ(42.0f, boost::lexical_cast<float>(v["List"][0]["a"].asString()));
+ ASSERT_FLOAT_EQ(44.37f, boost::lexical_cast<float>(v["List"][0]["b"].asString()));
+ ASSERT_FLOAT_EQ(-43.0f, boost::lexical_cast<float>(v["List"][0]["c"].asString()));
ASSERT_EQ("test3", v["List"][1][0].asString());
ASSERT_EQ("test1", v["List"][1][1].asString());
ASSERT_EQ("test2", v["List"][1][2].asString());
diff --git a/UnitTestsSources/MemoryCacheTests.cpp b/UnitTestsSources/MemoryCacheTests.cpp
index 5d1d051..42129b1 100644
--- a/UnitTestsSources/MemoryCacheTests.cpp
+++ b/UnitTestsSources/MemoryCacheTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/MultiThreadingTests.cpp b/UnitTestsSources/MultiThreadingTests.cpp
index 5d2ba06..8ccfd15 100644
--- a/UnitTestsSources/MultiThreadingTests.cpp
+++ b/UnitTestsSources/MultiThreadingTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -138,7 +138,7 @@ TEST(ReusableDicomUserConnection, DISABLED_Basic)
{
RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote);
- lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676281");
+ lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676281", 0);
}
printf("**\n"); fflush(stdout);
@@ -148,7 +148,7 @@ TEST(ReusableDicomUserConnection, DISABLED_Basic)
{
RemoteModalityParameters remote("STORESCP", "localhost", 2000, ModalityManufacturer_Generic);
ReusableDicomUserConnection::Locker lock(c, "ORTHANC", remote);
- lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676277");
+ lock.GetConnection().StoreFile("/home/jodogne/DICOM/Cardiac/MR.X.1.2.276.0.7230010.3.1.4.2831157719.2256.1336386844.676277", 0);
}
Toolbox::ServerBarrier();
diff --git a/UnitTestsSources/PluginsTests.cpp b/UnitTestsSources/PluginsTests.cpp
index bf614fb..516e140 100644
--- a/UnitTestsSources/PluginsTests.cpp
+++ b/UnitTestsSources/PluginsTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -57,7 +57,7 @@ TEST(SharedLibrary, Basic)
ASSERT_TRUE(l.HasFunction("GetVersionExW"));
ASSERT_FALSE(l.HasFunction("world"));
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__)
SharedLibrary l("libdl.so");
ASSERT_THROW(l.GetFunction("world"), OrthancException);
ASSERT_TRUE(l.GetFunction("dlopen") != NULL);
diff --git a/UnitTestsSources/PrecompiledHeadersUnitTests.cpp b/UnitTestsSources/PrecompiledHeadersUnitTests.cpp
index 43974ae..9c1d856 100644
--- a/UnitTestsSources/PrecompiledHeadersUnitTests.cpp
+++ b/UnitTestsSources/PrecompiledHeadersUnitTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/PrecompiledHeadersUnitTests.h b/UnitTestsSources/PrecompiledHeadersUnitTests.h
index 2f597b1..4efcd72 100644
--- a/UnitTestsSources/PrecompiledHeadersUnitTests.h
+++ b/UnitTestsSources/PrecompiledHeadersUnitTests.h
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/RestApiTests.cpp b/UnitTestsSources/RestApiTests.cpp
index 3302ae7..c60e6fc 100644
--- a/UnitTestsSources/RestApiTests.cpp
+++ b/UnitTestsSources/RestApiTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/SQLiteChromiumTests.cpp b/UnitTestsSources/SQLiteChromiumTests.cpp
index 607272d..6afddfe 100644
--- a/UnitTestsSources/SQLiteChromiumTests.cpp
+++ b/UnitTestsSources/SQLiteChromiumTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/SQLiteTests.cpp b/UnitTestsSources/SQLiteTests.cpp
index dd2c360..c3ddafc 100644
--- a/UnitTestsSources/SQLiteTests.cpp
+++ b/UnitTestsSources/SQLiteTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
diff --git a/UnitTestsSources/ServerIndexTests.cpp b/UnitTestsSources/ServerIndexTests.cpp
index 1ff178c..4dd7233 100644
--- a/UnitTestsSources/ServerIndexTests.cpp
+++ b/UnitTestsSources/ServerIndexTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -790,10 +790,10 @@ TEST(ServerIndex, AttachmentRecycling)
{
std::string id = boost::lexical_cast<std::string>(i);
DicomMap instance;
- instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id);
- instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id);
- instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id);
- instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id);
+ instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id, false);
+ instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id, false);
+ instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id, false);
+ instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id, false);
std::map<MetadataType, std::string> instanceMetadata;
DicomInstanceToStore toStore;
@@ -824,7 +824,7 @@ TEST(ServerIndex, AttachmentRecycling)
}
// Because the DB is in memory, the SQLite index must not have been created
- ASSERT_THROW(Toolbox::GetFileSize(path + "/index"), OrthancException);
+ ASSERT_FALSE(Toolbox::IsRegularFile(path + "/index"));
context.Stop();
db.Close();
diff --git a/UnitTestsSources/StreamTests.cpp b/UnitTestsSources/StreamTests.cpp
index 82f6262..397c25b 100644
--- a/UnitTestsSources/StreamTests.cpp
+++ b/UnitTestsSources/StreamTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -74,7 +74,7 @@ TEST(Gzip, Empty)
std::string uncompressed;
IBufferCompressor::Uncompress(uncompressed, c, compressed);
- ASSERT_EQ(0, uncompressed.size());
+ ASSERT_TRUE(uncompressed.empty());
}
@@ -107,7 +107,7 @@ TEST(Gzip, EmptyWithPrefix)
std::string uncompressed;
IBufferCompressor::Uncompress(uncompressed, c, compressed);
- ASSERT_EQ(0, uncompressed.size());
+ ASSERT_TRUE(uncompressed.empty());
}
@@ -154,6 +154,7 @@ TEST(Zlib, DISABLED_Corrupted) // Disabled because it may result in a crash
ZlibCompressor c;
IBufferCompressor::Compress(compressed, c, s);
+ ASSERT_FALSE(compressed.empty());
compressed[compressed.size() - 1] = 'a';
std::string u;
@@ -172,7 +173,7 @@ TEST(Zlib, Empty)
std::string uncompressed;
IBufferCompressor::Uncompress(uncompressed, c, compressed);
- ASSERT_EQ(0u, uncompressed.size());
+ ASSERT_TRUE(uncompressed.empty());
}
diff --git a/UnitTestsSources/UnitTestsMain.cpp b/UnitTestsSources/UnitTestsMain.cpp
index 359b737..42882e2 100644
--- a/UnitTestsSources/UnitTestsMain.cpp
+++ b/UnitTestsSources/UnitTestsMain.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -364,6 +364,12 @@ TEST(Toolbox, Base64)
std::string decoded;
Toolbox::DecodeBase64(decoded, hello);
ASSERT_EQ("Hello world", decoded);
+
+ // Invalid character
+ ASSERT_THROW(Toolbox::DecodeBase64(decoded, "?"), OrthancException);
+
+ // All the allowed characters
+ Toolbox::DecodeBase64(decoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
}
TEST(Toolbox, PathToExecutable)
@@ -410,10 +416,6 @@ TEST(Toolbox, ConvertFromLatin1)
// This is a Latin-1 test string
const unsigned char data[10] = { 0xe0, 0xe9, 0xea, 0xe7, 0x26, 0xc6, 0x61, 0x62, 0x63, 0x00 };
- /*FILE* f = fopen("/tmp/tutu", "w");
- fwrite(&data[0], 9, 1, f);
- fclose(f);*/
-
std::string s((char*) &data[0], 10);
ASSERT_EQ("&abc", Toolbox::ConvertToAscii(s));
@@ -458,7 +460,7 @@ TEST(Toolbox, UrlDecode)
}
-#if defined(__linux)
+#if defined(__linux__)
TEST(OrthancInitialization, AbsoluteDirectory)
{
ASSERT_EQ("/tmp/hello", Configuration::InterpretRelativePath("/tmp", "hello"));
@@ -558,6 +560,16 @@ TEST(Toolbox, WriteFile)
ASSERT_EQ(11u, t.size());
ASSERT_EQ(0, t[5]);
ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size()));
+
+ std::string h;
+ ASSERT_EQ(true, Toolbox::ReadHeader(h, path.c_str(), 1));
+ ASSERT_EQ(1u, h.size());
+ ASSERT_EQ('H', h[0]);
+ ASSERT_TRUE(Toolbox::ReadHeader(h, path.c_str(), 0));
+ ASSERT_EQ(0u, h.size());
+ ASSERT_FALSE(Toolbox::ReadHeader(h, path.c_str(), 32));
+ ASSERT_EQ(11u, h.size());
+ ASSERT_EQ(0, memcmp(s.c_str(), h.c_str(), s.size()));
}
std::string u;
@@ -622,7 +634,7 @@ TEST(Toolbox, Enumerations)
-#if defined(__linux)
+#if defined(__linux__)
#include <endian.h>
#elif defined(__FreeBSD__)
#include <machine/endian.h>
@@ -647,7 +659,7 @@ TEST(Toolbox, Endianness)
* Linux.
**/
-#elif defined(__linux) || defined(__FreeBSD_kernel__)
+#elif defined(__linux__) || defined(__FreeBSD_kernel__)
#if !defined(__BYTE_ORDER)
# error Support your platform here
@@ -677,6 +689,188 @@ TEST(Toolbox, Endianness)
}
+#include "../Core/Endianness.h"
+
+static void ASSERT_EQ16(uint16_t a, uint16_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_EQ(static_cast<unsigned int>(a), static_cast<unsigned int>(b));
+#else
+ ASSERT_EQ(a, b);
+#endif
+}
+
+static void ASSERT_NE16(uint16_t a, uint16_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_NE(static_cast<unsigned int>(a), static_cast<unsigned int>(b));
+#else
+ ASSERT_NE(a, b);
+#endif
+}
+
+static void ASSERT_EQ32(uint32_t a, uint32_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_EQ(static_cast<unsigned int>(a), static_cast<unsigned int>(b));
+#else
+ ASSERT_EQ(a, b);
+#endif
+}
+
+static void ASSERT_NE32(uint32_t a, uint32_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_NE(static_cast<unsigned int>(a), static_cast<unsigned int>(b));
+#else
+ ASSERT_NE(a, b);
+#endif
+}
+
+static void ASSERT_EQ64(uint64_t a, uint64_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_EQ(static_cast<unsigned int>(a), static_cast<unsigned int>(b));
+#else
+ ASSERT_EQ(a, b);
+#endif
+}
+
+static void ASSERT_NE64(uint64_t a, uint64_t b)
+{
+#ifdef __MINGW32__
+ // This cast solves a linking problem with MinGW
+ ASSERT_NE(static_cast<unsigned long long>(a), static_cast<unsigned long long>(b));
+#else
+ ASSERT_NE(a, b);
+#endif
+}
+
+
+
+TEST(Toolbox, EndiannessConversions16)
+{
+ Endianness e = Toolbox::DetectEndianness();
+
+ for (unsigned int i = 0; i < 65536; i += 17)
+ {
+ uint16_t v = static_cast<uint16_t>(i);
+ ASSERT_EQ16(v, be16toh(htobe16(v)));
+ ASSERT_EQ16(v, le16toh(htole16(v)));
+
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&v);
+ if (bytes[0] != bytes[1])
+ {
+ ASSERT_NE16(v, le16toh(htobe16(v)));
+ ASSERT_NE16(v, be16toh(htole16(v)));
+ }
+ else
+ {
+ ASSERT_EQ16(v, le16toh(htobe16(v)));
+ ASSERT_EQ16(v, be16toh(htole16(v)));
+ }
+
+ switch (e)
+ {
+ case Endianness_Little:
+ ASSERT_EQ16(v, htole16(v));
+ if (bytes[0] != bytes[1])
+ {
+ ASSERT_NE16(v, htobe16(v));
+ }
+ else
+ {
+ ASSERT_EQ16(v, htobe16(v));
+ }
+ break;
+
+ case Endianness_Big:
+ ASSERT_EQ16(v, htobe16(v));
+ if (bytes[0] != bytes[1])
+ {
+ ASSERT_NE16(v, htole16(v));
+ }
+ else
+ {
+ ASSERT_EQ16(v, htole16(v));
+ }
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
+}
+
+
+TEST(Toolbox, EndiannessConversions32)
+{
+ const uint32_t v = 0xff010203u;
+ const uint32_t r = 0x030201ffu;
+ ASSERT_EQ32(v, be32toh(htobe32(v)));
+ ASSERT_EQ32(v, le32toh(htole32(v)));
+ ASSERT_NE32(v, be32toh(htole32(v)));
+ ASSERT_NE32(v, le32toh(htobe32(v)));
+
+ switch (Toolbox::DetectEndianness())
+ {
+ case Endianness_Little:
+ ASSERT_EQ32(r, htobe32(v));
+ ASSERT_EQ32(v, htole32(v));
+ ASSERT_EQ32(r, be32toh(v));
+ ASSERT_EQ32(v, le32toh(v));
+ break;
+
+ case Endianness_Big:
+ ASSERT_EQ32(v, htobe32(v));
+ ASSERT_EQ32(r, htole32(v));
+ ASSERT_EQ32(v, be32toh(v));
+ ASSERT_EQ32(r, le32toh(v));
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+}
+
+
+TEST(Toolbox, EndiannessConversions64)
+{
+ const uint64_t v = 0xff01020304050607LL;
+ const uint64_t r = 0x07060504030201ffLL;
+ ASSERT_EQ64(v, be64toh(htobe64(v)));
+ ASSERT_EQ64(v, le64toh(htole64(v)));
+ ASSERT_NE64(v, be64toh(htole64(v)));
+ ASSERT_NE64(v, le64toh(htobe64(v)));
+
+ switch (Toolbox::DetectEndianness())
+ {
+ case Endianness_Little:
+ ASSERT_EQ64(r, htobe64(v));
+ ASSERT_EQ64(v, htole64(v));
+ ASSERT_EQ64(r, be64toh(v));
+ ASSERT_EQ64(v, le64toh(v));
+ break;
+
+ case Endianness_Big:
+ ASSERT_EQ64(v, htobe64(v));
+ ASSERT_EQ64(r, htole64(v));
+ ASSERT_EQ64(v, be64toh(v));
+ ASSERT_EQ64(r, le64toh(v));
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+}
+
+
+
#if ORTHANC_PUGIXML_ENABLED == 1
TEST(Toolbox, Xml)
{
@@ -732,6 +926,25 @@ TEST(Toolbox, StartsWith)
}
+TEST(Toolbox, UriEncode)
+{
+ std::string s;
+
+ // Unreserved characters must not be modified
+ std::string t = "aAzZ09.-~_";
+ Toolbox::UriEncode(s, t);
+ ASSERT_EQ(t, s);
+
+ Toolbox::UriEncode(s, "!#$&'()*+,/:;=?@[]"); ASSERT_EQ("%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D", s);
+ Toolbox::UriEncode(s, "%"); ASSERT_EQ("%25", s);
+
+ // Encode characters from UTF-8. This is the test string from the
+ // file "../Resources/EncodingTests.py"
+ Toolbox::UriEncode(s, "\x54\x65\x73\x74\xc3\xa9\xc3\xa4\xc3\xb6\xc3\xb2\xd0\x94\xce\x98\xc4\x9d\xd7\x93\xd8\xb5\xc4\xb7\xd1\x9b\xe0\xb9\x9b\xef\xbe\x88\xc4\xb0");
+ ASSERT_EQ("Test%C3%A9%C3%A4%C3%B6%C3%B2%D0%94%CE%98%C4%9D%D7%93%D8%B5%C4%B7%D1%9B%E0%B9%9B%EF%BE%88%C4%B0", s);
+}
+
+
int main(int argc, char **argv)
{
Logging::Initialize();
diff --git a/UnitTestsSources/VersionsTests.cpp b/UnitTestsSources/VersionsTests.cpp
index a70ddb9..f9b738a 100644
--- a/UnitTestsSources/VersionsTests.cpp
+++ b/UnitTestsSources/VersionsTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
@@ -102,7 +102,7 @@ TEST(Versions, ZlibStatic)
TEST(Versions, BoostStatic)
{
- ASSERT_STREQ("1_59", BOOST_LIB_VERSION);
+ ASSERT_STREQ("1_60", BOOST_LIB_VERSION);
}
TEST(Versions, CurlStatic)
@@ -113,7 +113,7 @@ TEST(Versions, CurlStatic)
TEST(Versions, PngStatic)
{
- ASSERT_EQ(10512, png_access_version_number());
+ ASSERT_EQ(10512u, png_access_version_number());
ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING);
}
diff --git a/UnitTestsSources/ZipTests.cpp b/UnitTestsSources/ZipTests.cpp
index c40402e..5d0cc1e 100644
--- a/UnitTestsSources/ZipTests.cpp
+++ b/UnitTestsSources/ZipTests.cpp
@@ -1,6 +1,6 @@
/**
* Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
*
* This program is free software: you can redistribute it and/or
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/orthanc.git
More information about the debian-med-commit
mailing list