[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, &parameters, 
-                                        &dataset, frame, startFragment, target->GetBuffer(), 
-                                        target->GetSize(), decompressedColorModel);
+    OFCondition c = codec.decodeFrame(&representationParameter, 
+                                      pixelSequence, &parameters, 
+                                      &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, &params);
+  }
+  
+
+
+  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(&params, 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, &params);
+  }
+
+
+  /**
+   * @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, &params) != 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, &params);
+  }
+
+
+  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(&params, 0, sizeof(params));
+    params.answers = answers;
+    params.dicom = dicom;
+    params.size = size;
+
+    return context->InvokeService(context, _OrthancPluginService_FindAddAnswer, &params);
+  }
+
+
+  /**
+   * @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(&params, 0, sizeof(params));
+    params.answers = answers;
+
+    return context->InvokeService(context, _OrthancPluginService_FindMarkIncomplete, &params);
+  }
+
+
+
+  /**
+   * @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(&params, 0, sizeof(params));
+    params.query = query;
+    params.resultUint32 = &count;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFindQuerySize, &params) != 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(&params, 0, sizeof(params));
+    params.query = query;
+    params.index = index;
+    params.resultGroup = group;
+    params.resultElement = element;
+
+    return context->InvokeService(context, _OrthancPluginService_GetFindQueryTag, &params);
+  }
+
+
+  /**
+   * @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(&params, 0, sizeof(params));
+    params.query = query;
+    params.index = index;
+    params.resultString = &result;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFindQueryTagName, &params) != 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(&params, 0, sizeof(params));
+    params.query = query;
+    params.index = index;
+    params.resultString = &result;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetFindQueryValue, &params) != 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, &params);
+  }
+
+
+
+
 #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