# HG changeset patch # User Sebastien Jodogne # Date 1379510827 -7200 # Node ID b26a7c397c34b82518a7b78d27ef9ce0e979e8af # Parent f6e61e329bbfeb14e338d72111dc0e240b7c92ee# Parent 11fee43c586106d01cd8b396938a45c422038da1 merge diff -r f6e61e329bbf -r b26a7c397c34 CMakeLists.txt --- a/CMakeLists.txt Mon Apr 29 12:48:10 2013 +0200 +++ b/CMakeLists.txt Wed Sep 18 15:27:07 2013 +0200 @@ -9,27 +9,25 @@ # Parameters of the build SET(STATIC_BUILD ON CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") -SET(STANDALONE_BUILD OFF CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)") +SET(STANDALONE_BUILD ON CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)") SET(ENABLE_SSL ON CACHE BOOL "Include support for SSL") SET(BUILD_UNIT_TESTS ON CACHE BOOL "Build the unit tests") +SET(DCMTK_DICTIONARY_DIR "/usr/share/dcmtk" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (ignored in standalone builds)") -# Advanced parameters (for Debian packaging) -SET(USE_DYNAMIC_JSONCPP OFF CACHE BOOL "Use the dynamic version of JsonCpp (only for Debian sid)") +# Advanced parameters to fine-tune linking against system libraries +SET(USE_DYNAMIC_JSONCPP OFF CACHE BOOL "Use the dynamic version of JsonCpp") SET(USE_DYNAMIC_GOOGLE_LOG ON CACHE BOOL "Use the dynamic version of Google Log") -SET(USE_DYNAMIC_GOOGLE_TEST ON CACHE BOOL "Use the dynamic version of Google Test (not for Debian sid)") +SET(USE_DYNAMIC_GOOGLE_TEST ON CACHE BOOL "Use the dynamic version of Google Test") SET(USE_DYNAMIC_SQLITE ON CACHE BOOL "Use the dynamic version of SQLite") SET(USE_DYNAMIC_MONGOOSE OFF CACHE BOOL "Use the dynamic version of Mongoose") -SET(DEBIAN_FORCE_HARDENING OFF CACHE BOOL "Force the injection of Debian hardening flags (unrecommended)") -SET(DEBIAN_USE_GTEST_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (only for Debian sid)") -SET(ONLY_CORE_LIBRARY OFF CACHE BOOL "Only build the core library") +SET(USE_DYNAMIC_LUA OFF CACHE BOOL "Use the dynamic version of Lua") +SET(DEBIAN_USE_GTEST_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") mark_as_advanced(USE_DYNAMIC_JSONCPP) mark_as_advanced(USE_DYNAMIC_GOOGLE_LOG) mark_as_advanced(USE_DYNAMIC_GOOGLE_TEST) mark_as_advanced(USE_DYNAMIC_SQLITE) -mark_as_advanced(DEBIAN_FORCE_HARDENING) -mark_as_advanced(DEBIAN_USE_STATIC_GOOGLE_TEST) -mark_as_advanced(ONLY_CORE_LIBRARY) +mark_as_advanced(DEBIAN_USE_GTEST_SOURCE_PACKAGE) # Some basic inclusions include(CheckIncludeFiles) @@ -62,23 +60,21 @@ endif() include(${CMAKE_SOURCE_DIR}/Resources/CMake/BoostConfiguration.cmake) - -if(NOT ONLY_CORE_LIBRARY) - include(${CMAKE_SOURCE_DIR}/Resources/CMake/DcmtkConfiguration.cmake) -endif() - +include(${CMAKE_SOURCE_DIR}/Resources/CMake/DcmtkConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/MongooseConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/ZlibConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/SQLiteConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/JsonCppConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibCurlConfiguration.cmake) include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibPngConfiguration.cmake) +include(${CMAKE_SOURCE_DIR}/Resources/CMake/LuaConfiguration.cmake) # Prepare the embedded files set(EMBEDDED_FILES PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql CONFIGURATION_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json + LUA_TOOLBOX ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua ) if (${STANDALONE_BUILD}) @@ -119,10 +115,12 @@ Core/DicomFormat/DicomTag.cpp Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomInstanceHasher.cpp + Core/Enumerations.cpp Core/FileStorage/FileStorage.cpp Core/FileStorage/StorageAccessor.cpp Core/FileStorage/CompressedFileStorageAccessor.cpp Core/FileStorage/FileStorageAccessor.cpp + Core/HttpClient.cpp Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpHandler.cpp @@ -134,7 +132,11 @@ Core/RestApi/RestApiOutput.cpp Core/RestApi/RestApi.cpp Core/MultiThreading/BagOfRunnablesBySteps.cpp - Core/PngWriter.cpp + Core/MultiThreading/SharedMessageQueue.cpp + Core/MultiThreading/ThreadedCommandProcessor.cpp + Core/MultiThreading/ArrayFilledByThreads.cpp + Core/FileFormats/PngReader.cpp + Core/FileFormats/PngWriter.cpp Core/SQLite/Connection.cpp Core/SQLite/FunctionContext.cpp Core/SQLite/Statement.cpp @@ -143,68 +145,71 @@ Core/SQLite/Transaction.cpp Core/Toolbox.cpp Core/Uuid.cpp + Core/Lua/LuaContext.cpp + Core/Lua/LuaFunctionCall.cpp - OrthancCppClient/HttpClient.cpp - OrthancCppClient/HttpException.cpp + OrthancCppClient/OrthancConnection.cpp + OrthancCppClient/Study.cpp + OrthancCppClient/Series.cpp + OrthancCppClient/Instance.cpp + OrthancCppClient/Patient.cpp ) +add_library(ServerLibrary + STATIC + ${DCMTK_SOURCES} + OrthancServer/DicomProtocol/DicomFindAnswers.cpp + OrthancServer/DicomProtocol/DicomServer.cpp + OrthancServer/DicomProtocol/DicomUserConnection.cpp + OrthancServer/FromDcmtkBridge.cpp + OrthancServer/Internals/CommandDispatcher.cpp + OrthancServer/Internals/FindScp.cpp + OrthancServer/Internals/MoveScp.cpp + OrthancServer/Internals/StoreScp.cpp + OrthancServer/OrthancInitialization.cpp + OrthancServer/OrthancRestApi.cpp + OrthancServer/ServerIndex.cpp + OrthancServer/ToDcmtkBridge.cpp + OrthancServer/DatabaseWrapper.cpp + OrthancServer/ServerContext.cpp + OrthancServer/ServerEnumerations.cpp + OrthancServer/ServerToolbox.cpp + ) -if(NOT ONLY_CORE_LIBRARY) - add_library(ServerLibrary - STATIC - ${DCMTK_SOURCES} - OrthancServer/DicomProtocol/DicomFindAnswers.cpp - OrthancServer/DicomProtocol/DicomServer.cpp - OrthancServer/DicomProtocol/DicomUserConnection.cpp - OrthancServer/FromDcmtkBridge.cpp - OrthancServer/Internals/CommandDispatcher.cpp - OrthancServer/Internals/FindScp.cpp - OrthancServer/Internals/MoveScp.cpp - OrthancServer/Internals/StoreScp.cpp - OrthancServer/OrthancInitialization.cpp - OrthancServer/OrthancRestApi.cpp - OrthancServer/ServerIndex.cpp - OrthancServer/ToDcmtkBridge.cpp - OrthancServer/DatabaseWrapper.cpp - OrthancServer/ServerContext.cpp - OrthancServer/ServerEnumerations.cpp - OrthancServer/ServerToolbox.cpp - ) - - # Ensure autogenerated code is built before building ServerLibrary - add_dependencies(ServerLibrary CoreLibrary) +# Ensure autogenerated code is built before building ServerLibrary +add_dependencies(ServerLibrary CoreLibrary) - add_executable(Orthanc - OrthancServer/main.cpp - ) +add_executable(Orthanc + OrthancServer/main.cpp + ) - target_link_libraries(Orthanc ServerLibrary CoreLibrary) +target_link_libraries(Orthanc ServerLibrary CoreLibrary) - install( - TARGETS Orthanc - RUNTIME DESTINATION bin - ) +install( + TARGETS Orthanc + RUNTIME DESTINATION bin + ) - # Build the unit tests if required - if (BUILD_UNIT_TESTS) - add_definitions(-DORTHANC_BUILD_UNIT_TESTS=1) - include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake) - add_executable(UnitTests - ${GTEST_SOURCES} - UnitTests/FileStorage.cpp - UnitTests/MemoryCache.cpp - UnitTests/PngWriter.cpp - UnitTests/RestApi.cpp - UnitTests/SQLite.cpp - UnitTests/SQLiteChromium.cpp - UnitTests/ServerIndex.cpp - UnitTests/Versions.cpp - UnitTests/Zip.cpp - UnitTests/main.cpp - ) - target_link_libraries(UnitTests ServerLibrary CoreLibrary) - endif() +# Build the unit tests if required +if (BUILD_UNIT_TESTS) + add_definitions(-DORTHANC_BUILD_UNIT_TESTS=1) + include(${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleTestConfiguration.cmake) + add_executable(UnitTests + ${GTEST_SOURCES} + UnitTests/FileStorage.cpp + UnitTests/MemoryCache.cpp + UnitTests/Png.cpp + UnitTests/RestApi.cpp + UnitTests/SQLite.cpp + UnitTests/SQLiteChromium.cpp + UnitTests/ServerIndex.cpp + UnitTests/Versions.cpp + UnitTests/Zip.cpp + UnitTests/Lua.cpp + UnitTests/main.cpp + ) + target_link_libraries(UnitTests ServerLibrary CoreLibrary) endif() diff -r f6e61e329bbf -r b26a7c397c34 Core/Cache/CacheIndex.h --- a/Core/Cache/CacheIndex.h Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include -#include -#include - -#include "../OrthancException.h" -#include "../Toolbox.h" - -namespace Orthanc -{ - /** - * This class implements the index of a cache with least recently - * used (LRU) recycling policy. All the items of the cache index - * can be associated with a payload. - * Reference: http://stackoverflow.com/a/2504317 - **/ - template - class CacheIndex : public boost::noncopyable - { - private: - typedef std::list< std::pair > Queue; - typedef std::map Index; - - Index index_; - Queue queue_; - - /** - * Internal method for debug builds to check whether the internal - * data structures are not corrupted. - **/ - void CheckInvariants() const; - - public: - /** - * Add a new element to the cache index, and make it the most - * recent element. - * \param id The ID of the element. - * \param payload The payload of the element. - **/ - void Add(T id, Payload payload = Payload()); - - /** - * When accessing an element of the cache, this method tags the - * element as the most recently used. - * \param id The most recently accessed item. - **/ - void TagAsMostRecent(T id); - - /** - * Remove an element from the cache index. - * \param id The item to remove. - **/ - Payload Invalidate(T id); - - /** - * Get the oldest element in the cache and remove it. - * \return The oldest item. - **/ - T RemoveOldest() - { - Payload p; - return RemoveOldest(p); - } - - /** - * Get the oldest element in the cache, remove it and return the - * associated payload. - * \param payload Where to store the associated payload. - * \return The oldest item. - **/ - T RemoveOldest(Payload& payload); - - /** - * Check whether an element is contained in the cache. - * \param id The item. - * \return \c true iff the item is indexed by the cache. - **/ - bool Contains(T id) const - { - return index_.find(id) != index_.end(); - } - - bool Contains(T id, Payload& payload) const - { - typename Index::const_iterator it = index_.find(id); - if (it == index_.end()) - { - return false; - } - else - { - payload = it->second->second; - return true; - } - } - - /** - * Return the number of elements in the cache. - * \return The number of elements. - **/ - size_t GetSize() const - { - assert(index_.size() == queue_.size()); - return queue_.size(); - } - - /** - * Check whether the cache index is empty. - * \return \c true iff the cache is empty. - **/ - bool IsEmpty() const - { - return index_.empty(); - } - }; - - - - - /****************************************************************** - ** Implementation of the template - ******************************************************************/ - - template - void CacheIndex::CheckInvariants() const - { -#ifndef NDEBUG - assert(index_.size() == queue_.size()); - - for (typename Index::const_iterator - it = index_.begin(); it != index_.end(); it++) - { - assert(it->second != queue_.end()); - assert(it->second->first == it->first); - } -#endif - } - - - template - void CacheIndex::Add(T id, Payload payload) - { - if (Contains(id)) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - queue_.push_front(std::make_pair(id, payload)); - index_[id] = queue_.begin(); - - CheckInvariants(); - } - - - template - void CacheIndex::TagAsMostRecent(T id) - { - if (!Contains(id)) - { - throw OrthancException(ErrorCode_InexistentItem); - } - - typename Index::iterator it = index_.find(id); - assert(it != index_.end()); - - std::pair item = *(it->second); - - queue_.erase(it->second); - queue_.push_front(item); - index_[id] = queue_.begin(); - - CheckInvariants(); - } - - - template - Payload CacheIndex::Invalidate(T id) - { - if (!Contains(id)) - { - throw OrthancException(ErrorCode_InexistentItem); - } - - typename Index::iterator it = index_.find(id); - assert(it != index_.end()); - - Payload payload = it->second->second; - queue_.erase(it->second); - index_.erase(it); - - CheckInvariants(); - return payload; - } - - - template - T CacheIndex::RemoveOldest(Payload& payload) - { - if (IsEmpty()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - std::pair item = queue_.back(); - T oldest = item.first; - payload = item.second; - - queue_.pop_back(); - assert(index_.find(oldest) != index_.end()); - index_.erase(oldest); - - CheckInvariants(); - - return oldest; - } -} diff -r f6e61e329bbf -r b26a7c397c34 Core/Cache/ICachePageProvider.h --- a/Core/Cache/ICachePageProvider.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Cache/ICachePageProvider.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Cache/LeastRecentlyUsedIndex.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/LeastRecentlyUsedIndex.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,346 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include +#include +#include + +#include "../OrthancException.h" +#include "../Toolbox.h" + +namespace Orthanc +{ + /** + * This class implements the index of a cache with least recently + * used (LRU) recycling policy. All the items of the cache index + * can be associated with a payload. + * Reference: http://stackoverflow.com/a/2504317 + **/ + template + class LeastRecentlyUsedIndex : public boost::noncopyable + { + private: + typedef std::list< std::pair > Queue; + typedef std::map Index; + + Index index_; + Queue queue_; + + /** + * Internal method for debug builds to check whether the internal + * data structures are not corrupted. + **/ + void CheckInvariants() const; + + public: + /** + * Add a new element to the cache index, and make it the most + * recent element. + * \param id The ID of the element. + * \param payload The payload of the element. + **/ + void Add(T id, Payload payload = Payload()); + + void AddOrMakeMostRecent(T id, Payload payload = Payload()); + + /** + * When accessing an element of the cache, this method tags the + * element as the most recently used. + * \param id The most recently accessed item. + **/ + void MakeMostRecent(T id); + + void MakeMostRecent(T id, Payload updatedPayload); + + /** + * Remove an element from the cache index. + * \param id The item to remove. + **/ + Payload Invalidate(T id); + + /** + * Get the oldest element in the cache and remove it. + * \return The oldest item. + **/ + T RemoveOldest(); + + /** + * Get the oldest element in the cache, remove it and return the + * associated payload. + * \param payload Where to store the associated payload. + * \return The oldest item. + **/ + T RemoveOldest(Payload& payload); + + /** + * Check whether an element is contained in the cache. + * \param id The item. + * \return \c true iff the item is indexed by the cache. + **/ + bool Contains(T id) const + { + return index_.find(id) != index_.end(); + } + + bool Contains(T id, Payload& payload) const + { + typename Index::const_iterator it = index_.find(id); + if (it == index_.end()) + { + return false; + } + else + { + payload = it->second->second; + return true; + } + } + + /** + * Return the number of elements in the cache. + * \return The number of elements. + **/ + size_t GetSize() const + { + assert(index_.size() == queue_.size()); + return queue_.size(); + } + + /** + * Check whether the cache index is empty. + * \return \c true iff the cache is empty. + **/ + bool IsEmpty() const + { + return index_.empty(); + } + + const T& GetOldest() const; + + const Payload& GetOldestPayload() const; + }; + + + + + /****************************************************************** + ** Implementation of the template + ******************************************************************/ + + template + void LeastRecentlyUsedIndex::CheckInvariants() const + { +#ifndef NDEBUG + assert(index_.size() == queue_.size()); + + for (typename Index::const_iterator + it = index_.begin(); it != index_.end(); it++) + { + assert(it->second != queue_.end()); + assert(it->second->first == it->first); + } +#endif + } + + + template + void LeastRecentlyUsedIndex::Add(T id, Payload payload) + { + if (Contains(id)) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + queue_.push_front(std::make_pair(id, payload)); + index_[id] = queue_.begin(); + + CheckInvariants(); + } + + + template + void LeastRecentlyUsedIndex::MakeMostRecent(T id) + { + if (!Contains(id)) + { + throw OrthancException(ErrorCode_InexistentItem); + } + + typename Index::iterator it = index_.find(id); + assert(it != index_.end()); + + std::pair item = *(it->second); + + queue_.erase(it->second); + queue_.push_front(item); + index_[id] = queue_.begin(); + + CheckInvariants(); + } + + + template + void LeastRecentlyUsedIndex::AddOrMakeMostRecent(T id, Payload payload) + { + typename Index::iterator it = index_.find(id); + + if (it != index_.end()) + { + // Already existing. Make it most recent. + std::pair item = *(it->second); + item.second = payload; + queue_.erase(it->second); + queue_.push_front(item); + } + else + { + // New item + queue_.push_front(std::make_pair(id, payload)); + } + + index_[id] = queue_.begin(); + + CheckInvariants(); + } + + + template + void LeastRecentlyUsedIndex::MakeMostRecent(T id, Payload updatedPayload) + { + if (!Contains(id)) + { + throw OrthancException(ErrorCode_InexistentItem); + } + + typename Index::iterator it = index_.find(id); + assert(it != index_.end()); + + std::pair item = *(it->second); + item.second = updatedPayload; + + queue_.erase(it->second); + queue_.push_front(item); + index_[id] = queue_.begin(); + + CheckInvariants(); + } + + + template + Payload LeastRecentlyUsedIndex::Invalidate(T id) + { + if (!Contains(id)) + { + throw OrthancException(ErrorCode_InexistentItem); + } + + typename Index::iterator it = index_.find(id); + assert(it != index_.end()); + + Payload payload = it->second->second; + queue_.erase(it->second); + index_.erase(it); + + CheckInvariants(); + return payload; + } + + + template + T LeastRecentlyUsedIndex::RemoveOldest(Payload& payload) + { + if (IsEmpty()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + std::pair item = queue_.back(); + T oldest = item.first; + payload = item.second; + + queue_.pop_back(); + assert(index_.find(oldest) != index_.end()); + index_.erase(oldest); + + CheckInvariants(); + + return oldest; + } + + + template + T LeastRecentlyUsedIndex::RemoveOldest() + { + if (IsEmpty()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + std::pair item = queue_.back(); + T oldest = item.first; + + queue_.pop_back(); + assert(index_.find(oldest) != index_.end()); + index_.erase(oldest); + + CheckInvariants(); + + return oldest; + } + + + template + const T& LeastRecentlyUsedIndex::GetOldest() const + { + if (IsEmpty()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + return queue_.back().first; + } + + + template + const Payload& LeastRecentlyUsedIndex::GetOldestPayload() const + { + if (IsEmpty()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + return queue_.back().second; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Cache/MemoryCache.cpp --- a/Core/Cache/MemoryCache.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Cache/MemoryCache.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -46,7 +46,7 @@ { VLOG(1) << "Reusing a cache page"; assert(p != NULL); - index_.TagAsMostRecent(id); + index_.MakeMostRecent(id); return *p; } diff -r f6e61e329bbf -r b26a7c397c34 Core/Cache/MemoryCache.h --- a/Core/Cache/MemoryCache.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Cache/MemoryCache.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -33,7 +33,7 @@ #pragma once #include -#include "CacheIndex.h" +#include "LeastRecentlyUsedIndex.h" #include "ICachePageProvider.h" namespace Orthanc @@ -52,7 +52,7 @@ ICachePageProvider& provider_; size_t cacheSize_; - CacheIndex index_; + LeastRecentlyUsedIndex index_; Page& Load(const std::string& id); diff -r f6e61e329bbf -r b26a7c397c34 Core/ChunkedBuffer.cpp --- a/Core/ChunkedBuffer.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/ChunkedBuffer.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/ChunkedBuffer.h --- a/Core/ChunkedBuffer.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/ChunkedBuffer.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/BufferCompressor.cpp --- a/Core/Compression/BufferCompressor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/BufferCompressor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/BufferCompressor.h --- a/Core/Compression/BufferCompressor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/BufferCompressor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/HierarchicalZipWriter.cpp --- a/Core/Compression/HierarchicalZipWriter.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/HierarchicalZipWriter.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/HierarchicalZipWriter.h --- a/Core/Compression/HierarchicalZipWriter.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/HierarchicalZipWriter.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/ZipWriter.cpp --- a/Core/Compression/ZipWriter.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/ZipWriter.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/ZipWriter.h --- a/Core/Compression/ZipWriter.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/ZipWriter.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/ZlibCompressor.cpp --- a/Core/Compression/ZlibCompressor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/ZlibCompressor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Compression/ZlibCompressor.h --- a/Core/Compression/ZlibCompressor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Compression/ZlibCompressor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomArray.cpp --- a/Core/DicomFormat/DicomArray.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomArray.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomArray.h --- a/Core/DicomFormat/DicomArray.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomArray.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomElement.h --- a/Core/DicomFormat/DicomElement.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomElement.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomInstanceHasher.cpp --- a/Core/DicomFormat/DicomInstanceHasher.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomInstanceHasher.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomInstanceHasher.h --- a/Core/DicomFormat/DicomInstanceHasher.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomInstanceHasher.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomIntegerPixelAccessor.cpp --- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -231,25 +231,28 @@ pixel += channel * frameOffset_ / samplesPerPixel_ + x * bytesPerPixel_; } - int32_t v; + uint32_t v; v = pixel[0]; if (bytesPerPixel_ >= 2) - v = v + (static_cast(pixel[1]) << 8); + v = v + (static_cast(pixel[1]) << 8); if (bytesPerPixel_ >= 3) - v = v + (static_cast(pixel[2]) << 16); + v = v + (static_cast(pixel[2]) << 16); if (bytesPerPixel_ >= 4) - v = v + (static_cast(pixel[3]) << 24); + v = v + (static_cast(pixel[3]) << 24); - v = (v >> shift_) & mask_; + v = v >> shift_; if (v & signMask_) { - // Signed value: Not implemented yet - //throw OrthancException(ErrorCode_NotImplemented); - v = 0; + // Signed value + // http://en.wikipedia.org/wiki/Two%27s_complement#Subtraction_from_2N + return -static_cast(mask_) + static_cast(v & mask_) - 1; } - - return v; + else + { + // Unsigned value + return static_cast(v & mask_); + } } diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomIntegerPixelAccessor.h --- a/Core/DicomFormat/DicomIntegerPixelAccessor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomMap.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -76,9 +76,11 @@ DicomTag(0x0018, 0x0024), // SequenceName DicomTag(0x0018, 0x1030), // ProtocolName DicomTag(0x0020, 0x0011), // SeriesNumber - //DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES, + DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES, DICOM_TAG_IMAGES_IN_ACQUISITION, + DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, DICOM_TAG_NUMBER_OF_SLICES, + DICOM_TAG_NUMBER_OF_TIME_SLICES, DICOM_TAG_SERIES_INSTANCE_UID }; @@ -90,6 +92,7 @@ DICOM_TAG_IMAGE_INDEX, DICOM_TAG_INSTANCE_NUMBER, DICOM_TAG_NUMBER_OF_FRAMES, + DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, DICOM_TAG_SOP_INSTANCE_UID }; diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomMap.h --- a/Core/DicomFormat/DicomMap.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomMap.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomNullValue.h --- a/Core/DicomFormat/DicomNullValue.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomNullValue.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomString.h --- a/Core/DicomFormat/DicomString.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomString.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomTag.cpp --- a/Core/DicomFormat/DicomTag.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomTag.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomTag.h --- a/Core/DicomFormat/DicomTag.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomTag.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -95,6 +95,7 @@ static const DicomTag DICOM_TAG_INSTANCE_NUMBER(0x0020, 0x0013); static const DicomTag DICOM_TAG_NUMBER_OF_SLICES(0x0054, 0x0081); + static const DicomTag DICOM_TAG_NUMBER_OF_TIME_SLICES(0x0054, 0x0101); static const DicomTag DICOM_TAG_NUMBER_OF_FRAMES(0x0028, 0x0008); static const DicomTag DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES(0x0018, 0x1090); static const DicomTag DICOM_TAG_IMAGES_IN_ACQUISITION(0x0020, 0x1002); @@ -105,4 +106,8 @@ static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016); static const DicomTag DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID(0x0002, 0x0002); static const DicomTag DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID(0x0002, 0x0003); + + // DICOM tags used for fMRI (thanks to Will Ryder) + static const DicomTag DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS(0x0020, 0x0105); + static const DicomTag DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER(0x0020, 0x0100); } diff -r f6e61e329bbf -r b26a7c397c34 Core/DicomFormat/DicomValue.h --- a/Core/DicomFormat/DicomValue.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/DicomFormat/DicomValue.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/EnumerationDictionary.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/EnumerationDictionary.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,122 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "OrthancException.h" + +#include +#include +#include + +namespace Orthanc +{ + namespace Toolbox + { + template + class EnumerationDictionary + { + private: + typedef std::map EnumerationToString; + typedef std::map StringToEnumeration; + + EnumerationToString enumerationToString_; + StringToEnumeration stringToEnumeration_; + + public: + void Add(Enumeration value, const std::string& str) + { + // Check if these values are free + if (enumerationToString_.find(value) != enumerationToString_.end() || + stringToEnumeration_.find(str) != stringToEnumeration_.end()) + { + throw OrthancException(ErrorCode_BadRequest); + } + + // Prevent the registration of a number + try + { + boost::lexical_cast(str); + throw OrthancException(ErrorCode_BadRequest); + } + catch (boost::bad_lexical_cast) + { + // OK, the string is not a number + } + + enumerationToString_[value] = str; + stringToEnumeration_[str] = value; + stringToEnumeration_[boost::lexical_cast(static_cast(value))] = value; + } + + Enumeration Translate(const std::string& str) const + { + try + { + int value = boost::lexical_cast(str); + return static_cast(value); + } + catch (boost::bad_lexical_cast) + { + } + + typename StringToEnumeration::const_iterator + found = stringToEnumeration_.find(str); + + if (found == stringToEnumeration_.end()) + { + throw OrthancException(ErrorCode_InexistentItem); + } + else + { + return found->second; + } + } + + std::string Translate(Enumeration e) const + { + typename EnumerationToString::const_iterator + found = enumerationToString_.find(e); + + if (found == enumerationToString_.end()) + { + // No name for this item + return boost::lexical_cast(static_cast(e)); + } + else + { + return found->second; + } + } + }; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Enumerations.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Enumerations.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,225 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "Enumerations.h" + +#include "OrthancException.h" + +namespace Orthanc +{ + const char* EnumerationToString(HttpMethod method) + { + switch (method) + { + case HttpMethod_Get: + return "GET"; + + case HttpMethod_Post: + return "POST"; + + case HttpMethod_Delete: + return "DELETE"; + + case HttpMethod_Put: + return "PUT"; + + default: + return "?"; + } + } + + + const char* EnumerationToString(HttpStatus status) + { + switch (status) + { + case HttpStatus_100_Continue: + return "Continue"; + + case HttpStatus_101_SwitchingProtocols: + return "Switching Protocols"; + + case HttpStatus_102_Processing: + return "Processing"; + + case HttpStatus_200_Ok: + return "OK"; + + case HttpStatus_201_Created: + return "Created"; + + case HttpStatus_202_Accepted: + return "Accepted"; + + case HttpStatus_203_NonAuthoritativeInformation: + return "Non-Authoritative Information"; + + case HttpStatus_204_NoContent: + return "No Content"; + + case HttpStatus_205_ResetContent: + return "Reset Content"; + + case HttpStatus_206_PartialContent: + return "Partial Content"; + + case HttpStatus_207_MultiStatus: + return "Multi-Status"; + + case HttpStatus_208_AlreadyReported: + return "Already Reported"; + + case HttpStatus_226_IMUsed: + return "IM Used"; + + case HttpStatus_300_MultipleChoices: + return "Multiple Choices"; + + case HttpStatus_301_MovedPermanently: + return "Moved Permanently"; + + case HttpStatus_302_Found: + return "Found"; + + case HttpStatus_303_SeeOther: + return "See Other"; + + case HttpStatus_304_NotModified: + return "Not Modified"; + + case HttpStatus_305_UseProxy: + return "Use Proxy"; + + case HttpStatus_307_TemporaryRedirect: + return "Temporary Redirect"; + + case HttpStatus_400_BadRequest: + return "Bad Request"; + + case HttpStatus_401_Unauthorized: + return "Unauthorized"; + + case HttpStatus_402_PaymentRequired: + return "Payment Required"; + + case HttpStatus_403_Forbidden: + return "Forbidden"; + + case HttpStatus_404_NotFound: + return "Not Found"; + + case HttpStatus_405_MethodNotAllowed: + return "Method Not Allowed"; + + case HttpStatus_406_NotAcceptable: + return "Not Acceptable"; + + case HttpStatus_407_ProxyAuthenticationRequired: + return "Proxy Authentication Required"; + + case HttpStatus_408_RequestTimeout: + return "Request Timeout"; + + case HttpStatus_409_Conflict: + return "Conflict"; + + case HttpStatus_410_Gone: + return "Gone"; + + case HttpStatus_411_LengthRequired: + return "Length Required"; + + case HttpStatus_412_PreconditionFailed: + return "Precondition Failed"; + + case HttpStatus_413_RequestEntityTooLarge: + return "Request Entity Too Large"; + + case HttpStatus_414_RequestUriTooLong: + return "Request-URI Too Long"; + + case HttpStatus_415_UnsupportedMediaType: + return "Unsupported Media Type"; + + case HttpStatus_416_RequestedRangeNotSatisfiable: + return "Requested Range Not Satisfiable"; + + case HttpStatus_417_ExpectationFailed: + return "Expectation Failed"; + + case HttpStatus_422_UnprocessableEntity: + return "Unprocessable Entity"; + + case HttpStatus_423_Locked: + return "Locked"; + + case HttpStatus_424_FailedDependency: + return "Failed Dependency"; + + case HttpStatus_426_UpgradeRequired: + return "Upgrade Required"; + + case HttpStatus_500_InternalServerError: + return "Internal Server Error"; + + case HttpStatus_501_NotImplemented: + return "Not Implemented"; + + case HttpStatus_502_BadGateway: + return "Bad Gateway"; + + case HttpStatus_503_ServiceUnavailable: + return "Service Unavailable"; + + case HttpStatus_504_GatewayTimeout: + return "Gateway Timeout"; + + case HttpStatus_505_HttpVersionNotSupported: + return "HTTP Version Not Supported"; + + case HttpStatus_506_VariantAlsoNegotiates: + return "Variant Also Negotiates"; + + case HttpStatus_507_InsufficientStorage: + return "Insufficient Storage"; + + case HttpStatus_509_BandwidthLimitExceeded: + return "Bandwidth Limit Exceeded"; + + case HttpStatus_510_NotExtended: + return "Not Extended"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Enumerations.h --- a/Core/Enumerations.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Enumerations.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -32,10 +32,15 @@ #pragma once -#include "../OrthancCppClient/HttpEnumerations.h" - namespace Orthanc { + enum Endianness + { + Endianness_Unknown, + Endianness_Big, + Endianness_Little + }; + enum ErrorCode { // Generic error codes @@ -49,6 +54,7 @@ ErrorCode_BadSequenceOfCalls, ErrorCode_InexistentItem, ErrorCode_BadRequest, + ErrorCode_NetworkProtocol, // Specific error codes ErrorCode_UriSyntax, @@ -65,7 +71,97 @@ { PixelFormat_RGB24, PixelFormat_Grayscale8, - PixelFormat_Grayscale16 + PixelFormat_Grayscale16, + PixelFormat_SignedGrayscale16 + }; + + enum ImageExtractionMode + { + ImageExtractionMode_Preview, + ImageExtractionMode_UInt8, + ImageExtractionMode_UInt16, + ImageExtractionMode_Int16 + }; + + + /** + * Most common, non-joke and non-experimental HTTP status codes + * http://en.wikipedia.org/wiki/List_of_HTTP_status_codes + **/ + enum HttpStatus + { + HttpStatus_None = -1, + + // 1xx Informational + HttpStatus_100_Continue = 100, + HttpStatus_101_SwitchingProtocols = 101, + HttpStatus_102_Processing = 102, + + // 2xx Success + HttpStatus_200_Ok = 200, + HttpStatus_201_Created = 201, + HttpStatus_202_Accepted = 202, + HttpStatus_203_NonAuthoritativeInformation = 203, + HttpStatus_204_NoContent = 204, + HttpStatus_205_ResetContent = 205, + HttpStatus_206_PartialContent = 206, + HttpStatus_207_MultiStatus = 207, + HttpStatus_208_AlreadyReported = 208, + HttpStatus_226_IMUsed = 226, + + // 3xx Redirection + HttpStatus_300_MultipleChoices = 300, + HttpStatus_301_MovedPermanently = 301, + HttpStatus_302_Found = 302, + HttpStatus_303_SeeOther = 303, + HttpStatus_304_NotModified = 304, + HttpStatus_305_UseProxy = 305, + HttpStatus_307_TemporaryRedirect = 307, + + // 4xx Client Error + HttpStatus_400_BadRequest = 400, + HttpStatus_401_Unauthorized = 401, + HttpStatus_402_PaymentRequired = 402, + HttpStatus_403_Forbidden = 403, + HttpStatus_404_NotFound = 404, + HttpStatus_405_MethodNotAllowed = 405, + HttpStatus_406_NotAcceptable = 406, + HttpStatus_407_ProxyAuthenticationRequired = 407, + HttpStatus_408_RequestTimeout = 408, + HttpStatus_409_Conflict = 409, + HttpStatus_410_Gone = 410, + HttpStatus_411_LengthRequired = 411, + HttpStatus_412_PreconditionFailed = 412, + HttpStatus_413_RequestEntityTooLarge = 413, + HttpStatus_414_RequestUriTooLong = 414, + HttpStatus_415_UnsupportedMediaType = 415, + HttpStatus_416_RequestedRangeNotSatisfiable = 416, + HttpStatus_417_ExpectationFailed = 417, + HttpStatus_422_UnprocessableEntity = 422, + HttpStatus_423_Locked = 423, + HttpStatus_424_FailedDependency = 424, + HttpStatus_426_UpgradeRequired = 426, + + // 5xx Server Error + HttpStatus_500_InternalServerError = 500, + HttpStatus_501_NotImplemented = 501, + HttpStatus_502_BadGateway = 502, + HttpStatus_503_ServiceUnavailable = 503, + HttpStatus_504_GatewayTimeout = 504, + HttpStatus_505_HttpVersionNotSupported = 505, + HttpStatus_506_VariantAlsoNegotiates = 506, + HttpStatus_507_InsufficientStorage = 507, + HttpStatus_509_BandwidthLimitExceeded = 509, + HttpStatus_510_NotExtended = 510 + }; + + + enum HttpMethod + { + HttpMethod_Get = 0, + HttpMethod_Post = 1, + HttpMethod_Delete = 2, + HttpMethod_Put = 3 }; @@ -86,4 +182,10 @@ FileContentType_Dicom = 1, FileContentType_Json = 2 }; + + + + const char* EnumerationToString(HttpMethod method); + + const char* EnumerationToString(HttpStatus status); } diff -r f6e61e329bbf -r b26a7c397c34 Core/FileFormats/PngReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngReader.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,305 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "PngReader.h" + +#include "../OrthancException.h" +#include "../Toolbox.h" + +#include +#include // For memcpy() + +namespace Orthanc +{ + namespace + { + struct FileRabi + { + FILE* fp_; + + FileRabi(const char* filename) + { + fp_ = fopen(filename, "rb"); + if (!fp_) + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + + ~FileRabi() + { + if (fp_) + fclose(fp_); + } + }; + } + + + struct PngReader::PngRabi + { + png_structp png_; + png_infop info_; + png_infop endInfo_; + + void Destruct() + { + if (png_) + { + png_destroy_read_struct(&png_, &info_, &endInfo_); + + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + } + } + + PngRabi() + { + png_ = NULL; + info_ = NULL; + endInfo_ = NULL; + + png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + info_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, NULL, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + endInfo_ = png_create_info_struct(png_); + if (!info_) + { + png_destroy_read_struct(&png_, &info_, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + ~PngRabi() + { + Destruct(); + } + + static void MemoryCallback(png_structp png_ptr, + png_bytep data, + png_size_t size); + }; + + + void PngReader::CheckHeader(const void* header) + { + int is_png = !png_sig_cmp((png_bytep) header, 0, 8); + if (!is_png) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + PngReader::PngReader() + { + width_ = 0; + height_ = 0; + pitch_ = 0; + format_ = PixelFormat_Grayscale8; + } + + void PngReader::Read(PngRabi& rabi) + { + png_set_sig_bytes(rabi.png_, 8); + + png_read_info(rabi.png_, rabi.info_); + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + int compression_type, filter_method; + // get size and bit-depth of the PNG-image + png_get_IHDR(rabi.png_, rabi.info_, + &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + width_ = width; + height_ = height; + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8) + { + format_ = PixelFormat_Grayscale8; + pitch_ = width_; + } + else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) + { + format_ = PixelFormat_Grayscale16; + pitch_ = 2 * width_; + + if (Toolbox::DetectEndianness() == Endianness_Little) + { + png_set_swap(rabi.png_); + } + } + else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8) + { + format_ = PixelFormat_Grayscale8; + pitch_ = 3 * width_; + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + buffer_.resize(height_ * pitch_); + + if (height_ == 0 || width_ == 0) + { + // Empty image, we are done + return; + } + + png_read_update_info(rabi.png_, rabi.info_); + + std::vector rows(height_); + for (size_t i = 0; i < height_; i++) + { + rows[i] = &buffer_[0] + i * pitch_; + } + + png_read_image(rabi.png_, &rows[0]); + } + + void PngReader::ReadFromFile(const char* filename) + { + FileRabi f(filename); + + char header[8]; + if (fread(header, 1, 8, f.fp_) != 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(header); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + png_init_io(rabi.png_, f.fp_); + + Read(rabi); + } + + + + namespace + { + struct MemoryBuffer + { + const uint8_t* buffer_; + size_t size_; + size_t pos_; + bool ok_; + }; + } + + + void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) + { + MemoryBuffer* from = (MemoryBuffer*) png_get_io_ptr(png_ptr); + + if (!from->ok_) + { + return; + } + + if (from->pos_ + byteCountToRead > from->size_) + { + from->ok_ = false; + return; + } + + memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead); + + from->pos_ += byteCountToRead; + } + + + void PngReader::ReadFromMemory(const void* buffer, + size_t size) + { + if (size < 8) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + CheckHeader(buffer); + + PngRabi rabi; + + if (setjmp(png_jmpbuf(rabi.png_))) + { + rabi.Destruct(); + throw OrthancException(ErrorCode_BadFileFormat); + } + + MemoryBuffer tmp; + tmp.buffer_ = reinterpret_cast(buffer) + 8; // We skip the header + tmp.size_ = size - 8; + tmp.pos_ = 0; + tmp.ok_ = true; + + png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback); + + Read(rabi); + + if (!tmp.ok_) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + void PngReader::ReadFromMemory(const std::string& buffer) + { + if (buffer.size() != 0) + ReadFromMemory(&buffer[0], buffer.size()); + else + ReadFromMemory(NULL, 0); + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/FileFormats/PngReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngReader.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,104 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + class PngReader + { + private: + struct PngRabi; + + PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int pitch_; + std::vector buffer_; + + void CheckHeader(const void* header); + + void Read(PngRabi& rabi); + + public: + PngReader(); + + PixelFormat GetFormat() const + { + return format_; + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetPitch() const + { + return pitch_; + } + + const void* GetBuffer() const + { + if (buffer_.size() > 0) + return &buffer_[0]; + else + return NULL; + } + + const void* GetBuffer(unsigned int y) const + { + if (buffer_.size() > 0) + return &buffer_[y * pitch_]; + else + return NULL; + } + + void ReadFromFile(const char* filename); + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/FileFormats/PngWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngWriter.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,260 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "PngWriter.h" + +#include +#include +#include +#include "../OrthancException.h" +#include "../ChunkedBuffer.h" +#include "../Toolbox.h" + + +// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4 +// http://zarb.org/~gc/html/libpng.html +/* + void write_row_callback(png_ptr, png_uint_32 row, int pass) + { + }*/ + + + + +/* bool isError_; + +// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2 + +static void ErrorHandler(png_structp png, png_const_charp message) +{ +printf("** [%s]\n", message); + +PngWriter* that = (PngWriter*) png_get_error_ptr(png); +that->isError_ = true; +printf("** %d\n", (int)that); + +//((PngWriter*) payload)->isError_ = true; +} + +static void WarningHandler(png_structp png, png_const_charp message) +{ + printf("++ %d\n", (int)message); +}*/ + + +namespace Orthanc +{ + struct PngWriter::PImpl + { + png_structp png_; + png_infop info_; + + // Filled by Prepare() + std::vector rows_; + int bitDepth_; + int colorType_; + }; + + + + PngWriter::PngWriter() : pimpl_(new PImpl) + { + pimpl_->png_ = NULL; + pimpl_->info_ = NULL; + + pimpl_->png_ = png_create_write_struct + (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler); + if (!pimpl_->png_) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + pimpl_->info_ = png_create_info_struct(pimpl_->png_); + if (!pimpl_->info_) + { + png_destroy_write_struct(&pimpl_->png_, NULL); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + PngWriter::~PngWriter() + { + if (pimpl_->info_) + { + png_destroy_info_struct(pimpl_->png_, &pimpl_->info_); + } + + if (pimpl_->png_) + { + png_destroy_write_struct(&pimpl_->png_, NULL); + } + } + + + + void PngWriter::Prepare(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + pimpl_->rows_.resize(height); + for (unsigned int y = 0; y < height; y++) + { + pimpl_->rows_[y] = const_cast(reinterpret_cast(buffer)) + y * pitch; + } + + switch (format) + { + case PixelFormat_RGB24: + pimpl_->bitDepth_ = 8; + pimpl_->colorType_ = PNG_COLOR_TYPE_RGB; + break; + + case PixelFormat_Grayscale8: + pimpl_->bitDepth_ = 8; + pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; + break; + + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + pimpl_->bitDepth_ = 16; + pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void PngWriter::Compress(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format) + { + png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height, + pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(pimpl_->png_, pimpl_->info_); + + if (height > 0) + { + switch (format) + { + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]); + + if (Toolbox::DetectEndianness() == Endianness_Little) + { + // Must swap the endianness!! + png_write_png(pimpl_->png_, pimpl_->info_, PNG_TRANSFORM_SWAP_ENDIAN, NULL); + } + + break; + + default: + png_write_image(pimpl_->png_, &pimpl_->rows_[0]); + } + } + + png_write_end(pimpl_->png_, NULL); + } + + + void PngWriter::WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + Prepare(width, height, pitch, format, buffer); + + FILE* fp = fopen(filename, "wb"); + if (!fp) + { + throw OrthancException(ErrorCode_CannotWriteFile); + } + + png_init_io(pimpl_->png_, fp); + + if (setjmp(png_jmpbuf(pimpl_->png_))) + { + // Error during writing PNG + throw OrthancException(ErrorCode_CannotWriteFile); + } + + Compress(width, height, pitch, format); + + fclose(fp); + } + + + + + static void MemoryCallback(png_structp png_ptr, + png_bytep data, + png_size_t size) + { + ChunkedBuffer* buffer = (ChunkedBuffer*) png_get_io_ptr(png_ptr); + buffer->AddChunk(reinterpret_cast(data), size); + } + + + + void PngWriter::WriteToMemory(std::string& png, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + ChunkedBuffer chunks; + + Prepare(width, height, pitch, format, buffer); + + if (setjmp(png_jmpbuf(pimpl_->png_))) + { + // Error during writing PNG + throw OrthancException(ErrorCode_InternalError); + } + + png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL); + + Compress(width, height, pitch, format); + + chunks.Flatten(png); + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/FileFormats/PngWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileFormats/PngWriter.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,78 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include +#include + +namespace Orthanc +{ + class PngWriter + { + private: + struct PImpl; + boost::shared_ptr pimpl_; + + void Compress(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format); + + void Prepare(unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + public: + PngWriter(); + + ~PngWriter(); + + void WriteToFile(const char* filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + void WriteToMemory(std::string& png, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/CompressedFileStorageAccessor.cpp --- a/Core/FileStorage/CompressedFileStorageAccessor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/CompressedFileStorageAccessor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/CompressedFileStorageAccessor.h --- a/Core/FileStorage/CompressedFileStorageAccessor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/CompressedFileStorageAccessor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/FileInfo.h --- a/Core/FileStorage/FileInfo.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/FileInfo.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/FileStorage.cpp --- a/Core/FileStorage/FileStorage.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/FileStorage.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/FileStorage.h --- a/Core/FileStorage/FileStorage.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/FileStorage.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/FileStorageAccessor.cpp --- a/Core/FileStorage/FileStorageAccessor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/FileStorageAccessor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/FileStorageAccessor.h --- a/Core/FileStorage/FileStorageAccessor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/FileStorageAccessor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/StorageAccessor.cpp --- a/Core/FileStorage/StorageAccessor.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/StorageAccessor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/FileStorage/StorageAccessor.h --- a/Core/FileStorage/StorageAccessor.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/FileStorage/StorageAccessor.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpClient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpClient.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,262 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "HttpClient.h" + +#include "../Core/Toolbox.h" +#include "../Core/OrthancException.h" + +#include +#include + + +namespace Orthanc +{ + struct HttpClient::PImpl + { + CURL* curl_; + struct curl_slist *postHeaders_; + }; + + + static CURLcode CheckCode(CURLcode code) + { + if (code != CURLE_OK) + { + throw OrthancException("libCURL error: " + std::string(curl_easy_strerror(code))); + } + + return code; + } + + + static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *payload) + { + std::string& target = *(static_cast(payload)); + + size_t length = size * nmemb; + if (length == 0) + return 0; + + size_t pos = target.size(); + + target.resize(pos + length); + memcpy(&target.at(pos), buffer, length); + + return length; + } + + + void HttpClient::Setup() + { + pimpl_->postHeaders_ = NULL; + if ((pimpl_->postHeaders_ = curl_slist_append(pimpl_->postHeaders_, "Expect:")) == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + pimpl_->curl_ = curl_easy_init(); + if (!pimpl_->curl_) + { + curl_slist_free_all(pimpl_->postHeaders_); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlCallback)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); + +#if ORTHANC_SSL_ENABLED == 1 + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); +#endif + + // This fixes the "longjmp causes uninitialized stack frame" crash + // that happens on modern Linux versions. + // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1)); + + url_ = ""; + method_ = HttpMethod_Get; + lastStatus_ = HttpStatus_200_Ok; + isVerbose_ = false; + } + + + HttpClient::HttpClient() : pimpl_(new PImpl) + { + Setup(); + } + + + HttpClient::HttpClient(const HttpClient& other) : pimpl_(new PImpl) + { + Setup(); + + if (other.IsVerbose()) + { + SetVerbose(true); + } + + if (other.credentials_.size() != 0) + { + credentials_ = other.credentials_; + } + } + + + HttpClient::~HttpClient() + { + curl_easy_cleanup(pimpl_->curl_); + curl_slist_free_all(pimpl_->postHeaders_); + } + + + void HttpClient::SetVerbose(bool isVerbose) + { + isVerbose_ = isVerbose; + + if (isVerbose_) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0)); + } + } + + + bool HttpClient::Apply(std::string& answer) + { + answer.clear(); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL)); + + if (credentials_.size() != 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str())); + } + + switch (method_) + { + case HttpMethod_Get: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L)); + break; + + case HttpMethod_Post: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_)); + + if (postData_.size() > 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size())); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); + } + + break; + + case HttpMethod_Delete: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE")); + break; + + case HttpMethod_Put: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L)); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + // Do the actual request + CheckCode(curl_easy_perform(pimpl_->curl_)); + + long status; + CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status)); + + if (status == 0) + { + // This corresponds to a call to an inexistent host + lastStatus_ = HttpStatus_500_InternalServerError; + } + else + { + lastStatus_ = static_cast(status); + } + + return (status >= 200 && status < 300); + } + + + bool HttpClient::Apply(Json::Value& answer) + { + std::string s; + if (Apply(s)) + { + Json::Reader reader; + return reader.parse(s, answer); + } + else + { + return false; + } + } + + + void HttpClient::SetCredentials(const char* username, + const char* password) + { + credentials_ = std::string(username) + ":" + std::string(password); + } + + + void HttpClient::GlobalInitialize() + { + CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT)); + } + + void HttpClient::GlobalFinalize() + { + curl_global_cleanup(); + } + + const char* HttpClient::GetLastStatusText() const + { + return EnumerationToString(lastStatus_); + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpClient.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpClient.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,127 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Core/Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + class HttpClient + { + private: + struct PImpl; + boost::shared_ptr pimpl_; + + std::string url_; + std::string credentials_; + HttpMethod method_; + HttpStatus lastStatus_; + std::string postData_; + bool isVerbose_; + + void Setup(); + + void operator= (const HttpClient&); // Forbidden + + public: + HttpClient(const HttpClient& base); + + HttpClient(); + + ~HttpClient(); + + void SetUrl(const char* url) + { + url_ = std::string(url); + } + + void SetUrl(const std::string& url) + { + url_ = url; + } + + const std::string& GetUrl() const + { + return url_; + } + + void SetMethod(HttpMethod method) + { + method_ = method; + } + + HttpMethod GetMethod() const + { + return method_; + } + + std::string& AccessPostData() + { + return postData_; + } + + const std::string& AccessPostData() const + { + return postData_; + } + + void SetVerbose(bool isVerbose); + + bool IsVerbose() const + { + return isVerbose_; + } + + bool Apply(std::string& answer); + + bool Apply(Json::Value& answer); + + HttpStatus GetLastStatus() const + { + return lastStatus_; + } + + const char* GetLastStatusText() const; + + void SetCredentials(const char* username, + const char* password); + + static void GlobalInitialize(); + + static void GlobalFinalize(); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/BufferHttpSender.h --- a/Core/HttpServer/BufferHttpSender.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/BufferHttpSender.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/EmbeddedResourceHttpHandler.cpp --- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -58,13 +58,13 @@ void EmbeddedResourceHttpHandler::Handle( HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& arguments, const std::string&) { - if (method != Orthanc_HttpMethod_Get) + if (method != HttpMethod_Get) { output.SendMethodNotAllowedError("GET"); return; @@ -82,7 +82,7 @@ catch (OrthancException& e) { LOG(WARNING) << "Unable to find HTTP resource: " << resourcePath; - output.SendHeader(Orthanc_HttpStatus_404_NotFound); + output.SendHeader(HttpStatus_404_NotFound); } } } diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/EmbeddedResourceHttpHandler.h --- a/Core/HttpServer/EmbeddedResourceHttpHandler.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -54,7 +54,7 @@ virtual void Handle( HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& arguments, diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -127,13 +127,13 @@ void FilesystemHttpHandler::Handle( HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& arguments, const std::string&) { - if (method != Orthanc_HttpMethod_Get) + if (method != HttpMethod_Get) { output.SendMethodNotAllowedError("GET"); return; @@ -161,7 +161,7 @@ } else { - output.SendHeader(Orthanc_HttpStatus_404_NotFound); + output.SendHeader(HttpStatus_404_NotFound); } } } diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/FilesystemHttpHandler.h --- a/Core/HttpServer/FilesystemHttpHandler.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -56,7 +56,7 @@ virtual void Handle( HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& arguments, diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/FilesystemHttpSender.cpp --- a/Core/HttpServer/FilesystemHttpSender.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/FilesystemHttpSender.h --- a/Core/HttpServer/FilesystemHttpSender.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/FilesystemHttpSender.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpFileSender.cpp --- a/Core/HttpServer/HttpFileSender.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpFileSender.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -47,7 +47,7 @@ if (!SendData(output)) { - output.SendHeader(Orthanc_HttpStatus_500_InternalServerError); + output.SendHeader(HttpStatus_500_InternalServerError); } } } diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpFileSender.h --- a/Core/HttpServer/HttpFileSender.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpFileSender.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpHandler.cpp --- a/Core/HttpServer/HttpHandler.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpHandler.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpHandler.h --- a/Core/HttpServer/HttpHandler.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpHandler.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -36,7 +36,6 @@ #include #include #include "../Toolbox.h" -#include "../../OrthancCppClient/HttpEnumerations.h" namespace Orthanc { @@ -54,7 +53,7 @@ virtual bool IsServedUri(const UriComponents& uri) = 0; virtual void Handle(HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& getArguments, diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpOutput.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -38,7 +38,6 @@ #include #include "../OrthancException.h" #include "../Toolbox.h" -#include "../../OrthancCppClient/HttpException.h" namespace Orthanc { @@ -104,17 +103,17 @@ void HttpOutput::SendMethodNotAllowedError(const std::string& allowed) { std::string s = - "HTTP/1.1 405 " + std::string(HttpException::GetDescription(Orthanc_HttpStatus_405_MethodNotAllowed)) + + "HTTP/1.1 405 " + std::string(EnumerationToString(HttpStatus_405_MethodNotAllowed)) + "\r\nAllow: " + allowed + "\r\n\r\n"; Send(&s[0], s.size()); } - void HttpOutput::SendHeader(Orthanc_HttpStatus status) + void HttpOutput::SendHeader(HttpStatus status) { - if (status == Orthanc_HttpStatus_200_Ok || - status == Orthanc_HttpStatus_405_MethodNotAllowed) + if (status == HttpStatus_200_Ok || + status == HttpStatus_405_MethodNotAllowed) { throw OrthancException("Please use the dedicated methods to this HTTP status code in HttpOutput"); } @@ -123,11 +122,11 @@ } - void HttpOutput::SendHeaderInternal(Orthanc_HttpStatus status) + void HttpOutput::SendHeaderInternal(HttpStatus status) { std::string s = "HTTP/1.1 " + boost::lexical_cast(status) + - " " + std::string(HttpException::GetDescription(status)) + + " " + std::string(EnumerationToString(status)) + "\r\n\r\n"; Send(&s[0], s.size()); } @@ -190,7 +189,7 @@ void HttpOutput::Redirect(const std::string& path) { std::string s = - "HTTP/1.1 301 " + std::string(HttpException::GetDescription(Orthanc_HttpStatus_301_MovedPermanently)) + + "HTTP/1.1 301 " + std::string(EnumerationToString(HttpStatus_301_MovedPermanently)) + "\r\nLocation: " + path + "\r\n\r\n"; Send(&s[0], s.size()); diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/HttpOutput.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -45,7 +45,7 @@ private: typedef std::list< std::pair > Header; - void SendHeaderInternal(Orthanc_HttpStatus status); + void SendHeaderInternal(HttpStatus status); void PrepareOkHeader(Header& header, const char* contentType, @@ -74,7 +74,7 @@ void SendMethodNotAllowedError(const std::string& allowed); - void SendHeader(Orthanc_HttpStatus status); + void SendHeader(HttpStatus status); void Redirect(const std::string& path); diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -268,9 +268,9 @@ - static PostDataStatus ReadPostData(std::string& postData, - struct mg_connection *connection, - const HttpHandler::Arguments& headers) + static PostDataStatus ReadBody(std::string& postData, + struct mg_connection *connection, + const HttpHandler::Arguments& headers) { HttpHandler::Arguments::const_iterator cs = headers.find("content-length"); if (cs == headers.end()) @@ -303,6 +303,7 @@ { return PostDataStatus_Failure; } + assert(r <= length); length -= r; pos += r; @@ -322,7 +323,7 @@ std::string boundary = "--" + contentType.substr(multipartLength); std::string postData; - PostDataStatus status = ReadPostData(postData, connection, headers); + PostDataStatus status = ReadBody(postData, connection, headers); if (status != PostDataStatus_Success) { @@ -454,6 +455,114 @@ } + static std::string GetAuthenticatedUsername(const HttpHandler::Arguments& headers) + { + HttpHandler::Arguments::const_iterator auth = headers.find("authorization"); + + if (auth == headers.end()) + { + return ""; + } + + std::string s = auth->second; + if (s.substr(0, 6) != "Basic ") + { + return ""; + } + + std::string b64 = s.substr(6); + std::string decoded = Toolbox::DecodeBase64(b64); + size_t semicolons = decoded.find(':'); + + if (semicolons == std::string::npos) + { + // Bad-formatted request + return ""; + } + else + { + return decoded.substr(0, semicolons); + } + } + + + static bool ExtractMethod(HttpMethod& method, + const struct mg_request_info *request, + const HttpHandler::Arguments& headers, + const HttpHandler::Arguments& argumentsGET) + { + std::string overriden; + + // Check whether some PUT/DELETE faking is done + + // 1. Faking with Google's approach + HttpHandler::Arguments::const_iterator methodOverride = + headers.find("x-http-method-override"); + + if (methodOverride != headers.end()) + { + overriden = methodOverride->second; + } + else if (!strcmp(request->request_method, "GET")) + { + // 2. Faking with Ruby on Rail's approach + // GET /my/resource?_method=delete <=> DELETE /my/resource + methodOverride = argumentsGET.find("_method"); + if (methodOverride != argumentsGET.end()) + { + overriden = methodOverride->second; + } + } + + if (overriden.size() > 0) + { + // A faking has been done within this request + Toolbox::ToUpperCase(overriden); + + LOG(INFO) << "HTTP method faking has been detected for " << overriden; + + if (overriden == "PUT") + { + method = HttpMethod_Put; + return true; + } + else if (overriden == "DELETE") + { + method = HttpMethod_Delete; + return true; + } + else + { + return false; + } + } + + // No PUT/DELETE faking was present + if (!strcmp(request->request_method, "GET")) + { + method = HttpMethod_Get; + } + else if (!strcmp(request->request_method, "POST")) + { + method = HttpMethod_Post; + } + else if (!strcmp(request->request_method, "DELETE")) + { + method = HttpMethod_Delete; + } + else if (!strcmp(request->request_method, "PUT")) + { + method = HttpMethod_Put; + } + else + { + return false; + } + + return true; + } + + static void* Callback(enum mg_event event, struct mg_connection *connection, @@ -464,30 +573,7 @@ MongooseServer* that = (MongooseServer*) (request->user_data); MongooseOutput output(connection); - // Compute the method - Orthanc_HttpMethod method; - if (!strcmp(request->request_method, "GET")) - { - method = Orthanc_HttpMethod_Get; - } - else if (!strcmp(request->request_method, "POST")) - { - method = Orthanc_HttpMethod_Post; - } - else if (!strcmp(request->request_method, "DELETE")) - { - method = Orthanc_HttpMethod_Delete; - } - else if (!strcmp(request->request_method, "PUT")) - { - method = Orthanc_HttpMethod_Put; - } - else - { - output.SendHeader(Orthanc_HttpStatus_405_MethodNotAllowed); - return (void*) ""; - } - + // Check remote calls if (!that->IsRemoteAccessAllowed() && request->remote_ip != LOCALHOST) { @@ -495,8 +581,9 @@ return (void*) ""; } - HttpHandler::Arguments arguments, headers; + // Extract the HTTP headers + HttpHandler::Arguments headers; for (int i = 0; i < request->num_headers; i++) { std::string name = request->http_headers[i].name; @@ -504,6 +591,24 @@ headers.insert(std::make_pair(name, request->http_headers[i].value)); } + + // Extract the GET arguments + HttpHandler::Arguments argumentsGET; + if (!strcmp(request->request_method, "GET")) + { + HttpHandler::ParseGetQuery(argumentsGET, request->query_string); + } + + + // Compute the HTTP method, taking method faking into consideration + HttpMethod method; + if (!ExtractMethod(method, request, headers, argumentsGET)) + { + output.SendHeader(HttpStatus_400_BadRequest); + return (void*) ""; + } + + // Authenticate this connection if (that->IsAuthenticationEnabled() && !Authorize(*that, headers, output)) @@ -511,83 +616,115 @@ return (void*) ""; } - std::string postData; - if (method == Orthanc_HttpMethod_Get) + // Apply the filter, if it is installed + const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter(); + if (filter != NULL) { - HttpHandler::ParseGetQuery(arguments, request->query_string); + std::string username = GetAuthenticatedUsername(headers); + + char remoteIp[24]; + sprintf(remoteIp, "%d.%d.%d.%d", + reinterpret_cast(&request->remote_ip) [3], + reinterpret_cast(&request->remote_ip) [2], + reinterpret_cast(&request->remote_ip) [1], + reinterpret_cast(&request->remote_ip) [0]); + + if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str())) + { + SendUnauthorized(output); + return (void*) ""; + } } - else if (method == Orthanc_HttpMethod_Post || - method == Orthanc_HttpMethod_Put) + + + // Extract the body of the request for PUT and POST + std::string body; + if (method == HttpMethod_Post || + method == HttpMethod_Put) { + PostDataStatus status; + HttpHandler::Arguments::const_iterator ct = headers.find("content-type"); if (ct == headers.end()) { - output.SendHeader(Orthanc_HttpStatus_400_BadRequest); - return (void*) ""; - } - - PostDataStatus status; - - std::string contentType = ct->second; - if (contentType.size() >= multipartLength && - !memcmp(contentType.c_str(), multipart, multipartLength)) - { - status = ParseMultipartPost(postData, connection, headers, contentType, that->GetChunkStore()); + // No content-type specified. Assume no multi-part content occurs at this point. + status = ReadBody(body, connection, headers); } else { - status = ReadPostData(postData, connection, headers); + std::string contentType = ct->second; + if (contentType.size() >= multipartLength && + !memcmp(contentType.c_str(), multipart, multipartLength)) + { + status = ParseMultipartPost(body, connection, headers, contentType, that->GetChunkStore()); + } + else + { + status = ReadBody(body, connection, headers); + } } switch (status) { - case PostDataStatus_NoLength: - output.SendHeader(Orthanc_HttpStatus_411_LengthRequired); - return (void*) ""; + case PostDataStatus_NoLength: + output.SendHeader(HttpStatus_411_LengthRequired); + return (void*) ""; - case PostDataStatus_Failure: - output.SendHeader(Orthanc_HttpStatus_400_BadRequest); - return (void*) ""; + case PostDataStatus_Failure: + output.SendHeader(HttpStatus_400_BadRequest); + return (void*) ""; - case PostDataStatus_Pending: - output.AnswerBufferWithContentType(NULL, 0, ""); - return (void*) ""; + case PostDataStatus_Pending: + output.AnswerBufferWithContentType(NULL, 0, ""); + return (void*) ""; - default: - break; + default: + break; } } + + // Call the proper handler for this URI UriComponents uri; - Toolbox::SplitUriComponents(uri, request->uri); + try + { + Toolbox::SplitUriComponents(uri, request->uri); + } + catch (OrthancException) + { + output.SendHeader(HttpStatus_400_BadRequest); + return (void*) ""; + } + HttpHandler* handler = that->FindHandler(uri); if (handler) { try { - handler->Handle(output, method, uri, headers, arguments, postData); + LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri); + handler->Handle(output, method, uri, headers, argumentsGET, body); } catch (OrthancException& e) { LOG(ERROR) << "MongooseServer Exception [" << e.What() << "]"; - output.SendHeader(Orthanc_HttpStatus_500_InternalServerError); + output.SendHeader(HttpStatus_500_InternalServerError); } catch (boost::bad_lexical_cast&) { LOG(ERROR) << "MongooseServer Exception: Bad lexical cast"; - output.SendHeader(Orthanc_HttpStatus_400_BadRequest); + output.SendHeader(HttpStatus_400_BadRequest); } catch (std::runtime_error&) { LOG(ERROR) << "MongooseServer Exception: Presumably a bad JSON request"; - output.SendHeader(Orthanc_HttpStatus_400_BadRequest); + output.SendHeader(HttpStatus_400_BadRequest); } } else { - output.SendHeader(Orthanc_HttpStatus_404_NotFound); + output.SendHeader(HttpStatus_404_NotFound); } // Mark as processed @@ -613,6 +750,7 @@ authentication_ = false; ssl_ = false; port_ = 8000; + filter_ = NULL; } @@ -737,6 +875,11 @@ remoteAllowed_ = allowed; } + void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter) + { + Stop(); + filter_ = &filter; + } bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const { diff -r f6e61e329bbf -r b26a7c397c34 Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/HttpServer/MongooseServer.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -44,6 +44,19 @@ { class ChunkStore; + class IIncomingHttpRequestFilter + { + public: + virtual ~IIncomingHttpRequestFilter() + { + } + + virtual bool IsAllowed(HttpMethod method, + const char* uri, + const char* ip, + const char* username) const = 0; + }; + class MongooseServer { private: @@ -62,6 +75,7 @@ bool ssl_; std::string certificate_; uint16_t port_; + IIncomingHttpRequestFilter* filter_; bool IsRunning() const; @@ -116,6 +130,13 @@ void SetRemoteAccessAllowed(bool allowed); + const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const + { + return filter_; + } + + void SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter); + void ClearHandlers(); // Can return NULL if no handler is associated to this URI diff -r f6e61e329bbf -r b26a7c397c34 Core/ICommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ICommand.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,48 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IDynamicObject.h" + +namespace Orthanc +{ + /** + * This class is the base class for the "Command" design pattern. + * http://en.wikipedia.org/wiki/Command_pattern + **/ + class ICommand : public IDynamicObject + { + public: + virtual bool Execute() = 0; + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/IDynamicObject.h --- a/Core/IDynamicObject.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/IDynamicObject.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/Lua/LuaContext.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Lua/LuaContext.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,150 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "LuaContext.h" + +#include + +extern "C" +{ +#include +#include +} + +namespace Orthanc +{ + int LuaContext::PrintToLog(lua_State *state) + { + // Get the pointer to the "LuaContext" underlying object + lua_getglobal(state, "_LuaContext"); + assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA); + LuaContext* that = const_cast(reinterpret_cast(lua_topointer(state, -1))); + assert(that != NULL); + lua_pop(state, 1); + + // http://medek.wordpress.com/2009/02/03/wrapping-lua-errors-and-print-function/ + int nArgs = lua_gettop(state); + lua_getglobal(state, "tostring"); + + // Make sure you start at 1 *NOT* 0 for arrays in Lua. + std::string result; + + for (int i = 1; i <= nArgs; i++) + { + const char *s; + lua_pushvalue(state, -1); + lua_pushvalue(state, i); + lua_call(state, 1, 1); + s = lua_tostring(state, -1); + + if (result.size() > 0) + result.append(", "); + + if (s == NULL) + result.append(""); + else + result.append(s); + + lua_pop(state, 1); + } + + LOG(INFO) << "Lua says: " << result; + that->log_.append(result); + that->log_.append("\n"); + + return 0; + } + + + LuaContext::LuaContext() + { + lua_ = luaL_newstate(); + if (!lua_) + { + throw LuaException("Unable to create the Lua context"); + } + + luaL_openlibs(lua_); + lua_register(lua_, "print", PrintToLog); + + lua_pushlightuserdata(lua_, this); + lua_setglobal(lua_, "_LuaContext"); + } + + + LuaContext::~LuaContext() + { + lua_close(lua_); + } + + + void LuaContext::Execute(std::string* output, + const std::string& command) + { + boost::mutex::scoped_lock lock(mutex_); + + log_.clear(); + int error = (luaL_loadbuffer(lua_, command.c_str(), command.size(), "line") || + lua_pcall(lua_, 0, 0, 0)); + + if (error) + { + assert(lua_gettop(lua_) >= 1); + + std::string description(lua_tostring(lua_, -1)); + lua_pop(lua_, 1); /* pop error message from the stack */ + throw LuaException(description); + } + + if (output != NULL) + { + *output = log_; + } + } + + + void LuaContext::Execute(EmbeddedResources::FileResourceId resource) + { + std::string command; + EmbeddedResources::GetFileResource(command, resource); + Execute(command); + } + + + bool LuaContext::IsExistingFunction(const char* name) + { + boost::mutex::scoped_lock lock(mutex_); + lua_settop(lua_, 0); + lua_getglobal(lua_, name); + return lua_type(lua_, -1) == LUA_TFUNCTION; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Lua/LuaContext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Lua/LuaContext.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,83 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "LuaException.h" + +#include + +extern "C" +{ +#include +} + +#include + + +namespace Orthanc +{ + class LuaContext : public boost::noncopyable + { + private: + friend class LuaFunctionCall; + + lua_State *lua_; + boost::mutex mutex_; + std::string log_; + + static int PrintToLog(lua_State *L); + + void Execute(std::string* output, + const std::string& command); + + public: + LuaContext(); + + ~LuaContext(); + + void Execute(const std::string& command) + { + Execute(NULL, command); + } + + void Execute(std::string& output, + const std::string& command) + { + Execute(&output, command); + } + + void Execute(EmbeddedResources::FileResourceId resource); + + bool IsExistingFunction(const char* name); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Lua/LuaException.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Lua/LuaException.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,52 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../OrthancException.h" + +namespace Orthanc +{ + class LuaException : public OrthancException + { + public: + LuaException(const char* explanation) : + OrthancException(explanation) + { + } + + LuaException(const std::string& explanation) : + OrthancException(explanation) + { + } + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Lua/LuaFunctionCall.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Lua/LuaFunctionCall.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,192 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "LuaFunctionCall.h" + + +namespace Orthanc +{ + void LuaFunctionCall::CheckAlreadyExecuted() + { + if (isExecuted_) + { + throw LuaException("Arguments cannot be pushed after the function is executed"); + } + } + + LuaFunctionCall::LuaFunctionCall(LuaContext& context, + const char* functionName) : + context_(context), + lock_(context.mutex_), + isExecuted_(false) + { + // Clear the stack to fulfill the invariant + lua_settop(context_.lua_, 0); + lua_getglobal(context_.lua_, functionName); + } + + void LuaFunctionCall::PushString(const std::string& value) + { + CheckAlreadyExecuted(); + lua_pushstring(context_.lua_, value.c_str()); + } + + void LuaFunctionCall::PushBoolean(bool value) + { + CheckAlreadyExecuted(); + lua_pushboolean(context_.lua_, value); + } + + void LuaFunctionCall::PushInteger(int value) + { + CheckAlreadyExecuted(); + lua_pushinteger(context_.lua_, value); + } + + void LuaFunctionCall::PushDouble(double value) + { + CheckAlreadyExecuted(); + lua_pushnumber(context_.lua_, value); + } + + void LuaFunctionCall::PushJSON(const Json::Value& value) + { + CheckAlreadyExecuted(); + + if (value.isString()) + { + lua_pushstring(context_.lua_, value.asCString()); + } + else if (value.isDouble()) + { + lua_pushnumber(context_.lua_, value.asDouble()); + } + else if (value.isInt()) + { + lua_pushinteger(context_.lua_, value.asInt()); + } + else if (value.isUInt()) + { + lua_pushinteger(context_.lua_, value.asUInt()); + } + else if (value.isBool()) + { + lua_pushboolean(context_.lua_, value.asBool()); + } + else if (value.isNull()) + { + lua_pushnil(context_.lua_); + } + else if (value.isArray()) + { + lua_newtable(context_.lua_); + + // http://lua-users.org/wiki/SimpleLuaApiExample + for (Json::Value::ArrayIndex i = 0; i < value.size(); i++) + { + // Push the table index (note the "+1" because of Lua conventions) + lua_pushnumber(context_.lua_, i + 1); + + // Push the value of the cell + PushJSON(value[i]); + + // Stores the pair in the table + lua_rawset(context_.lua_, -3); + } + } + else if (value.isObject()) + { + lua_newtable(context_.lua_); + + Json::Value::Members members = value.getMemberNames(); + + for (Json::Value::Members::const_iterator + it = members.begin(); it != members.end(); it++) + { + // Push the index of the cell + lua_pushstring(context_.lua_, it->c_str()); + + // Push the value of the cell + PushJSON(value[*it]); + + // Stores the pair in the table + lua_rawset(context_.lua_, -3); + } + } + else + { + throw LuaException("Unsupported JSON conversion"); + } + } + + void LuaFunctionCall::Execute(int numOutputs) + { + CheckAlreadyExecuted(); + + assert(lua_gettop(context_.lua_) >= 1); + int nargs = lua_gettop(context_.lua_) - 1; + int error = lua_pcall(context_.lua_, nargs, numOutputs, 0); + + if (error) + { + assert(lua_gettop(context_.lua_) >= 1); + + std::string description(lua_tostring(context_.lua_, -1)); + lua_pop(context_.lua_, 1); /* pop error message from the stack */ + throw LuaException(description); + } + + if (lua_gettop(context_.lua_) < numOutputs) + { + throw LuaException("The function does not give the expected number of outputs"); + } + + isExecuted_ = true; + } + + bool LuaFunctionCall::ExecutePredicate() + { + Execute(1); + + if (lua_gettop(context_.lua_) == 0) + { + throw LuaException("No output was provided by the function"); + } + + if (!lua_isboolean(context_.lua_, 1)) + { + throw LuaException("The function is not a predicate (only true/false outputs allowed)"); + } + + return lua_toboolean(context_.lua_, 1) != 0; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/Lua/LuaFunctionCall.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Lua/LuaFunctionCall.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,69 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "LuaContext.h" + +#include + + +namespace Orthanc +{ + class LuaFunctionCall : public boost::noncopyable + { + private: + LuaContext& context_; + boost::mutex::scoped_lock lock_; + bool isExecuted_; + + void CheckAlreadyExecuted(); + + public: + LuaFunctionCall(LuaContext& context, + const char* functionName); + + void PushString(const std::string& value); + + void PushBoolean(bool value); + + void PushInteger(int value); + + void PushDouble(double value); + + void PushJSON(const Json::Value& value); + + void Execute(int numOutputs = 0); + + bool ExecutePredicate(); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/ArrayFilledByThreads.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/ArrayFilledByThreads.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,121 @@ +#include "ArrayFilledByThreads.h" + +#include "../MultiThreading/ThreadedCommandProcessor.h" +#include "../OrthancException.h" + +namespace Orthanc +{ + class ArrayFilledByThreads::Command : public ICommand + { + private: + ArrayFilledByThreads& that_; + size_t index_; + + public: + Command(ArrayFilledByThreads& that, + size_t index) : + that_(that), + index_(index) + { + } + + virtual bool Execute() + { + std::auto_ptr obj(that_.filler_.GetFillerItem(index_)); + if (obj.get() == NULL) + { + return false; + } + else + { + boost::mutex::scoped_lock lock(that_.mutex_); + that_.array_[index_] = obj.release(); + return true; + } + } + }; + + void ArrayFilledByThreads::Clear() + { + for (size_t i = 0; i < array_.size(); i++) + { + if (array_[i]) + delete array_[i]; + } + + array_.clear(); + filled_ = false; + } + + void ArrayFilledByThreads::Update() + { + if (!filled_) + { + array_.resize(filler_.GetFillerSize()); + + Orthanc::ThreadedCommandProcessor processor(threadCount_); + for (size_t i = 0; i < array_.size(); i++) + { + processor.Post(new Command(*this, i)); + } + + processor.Join(); + filled_ = true; + } + } + + + ArrayFilledByThreads::ArrayFilledByThreads(IFiller& filler) : filler_(filler) + { + filled_ = false; + threadCount_ = 4; + } + + + ArrayFilledByThreads::~ArrayFilledByThreads() + { + Clear(); + } + + + void ArrayFilledByThreads::Reload() + { + Clear(); + Update(); + } + + + void ArrayFilledByThreads::Invalidate() + { + Clear(); + } + + + void ArrayFilledByThreads::SetThreadCount(unsigned int t) + { + if (t < 1) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + threadCount_ = t; + } + + + size_t ArrayFilledByThreads::GetSize() + { + Update(); + return array_.size(); + } + + + IDynamicObject& ArrayFilledByThreads::GetItem(size_t index) + { + if (index >= GetSize()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + return *array_[index]; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/ArrayFilledByThreads.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/ArrayFilledByThreads.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "../ICommand.h" + +namespace Orthanc +{ + class ArrayFilledByThreads + { + public: + class IFiller + { + public: + virtual size_t GetFillerSize() = 0; + + virtual IDynamicObject* GetFillerItem(size_t index) = 0; + }; + + private: + IFiller& filler_; + boost::mutex mutex_; + std::vector array_; + bool filled_; + unsigned int threadCount_; + + class Command; + + void Clear(); + + void Update(); + + public: + ArrayFilledByThreads(IFiller& filler); + + ~ArrayFilledByThreads(); + + void Reload(); + + void Invalidate(); + + void SetThreadCount(unsigned int t); + + unsigned int GetThreadCount() const + { + return threadCount_; + } + + size_t GetSize(); + + IDynamicObject& GetItem(size_t index); + }; +} + diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/BagOfRunnablesBySteps.cpp --- a/Core/MultiThreading/BagOfRunnablesBySteps.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -102,7 +102,11 @@ assert(t.get() != NULL); assert(bag->pimpl_->activeThreads_.find(r.get()) == bag->pimpl_->activeThreads_.end()); - t->join(); + if (t->joinable()) + { + t->join(); + } + bag->pimpl_->oneThreadIsJoined_.notify_one(); } @@ -128,7 +132,11 @@ // Stop the finish listener pimpl_->stopFinishListener_ = true; pimpl_->oneThreadIsStopped_.notify_one(); // Awakens the listener - pimpl_->finishListener_->join(); + + if (pimpl_->finishListener_->joinable()) + { + pimpl_->finishListener_->join(); + } } diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/BagOfRunnablesBySteps.h --- a/Core/MultiThreading/BagOfRunnablesBySteps.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/MultiThreading/BagOfRunnablesBySteps.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/IRunnableBySteps.h --- a/Core/MultiThreading/IRunnableBySteps.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/MultiThreading/IRunnableBySteps.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/SharedMessageQueue.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/SharedMessageQueue.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,122 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "SharedMessageQueue.h" + +namespace Orthanc +{ + SharedMessageQueue::SharedMessageQueue(unsigned int maxSize) + { + maxSize_ = maxSize; + } + + + SharedMessageQueue::~SharedMessageQueue() + { + for (Queue::iterator it = queue_.begin(); it != queue_.end(); it++) + { + delete *it; + } + } + + + void SharedMessageQueue::Enqueue(IDynamicObject* message) + { + boost::mutex::scoped_lock lock(mutex_); + + if (maxSize_ != 0 && queue_.size() > maxSize_) + { + // Too many elements in the queue: First remove the oldest + delete queue_.front(); + queue_.pop_front(); + } + + queue_.push_back(message); + elementAvailable_.notify_one(); + } + + + IDynamicObject* SharedMessageQueue::Dequeue(int32_t millisecondsTimeout) + { + boost::mutex::scoped_lock lock(mutex_); + + // Wait for a message to arrive in the FIFO queue + while (queue_.empty()) + { + if (millisecondsTimeout == 0) + { + elementAvailable_.wait(lock); + } + else + { + bool success = elementAvailable_.timed_wait + (lock, boost::posix_time::milliseconds(millisecondsTimeout)); + if (!success) + { + return NULL; + } + } + } + + std::auto_ptr message(queue_.front()); + queue_.pop_front(); + emptied_.notify_all(); + + return message.release(); + } + + + + bool SharedMessageQueue::WaitEmpty(int32_t millisecondsTimeout) + { + boost::mutex::scoped_lock lock(mutex_); + + // Wait for the queue to become empty + if (!queue_.empty()) + { + if (millisecondsTimeout == 0) + { + emptied_.wait(lock); + } + else + { + if (!emptied_.timed_wait + (lock, boost::posix_time::milliseconds(millisecondsTimeout))) + { + return false; + } + } + } + + return true; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/SharedMessageQueue.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/SharedMessageQueue.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,67 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../IDynamicObject.h" + +#include +#include +#include + +namespace Orthanc +{ + class SharedMessageQueue + { + private: + typedef std::list Queue; + + unsigned int maxSize_; + Queue queue_; + boost::mutex mutex_; + boost::condition_variable elementAvailable_; + boost::condition_variable emptied_; + + public: + SharedMessageQueue(unsigned int maxSize = 0); + + ~SharedMessageQueue(); + + // This transfers the ownership of the message + void Enqueue(IDynamicObject* message); + + // The caller is responsible to delete the dequeud message! + IDynamicObject* Dequeue(int32_t millisecondsTimeout); + + bool WaitEmpty(int32_t millisecondsTimeout); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/ThreadedCommandProcessor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/ThreadedCommandProcessor.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,205 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "ThreadedCommandProcessor.h" + +#include "../OrthancException.h" + +namespace Orthanc +{ + static const int32_t TIMEOUT = 10; + + + void ThreadedCommandProcessor::Processor(ThreadedCommandProcessor* that) + { + while (!that->done_) + { + std::auto_ptr command(that->queue_.Dequeue(TIMEOUT)); + + if (command.get() != NULL) + { + bool success = false; + + try + { + if (that->success_) + { + // No command has failed so far + + if (that->cancel_) + { + // The commands have been canceled. Skip the execution + // of this command, yet mark it as succeeded. + success = true; + } + else + { + success = dynamic_cast(*command).Execute(); + } + } + else + { + // A command has already failed. Skip the execution of this command. + } + } + catch (OrthancException) + { + } + + { + boost::mutex::scoped_lock lock(that->mutex_); + assert(that->remainingCommands_ > 0); + that->remainingCommands_--; + + if (!success) + { + if (!that->cancel_ && that->listener_ && that->success_) + { + // This is the first command that fails + that->listener_->SignalFailure(); + } + + that->success_ = false; + } + else + { + if (!that->cancel_ && that->listener_) + { + if (that->remainingCommands_ == 0) + { + that->listener_->SignalSuccess(that->totalCommands_); + } + else + { + that->listener_->SignalProgress(that->totalCommands_ - that->remainingCommands_, + that->totalCommands_); + } + } + } + + that->processedCommand_.notify_all(); + } + } + } + } + + + ThreadedCommandProcessor::ThreadedCommandProcessor(unsigned int numThreads) + { + if (numThreads < 1) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + listener_ = NULL; + success_ = true; + done_ = false; + cancel_ = false; + threads_.resize(numThreads); + remainingCommands_ = 0; + totalCommands_ = 0; + + for (unsigned int i = 0; i < numThreads; i++) + { + threads_[i] = new boost::thread(Processor, this); + } + } + + + ThreadedCommandProcessor::~ThreadedCommandProcessor() + { + done_ = true; + + for (unsigned int i = 0; i < threads_.size(); i++) + { + boost::thread* t = threads_[i]; + + if (t != NULL) + { + if (t->joinable()) + { + t->join(); + } + + delete t; + } + } + } + + + void ThreadedCommandProcessor::Post(ICommand* command) + { + boost::mutex::scoped_lock lock(mutex_); + queue_.Enqueue(command); + remainingCommands_++; + totalCommands_++; + } + + + bool ThreadedCommandProcessor::Join() + { + boost::mutex::scoped_lock lock(mutex_); + + while (!remainingCommands_ == 0) + { + processedCommand_.wait(lock); + } + + if (cancel_ && listener_) + { + listener_->SignalCancel(); + } + + // Reset the sequence counters for subsequent commands + bool hasSucceeded = success_; + success_ = true; + totalCommands_ = 0; + cancel_ = false; + + return hasSucceeded; + } + + + void ThreadedCommandProcessor::Cancel() + { + boost::mutex::scoped_lock lock(mutex_); + + cancel_ = true; + } + + + void ThreadedCommandProcessor::SetListener(IListener& listener) + { + boost::mutex::scoped_lock lock(mutex_); + listener_ = &listener; + } +} diff -r f6e61e329bbf -r b26a7c397c34 Core/MultiThreading/ThreadedCommandProcessor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MultiThreading/ThreadedCommandProcessor.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,94 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../ICommand.h" + +#include "SharedMessageQueue.h" + +namespace Orthanc +{ + class ThreadedCommandProcessor + { + public: + class IListener + { + public: + virtual ~IListener() + { + } + + virtual void SignalProgress(unsigned int current, + unsigned int total) = 0; + + virtual void SignalSuccess(unsigned int total) = 0; + + virtual void SignalFailure() = 0; + + virtual void SignalCancel() = 0; + }; + + private: + SharedMessageQueue queue_; + bool done_; + bool cancel_; + std::vector threads_; + IListener* listener_; + + boost::mutex mutex_; + bool success_; + unsigned int remainingCommands_, totalCommands_; + boost::condition_variable processedCommand_; + + static void Processor(ThreadedCommandProcessor* that); + + public: + ThreadedCommandProcessor(unsigned int numThreads); + + ~ThreadedCommandProcessor(); + + // This takes the ownership of the command + void Post(ICommand* command); + + bool Join(); + + void Cancel(); + + void SetListener(IListener& listener); + + IListener& GetListener() const + { + return *listener_; + } + }; +} diff -r f6e61e329bbf -r b26a7c397c34 Core/OrthancException.cpp --- a/Core/OrthancException.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/OrthancException.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -102,6 +102,9 @@ case ErrorCode_BadRequest: return "Bad request"; + case ErrorCode_NetworkProtocol: + return "Error in the network protocol"; + case ErrorCode_Custom: default: return "???"; diff -r f6e61e329bbf -r b26a7c397c34 Core/OrthancException.h --- a/Core/OrthancException.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/OrthancException.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -39,13 +39,19 @@ { class OrthancException { - private: + protected: ErrorCode error_; std::string custom_; public: static const char* GetDescription(ErrorCode error); + OrthancException(const char* custom) + { + error_ = ErrorCode_Custom; + custom_ = custom; + } + OrthancException(const std::string& custom) { error_ = ErrorCode_Custom; diff -r f6e61e329bbf -r b26a7c397c34 Core/PngWriter.cpp --- a/Core/PngWriter.cpp Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,252 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "PngWriter.h" - -#include -#include -#include -#include "OrthancException.h" -#include "ChunkedBuffer.h" - - -// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-4 -// http://zarb.org/~gc/html/libpng.html -/* - void write_row_callback(png_ptr, png_uint_32 row, int pass) - { - }*/ - - - - -/* bool isError_; - -// http://www.libpng.org/pub/png/book/chapter14.html#png.ch14.div.2 - -static void ErrorHandler(png_structp png, png_const_charp message) -{ -printf("** [%s]\n", message); - -PngWriter* that = (PngWriter*) png_get_error_ptr(png); -that->isError_ = true; -printf("** %d\n", (int)that); - -//((PngWriter*) payload)->isError_ = true; -} - -static void WarningHandler(png_structp png, png_const_charp message) -{ - printf("++ %d\n", (int)message); -}*/ - - -namespace Orthanc -{ - struct PngWriter::PImpl - { - png_structp png_; - png_infop info_; - - // Filled by Prepare() - std::vector rows_; - int bitDepth_; - int colorType_; - }; - - - - PngWriter::PngWriter() : pimpl_(new PImpl) - { - pimpl_->png_ = NULL; - pimpl_->info_ = NULL; - - pimpl_->png_ = png_create_write_struct - (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //this, ErrorHandler, WarningHandler); - if (!pimpl_->png_) - { - throw OrthancException(ErrorCode_NotEnoughMemory); - } - - pimpl_->info_ = png_create_info_struct(pimpl_->png_); - if (!pimpl_->info_) - { - png_destroy_write_struct(&pimpl_->png_, NULL); - throw OrthancException(ErrorCode_NotEnoughMemory); - } - } - - PngWriter::~PngWriter() - { - if (pimpl_->info_) - { - png_destroy_info_struct(pimpl_->png_, &pimpl_->info_); - } - - if (pimpl_->png_) - { - png_destroy_write_struct(&pimpl_->png_, NULL); - } - } - - - - void PngWriter::Prepare(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - pimpl_->rows_.resize(height); - for (unsigned int y = 0; y < height; y++) - { - pimpl_->rows_[y] = const_cast(reinterpret_cast(buffer)) + y * pitch; - } - - switch (format) - { - case PixelFormat_RGB24: - pimpl_->bitDepth_ = 8; - pimpl_->colorType_ = PNG_COLOR_TYPE_RGB; - break; - - case PixelFormat_Grayscale8: - pimpl_->bitDepth_ = 8; - pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; - break; - - case PixelFormat_Grayscale16: - pimpl_->bitDepth_ = 16; - pimpl_->colorType_ = PNG_COLOR_TYPE_GRAY; - break; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void PngWriter::Compress(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format) - { - png_set_IHDR(pimpl_->png_, pimpl_->info_, width, height, - pimpl_->bitDepth_, pimpl_->colorType_, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(pimpl_->png_, pimpl_->info_); - - if (height > 0) - { - switch (format) - { - case PixelFormat_Grayscale16: - // Must swap the endianness!! - png_set_rows(pimpl_->png_, pimpl_->info_, &pimpl_->rows_[0]); - png_write_png(pimpl_->png_, pimpl_->info_, PNG_TRANSFORM_SWAP_ENDIAN, NULL); - break; - - default: - png_write_image(pimpl_->png_, &pimpl_->rows_[0]); - } - } - - png_write_end(pimpl_->png_, NULL); - } - - - void PngWriter::WriteToFile(const char* filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - Prepare(width, height, pitch, format, buffer); - - FILE* fp = fopen(filename, "wb"); - if (!fp) - { - throw OrthancException(ErrorCode_CannotWriteFile); - } - - png_init_io(pimpl_->png_, fp); - - if (setjmp(png_jmpbuf(pimpl_->png_))) - { - // Error during writing PNG - throw OrthancException(ErrorCode_CannotWriteFile); - } - - Compress(width, height, pitch, format); - - fclose(fp); - } - - - - - static void MemoryCallback(png_structp png_ptr, - png_bytep data, - png_size_t size) - { - ChunkedBuffer* buffer = (ChunkedBuffer*) png_get_io_ptr(png_ptr); - buffer->AddChunk(reinterpret_cast(data), size); - } - - - - void PngWriter::WriteToMemory(std::string& png, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - ChunkedBuffer chunks; - - Prepare(width, height, pitch, format, buffer); - - if (setjmp(png_jmpbuf(pimpl_->png_))) - { - // Error during writing PNG - throw OrthancException(ErrorCode_InternalError); - } - - png_set_write_fn(pimpl_->png_, &chunks, MemoryCallback, NULL); - - Compress(width, height, pitch, format); - - chunks.Flatten(png); - } -} diff -r f6e61e329bbf -r b26a7c397c34 Core/PngWriter.h --- a/Core/PngWriter.h Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Enumerations.h" - -#include -#include - -namespace Orthanc -{ - class PngWriter - { - private: - struct PImpl; - boost::shared_ptr pimpl_; - - void Compress(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format); - - void Prepare(unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - - public: - PngWriter(); - - ~PngWriter(); - - void WriteToFile(const char* filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - - void WriteToMemory(std::string& png, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); - }; -} diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApi.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -180,7 +180,7 @@ } void RestApi::Handle(HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& getArguments, @@ -191,14 +191,14 @@ RestApiPath::Components components; UriComponents trailing; - if (method == Orthanc_HttpMethod_Get) + if (method == HttpMethod_Get) { for (GetHandlers::const_iterator it = getHandlers_.begin(); it != getHandlers_.end(); it++) { if (it->first->Match(components, trailing, uri)) { - LOG(INFO) << "REST GET call on: " << Toolbox::FlattenUri(uri); + //LOG(INFO) << "REST GET call on: " << Toolbox::FlattenUri(uri); ok = true; GetCall call; call.output_ = &restOutput; @@ -213,14 +213,14 @@ } } } - else if (method == Orthanc_HttpMethod_Put) + else if (method == HttpMethod_Put) { for (PutHandlers::const_iterator it = putHandlers_.begin(); it != putHandlers_.end(); it++) { if (it->first->Match(components, trailing, uri)) { - LOG(INFO) << "REST PUT call on: " << Toolbox::FlattenUri(uri); + //LOG(INFO) << "REST PUT call on: " << Toolbox::FlattenUri(uri); ok = true; PutCall call; call.output_ = &restOutput; @@ -235,14 +235,14 @@ } } } - else if (method == Orthanc_HttpMethod_Post) + else if (method == HttpMethod_Post) { for (PostHandlers::const_iterator it = postHandlers_.begin(); it != postHandlers_.end(); it++) { if (it->first->Match(components, trailing, uri)) { - LOG(INFO) << "REST POST call on: " << Toolbox::FlattenUri(uri); + //LOG(INFO) << "REST POST call on: " << Toolbox::FlattenUri(uri); ok = true; PostCall call; call.output_ = &restOutput; @@ -257,14 +257,14 @@ } } } - else if (method == Orthanc_HttpMethod_Delete) + else if (method == HttpMethod_Delete) { for (DeleteHandlers::const_iterator it = deleteHandlers_.begin(); it != deleteHandlers_.end(); it++) { if (it->first->Match(components, trailing, uri)) { - LOG(INFO) << "REST DELETE call on: " << Toolbox::FlattenUri(uri); + //LOG(INFO) << "REST DELETE call on: " << Toolbox::FlattenUri(uri); ok = true; DeleteCall call; call.output_ = &restOutput; @@ -280,7 +280,8 @@ if (!ok) { - LOG(INFO) << "REST method " << method << " not allowed on: " << Toolbox::FlattenUri(uri); + LOG(INFO) << "REST method " << EnumerationToString(method) + << " not allowed on: " << Toolbox::FlattenUri(uri); output.SendMethodNotAllowedError(GetAcceptedMethods(uri)); } } diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApi.h --- a/Core/RestApi/RestApi.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApi.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -212,7 +212,7 @@ virtual bool IsServedUri(const UriComponents& uri); virtual void Handle(HttpOutput& output, - Orthanc_HttpMethod method, + HttpMethod method, const UriComponents& uri, const Arguments& headers, const Arguments& getArguments, diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApiOutput.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -48,7 +48,7 @@ { if (!alreadySent_) { - output_.SendHeader(Orthanc_HttpStatus_400_BadRequest); + output_.SendHeader(HttpStatus_400_BadRequest); } } @@ -100,10 +100,10 @@ alreadySent_ = true; } - void RestApiOutput::SignalError(Orthanc_HttpStatus status) + void RestApiOutput::SignalError(HttpStatus status) { - if (status != Orthanc_HttpStatus_403_Forbidden && - status != Orthanc_HttpStatus_415_UnsupportedMediaType) + if (status != HttpStatus_403_Forbidden && + status != HttpStatus_415_UnsupportedMediaType) { throw OrthancException("This HTTP status is not allowed in a REST API"); } diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApiOutput.h --- a/Core/RestApi/RestApiOutput.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApiOutput.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -74,7 +74,7 @@ size_t length, const std::string& contentType); - void SignalError(Orthanc_HttpStatus status); + void SignalError(HttpStatus status); void Redirect(const std::string& path); diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApiPath.cpp --- a/Core/RestApi/RestApiPath.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApiPath.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/RestApi/RestApiPath.h --- a/Core/RestApi/RestApiPath.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/RestApi/RestApiPath.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Connection.cpp --- a/Core/SQLite/Connection.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Connection.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Connection.h --- a/Core/SQLite/Connection.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Connection.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/FunctionContext.cpp --- a/Core/SQLite/FunctionContext.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/FunctionContext.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Redistribution and use in source and binary forms, with or without diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/FunctionContext.h --- a/Core/SQLite/FunctionContext.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/FunctionContext.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Redistribution and use in source and binary forms, with or without diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/IScalarFunction.h --- a/Core/SQLite/IScalarFunction.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/IScalarFunction.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Redistribution and use in source and binary forms, with or without diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Statement.cpp --- a/Core/SQLite/Statement.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Statement.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Statement.h --- a/Core/SQLite/Statement.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Statement.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/StatementId.cpp --- a/Core/SQLite/StatementId.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/StatementId.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/StatementId.h --- a/Core/SQLite/StatementId.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/StatementId.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/StatementReference.cpp --- a/Core/SQLite/StatementReference.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/StatementReference.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/StatementReference.h --- a/Core/SQLite/StatementReference.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/StatementReference.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Transaction.cpp --- a/Core/SQLite/Transaction.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Transaction.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/SQLite/Transaction.h --- a/Core/SQLite/Transaction.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/SQLite/Transaction.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * Copyright (c) 2012 The Chromium Authors. All rights reserved. diff -r f6e61e329bbf -r b26a7c397c34 Core/Toolbox.cpp --- a/Core/Toolbox.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Toolbox.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -34,6 +34,7 @@ #include "OrthancException.h" +#include #include #include #include @@ -131,9 +132,9 @@ #if defined(_WIN32) static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType) { - // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx - finish = true; - return true; + // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx + finish = true; + return true; } #else static void SignalHandler(int) @@ -168,7 +169,7 @@ void Toolbox::ServerBarrier() { #if defined(_WIN32) - SetConsoleCtrlHandler(ConsoleControlHandler, true); + SetConsoleCtrlHandler(ConsoleControlHandler, true); #else signal(SIGINT, SignalHandler); signal(SIGQUIT, SignalHandler); @@ -181,7 +182,7 @@ } #if defined(_WIN32) - SetConsoleCtrlHandler(ConsoleControlHandler, false); + SetConsoleCtrlHandler(ConsoleControlHandler, false); #else signal(SIGINT, NULL); signal(SIGQUIT, NULL); @@ -207,10 +208,10 @@ const std::string& path) { boost::filesystem::ifstream f; - f.open(path, std::ifstream::in | std::ios::binary); + f.open(path, std::ifstream::in | std::ifstream::binary); if (!f.good()) { - throw OrthancException("Unable to open a file"); + throw OrthancException(ErrorCode_InexistentFile); } // http://www.cplusplus.com/reference/iostream/istream/tellg/ @@ -228,6 +229,26 @@ } + void Toolbox::WriteFile(const std::string& content, + const std::string& path) + { + boost::filesystem::ofstream f; + f.open(path, std::ofstream::binary); + if (!f.good()) + { + throw OrthancException(ErrorCode_CannotWriteFile); + } + + if (content.size() != 0) + { + f.write(content.c_str(), content.size()); + } + + f.close(); + } + + + void Toolbox::RemoveFile(const std::string& path) { if (boost::filesystem::exists(path)) @@ -565,6 +586,33 @@ } } + bool Toolbox::IsSHA1(const std::string& str) + { + if (str.size() != 44) + { + return false; + } + + for (unsigned int i = 0; i < 44; i++) + { + if (i == 8 || + i == 17 || + i == 26 || + i == 35) + { + if (str[i] != '-') + return false; + } + else + { + if (!isalnum(str[i])) + return false; + } + } + + return true; + } + std::string Toolbox::GetNowIsoString() { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); @@ -644,4 +692,29 @@ s.resize(target); } + + + Endianness Toolbox::DetectEndianness() + { + // http://sourceforge.net/p/predef/wiki/Endianness/ + + uint8_t buffer[4]; + + buffer[0] = 0x00; + buffer[1] = 0x01; + buffer[2] = 0x02; + buffer[3] = 0x03; + + switch (*((uint32_t *)buffer)) + { + case 0x00010203: + return Endianness_Big; + + case 0x03020100: + return Endianness_Little; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } } diff -r f6e61e329bbf -r b26a7c397c34 Core/Toolbox.h --- a/Core/Toolbox.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Toolbox.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -32,6 +32,8 @@ #pragma once +#include "Enumerations.h" + #include #include #include @@ -55,6 +57,9 @@ void ReadFile(std::string& content, const std::string& path); + void WriteFile(const std::string& content, + const std::string& path); + void Sleep(uint32_t seconds); void USleep(uint64_t microSeconds); @@ -80,6 +85,8 @@ void ComputeSHA1(std::string& result, const std::string& data); + bool IsSHA1(const std::string& str); + std::string DecodeBase64(const std::string& data); std::string EncodeBase64(const std::string& data); @@ -99,5 +106,7 @@ // In-place percent-decoding for URL void UrlDecode(std::string& s); + + Endianness DetectEndianness(); } } diff -r f6e61e329bbf -r b26a7c397c34 Core/Uuid.cpp --- a/Core/Uuid.cpp Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Uuid.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -96,6 +96,28 @@ } + bool StartsWithUuid(const std::string& str) + { + if (str.size() < 36) + { + return false; + } + + if (str.size() == 36) + { + return IsUuid(str); + } + + assert(str.size() > 36); + if (!isspace(str[36])) + { + return false; + } + + return IsUuid(str.substr(0, 36)); + } + + static std::string CreateTemporaryPath(const char* extension) { #if BOOST_HAS_FILESYSTEM_V3 == 1 diff -r f6e61e329bbf -r b26a7c397c34 Core/Uuid.h --- a/Core/Uuid.h Mon Apr 29 12:48:10 2013 +0200 +++ b/Core/Uuid.h Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -51,6 +51,8 @@ bool IsUuid(const std::string& str); + bool StartsWithUuid(const std::string& str); + class TemporaryFile { private: diff -r f6e61e329bbf -r b26a7c397c34 NEWS --- a/NEWS Mon Apr 29 12:48:10 2013 +0200 +++ b/NEWS Wed Sep 18 15:27:07 2013 +0200 @@ -1,6 +1,62 @@ Pending changes in the mainline =============================== +* Switch to Boost 1.54.0 (cf. issue #9) + + +Version 0.6.1 (2013/09/16) +========================== + +* Detection of stable patients/studies/series +* C-Find SCU at the instance level +* Link from modified to original resource in Orthanc Explorer +* Fix of issue #8 +* Anonymization of the medical alerts tag (0010,2000) + + +Version 0.6.0 (2013/07/16) +========================== + +Major changes +------------- + +* Introduction of the C++ client +* Send DICOM resources to other Orthanc instances through HTTP +* Access to signed images (instances/.../image-int16) + (Closes: Debian #716958) + +Minor changes +------------- + +* Export of DICOM files to the host filesystem (instances/.../export) +* Statistics about patients, studies, series and instances +* Link from anonymized to original resource in Orthanc Explorer +* Fixes for Red Hat and Debian packaging +* Fixes for history in Orthanc Explorer +* Fixes for boost::thread, as reported by Cyril Paulus +* Fix licensing (Closes: Debian #712038) + +Metadata +-------- + +* Access to the metadata through the REST API (.../metadata) +* Support of user-defined metadata +* "LastUpdate" metadata for patients, studies and series +* "/tools/now" to be used in combination with "LastUpdate" +* Improved support of series with temporal positions + + +Version 0.5.2 (2013/05/07) +========================== + +* "Bulk" Store-SCU (send several DICOM instances with the same + DICOM connexion) +* Store-SCU for patients and studies in Orthanc Explorer +* Filtering of incoming DICOM instances (through Lua scripting) +* Filtering of incoming HTTP requests (through Lua scripting) +* Clearing of "/exports" and "/changes" +* Check MD5 of third party downloads +* Faking of the HTTP methods PUT and DELETE Version 0.5.1 (2013/04/17) @@ -9,7 +65,7 @@ * Support of RGB images * Fix of store SCU in release builds * Possibility to store the SQLite index at another place than the - DICOM instances + DICOM instances (for performance) Version 0.5.0 (2013/01/31) diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/CMakeLists.txt --- a/OrthancCppClient/CMakeLists.txt Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -# Mini-project to check whether "OrthancCppClient" can compile in a -# standalone fashion - -cmake_minimum_required(VERSION 2.8) - -project(OrthancCppClientTest) - -SET(STATIC_BUILD OFF) - -include(${CMAKE_SOURCE_DIR}/../Resources/CMake/DownloadPackage.cmake) -include(${CMAKE_SOURCE_DIR}/../Resources/CMake/JsonCppConfiguration.cmake) -include(${CMAKE_SOURCE_DIR}/../Resources/CMake/LibCurlConfiguration.cmake) - -if (${CMAKE_COMPILER_IS_GNUCXX}) - set(CMAKE_C_FLAGS "-Wall -pedantic -Wno-implicit-function-declaration") # --std=c99 makes libcurl not to compile - set(CMAKE_CXX_FLAGS "-Wall -pedantic -Wno-long-long -Wno-variadic-macros") - set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed") - set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") -elseif (${MSVC}) - add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) -endif() - -add_library(OrthancCppClient - SHARED - - ${THIRD_PARTY_SOURCES} - HttpException.cpp - HttpClient.cpp - ) - -add_executable(Test - main.cpp - ) - -target_link_libraries(Test OrthancCppClient) diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/HttpClient.cpp --- a/OrthancCppClient/HttpClient.cpp Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,208 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#include "HttpClient.h" - -#include -#include - - -namespace Orthanc -{ - struct HttpClient::PImpl - { - CURL* curl_; - struct curl_slist *postHeaders_; - }; - - - static CURLcode CheckCode(CURLcode code) - { - if (code != CURLE_OK) - { - printf("ICI: %s\n", curl_easy_strerror(code)); - throw HttpException("CURL: " + std::string(curl_easy_strerror(code))); - } - - return code; - } - - - static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *payload) - { - std::string& target = *(static_cast(payload)); - - size_t length = size * nmemb; - if (length == 0) - return 0; - - size_t pos = target.size(); - - target.resize(pos + length); - memcpy(&target.at(pos), buffer, length); - - return length; - } - - - HttpClient::HttpClient() : pimpl_(new PImpl) - { - pimpl_->postHeaders_ = NULL; - if ((pimpl_->postHeaders_ = curl_slist_append(pimpl_->postHeaders_, "Expect:")) == NULL) - { - throw HttpException("HttpClient: Not enough memory"); - } - - pimpl_->curl_ = curl_easy_init(); - if (!pimpl_->curl_) - { - curl_slist_free_all(pimpl_->postHeaders_); - throw HttpException("HttpClient: Not enough memory"); - } - - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlCallback)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); - -#if ORTHANC_SSL_ENABLED == 1 - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); -#endif - - url_ = ""; - method_ = Orthanc_HttpMethod_Get; - lastStatus_ = Orthanc_HttpStatus_200_Ok; - isVerbose_ = false; - } - - - HttpClient::~HttpClient() - { - curl_easy_cleanup(pimpl_->curl_); - curl_slist_free_all(pimpl_->postHeaders_); - } - - - void HttpClient::SetVerbose(bool isVerbose) - { - isVerbose_ = isVerbose; - - if (isVerbose_) - { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1)); - } - else - { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0)); - } - } - - - bool HttpClient::Apply(std::string& answer) - { - answer.clear(); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL)); - - switch (method_) - { - case Orthanc_HttpMethod_Get: - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L)); - break; - - case Orthanc_HttpMethod_Post: - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_)); - - if (postData_.size() > 0) - { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str())); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size())); - } - else - { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); - } - - break; - - case Orthanc_HttpMethod_Delete: - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE")); - break; - - case Orthanc_HttpMethod_Put: - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L)); - break; - - default: - throw HttpException("HttpClient: Internal error"); - } - - // Do the actual request - CheckCode(curl_easy_perform(pimpl_->curl_)); - - long status; - CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status)); - - if (status == 0) - { - // This corresponds to a call to an inexistent host - lastStatus_ = Orthanc_HttpStatus_500_InternalServerError; - } - else - { - lastStatus_ = static_cast(status); - } - - return (status >= 200 && status < 300); - } - - - bool HttpClient::Apply(Json::Value& answer) - { - std::string s; - if (Apply(s)) - { - Json::Reader reader; - return reader.parse(s, answer); - } - else - { - return false; - } - } - - - void HttpClient::SetPassword(const char* username, - const char* password) - { - std::string s = std::string(username) + ":" + std::string(password); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, s.c_str())); - } -} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/HttpClient.h --- a/OrthancCppClient/HttpClient.h Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#pragma once - -#include "HttpEnumerations.h" -#include "HttpException.h" - -#include -#include -#include - -namespace Orthanc -{ - class HttpClient - { - private: - struct PImpl; - boost::shared_ptr pimpl_; - - std::string url_; - Orthanc_HttpMethod method_; - Orthanc_HttpStatus lastStatus_; - std::string postData_; - bool isVerbose_; - - public: - HttpClient(); - - ~HttpClient(); - - void SetUrl(const char* url) - { - url_ = std::string(url); - } - - void SetUrl(const std::string& url) - { - url_ = url; - } - - const std::string& GetUrl() const - { - return url_; - } - - void SetMethod(Orthanc_HttpMethod method) - { - method_ = method; - } - - Orthanc_HttpMethod GetMethod() const - { - return method_; - } - - std::string& AccessPostData() - { - return postData_; - } - - const std::string& AccessPostData() const - { - return postData_; - } - - void SetVerbose(bool isVerbose); - - bool IsVerbose() const - { - return isVerbose_; - } - - bool Apply(std::string& answer); - - bool Apply(Json::Value& answer); - - Orthanc_HttpStatus GetLastStatus() const - { - return lastStatus_; - } - - const char* GetLastStatusText() const - { - return HttpException::GetDescription(lastStatus_); - } - - void SetPassword(const char* username, - const char* password); - }; -} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/HttpEnumerations.h --- a/OrthancCppClient/HttpEnumerations.h Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#pragma once - - -/** - * This file contains the enumerations for the access to the Orthanc - * REST API in C and C++. Namespaces are not used, in order to enable - * the access in C. - **/ - -// Most common, non-joke and non-experimental HTTP status codes -// http://en.wikipedia.org/wiki/List_of_HTTP_status_codes -enum Orthanc_HttpStatus -{ - Orthanc_HttpStatus_None = -1, - - // 1xx Informational - Orthanc_HttpStatus_100_Continue = 100, - Orthanc_HttpStatus_101_SwitchingProtocols = 101, - Orthanc_HttpStatus_102_Processing = 102, - - // 2xx Success - Orthanc_HttpStatus_200_Ok = 200, - Orthanc_HttpStatus_201_Created = 201, - Orthanc_HttpStatus_202_Accepted = 202, - Orthanc_HttpStatus_203_NonAuthoritativeInformation = 203, - Orthanc_HttpStatus_204_NoContent = 204, - Orthanc_HttpStatus_205_ResetContent = 205, - Orthanc_HttpStatus_206_PartialContent = 206, - Orthanc_HttpStatus_207_MultiStatus = 207, - Orthanc_HttpStatus_208_AlreadyReported = 208, - Orthanc_HttpStatus_226_IMUsed = 226, - - // 3xx Redirection - Orthanc_HttpStatus_300_MultipleChoices = 300, - Orthanc_HttpStatus_301_MovedPermanently = 301, - Orthanc_HttpStatus_302_Found = 302, - Orthanc_HttpStatus_303_SeeOther = 303, - Orthanc_HttpStatus_304_NotModified = 304, - Orthanc_HttpStatus_305_UseProxy = 305, - Orthanc_HttpStatus_307_TemporaryRedirect = 307, - - // 4xx Client Error - Orthanc_HttpStatus_400_BadRequest = 400, - Orthanc_HttpStatus_401_Unauthorized = 401, - Orthanc_HttpStatus_402_PaymentRequired = 402, - Orthanc_HttpStatus_403_Forbidden = 403, - Orthanc_HttpStatus_404_NotFound = 404, - Orthanc_HttpStatus_405_MethodNotAllowed = 405, - Orthanc_HttpStatus_406_NotAcceptable = 406, - Orthanc_HttpStatus_407_ProxyAuthenticationRequired = 407, - Orthanc_HttpStatus_408_RequestTimeout = 408, - Orthanc_HttpStatus_409_Conflict = 409, - Orthanc_HttpStatus_410_Gone = 410, - Orthanc_HttpStatus_411_LengthRequired = 411, - Orthanc_HttpStatus_412_PreconditionFailed = 412, - Orthanc_HttpStatus_413_RequestEntityTooLarge = 413, - Orthanc_HttpStatus_414_RequestUriTooLong = 414, - Orthanc_HttpStatus_415_UnsupportedMediaType = 415, - Orthanc_HttpStatus_416_RequestedRangeNotSatisfiable = 416, - Orthanc_HttpStatus_417_ExpectationFailed = 417, - Orthanc_HttpStatus_422_UnprocessableEntity = 422, - Orthanc_HttpStatus_423_Locked = 423, - Orthanc_HttpStatus_424_FailedDependency = 424, - Orthanc_HttpStatus_426_UpgradeRequired = 426, - - // 5xx Server Error - Orthanc_HttpStatus_500_InternalServerError = 500, - Orthanc_HttpStatus_501_NotImplemented = 501, - Orthanc_HttpStatus_502_BadGateway = 502, - Orthanc_HttpStatus_503_ServiceUnavailable = 503, - Orthanc_HttpStatus_504_GatewayTimeout = 504, - Orthanc_HttpStatus_505_HttpVersionNotSupported = 505, - Orthanc_HttpStatus_506_VariantAlsoNegotiates = 506, - Orthanc_HttpStatus_507_InsufficientStorage = 507, - Orthanc_HttpStatus_509_BandwidthLimitExceeded = 509, - Orthanc_HttpStatus_510_NotExtended = 510 -}; - - -enum Orthanc_HttpMethod -{ - Orthanc_HttpMethod_Get = 0, - Orthanc_HttpMethod_Post = 1, - Orthanc_HttpMethod_Delete = 2, - Orthanc_HttpMethod_Put = 3 -}; diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/HttpException.cpp --- a/OrthancCppClient/HttpException.cpp Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,208 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#include "HttpException.h" - -namespace Orthanc -{ - const char* HttpException::What() const - { - if (status_ == Orthanc_HttpStatus_None) - { - return custom_.c_str(); - } - else - { - return GetDescription(status_); - } - } - - const char* HttpException::GetDescription(Orthanc_HttpStatus status) - { - switch (status) - { - case Orthanc_HttpStatus_100_Continue: - return "Continue"; - - case Orthanc_HttpStatus_101_SwitchingProtocols: - return "Switching Protocols"; - - case Orthanc_HttpStatus_102_Processing: - return "Processing"; - - case Orthanc_HttpStatus_200_Ok: - return "OK"; - - case Orthanc_HttpStatus_201_Created: - return "Created"; - - case Orthanc_HttpStatus_202_Accepted: - return "Accepted"; - - case Orthanc_HttpStatus_203_NonAuthoritativeInformation: - return "Non-Authoritative Information"; - - case Orthanc_HttpStatus_204_NoContent: - return "No Content"; - - case Orthanc_HttpStatus_205_ResetContent: - return "Reset Content"; - - case Orthanc_HttpStatus_206_PartialContent: - return "Partial Content"; - - case Orthanc_HttpStatus_207_MultiStatus: - return "Multi-Status"; - - case Orthanc_HttpStatus_208_AlreadyReported: - return "Already Reported"; - - case Orthanc_HttpStatus_226_IMUsed: - return "IM Used"; - - case Orthanc_HttpStatus_300_MultipleChoices: - return "Multiple Choices"; - - case Orthanc_HttpStatus_301_MovedPermanently: - return "Moved Permanently"; - - case Orthanc_HttpStatus_302_Found: - return "Found"; - - case Orthanc_HttpStatus_303_SeeOther: - return "See Other"; - - case Orthanc_HttpStatus_304_NotModified: - return "Not Modified"; - - case Orthanc_HttpStatus_305_UseProxy: - return "Use Proxy"; - - case Orthanc_HttpStatus_307_TemporaryRedirect: - return "Temporary Redirect"; - - case Orthanc_HttpStatus_400_BadRequest: - return "Bad Request"; - - case Orthanc_HttpStatus_401_Unauthorized: - return "Unauthorized"; - - case Orthanc_HttpStatus_402_PaymentRequired: - return "Payment Required"; - - case Orthanc_HttpStatus_403_Forbidden: - return "Forbidden"; - - case Orthanc_HttpStatus_404_NotFound: - return "Not Found"; - - case Orthanc_HttpStatus_405_MethodNotAllowed: - return "Method Not Allowed"; - - case Orthanc_HttpStatus_406_NotAcceptable: - return "Not Acceptable"; - - case Orthanc_HttpStatus_407_ProxyAuthenticationRequired: - return "Proxy Authentication Required"; - - case Orthanc_HttpStatus_408_RequestTimeout: - return "Request Timeout"; - - case Orthanc_HttpStatus_409_Conflict: - return "Conflict"; - - case Orthanc_HttpStatus_410_Gone: - return "Gone"; - - case Orthanc_HttpStatus_411_LengthRequired: - return "Length Required"; - - case Orthanc_HttpStatus_412_PreconditionFailed: - return "Precondition Failed"; - - case Orthanc_HttpStatus_413_RequestEntityTooLarge: - return "Request Entity Too Large"; - - case Orthanc_HttpStatus_414_RequestUriTooLong: - return "Request-URI Too Long"; - - case Orthanc_HttpStatus_415_UnsupportedMediaType: - return "Unsupported Media Type"; - - case Orthanc_HttpStatus_416_RequestedRangeNotSatisfiable: - return "Requested Range Not Satisfiable"; - - case Orthanc_HttpStatus_417_ExpectationFailed: - return "Expectation Failed"; - - case Orthanc_HttpStatus_422_UnprocessableEntity: - return "Unprocessable Entity"; - - case Orthanc_HttpStatus_423_Locked: - return "Locked"; - - case Orthanc_HttpStatus_424_FailedDependency: - return "Failed Dependency"; - - case Orthanc_HttpStatus_426_UpgradeRequired: - return "Upgrade Required"; - - case Orthanc_HttpStatus_500_InternalServerError: - return "Internal Server Error"; - - case Orthanc_HttpStatus_501_NotImplemented: - return "Not Implemented"; - - case Orthanc_HttpStatus_502_BadGateway: - return "Bad Gateway"; - - case Orthanc_HttpStatus_503_ServiceUnavailable: - return "Service Unavailable"; - - case Orthanc_HttpStatus_504_GatewayTimeout: - return "Gateway Timeout"; - - case Orthanc_HttpStatus_505_HttpVersionNotSupported: - return "HTTP Version Not Supported"; - - case Orthanc_HttpStatus_506_VariantAlsoNegotiates: - return "Variant Also Negotiates"; - - case Orthanc_HttpStatus_507_InsufficientStorage: - return "Insufficient Storage"; - - case Orthanc_HttpStatus_509_BandwidthLimitExceeded: - return "Bandwidth Limit Exceeded"; - - case Orthanc_HttpStatus_510_NotExtended: - return "Not Extended"; - - default: - throw HttpException("Unknown HTTP status"); - } - } -} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/HttpException.h --- a/OrthancCppClient/HttpException.h Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#pragma once - -#include "HttpEnumerations.h" - -#include - -namespace Orthanc -{ - class HttpException - { - private: - Orthanc_HttpStatus status_; - std::string custom_; - - public: - static const char* GetDescription(Orthanc_HttpStatus status); - - HttpException(const std::string& custom) - { - status_ = Orthanc_HttpStatus_None; - custom_ = custom; - } - - HttpException(Orthanc_HttpStatus status) - { - status_ = status; - } - - Orthanc_HttpStatus GetHttpStatus() const - { - return status_; - } - - const char* What() const; - }; -} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Instance.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Instance.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,224 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "Instance.h" + +#include "OrthancConnection.h" +#include "../Core/OrthancException.h" + +#include + +namespace OrthancClient +{ + void Instance::DownloadImage() + { + if (reader_.get() == NULL) + { + const char* suffix; + switch (mode_) + { + case Orthanc::ImageExtractionMode_Preview: + suffix = "preview"; + break; + + case Orthanc::ImageExtractionMode_UInt8: + suffix = "image-uint8"; + break; + + case Orthanc::ImageExtractionMode_UInt16: + suffix = "image-uint16"; + break; + + case Orthanc::ImageExtractionMode_Int16: + suffix = "image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + Orthanc::HttpClient client(connection_.GetHttpClient()); + client.SetUrl(connection_.GetOrthancUrl() + "/instances/" + id_ + "/" + suffix); + std::string png; + + if (!client.Apply(png)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + reader_.reset(new Orthanc::PngReader); + reader_->ReadFromMemory(png); + } + } + + Instance::Instance(const OrthancConnection& connection, + const std::string& id) : + connection_(connection), + id_(id), + mode_(Orthanc::ImageExtractionMode_Int16) + { + Orthanc::HttpClient client(connection_.GetHttpClient()); + + client.SetUrl(connection_.GetOrthancUrl() + "/instances/" + id_ + "/simplified-tags"); + Json::Value v; + if (!client.Apply(tags_)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + std::string Instance::GetTagAsString(const char* tag) + { + if (tags_.isMember(tag)) + { + return tags_[tag].asString(); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); + } + } + + float Instance::GetTagAsFloat(const char* tag) + { + std::string value = GetTagAsString(tag); + + try + { + return boost::lexical_cast(value); + } + catch (boost::bad_lexical_cast) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + int Instance::GetTagAsInt(const char* tag) + { + std::string value = GetTagAsString(tag); + + try + { + return boost::lexical_cast(value); + } + catch (boost::bad_lexical_cast) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + unsigned int Instance::GetWidth() + { + DownloadImage(); + return reader_->GetWidth(); + } + + unsigned int Instance::GetHeight() + { + DownloadImage(); + return reader_->GetHeight(); + } + + unsigned int Instance::GetPitch() + { + DownloadImage(); + return reader_->GetPitch(); + } + + Orthanc::PixelFormat Instance::GetPixelFormat() + { + DownloadImage(); + return reader_->GetFormat(); + } + + const void* Instance::GetBuffer() + { + DownloadImage(); + return reader_->GetBuffer(); + } + + const void* Instance::GetBuffer(unsigned int y) + { + DownloadImage(); + return reader_->GetBuffer(y); + } + + void Instance::DiscardImage() + { + reader_.reset(); + } + + + void Instance::SetImageExtractionMode(Orthanc::ImageExtractionMode mode) + { + if (mode_ == mode) + { + return; + } + + DiscardImage(); + mode_ = mode; + } + + + void Instance::SplitVectorOfFloats(std::vector& target, + const char* tag) + { + const std::string value = GetTagAsString(tag); + + target.clear(); + + try + { + std::string tmp; + for (size_t i = 0; i < value.size(); i++) + { + if (value[i] == '\\') + { + target.push_back(boost::lexical_cast(tmp)); + tmp.clear(); + } + else + { + tmp.push_back(value[i]); + } + } + + target.push_back(boost::lexical_cast(tmp)); + } + catch (boost::bad_lexical_cast) + { + // Unable to parse the Image Orientation Patient. + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Instance.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Instance.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,95 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +#include "../Core/IDynamicObject.h" +#include "../Core/FileFormats/PngReader.h" + +namespace OrthancClient +{ + class OrthancConnection; + + class Instance : public Orthanc::IDynamicObject + { + private: + const OrthancConnection& connection_; + std::string id_; + Json::Value tags_; + std::auto_ptr reader_; + Orthanc::ImageExtractionMode mode_; + + void DownloadImage(); + + public: + Instance(const OrthancConnection& connection, + const std::string& id); + + const std::string& GetId() const + { + return id_; + } + + void SetImageExtractionMode(Orthanc::ImageExtractionMode mode); + + Orthanc::ImageExtractionMode GetImageExtractionMode() const + { + return mode_; + } + + std::string GetTagAsString(const char* tag); + + float GetTagAsFloat(const char* tag); + + int GetTagAsInt(const char* tag); + + unsigned int GetWidth(); + + unsigned int GetHeight(); + + unsigned int GetPitch(); + + Orthanc::PixelFormat GetPixelFormat(); + + const void* GetBuffer(); + + const void* GetBuffer(unsigned int y); + + void DiscardImage(); + + void SplitVectorOfFloats(std::vector& target, + const char* tag); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/OrthancConnection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/OrthancConnection.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,76 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "OrthancConnection.h" + +#include "../Core/OrthancException.h" + +namespace OrthancClient +{ + void OrthancConnection::ReadPatients() + { + client_.SetUrl(orthancUrl_ + "/patients"); + Json::Value v; + if (!client_.Apply(content_)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + Orthanc::IDynamicObject* OrthancConnection::GetFillerItem(size_t index) + { + Json::Value::ArrayIndex tmp = static_cast(index); + return new Patient(*this, content_[tmp].asString()); + } + + Patient& OrthancConnection::GetPatient(unsigned int index) + { + return dynamic_cast(patients_.GetItem(index)); + } + + OrthancConnection::OrthancConnection(const char* orthancUrl) : + orthancUrl_(orthancUrl), patients_(*this) + { + ReadPatients(); + } + + OrthancConnection::OrthancConnection(const char* orthancUrl, + const char* username, + const char* password) : + orthancUrl_(orthancUrl), patients_(*this) + { + client_.SetCredentials(username, password); + ReadPatients(); + } + + +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/OrthancConnection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/OrthancConnection.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,99 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Core/HttpClient.h" + +#include "Patient.h" + +namespace OrthancClient +{ + class OrthancConnection : + public boost::noncopyable, + private Orthanc::ArrayFilledByThreads::IFiller + { + private: + Orthanc::HttpClient client_; + std::string orthancUrl_; + Orthanc::ArrayFilledByThreads patients_; + Json::Value content_; + + void ReadPatients(); + + virtual size_t GetFillerSize() + { + return content_.size(); + } + + virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); + + public: + OrthancConnection(const char* orthancUrl); + + OrthancConnection(const char* orthancUrl, + const char* username, + const char* password); + + unsigned int GetThreadCount() const + { + return patients_.GetThreadCount(); + } + + void SetThreadCount(unsigned int threadCount) + { + patients_.SetThreadCount(threadCount); + } + + void Reload() + { + patients_.Reload(); + } + + const Orthanc::HttpClient& GetHttpClient() const + { + return client_; + } + + const std::string& GetOrthancUrl() const + { + return orthancUrl_; + } + + unsigned int GetPatientCount() + { + return patients_.GetSize(); + } + + Patient& GetPatient(unsigned int index); + }; +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Patient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Patient.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,78 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "Patient.h" + +#include "OrthancConnection.h" +#include "../Core/OrthancException.h" + +namespace OrthancClient +{ + void Patient::ReadPatient() + { + Orthanc::HttpClient client(connection_.GetHttpClient()); + client.SetUrl(connection_.GetOrthancUrl() + "/patients/" + id_); + Json::Value v; + if (!client.Apply(patient_)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + Orthanc::IDynamicObject* Patient::GetFillerItem(size_t index) + { + Json::Value::ArrayIndex tmp = static_cast(index); + return new Study(connection_, patient_["Studies"][tmp].asString()); + } + + Patient::Patient(const OrthancConnection& connection, + const std::string& id) : + connection_(connection), + id_(id), + studies_(*this) + { + studies_.SetThreadCount(connection.GetThreadCount()); + ReadPatient(); + } + + std::string Patient::GetMainDicomTag(const char* tag, const char* defaultValue) const + { + if (patient_["MainDicomTags"].isMember(tag)) + { + return patient_["MainDicomTags"][tag].asString(); + } + else + { + return defaultValue; + } + } +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Patient.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Patient.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,85 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "Study.h" + +namespace OrthancClient +{ + class Patient : + public Orthanc::IDynamicObject, + private Orthanc::ArrayFilledByThreads::IFiller + { + private: + const OrthancConnection& connection_; + std::string id_; + Json::Value patient_; + Orthanc::ArrayFilledByThreads studies_; + + void ReadPatient(); + + virtual size_t GetFillerSize() + { + return patient_["Studies"].size(); + } + + virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); + + public: + Patient(const OrthancConnection& connection, + const std::string& id); + + void Reload() + { + studies_.Reload(); + } + + unsigned int GetStudyCount() + { + return studies_.GetSize(); + } + + Study& GetStudy(unsigned int index) + { + return dynamic_cast(studies_.GetItem(index)); + } + + const std::string& GetId() const + { + return id_; + } + + std::string GetMainDicomTag(const char* tag, + const char* defaultValue) const; + }; +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Series.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Series.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,432 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "Series.h" + +#include "OrthancConnection.h" +#include "../Core/OrthancException.h" + +#include +#include + +namespace OrthancClient +{ + namespace + { + class SliceLocator + { + private: + float normal_[3]; + + public: + SliceLocator(Instance& someSlice) + { + /** + * Compute the slice normal from Image Orientation Patient. + * http://nipy.sourceforge.net/nibabel/dicom/dicom_orientation.html#dicom-z-from-slice + * http://www.itk.org/pipermail/insight-users/2003-September/004762.html + **/ + + std::vector cosines; + someSlice.SplitVectorOfFloats(cosines, "ImageOrientationPatient"); + + if (cosines.size() != 6) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + normal_[0] = cosines[1] * cosines[5] - cosines[2] * cosines[4]; + normal_[1] = cosines[2] * cosines[3] - cosines[0] * cosines[5]; + normal_[2] = cosines[0] * cosines[4] - cosines[1] * cosines[3]; + } + + + /** + * Compute the distance of some slice along the slice normal. + **/ + float ComputeSliceLocation(Instance& instance) const + { + std::vector ipp; + instance.SplitVectorOfFloats(ipp, "ImagePositionPatient"); + if (ipp.size() != 3) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + float dist = 0; + + for (int i = 0; i < 3; i++) + { + dist += normal_[i] * ipp[i]; + } + + return dist; + } + }; + + class ImageDownloadCommand : public Orthanc::ICommand + { + private: + Orthanc::PixelFormat format_; + Orthanc::ImageExtractionMode mode_; + Instance& instance_; + void* target_; + size_t lineStride_; + + public: + ImageDownloadCommand(Instance& instance, + Orthanc::PixelFormat format, + Orthanc::ImageExtractionMode mode, + void* target, + size_t lineStride) : + format_(format), + mode_(mode), + instance_(instance), + target_(target), + lineStride_(lineStride) + { + instance_.SetImageExtractionMode(mode); + } + + virtual bool Execute() + { + using namespace Orthanc; + + unsigned int width = instance_.GetHeight(); + + for (unsigned int y = 0; y < instance_.GetHeight(); y++) + { + uint8_t* p = reinterpret_cast(target_) + y * lineStride_; + + if (instance_.GetPixelFormat() == format_) + { + memcpy(p, instance_.GetBuffer(y), 2 * instance_.GetWidth()); + } + else if (instance_.GetPixelFormat() == PixelFormat_Grayscale8 && + format_ == PixelFormat_RGB24) + { + const uint8_t* s = reinterpret_cast(instance_.GetBuffer(y)); + for (unsigned int x = 0; x < width; x++, s++, p += 3) + { + p[0] = *s; + p[1] = *s; + p[2] = *s; + } + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + } + + // Do not keep the image in memory, as we are loading 3D images + instance_.DiscardImage(); + + return true; + } + }; + } + + + void Series::Check3DImage() + { + if (!Is3DImage()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + bool Series::Is3DImageInternal() + { + try + { + if (GetInstanceCount() == 0) + { + return true; + } + + for (unsigned int i = 0; i < GetInstanceCount(); i++) + { + if (GetInstance(0).GetTagAsString("Columns") != GetInstance(i).GetTagAsString("Columns") || + GetInstance(0).GetTagAsString("Rows") != GetInstance(i).GetTagAsString("Rows") || + GetInstance(0).GetTagAsString("ImageOrientationPatient") != GetInstance(i).GetTagAsString("ImageOrientationPatient") || + GetInstance(0).GetTagAsString("SliceThickness") != GetInstance(i).GetTagAsString("SliceThickness") || + GetInstance(0).GetTagAsString("PixelSpacing") != GetInstance(i).GetTagAsString("PixelSpacing")) + { + return false; + } + } + + SliceLocator locator(GetInstance(0)); + std::set l; + for (unsigned int i = 0; i < GetInstanceCount(); i++) + { + l.insert(locator.ComputeSliceLocation(GetInstance(i))); + } + + return l.size() == GetInstanceCount(); + } + catch (Orthanc::OrthancException) + { + return false; + } + } + + void Series::ReadSeries() + { + Orthanc::HttpClient client(connection_.GetHttpClient()); + + client.SetUrl(connection_.GetOrthancUrl() + "/series/" + id_); + Json::Value v; + if (!client.Apply(series_)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + Orthanc::IDynamicObject* Series::GetFillerItem(size_t index) + { + Json::Value::ArrayIndex tmp = static_cast(index); + return new Instance(connection_, series_["Instances"][tmp].asString()); + } + + Series::Series(const OrthancConnection& connection, + const std::string& id) : + connection_(connection), + id_(id), + instances_(*this) + { + ReadSeries(); + status_ = Status3DImage_NotTested; + + instances_.SetThreadCount(connection.GetThreadCount()); + } + + + bool Series::Is3DImage() + { + if (status_ == Status3DImage_NotTested) + { + status_ = Is3DImageInternal() ? Status3DImage_True : Status3DImage_False; + } + + return status_ == Status3DImage_True; + } + + unsigned int Series::GetInstanceCount() + { + return instances_.GetSize(); + } + + Instance& Series::GetInstance(unsigned int index) + { + return dynamic_cast(instances_.GetItem(index)); + } + + std::string Series::GetUrl() const + { + return connection_.GetOrthancUrl() + "/series/" + id_; + } + + unsigned int Series::GetWidth() + { + Check3DImage(); + + if (GetInstanceCount() == 0) + return 0; + else + return GetInstance(0).GetTagAsInt("Columns"); + } + + unsigned int Series::GetHeight() + { + Check3DImage(); + + if (GetInstanceCount() == 0) + return 0; + else + return GetInstance(0).GetTagAsInt("Rows"); + } + + void Series::GetVoxelSize(float& sizeX, float& sizeY, float& sizeZ) + { + Check3DImage(); + + if (GetInstanceCount() == 0) + { + sizeX = 0; + sizeY = 0; + sizeZ = 0; + } + else + { + try + { + std::string s = GetInstance(0).GetTagAsString("PixelSpacing"); + size_t pos = s.find('\\'); + assert(pos != std::string::npos); + std::string sy = s.substr(0, pos); + std::string sx = s.substr(pos + 1); + + sizeX = boost::lexical_cast(sx); + sizeY = boost::lexical_cast(sy); + sizeZ = GetInstance(0).GetTagAsFloat("SliceThickness"); + } + catch (boost::bad_lexical_cast) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + } + + + std::string Series::GetMainDicomTag(const char* tag, const char* defaultValue) const + { + if (series_["MainDicomTags"].isMember(tag)) + { + return series_["MainDicomTags"][tag].asString(); + } + else + { + return defaultValue; + } + } + + + + void Series::Load3DImage(void* target, + Orthanc::PixelFormat format, + size_t lineStride, + size_t stackStride, + Orthanc::ThreadedCommandProcessor::IListener* listener) + { + using namespace Orthanc; + + // Choose the extraction mode, depending on the format of the + // target image. + + uint8_t bytesPerPixel; + ImageExtractionMode mode; + + switch (format) + { + case PixelFormat_RGB24: + bytesPerPixel = 3; + mode = ImageExtractionMode_Preview; + break; + + case PixelFormat_Grayscale8: + bytesPerPixel = 1; + mode = ImageExtractionMode_UInt8; // Preview ??? + break; + + case PixelFormat_Grayscale16: + bytesPerPixel = 2; + mode = ImageExtractionMode_UInt16; + break; + + case PixelFormat_SignedGrayscale16: + bytesPerPixel = 2; + mode = ImageExtractionMode_UInt16; + format = PixelFormat_Grayscale16; + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + + // Check that the target image is properly sized + unsigned int sx = GetWidth(); + unsigned int sy = GetHeight(); + + if (lineStride < sx * bytesPerPixel || + stackStride < sx * sy * bytesPerPixel) + { + throw OrthancException(ErrorCode_BadRequest); + } + + if (sx == 0 || sy == 0 || GetInstanceCount() == 0) + { + // Empty image, nothing to do + if (listener) + listener->SignalSuccess(0); + return; + } + + + /** + * Order the stacks according to their distance along the slice + * normal (using the "Image Position Patient" tag). This works + * even if the "SliceLocation" tag is absent. + **/ + SliceLocator locator(GetInstance(0)); + + typedef std::map Instances; + Instances instances; + for (unsigned int i = 0; i < GetInstanceCount(); i++) + { + float dist = locator.ComputeSliceLocation(GetInstance(i)); + instances[dist] = &GetInstance(i); + } + + if (instances.size() != GetInstanceCount()) + { + // Several instances have the same Z coordinate + throw OrthancException(ErrorCode_NotImplemented); + } + + + // Submit the download of each stack as a set of commands + ThreadedCommandProcessor processor(connection_.GetThreadCount()); + + if (listener != NULL) + { + processor.SetListener(*listener); + } + + uint8_t* stackTarget = reinterpret_cast(target); + for (Instances::iterator it = instances.begin(); it != instances.end(); it++) + { + processor.Post(new ImageDownloadCommand(*it->second, format, mode, stackTarget, lineStride)); + stackTarget += stackStride; + } + + + // Wait for all the stacks to be downloaded + if (!processor.Join()) + { + throw OrthancException(ErrorCode_NetworkProtocol); + } + } + +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Series.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Series.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,127 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "Instance.h" + +#include "../Core/MultiThreading/ArrayFilledByThreads.h" +#include "../Core/MultiThreading/ThreadedCommandProcessor.h" + +namespace OrthancClient +{ + class Series : + public Orthanc::IDynamicObject, + private Orthanc::ArrayFilledByThreads::IFiller + { + private: + enum Status3DImage + { + Status3DImage_NotTested, + Status3DImage_True, + Status3DImage_False + }; + + const OrthancConnection& connection_; + std::string id_; + Json::Value series_; + Orthanc::ArrayFilledByThreads instances_; + Status3DImage status_; + + void Check3DImage(); + + bool Is3DImageInternal(); + + void ReadSeries(); + + virtual size_t GetFillerSize() + { + return series_["Instances"].size(); + } + + virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); + + void Load3DImage(void* target, + Orthanc::PixelFormat format, + size_t lineStride, + size_t stackStride, + Orthanc::ThreadedCommandProcessor::IListener* listener); + + public: + Series(const OrthancConnection& connection, + const std::string& id); + + void Reload() + { + instances_.Reload(); + } + + bool Is3DImage(); + + unsigned int GetInstanceCount(); + + Instance& GetInstance(unsigned int index); + + const std::string& GetId() const + { + return id_; + } + + std::string GetUrl() const; + + unsigned int GetWidth(); + + unsigned int GetHeight(); + + void GetVoxelSize(float& sizeX, float& sizeY, float& sizeZ); + + std::string GetMainDicomTag(const char* tag, + const char* defaultValue) const; + + void Load3DImage(void* target, + Orthanc::PixelFormat format, + size_t lineStride, + size_t stackStride, + Orthanc::ThreadedCommandProcessor::IListener& listener) + { + Load3DImage(target, format, lineStride, stackStride, &listener); + } + + void Load3DImage(void* target, + Orthanc::PixelFormat format, + size_t lineStride, + size_t stackStride) + { + Load3DImage(target, format, lineStride, stackStride, NULL); + } + }; +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Study.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Study.cpp Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,78 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "Study.h" + +#include "OrthancConnection.h" +#include "../Core/OrthancException.h" + +namespace OrthancClient +{ + void Study::ReadStudy() + { + Orthanc::HttpClient client(connection_.GetHttpClient()); + client.SetUrl(connection_.GetOrthancUrl() + "/studies/" + id_); + Json::Value v; + if (!client.Apply(study_)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + Orthanc::IDynamicObject* Study::GetFillerItem(size_t index) + { + Json::Value::ArrayIndex tmp = static_cast(index); + return new Series(connection_, study_["Series"][tmp].asString()); + } + + Study::Study(const OrthancConnection& connection, + const std::string& id) : + connection_(connection), + id_(id), + series_(*this) + { + series_.SetThreadCount(connection.GetThreadCount()); + ReadStudy(); + } + + std::string Study::GetMainDicomTag(const char* tag, const char* defaultValue) const + { + if (study_["MainDicomTags"].isMember(tag)) + { + return study_["MainDicomTags"][tag].asString(); + } + else + { + return defaultValue; + } + } +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/Study.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancCppClient/Study.h Wed Sep 18 15:27:07 2013 +0200 @@ -0,0 +1,85 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "Series.h" + +namespace OrthancClient +{ + class Study : + public Orthanc::IDynamicObject, + private Orthanc::ArrayFilledByThreads::IFiller + { + private: + const OrthancConnection& connection_; + std::string id_; + Json::Value study_; + Orthanc::ArrayFilledByThreads series_; + + void ReadStudy(); + + virtual size_t GetFillerSize() + { + return study_["Series"].size(); + } + + virtual Orthanc::IDynamicObject* GetFillerItem(size_t index); + + public: + Study(const OrthancConnection& connection, + const std::string& id); + + void Reload() + { + series_.Reload(); + } + + unsigned int GetSeriesCount() + { + return series_.GetSize(); + } + + Series& GetSeries(unsigned int index) + { + return dynamic_cast(series_.GetItem(index)); + } + + const std::string& GetId() const + { + return id_; + } + + std::string GetMainDicomTag(const char* tag, + const char* defaultValue) const; + }; +} diff -r f6e61e329bbf -r b26a7c397c34 OrthancCppClient/main.cpp --- a/OrthancCppClient/main.cpp Mon Apr 29 12:48:10 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012 Medical Physics Department, CHU of Liege, - * Belgium - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - **/ - - -#include "HttpClient.h" - -#include - -int main() -{ - // Prepare a simple call to a Web service - Orthanc::HttpClient c; - c.SetUrl("http://nominatim.openstreetmap.org/search?format=json&q=chu+liege+belgium"); - - // Do the request and store the result in a JSON structure - Json::Value result; - c.Apply(result); - - // Display the JSON answer - std::cout << result << std::endl; - - return 0; -} diff -r f6e61e329bbf -r b26a7c397c34 OrthancExplorer/explorer.html --- a/OrthancExplorer/explorer.html Mon Apr 29 12:48:10 2013 +0200 +++ b/OrthancExplorer/explorer.html Wed Sep 18 15:27:07 2013 +0200 @@ -88,11 +88,24 @@ - - Delete this patient - Download ZIP - Anonymize

+ + +
@@ -121,11 +134,24 @@
@@ -154,15 +180,27 @@ -