# 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 @@
Protected
-
- Delete this patient
- Download ZIP
- Anonymize
+
+
+
@@ -154,15 +180,27 @@
-
+
+
Sending to Orthanc peer...
+
+
+
+
Sending to DICOM modality...
diff -r f6e61e329bbf -r b26a7c397c34 OrthancExplorer/explorer.js
--- a/OrthancExplorer/explorer.js Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancExplorer/explorer.js Wed Sep 18 15:27:07 2013 +0200
@@ -12,6 +12,11 @@
//$.mobile.page.prototype.options.addBackBtn = true;
//$.mobile.defaultPageTransition = 'slide';
+
+var currentPage = '';
+var currentUuid = '';
+
+
// http://stackoverflow.com/a/4673436
String.prototype.format = function() {
var args = arguments;
@@ -356,7 +361,25 @@
-$('#patient').live('pagebeforeshow', function() {
+function SetupAnonymizedOrModifiedFrom(buttonSelector, resource, resourceType, field)
+{
+ if (field in resource)
+ {
+ $(buttonSelector).closest('li').show();
+ $(buttonSelector).click(function(e) {
+ window.location.assign('explorer.html#' + resourceType + '?uuid=' + resource[field]);
+ });
+ }
+ else
+ {
+ $(buttonSelector).closest('li').hide();
+ }
+}
+
+
+
+function RefreshPatient()
+{
if ($.mobile.pageData) {
GetSingleResource('patients', $.mobile.pageData.uuid, function(patient) {
GetMultipleResources('studies', patient.Studies, function(studies) {
@@ -381,6 +404,9 @@
target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID));
}
+ SetupAnonymizedOrModifiedFrom('#patient-anonymized-from', patient, 'patient', 'AnonymizedFrom');
+ SetupAnonymizedOrModifiedFrom('#patient-modified-from', patient, 'patient', 'ModifiedFrom');
+
target.listview('refresh');
// Check whether this patient is protected
@@ -395,13 +421,17 @@
$('#protection').val(v).slider('refresh');
}
});
+
+ currentPage = 'patient';
+ currentUuid = $.mobile.pageData.uuid;
});
});
}
-});
+}
-$('#study').live('pagebeforeshow', function() {
+function RefreshStudy()
+{
if ($.mobile.pageData) {
GetSingleResource('studies', $.mobile.pageData.uuid, function(study) {
GetSingleResource('patients', study.ParentPatient, function(patient) {
@@ -417,6 +447,9 @@
.append(FormatStudy(study))
.listview('refresh');
+ SetupAnonymizedOrModifiedFrom('#study-anonymized-from', study, 'study', 'AnonymizedFrom');
+ SetupAnonymizedOrModifiedFrom('#study-modified-from', study, 'study', 'ModifiedFrom');
+
var target = $('#list-series');
$('li', target).remove();
for (var i = 0; i < series.length; i++) {
@@ -428,14 +461,18 @@
target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID));
}
target.listview('refresh');
+
+ currentPage = 'study';
+ currentUuid = $.mobile.pageData.uuid;
});
});
});
}
-});
+}
-$('#series').live('pagebeforeshow', function() {
+function RefreshSeries()
+{
if ($.mobile.pageData) {
GetSingleResource('series', $.mobile.pageData.uuid, function(series) {
GetSingleResource('studies', series.ParentStudy, function(study) {
@@ -456,18 +493,24 @@
.append(FormatSeries(series))
.listview('refresh');
+ SetupAnonymizedOrModifiedFrom('#series-anonymized-from', series, 'series', 'AnonymizedFrom');
+ SetupAnonymizedOrModifiedFrom('#series-modified-from', series, 'series', 'ModifiedFrom');
+
var target = $('#list-instances');
$('li', target).remove();
for (var i = 0; i < instances.length; i++) {
target.append(FormatInstance(instances[i], '#instance?uuid=' + instances[i].ID));
}
target.listview('refresh');
+
+ currentPage = 'series';
+ currentUuid = $.mobile.pageData.uuid;
});
});
});
});
}
-});
+}
@@ -522,7 +565,8 @@
}
-$('#instance').live('pagebeforeshow', function() {
+function RefreshInstance()
+{
if ($.mobile.pageData) {
GetSingleResource('instances', $.mobile.pageData.uuid, function(instance) {
GetSingleResource('series', instance.ParentSeries, function(series) {
@@ -554,15 +598,52 @@
}
});
+ SetupAnonymizedOrModifiedFrom('#instance-anonymized-from', instance, 'instance', 'AnonymizedFrom');
+ SetupAnonymizedOrModifiedFrom('#instance-modified-from', instance, 'instance', 'ModifiedFrom');
+
+ currentPage = 'instance';
+ currentUuid = $.mobile.pageData.uuid;
});
});
});
});
}
+}
+
+$(document).live('pagebeforehide', function() {
+ currentPage = '';
+ currentUuid = '';
});
+$('#patient').live('pagebeforeshow', RefreshPatient);
+$('#study').live('pagebeforeshow', RefreshStudy);
+$('#series').live('pagebeforeshow', RefreshSeries);
+$('#instance').live('pagebeforeshow', RefreshInstance);
+
+$(function() {
+ $(window).hashchange(function(e, data) {
+ // This fixes the navigation with the back button and with the anonymization
+ if ('uuid' in $.mobile.pageData &&
+ currentPage == $.mobile.pageData.active &&
+ currentUuid != $.mobile.pageData.uuid) {
+ if (currentPage == 'patient')
+ RefreshPatient();
+ else if (currentPage == 'study')
+ RefreshStudy();
+ else if (currentPage == 'series')
+ RefreshSeries();
+ else if (currentPage == 'instance')
+ RefreshInstance();
+ }
+ });
+});
+
+
+
+
+
function DeleteResource(path)
{
$.ajax({
@@ -708,6 +789,13 @@
function ChooseDicomModality(callback)
{
+ var clickedModality = '';
+ var clickedPeer = '';
+ var items = $('
')
+ .attr('data-divider-theme', 'd')
+ .attr('data-role', 'listview');
+
+ // Retrieve the list of the known DICOM modalities
$.ajax({
url: '../modalities',
type: 'GET',
@@ -715,37 +803,66 @@
async: false,
cache: false,
success: function(modalities) {
- var clickedModality = '';
- var items = $('')
- .attr('data-role', 'listview');
+ if (modalities.length > 0)
+ {
+ items.append('DICOM modalities ');
- for (var i = 0; i < modalities.length; i++) {
- var modality = modalities[i];
- var item = $('')
- .html('' + modality + ' ')
- .attr('modality', modality)
- .click(function() {
- clickedModality = $(this).attr('modality');
- });
- items.append(item);
+ for (var i = 0; i < modalities.length; i++) {
+ var name = modalities[i];
+ var item = $(' ')
+ .html('' + name + ' ')
+ .attr('name', name)
+ .click(function() {
+ clickedModality = $(this).attr('name');
+ });
+ items.append(item);
+ }
}
- $('#dialog').simpledialog2({
- mode: 'blank',
- animate: false,
- headerText: 'DICOM modality',
- headerClose: true,
- width: '100%',
- blankContent: items,
- callbackClose: function() {
- var timer;
- function WaitForDialogToClose() {
- if (!$('#dialog').is(':visible')) {
- clearInterval(timer);
- callback(clickedModality);
+ // Retrieve the list of the known Orthanc peers
+ $.ajax({
+ url: '../peers',
+ type: 'GET',
+ dataType: 'json',
+ async: false,
+ cache: false,
+ success: function(peers) {
+ if (peers.length > 0)
+ {
+ items.append(' Orthanc peers ');
+
+ for (var i = 0; i < peers.length; i++) {
+ var name = peers[i];
+ var item = $('')
+ .html('' + name + ' ')
+ .attr('name', name)
+ .click(function() {
+ clickedPeer = $(this).attr('name');
+ });
+ items.append(item);
}
}
- timer = setInterval(WaitForDialogToClose, 100);
+
+ // Launch the dialog
+ $('#dialog').simpledialog2({
+ mode: 'blank',
+ animate: false,
+ headerText: 'Choose target',
+ headerClose: true,
+ forceInput: false,
+ width: '100%',
+ blankContent: items,
+ callbackClose: function() {
+ var timer;
+ function WaitForDialogToClose() {
+ if (!$('#dialog').is(':visible')) {
+ clearInterval(timer);
+ callback(clickedModality, clickedPeer);
+ }
+ }
+ timer = setInterval(WaitForDialogToClose, 100);
+ }
+ });
}
});
}
@@ -753,29 +870,42 @@
}
-$('#instance-store,#series-store').live('click', function(e) {
- ChooseDicomModality(function(modality) {
- if (modality != '') {
+$('#instance-store,#series-store,#study-store,#patient-store').live('click', function(e) {
+ ChooseDicomModality(function(modality, peer) {
+ var url;
+ var loading;
+
+ if (modality != '')
+ {
+ url = '../modalities/' + modality + '/store';
+ loading = '#dicom-store';
+ }
+
+ if (peer != '')
+ {
+ url = '../peers/' + peer + '/store';
+ loading = '#peer-store';
+ }
+
+ if (url != '') {
$.ajax({
- url: '../modalities/' + modality + '/store',
+ url: url,
type: 'POST',
dataType: 'text',
data: $.mobile.pageData.uuid,
async: true, // Necessary to block UI
beforeSend: function() {
- $.blockUI({ message: $('#loading') });
+ $.blockUI({ message: $(loading) });
},
complete: function(s) {
$.unblockUI();
},
success: function(s) {
- //console.log('done !');
},
error: function() {
- alert('Error during C-Store');
+ alert('Error during store');
}
- });
-
+ });
}
});
});
@@ -841,7 +971,7 @@
//$.mobile.changePage('explorer.html#patient?uuid=' + s.PatientID);
window.location.assign('explorer.html#patient?uuid=' + s.PatientID);
- window.location.reload();
+ //window.location.reload();
}
});
},
diff -r f6e61e329bbf -r b26a7c397c34 OrthancExplorer/libs/jqm.page.params.js
--- a/OrthancExplorer/libs/jqm.page.params.js Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancExplorer/libs/jqm.page.params.js Wed Sep 18 15:27:07 2013 +0200
@@ -105,7 +105,9 @@
// http://stackoverflow.com/a/8295488
$(document).bind("pagebeforechange", function( event, data ) {
- $.mobile.pageData = (data && data.options && data.options.pageData)
+ $.mobile.pageData = (data && data.options && data.options.pageData)
? data.options.pageData
: {};
+
+ $.mobile.pageData.active = data.toPage[0].id;
});
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DatabaseWrapper.cpp
--- a/OrthancServer/DatabaseWrapper.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DatabaseWrapper.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
@@ -340,6 +340,15 @@
s.Run();
}
+ void DatabaseWrapper::DeleteMetadata(int64_t id,
+ MetadataType type)
+ {
+ SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?");
+ s.BindInt(0, id);
+ s.BindInt(1, type);
+ s.Run();
+ }
+
bool DatabaseWrapper::LookupMetadata(std::string& target,
int64_t id,
MetadataType type)
@@ -360,6 +369,23 @@
}
}
+ bool DatabaseWrapper::ListAvailableMetadata(std::list& target,
+ int64_t id)
+ {
+ target.clear();
+
+ SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type FROM Metadata WHERE id=?");
+ s.BindInt(0, id);
+
+ while (s.Step())
+ {
+ target.push_back(static_cast(s.ColumnInt(0)));
+ }
+
+ return true;
+ }
+
+
std::string DatabaseWrapper::GetMetadata(int64_t id,
MetadataType type,
const std::string& defaultValue)
@@ -412,6 +438,21 @@
s.Run();
}
+ void DatabaseWrapper::ListAvailableAttachments(std::list& result,
+ int64_t id)
+ {
+ result.clear();
+
+ SQLite::Statement s(db_, SQLITE_FROM_HERE,
+ "SELECT fileType FROM AttachedFiles WHERE id=?");
+ s.BindInt(0, id);
+
+ while (s.Step())
+ {
+ result.push_back(static_cast(s.ColumnInt(0)));
+ }
+ }
+
bool DatabaseWrapper::LookupAttachment(FileInfo& attachment,
int64_t id,
FileContentType contentType)
@@ -551,8 +592,8 @@
Json::Value item = Json::objectValue;
item["Seq"] = static_cast(seq);
- item["ChangeType"] = ToString(changeType);
- item["ResourceType"] = ToString(resourceType);
+ item["ChangeType"] = EnumerationToString(changeType);
+ item["ResourceType"] = EnumerationToString(resourceType);
item["ID"] = publicId;
item["Path"] = GetBasePath(resourceType, publicId);
item["Date"] = date;
@@ -626,7 +667,7 @@
Json::Value item = Json::objectValue;
item["Seq"] = static_cast(seq);
- item["ResourceType"] = ToString(resourceType);
+ item["ResourceType"] = EnumerationToString(resourceType);
item["ID"] = publicId;
item["Path"] = GetBasePath(resourceType, publicId);
item["RemoteModality"] = s.ColumnString(3);
@@ -902,4 +943,56 @@
return 1;
}
}
+
+
+ void DatabaseWrapper::ClearTable(const std::string& tableName)
+ {
+ db_.Execute("DELETE FROM " + tableName);
+ }
+
+
+ bool DatabaseWrapper::IsExistingResource(int64_t internalId)
+ {
+ SQLite::Statement s(db_, SQLITE_FROM_HERE,
+ "SELECT * FROM Resources WHERE internalId=?");
+ s.BindInt(0, internalId);
+ return s.Step();
+ }
+
+
+ void DatabaseWrapper::LookupTagValue(std::list& result,
+ DicomTag tag,
+ const std::string& value)
+ {
+ SQLite::Statement s(db_, SQLITE_FROM_HERE,
+ "SELECT id FROM MainDicomTags WHERE tagGroup=? AND tagElement=? and value=?");
+
+ s.BindInt(0, tag.GetGroup());
+ s.BindInt(1, tag.GetElement());
+ s.BindString(2, value);
+
+ result.clear();
+
+ while (s.Step())
+ {
+ result.push_back(s.ColumnInt64(0));
+ }
+ }
+
+
+ void DatabaseWrapper::LookupTagValue(std::list& result,
+ const std::string& value)
+ {
+ SQLite::Statement s(db_, SQLITE_FROM_HERE,
+ "SELECT id FROM MainDicomTags WHERE value=?");
+
+ s.BindString(0, value);
+
+ result.clear();
+
+ while (s.Step())
+ {
+ result.push_back(s.ColumnInt64(0));
+ }
+ }
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DatabaseWrapper.h
--- a/OrthancServer/DatabaseWrapper.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DatabaseWrapper.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
@@ -108,10 +108,16 @@
MetadataType type,
const std::string& value);
+ void DeleteMetadata(int64_t id,
+ MetadataType type);
+
bool LookupMetadata(std::string& target,
int64_t id,
MetadataType type);
+ bool ListAvailableMetadata(std::list& target,
+ int64_t id);
+
std::string GetMetadata(int64_t id,
MetadataType type,
const std::string& defaultValue = "");
@@ -123,6 +129,9 @@
void AddAttachment(int64_t id,
const FileInfo& attachment);
+ void ListAvailableAttachments(std::list& result,
+ int64_t id);
+
bool LookupAttachment(FileInfo& attachment,
int64_t id,
FileContentType contentType);
@@ -212,5 +221,16 @@
}
uint64_t IncrementGlobalSequence(GlobalProperty property);
+
+ void ClearTable(const std::string& tableName);
+
+ bool IsExistingResource(int64_t internalId);
+
+ void LookupTagValue(std::list& result,
+ DicomTag tag,
+ const std::string& value);
+
+ void LookupTagValue(std::list& result,
+ const std::string& value);
};
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DicomProtocol/DicomFindAnswers.cpp
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.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 OrthancServer/DicomProtocol/DicomFindAnswers.h
--- a/OrthancServer/DicomProtocol/DicomFindAnswers.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomFindAnswers.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 OrthancServer/DicomProtocol/DicomServer.cpp
--- a/OrthancServer/DicomProtocol/DicomServer.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.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
@@ -82,6 +82,8 @@
boost::filesystem::path p = directory;
p = p / filename;
+ LOG(WARNING) << "Loading the external DICOM dictionary " << p;
+
if (!dictionary.loadDictionary(p.string().c_str()))
{
throw OrthancException(ErrorCode_InternalError);
@@ -111,7 +113,7 @@
LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_PRIVATE);
#elif defined(__linux)
- std::string path = "/usr/share/dcmtk";
+ std::string path = DCMTK_DICTIONARY_DIR;
const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
if (env != NULL)
@@ -385,7 +387,11 @@
void DicomServer::Stop()
{
continue_ = false;
- pimpl_->thread_.join();
+
+ if (pimpl_->thread_.joinable())
+ {
+ pimpl_->thread_.join();
+ }
bagOfDispatchers_.StopAll();
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DicomProtocol/DicomServer.h
--- a/OrthancServer/DicomProtocol/DicomServer.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.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 OrthancServer/DicomProtocol/DicomUserConnection.cpp
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.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
@@ -294,7 +294,20 @@
break;
case FindRootModel_Instance:
- DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE");
+ if (manufacturer_ == ModalityManufacturer_ClearCanvas)
+ {
+ // This is a particular case for ClearCanvas, thanks to Peter Somlo .
+ // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J
+ // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx
+ printf("CLEAR CANVAS\n");
+ DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE");
+ }
+ else
+ {
+ printf("GENERIC\n");
+ DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE");
+ }
+
sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
// Accession number
@@ -453,6 +466,7 @@
distantAet_ = "ANY-SCP";
distantPort_ = 104;
distantHost_ = "127.0.0.1";
+ manufacturer_ = ModalityManufacturer_Generic;
pimpl_->net_ = NULL;
pimpl_->params_ = NULL;
@@ -476,6 +490,12 @@
distantAet_ = aet;
}
+ void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer)
+ {
+ Close();
+ manufacturer_ = manufacturer;
+ }
+
void DicomUserConnection::SetDistantHost(const std::string& host)
{
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DicomProtocol/DicomUserConnection.h
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.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,6 +33,7 @@
#pragma once
#include "DicomFindAnswers.h"
+#include "../ServerEnumerations.h"
#include
#include
@@ -59,6 +60,7 @@
std::string distantAet_;
std::string distantHost_;
uint16_t distantPort_;
+ ModalityManufacturer manufacturer_;
void CheckIsOpen() const;
@@ -106,6 +108,13 @@
return distantPort_;
}
+ void SetDistantManufacturer(ModalityManufacturer manufacturer);
+
+ ModalityManufacturer GetDistantManufacturer() const
+ {
+ return manufacturer_;
+ }
+
void Open();
void Close();
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/DicomProtocol/IApplicationEntityFilter.h
--- a/OrthancServer/DicomProtocol/IApplicationEntityFilter.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IApplicationEntityFilter.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 OrthancServer/DicomProtocol/IFindRequestHandler.h
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.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 OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h
--- a/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IFindRequestHandlerFactory.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 OrthancServer/DicomProtocol/IMoveRequestHandler.h
--- a/OrthancServer/DicomProtocol/IMoveRequestHandler.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IMoveRequestHandler.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 OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h
--- a/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IMoveRequestHandlerFactory.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 OrthancServer/DicomProtocol/IStoreRequestHandler.h
--- a/OrthancServer/DicomProtocol/IStoreRequestHandler.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IStoreRequestHandler.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 OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h
--- a/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/DicomProtocol/IStoreRequestHandlerFactory.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 OrthancServer/FromDcmtkBridge.cpp
--- a/OrthancServer/FromDcmtkBridge.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/FromDcmtkBridge.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
@@ -29,6 +29,49 @@
* along with this program. If not, see .
**/
+
+
+/*=========================================================================
+
+ This file is based on portions of the following project:
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+ Module: http://gdcm.sourceforge.net/Copyright.html
+
+Copyright (c) 2006-2011 Mathieu Malaterre
+Copyright (c) 1993-2005 CREATIS
+(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
+ contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
+ endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=========================================================================*/
+
+
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -38,7 +81,7 @@
#include "ToDcmtkBridge.h"
#include "../Core/Toolbox.h"
#include "../Core/OrthancException.h"
-#include "../Core/PngWriter.h"
+#include "../Core/FileFormats/PngWriter.h"
#include "../Core/Uuid.h"
#include "../Core/DicomFormat/DicomString.h"
#include "../Core/DicomFormat/DicomNullValue.h"
@@ -1097,9 +1140,9 @@
for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
{
int32_t v = accessor.GetValue(x, y);
- if (v < std::numeric_limits::min())
+ if (v < static_cast(std::numeric_limits::min()))
*pixel = std::numeric_limits::min();
- else if (v > std::numeric_limits::max())
+ else if (v > static_cast(std::numeric_limits::max()))
*pixel = std::numeric_limits::max();
else
*pixel = static_cast(v);
@@ -1240,6 +1283,11 @@
accessor.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size()));
accessor->SetCurrentFrame(frame);
}
+
+ if (accessor.get() == NULL)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
PixelFormat format;
bool supported = false;
@@ -1263,6 +1311,11 @@
format = PixelFormat_Grayscale16;
break;
+ case ImageExtractionMode_Int16:
+ supported = true;
+ format = PixelFormat_SignedGrayscale16;
+ break;
+
default:
supported = false;
break;
@@ -1314,6 +1367,10 @@
ExtractPngImageTruncate(result, *accessor, format);
break;
+ case ImageExtractionMode_Int16:
+ ExtractPngImageTruncate(result, *accessor, format);
+ break;
+
default:
throw OrthancException(ErrorCode_NotImplemented);
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/FromDcmtkBridge.h
--- a/OrthancServer/FromDcmtkBridge.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/FromDcmtkBridge.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
@@ -43,13 +43,6 @@
namespace Orthanc
{
- enum ImageExtractionMode
- {
- ImageExtractionMode_Preview,
- ImageExtractionMode_UInt8,
- ImageExtractionMode_UInt16
- };
-
enum DicomRootLevel
{
DicomRootLevel_Patient,
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/IServerIndexListener.h
--- a/OrthancServer/IServerIndexListener.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/IServerIndexListener.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 OrthancServer/Internals/CommandDispatcher.cpp
--- a/OrthancServer/Internals/CommandDispatcher.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/CommandDispatcher.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
@@ -41,9 +41,149 @@
#include
#include
+#define ORTHANC_PROMISCUOUS 1
+
static OFBool opt_rejectWithoutImplementationUID = OFFalse;
+
+#if ORTHANC_PROMISCUOUS == 1
+static
+DUL_PRESENTATIONCONTEXT *
+findPresentationContextID(LST_HEAD * head,
+ T_ASC_PresentationContextID presentationContextID)
+{
+ DUL_PRESENTATIONCONTEXT *pc;
+ LST_HEAD **l;
+ OFBool found = OFFalse;
+
+ if (head == NULL)
+ return NULL;
+
+ l = &head;
+ if (*l == NULL)
+ return NULL;
+
+ pc = OFstatic_cast(DUL_PRESENTATIONCONTEXT *, LST_Head(l));
+ (void)LST_Position(l, OFstatic_cast(LST_NODE *, pc));
+
+ while (pc && !found) {
+ if (pc->presentationContextID == presentationContextID) {
+ found = OFTrue;
+ } else {
+ pc = OFstatic_cast(DUL_PRESENTATIONCONTEXT *, LST_Next(l));
+ }
+ }
+ return pc;
+}
+
+
+/** accept all presenstation contexts for unknown SOP classes,
+ * i.e. UIDs appearing in the list of abstract syntaxes
+ * where no corresponding name is defined in the UID dictionary.
+ * @param params pointer to association parameters structure
+ * @param transferSyntax transfer syntax to accept
+ * @param acceptedRole SCU/SCP role to accept
+ */
+static OFCondition acceptUnknownContextsWithTransferSyntax(
+ T_ASC_Parameters * params,
+ const char* transferSyntax,
+ T_ASC_SC_ROLE acceptedRole)
+{
+ OFCondition cond = EC_Normal;
+ int n, i, k;
+ DUL_PRESENTATIONCONTEXT *dpc;
+ T_ASC_PresentationContext pc;
+ OFBool accepted = OFFalse;
+ OFBool abstractOK = OFFalse;
+
+ n = ASC_countPresentationContexts(params);
+ for (i = 0; i < n; i++)
+ {
+ cond = ASC_getPresentationContext(params, i, &pc);
+ if (cond.bad()) return cond;
+ abstractOK = OFFalse;
+ accepted = OFFalse;
+
+ if (dcmFindNameOfUID(pc.abstractSyntax) == NULL)
+ {
+ abstractOK = OFTrue;
+
+ /* check the transfer syntax */
+ for (k = 0; (k < OFstatic_cast(int, pc.transferSyntaxCount)) && !accepted; k++)
+ {
+ if (strcmp(pc.proposedTransferSyntaxes[k], transferSyntax) == 0)
+ {
+ accepted = OFTrue;
+ }
+ }
+ }
+
+ if (accepted)
+ {
+ cond = ASC_acceptPresentationContext(
+ params, pc.presentationContextID,
+ transferSyntax, acceptedRole);
+ if (cond.bad()) return cond;
+ } else {
+ T_ASC_P_ResultReason reason;
+
+ /* do not refuse if already accepted */
+ dpc = findPresentationContextID(params->DULparams.acceptedPresentationContext,
+ pc.presentationContextID);
+ if ((dpc == NULL) || ((dpc != NULL) && (dpc->result != ASC_P_ACCEPTANCE)))
+ {
+
+ if (abstractOK) {
+ reason = ASC_P_TRANSFERSYNTAXESNOTSUPPORTED;
+ } else {
+ reason = ASC_P_ABSTRACTSYNTAXNOTSUPPORTED;
+ }
+ /*
+ * If previously this presentation context was refused
+ * because of bad transfer syntax let it stay that way.
+ */
+ if ((dpc != NULL) && (dpc->result == ASC_P_TRANSFERSYNTAXESNOTSUPPORTED))
+ reason = ASC_P_TRANSFERSYNTAXESNOTSUPPORTED;
+
+ cond = ASC_refusePresentationContext(params, pc.presentationContextID, reason);
+ if (cond.bad()) return cond;
+ }
+ }
+ }
+ return EC_Normal;
+}
+
+
+/** accept all presenstation contexts for unknown SOP classes,
+ * i.e. UIDs appearing in the list of abstract syntaxes
+ * where no corresponding name is defined in the UID dictionary.
+ * This method is passed a list of "preferred" transfer syntaxes.
+ * @param params pointer to association parameters structure
+ * @param transferSyntax transfer syntax to accept
+ * @param acceptedRole SCU/SCP role to accept
+ */
+static OFCondition acceptUnknownContextsWithPreferredTransferSyntaxes(
+ T_ASC_Parameters * params,
+ const char* transferSyntaxes[], int transferSyntaxCount,
+ T_ASC_SC_ROLE acceptedRole = ASC_SC_ROLE_DEFAULT)
+{
+ OFCondition cond = EC_Normal;
+ /*
+ ** Accept in the order "least wanted" to "most wanted" transfer
+ ** syntax. Accepting a transfer syntax will override previously
+ ** accepted transfer syntaxes.
+ */
+ for (int i = transferSyntaxCount - 1; i >= 0; i--)
+ {
+ cond = acceptUnknownContextsWithTransferSyntax(params, transferSyntaxes[i], acceptedRole);
+ if (cond.bad()) return cond;
+ }
+ return cond;
+}
+#endif
+
+
namespace Orthanc
{
namespace Internals
@@ -150,6 +290,18 @@
return NULL;
}
+#if ORTHANC_PROMISCUOUS == 1
+ /* accept everything not known not to be a storage SOP class */
+ cond = acceptUnknownContextsWithPreferredTransferSyntaxes(
+ assoc->params, transferSyntaxes, numTransferSyntaxes);
+ if (cond.bad())
+ {
+ LOG(INFO) << cond.text();
+ AssociationCleanup(assoc);
+ return NULL;
+ }
+#endif
+
/* set our app title */
ASC_setAPTitles(assoc->params, NULL, NULL, server.GetApplicationEntityTitle().c_str());
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/Internals/CommandDispatcher.h
--- a/OrthancServer/Internals/CommandDispatcher.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/CommandDispatcher.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 OrthancServer/Internals/FindScp.cpp
--- a/OrthancServer/Internals/FindScp.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/FindScp.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 OrthancServer/Internals/FindScp.h
--- a/OrthancServer/Internals/FindScp.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/FindScp.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 OrthancServer/Internals/MoveScp.cpp
--- a/OrthancServer/Internals/MoveScp.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/MoveScp.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 OrthancServer/Internals/MoveScp.h
--- a/OrthancServer/Internals/MoveScp.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/MoveScp.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 OrthancServer/Internals/StoreScp.cpp
--- a/OrthancServer/Internals/StoreScp.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/StoreScp.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
@@ -117,7 +117,7 @@
FromDcmtkBridge::Convert(summary, **imageDataSet);
FromDcmtkBridge::ToJson(dicomJson, **imageDataSet);
- if (FromDcmtkBridge::SaveToMemoryBuffer(buffer, *imageDataSet) < 0)
+ if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, *imageDataSet))
{
LOG(ERROR) << "cannot write DICOM file to memory";
rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/Internals/StoreScp.h
--- a/OrthancServer/Internals/StoreScp.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/Internals/StoreScp.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 OrthancServer/OrthancInitialization.cpp
--- a/OrthancServer/OrthancInitialization.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/OrthancInitialization.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
@@ -32,8 +32,10 @@
#include "OrthancInitialization.h"
+#include "../Core/HttpClient.h"
#include "../Core/OrthancException.h"
#include "../Core/Toolbox.h"
+#include "ServerEnumerations.h"
#include
#include
@@ -47,6 +49,7 @@
static boost::mutex globalMutex_;
static std::auto_ptr configuration_;
+ static boost::filesystem::path defaultDirectory_;
static void ReadGlobalConfiguration(const char* configurationFile)
@@ -58,6 +61,7 @@
if (configurationFile)
{
Toolbox::ReadFile(content, configurationFile);
+ defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
LOG(INFO) << "Using the configuration from: " << configurationFile;
}
else
@@ -116,11 +120,51 @@
}
+ static void RegisterUserMetadata()
+ {
+ if (configuration_->isMember("UserMetadata"))
+ {
+ const Json::Value& parameter = (*configuration_) ["UserMetadata"];
+
+ Json::Value::Members members = parameter.getMemberNames();
+ for (size_t i = 0; i < members.size(); i++)
+ {
+ std::string info = "\"" + members[i] + "\" = " + parameter[members[i]].toStyledString();
+ LOG(INFO) << "Registering user-defined metadata: " << info;
+
+ if (!parameter[members[i]].asBool())
+ {
+ LOG(ERROR) << "Not a number in this user-defined metadata: " << info;
+ throw OrthancException(ErrorCode_BadParameterType);
+ }
+
+ int metadata = parameter[members[i]].asInt();
+
+ try
+ {
+ RegisterUserMetadata(metadata, members[i]);
+ }
+ catch (OrthancException e)
+ {
+ LOG(ERROR) << "Cannot register this user-defined metadata: " << info;
+ throw e;
+ }
+ }
+ }
+ }
+
+
void OrthancInitialize(const char* configurationFile)
{
boost::mutex::scoped_lock lock(globalMutex_);
+
+ InitializeServerEnumerations();
+ defaultDirectory_ = boost::filesystem::current_path();
ReadGlobalConfiguration(configurationFile);
- curl_global_init(CURL_GLOBAL_ALL);
+
+ HttpClient::GlobalInitialize();
+
+ RegisterUserMetadata();
}
@@ -128,7 +172,7 @@
void OrthancFinalize()
{
boost::mutex::scoped_lock lock(globalMutex_);
- curl_global_cleanup();
+ HttpClient::GlobalFinalize();
configuration_.reset(NULL);
}
@@ -186,7 +230,8 @@
void GetDicomModality(const std::string& name,
std::string& aet,
std::string& address,
- int& port)
+ int& port,
+ ModalityManufacturer& manufacturer)
{
boost::mutex::scoped_lock lock(globalMutex_);
@@ -197,7 +242,8 @@
const Json::Value& modalities = (*configuration_) ["DicomModalities"];
if (modalities.type() != Json::objectValue ||
- !modalities.isMember(name))
+ !modalities.isMember(name) ||
+ (modalities[name].size() != 3 && modalities[name].size() != 4))
{
throw OrthancException("");
}
@@ -207,6 +253,15 @@
aet = modalities[name].get(0u, "").asString();
address = modalities[name].get(1u, "").asString();
port = modalities[name].get(2u, "").asInt();
+
+ if (modalities[name].size() == 4)
+ {
+ manufacturer = StringToModalityManufacturer(modalities[name].get(3u, "").asString());
+ }
+ else
+ {
+ manufacturer = ModalityManufacturer_Generic;
+ }
}
catch (...)
{
@@ -216,36 +271,111 @@
- void GetListOfDicomModalities(std::set& target)
+ void GetOrthancPeer(const std::string& name,
+ std::string& url,
+ std::string& username,
+ std::string& password)
+ {
+ boost::mutex::scoped_lock lock(globalMutex_);
+
+ if (!configuration_->isMember("OrthancPeers"))
+ {
+ throw OrthancException("");
+ }
+
+ const Json::Value& modalities = (*configuration_) ["OrthancPeers"];
+ if (modalities.type() != Json::objectValue ||
+ !modalities.isMember(name))
+ {
+ throw OrthancException("");
+ }
+
+ try
+ {
+ url = modalities[name].get(0u, "").asString();
+
+ if (modalities[name].size() == 1)
+ {
+ username = "";
+ password = "";
+ }
+ else if (modalities[name].size() == 3)
+ {
+ username = modalities[name].get(1u, "").asString();
+ password = modalities[name].get(2u, "").asString();
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+ }
+ catch (...)
+ {
+ throw OrthancException(ErrorCode_BadFileFormat);
+ }
+
+ if (url.size() != 0 && url[url.size() - 1] != '/')
+ {
+ url += '/';
+ }
+ }
+
+
+ static bool ReadKeys(std::set& target,
+ const char* parameter,
+ bool onlyAlphanumeric)
{
boost::mutex::scoped_lock lock(globalMutex_);
target.clear();
- if (!configuration_->isMember("DicomModalities"))
+ if (!configuration_->isMember(parameter))
{
- return;
+ return true;
}
- const Json::Value& modalities = (*configuration_) ["DicomModalities"];
+ const Json::Value& modalities = (*configuration_) [parameter];
if (modalities.type() != Json::objectValue)
{
- throw OrthancException("Badly formatted list of DICOM modalities");
+ throw OrthancException(ErrorCode_BadFileFormat);
}
Json::Value::Members members = modalities.getMemberNames();
for (size_t i = 0; i < members.size(); i++)
{
- for (size_t j = 0; j < members[i].size(); j++)
+ if (onlyAlphanumeric)
{
- if (!isalnum(members[i][j]) && members[i][j] != '-')
+ for (size_t j = 0; j < members[i].size(); j++)
{
- throw OrthancException("Only alphanumeric and dash characters are allowed in the names of the modalities");
+ if (!isalnum(members[i][j]) && members[i][j] != '-')
+ {
+ return false;
+ }
}
}
target.insert(members[i]);
}
+
+ return true;
+ }
+
+
+ void GetListOfDicomModalities(std::set& target)
+ {
+ if (!ReadKeys(target, "DicomModalities", true))
+ {
+ throw OrthancException("Only alphanumeric and dash characters are allowed in the names of the modalities");
+ }
+ }
+
+
+ void GetListOfOrthancPeers(std::set& target)
+ {
+ if (!ReadKeys(target, "OrthancPeers", true))
+ {
+ throw OrthancException("Only alphanumeric and dash characters are allowed in the names of Orthanc peers");
+ }
}
@@ -275,4 +405,63 @@
httpServer.RegisterUser(username.c_str(), password.c_str());
}
}
+
+
+ std::string InterpretRelativePath(const std::string& baseDirectory,
+ const std::string& relativePath)
+ {
+ boost::filesystem::path base(baseDirectory);
+ boost::filesystem::path relative(relativePath);
+
+ /**
+ The following lines should be equivalent to this one:
+
+ return (base / relative).string();
+
+ However, for some unknown reason, some versions of Boost do not
+ make the proper path resolution when "baseDirectory" is an
+ absolute path. So, a hack is used below.
+ **/
+
+ if (relative.is_absolute())
+ {
+ return relative.string();
+ }
+ else
+ {
+ return (base / relative).string();
+ }
+ }
+
+ std::string InterpretStringParameterAsPath(const std::string& parameter)
+ {
+ boost::mutex::scoped_lock lock(globalMutex_);
+ return InterpretRelativePath(defaultDirectory_.string(), parameter);
+ }
+
+
+ void GetGlobalListOfStringsParameter(std::list& target,
+ const std::string& key)
+ {
+ boost::mutex::scoped_lock lock(globalMutex_);
+
+ target.clear();
+
+ if (!configuration_->isMember(key))
+ {
+ return;
+ }
+
+ const Json::Value& lst = (*configuration_) [key];
+
+ if (lst.type() != Json::arrayValue)
+ {
+ throw OrthancException("Badly formatted list of strings");
+ }
+
+ for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++)
+ {
+ target.push_back(lst[i].asString());
+ }
+ }
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/OrthancInitialization.h
--- a/OrthancServer/OrthancInitialization.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/OrthancInitialization.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
@@ -37,6 +37,7 @@
#include
#include
#include "../Core/HttpServer/MongooseServer.h"
+#include "ServerEnumerations.h"
namespace Orthanc
{
@@ -56,9 +57,25 @@
void GetDicomModality(const std::string& name,
std::string& aet,
std::string& address,
- int& port);
+ int& port,
+ ModalityManufacturer& manufacturer);
+
+ void GetOrthancPeer(const std::string& name,
+ std::string& url,
+ std::string& username,
+ std::string& password);
void GetListOfDicomModalities(std::set& target);
+ void GetListOfOrthancPeers(std::set& target);
+
void SetupRegisteredUsers(MongooseServer& httpServer);
+
+ std::string InterpretRelativePath(const std::string& baseDirectory,
+ const std::string& relativePath);
+
+ std::string InterpretStringParameterAsPath(const std::string& parameter);
+
+ void GetGlobalListOfStringsParameter(std::list& target,
+ const std::string& key);
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/OrthancRestApi.cpp
--- a/OrthancServer/OrthancRestApi.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/OrthancRestApi.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
@@ -32,9 +32,10 @@
#include "OrthancRestApi.h"
+#include "../Core/Compression/HierarchicalZipWriter.h"
+#include "../Core/HttpClient.h"
#include "../Core/HttpServer/FilesystemHttpSender.h"
#include "../Core/Uuid.h"
-#include "../Core/Compression/HierarchicalZipWriter.h"
#include "DicomProtocol/DicomUserConnection.h"
#include "FromDcmtkBridge.h"
#include "OrthancInitialization.h"
@@ -52,9 +53,13 @@
ServerContext& context = contextApi.GetContext()
#define RETRIEVE_MODALITIES(call) \
- const OrthancRestApi::Modalities& modalities = \
+ const OrthancRestApi::SetOfStrings& modalities = \
dynamic_cast(call.GetContext()).GetModalities();
+#define RETRIEVE_PEERS(call) \
+ const OrthancRestApi::SetOfStrings& peers = \
+ dynamic_cast(call.GetContext()).GetPeers();
+
namespace Orthanc
@@ -71,11 +76,13 @@
{
std::string aet, address;
int port;
- GetDicomModality(name, aet, address, port);
+ ModalityManufacturer manufacturer;
+ GetDicomModality(name, aet, address, port, manufacturer);
connection.SetLocalApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC"));
connection.SetDistantApplicationEntityTitle(aet);
connection.SetDistantHost(address);
connection.SetDistantPort(port);
+ connection.SetDistantManufacturer(manufacturer);
connection.Open();
}
@@ -174,6 +181,34 @@
call.GetOutput().AnswerJson(result);
}
+ static void DicomFindInstance(RestApi::PostCall& call)
+ {
+ DicomMap m;
+ DicomMap::SetupFindInstanceTemplate(m);
+ if (!MergeQueryAndTemplate(m, call.GetPostBody()))
+ {
+ return;
+ }
+
+ if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
+ m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
+ m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 ||
+ m.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2)
+ {
+ return;
+ }
+
+ DicomUserConnection connection;
+ ConnectToModality(connection, call.GetUriComponent("id", ""));
+
+ DicomFindAnswers answers;
+ connection.FindInstance(answers, m);
+
+ Json::Value result;
+ answers.ToJson(result);
+ call.GetOutput().AnswerJson(result);
+ }
+
static void DicomFind(RestApi::PostCall& call)
{
DicomMap m;
@@ -244,50 +279,90 @@
}
+ static bool GetInstancesToExport(std::list& instances,
+ const std::string& remote,
+ RestApi::PostCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string stripped = Toolbox::StripSpaces(call.GetPostBody());
+
+ Json::Value request;
+ if (Toolbox::IsSHA1(stripped))
+ {
+ // This is for compatibility with Orthanc <= 0.5.1.
+ request = stripped;
+ }
+ else if (!call.ParseJsonRequest(request))
+ {
+ // Bad JSON request
+ return false;
+ }
+
+ if (request.isString())
+ {
+ context.GetIndex().LogExportedResource(request.asString(), remote);
+ context.GetIndex().GetChildInstances(instances, request.asString());
+ }
+ else if (request.isArray())
+ {
+ for (Json::Value::ArrayIndex i = 0; i < request.size(); i++)
+ {
+ if (!request[i].isString())
+ {
+ return false;
+ }
+
+ std::string stripped = Toolbox::StripSpaces(request[i].asString());
+ if (!Toolbox::IsSHA1(stripped))
+ {
+ return false;
+ }
+
+ context.GetIndex().LogExportedResource(stripped, remote);
+
+ std::list tmp;
+ context.GetIndex().GetChildInstances(tmp, stripped);
+ instances.merge(tmp);
+ assert(tmp.size() == 0);
+ }
+ }
+ else
+ {
+ // Neither a string, nor a list of strings. Bad request.
+ return false;
+ }
+
+ return true;
+ }
+
+
static void DicomStore(RestApi::PostCall& call)
{
RETRIEVE_CONTEXT(call);
std::string remote = call.GetUriComponent("id", "");
+
+ std::list instances;
+ if (!GetInstancesToExport(instances, remote, call))
+ {
+ return;
+ }
+
DicomUserConnection connection;
ConnectToModality(connection, remote);
- const std::string& resourceId = call.GetPostBody();
-
- Json::Value found;
- if (context.GetIndex().LookupResource(found, resourceId, ResourceType_Series))
+ for (std::list::const_iterator
+ it = instances.begin(); it != instances.end(); it++)
{
- // The UUID corresponds to a series
- context.GetIndex().LogExportedResource(resourceId, remote);
-
- for (Json::Value::ArrayIndex i = 0; i < found["Instances"].size(); i++)
- {
- std::string instanceId = found["Instances"][i].asString();
- std::string dicom;
- context.ReadFile(dicom, instanceId, FileContentType_Dicom);
- connection.Store(dicom);
- }
-
- call.GetOutput().AnswerBuffer("{}", "application/json");
- }
- else if (context.GetIndex().LookupResource(found, resourceId, ResourceType_Instance))
- {
- // The UUID corresponds to an instance
- context.GetIndex().LogExportedResource(resourceId, remote);
+ LOG(INFO) << "Sending resource " << *it << " to modality \"" << remote << "\"";
std::string dicom;
- context.ReadFile(dicom, resourceId, FileContentType_Dicom);
+ context.ReadFile(dicom, *it, FileContentType_Dicom);
connection.Store(dicom);
-
- call.GetOutput().AnswerBuffer("{}", "application/json");
}
- else
- {
- // The POST body is not a known resource, assume that it
- // contains a raw DICOM instance
- connection.Store(resourceId);
- call.GetOutput().AnswerBuffer("{}", "application/json");
- }
+
+ call.GetOutput().AnswerBuffer("{}", "application/json");
}
@@ -338,6 +413,23 @@
}
}
+ static void ExecuteScript(RestApi::PostCall& call)
+ {
+ std::string result;
+ RETRIEVE_CONTEXT(call);
+ context.GetLuaContext().Execute(result, call.GetPostBody());
+ call.GetOutput().AnswerBuffer(result, "text/plain");
+ }
+
+ static void GetNowIsoString(RestApi::GetCall& call)
+ {
+ call.GetOutput().AnswerBuffer(Toolbox::GetNowIsoString(), "text/plain");
+ }
+
+
+
+
+
// List all the patients, studies, series or instances ----------------------
@@ -615,6 +707,14 @@
}
+ static void DeleteChanges(RestApi::DeleteCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+ context.GetIndex().DeleteChanges();
+ call.GetOutput().AnswerBuffer("", "text/plain");
+ }
+
+
static void GetExports(RestApi::GetCall& call)
{
RETRIEVE_CONTEXT(call);
@@ -632,6 +732,14 @@
}
}
+
+ static void DeleteExports(RestApi::DeleteCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+ context.GetIndex().DeleteExportedResources();
+ call.GetOutput().AnswerBuffer("", "text/plain");
+ }
+
// Get information about a single patient -----------------------------------
@@ -678,6 +786,21 @@
}
+ static void ExportInstanceFile(RestApi::PostCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string publicId = call.GetUriComponent("id", "");
+
+ std::string dicom;
+ context.ReadFile(dicom, publicId, FileContentType_Dicom);
+
+ Toolbox::WriteFile(dicom, call.GetPostBody());
+
+ call.GetOutput().AnswerBuffer("{}", "application/json");
+ }
+
+
template
static void GetInstanceTags(RestApi::GetCall& call)
{
@@ -801,7 +924,7 @@
result["Path"] = GetBasePath(ResourceType_Instance, publicId);
}
- result["Status"] = ToString(status);
+ result["Status"] = EnumerationToString(status);
call.GetOutput().AnswerJson(result);
}
@@ -809,7 +932,7 @@
// DICOM bridge -------------------------------------------------------------
- static bool IsExistingModality(const OrthancRestApi::Modalities& modalities,
+ static bool IsExistingModality(const OrthancRestApi::SetOfStrings& modalities,
const std::string& id)
{
return modalities.find(id) != modalities.end();
@@ -820,7 +943,7 @@
RETRIEVE_MODALITIES(call);
Json::Value result = Json::arrayValue;
- for (OrthancRestApi::Modalities::const_iterator
+ for (OrthancRestApi::SetOfStrings::const_iterator
it = modalities.begin(); it != modalities.end(); it++)
{
result.append(*it);
@@ -841,6 +964,7 @@
result.append("find-patient");
result.append("find-study");
result.append("find-series");
+ result.append("find-instance");
result.append("find");
result.append("store");
call.GetOutput().AnswerJson(result);
@@ -909,8 +1033,6 @@
throw OrthancException(ErrorCode_BadRequest);
}
- target.clear();
-
for (Json::Value::ArrayIndex i = 0; i < removals.size(); i++)
{
std::string name = removals[i].asString();
@@ -930,8 +1052,6 @@
throw OrthancException(ErrorCode_BadRequest);
}
- target.clear();
-
Json::Value::Members members = replacements.getMemberNames();
for (size_t i = 0; i < members.size(); i++)
{
@@ -977,7 +1097,7 @@
removals.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID
removals.insert(DicomTag(0x0008, 0x2111)); // Derivation Description
removals.insert(DicomTag(0x0010, 0x0010)); // Patient's Name
- removals.insert(DicomTag(0x0010, 0x0020)); // Patient ID
+ //removals.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*)
removals.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date
removals.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time
removals.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex
@@ -993,8 +1113,8 @@
removals.insert(DicomTag(0x0010, 0x4000)); // Patient Comments
removals.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number
removals.insert(DicomTag(0x0018, 0x1030)); // Protocol Name
- //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => generated below
- //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => generated below
+ //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => cf. below (*)
+ //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => cf. below (*)
removals.insert(DicomTag(0x0020, 0x0010)); // Study ID
removals.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID
removals.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID
@@ -1006,29 +1126,25 @@
removals.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID
removals.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID
+ /**
+ * (*) Patient ID, Study Instance UID and Series Instance UID
+ * are modified by "AnonymizeInstance()" if anonymizing a single
+ * instance, or by "RetrieveMappedUid()" if anonymizing a
+ * patient/study/series.
+ **/
+
+
// Some more removals (from the experience of DICOM files at the CHU of Liege)
removals.insert(DicomTag(0x0010, 0x1040)); // Patient's Address
removals.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician
+ removals.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers
+ removals.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts
// Set the DeidentificationMethod tag
replacements.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"));
- // Set the PatientIdentityRemoved
+ // Set the PatientIdentityRemoved tag
replacements.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES"));
-
- // Generate random study UID if not specified
- if (replacements.find(DICOM_TAG_STUDY_INSTANCE_UID) == replacements.end())
- {
- replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID,
- FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study)));
- }
-
- // Generate random series UID if not specified
- if (replacements.find(DICOM_TAG_SERIES_INSTANCE_UID) == replacements.end())
- {
- replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID,
- FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series)));
- }
}
@@ -1131,13 +1247,6 @@
replacements.insert(std::make_pair(DicomTag(0x0010, 0x0010), GeneratePatientName(context)));
}
- // Generate random Patient's ID if none is specified
- if (replacements.find(DICOM_TAG_PATIENT_ID) == replacements.end())
- {
- replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID,
- FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient)));
- }
-
return true;
}
else
@@ -1170,14 +1279,23 @@
UidMap& uidMap)
{
std::auto_ptr tag;
- if (level == DicomRootLevel_Series)
+
+ switch (level)
{
- tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID));
- }
- else
- {
- assert(level == DicomRootLevel_Study);
- tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID));
+ case DicomRootLevel_Series:
+ tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID));
+ break;
+
+ case DicomRootLevel_Study:
+ tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID));
+ break;
+
+ case DicomRootLevel_Patient:
+ tag.reset(new DicomTag(DICOM_TAG_PATIENT_ID));
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
}
std::string original;
@@ -1207,7 +1325,8 @@
}
- static void AnonymizeOrModifyResource(Removals& removals,
+ static void AnonymizeOrModifyResource(bool isAnonymization,
+ Removals& removals,
Replacements& replacements,
bool removePrivateTags,
MetadataType metadataType,
@@ -1244,15 +1363,20 @@
LOG(INFO) << "Modifying instance " << *it;
ParsedDicomFile& original = context.GetDicomFile(*it);
- bool isNewSeries = RetrieveMappedUid(original, DicomRootLevel_Series, replacements, uidMap);
+ DicomInstanceHasher originalHasher = original.GetHasher();
- bool isNewStudy = false;
- if (resourceType == ResourceType_Study ||
- resourceType == ResourceType_Patient)
+ if (isFirst && !isAnonymization)
{
- isNewStudy = RetrieveMappedUid(original, DicomRootLevel_Study, replacements, uidMap);
+ // If modifying a study or a series, keep the original patient ID.
+ std::string patientId = originalHasher.GetPatientId();
+ uidMap[std::make_pair(DicomRootLevel_Patient, patientId)] = patientId;
}
+ bool isNewSeries = RetrieveMappedUid(original, DicomRootLevel_Series, replacements, uidMap);
+ bool isNewStudy = RetrieveMappedUid(original, DicomRootLevel_Study, replacements, uidMap);
+ bool isNewPatient = RetrieveMappedUid(original, DicomRootLevel_Patient, replacements, uidMap);
+
+
/**
* Compute the resulting DICOM instance and store it into the Orthanc store.
**/
@@ -1269,11 +1393,10 @@
/**
- * Record metadata information (AnonimizedFrom/ModifiedFrom).
+ * Record metadata information (AnonymizedFrom/ModifiedFrom).
**/
DicomInstanceHasher modifiedHasher = modified->GetHasher();
- DicomInstanceHasher originalHasher = original.GetHasher();
if (isNewSeries)
{
@@ -1287,6 +1410,12 @@
metadataType, originalHasher.HashStudy());
}
+ if (isNewPatient)
+ {
+ context.GetIndex().SetMetadata(modifiedHasher.HashPatient(),
+ metadataType, originalHasher.HashPatient());
+ }
+
assert(*it == originalHasher.HashInstance());
assert(modifiedInstance == modifiedHasher.HashInstance());
context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
@@ -1318,7 +1447,7 @@
throw OrthancException(ErrorCode_InternalError);
}
- result["Type"] = ToString(resourceType);
+ result["Type"] = EnumerationToString(resourceType);
result["ID"] = newId;
result["Path"] = GetBasePath(resourceType, newId);
result["PatientID"] = modifiedHasher.HashPatient();
@@ -1352,6 +1481,27 @@
if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, call))
{
+ // Generate random patient ID if not specified
+ if (replacements.find(DICOM_TAG_PATIENT_ID) == replacements.end())
+ {
+ replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID,
+ FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient)));
+ }
+
+ // Generate random study UID if not specified
+ if (replacements.find(DICOM_TAG_STUDY_INSTANCE_UID) == replacements.end())
+ {
+ replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID,
+ FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study)));
+ }
+
+ // Generate random series UID if not specified
+ if (replacements.find(DICOM_TAG_SERIES_INSTANCE_UID) == replacements.end())
+ {
+ replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID,
+ FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series)));
+ }
+
AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call);
}
}
@@ -1365,7 +1515,7 @@
if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags,
MetadataType_ModifiedFrom, ChangeType_ModifiedSeries,
ResourceType_Series, call);
}
@@ -1380,7 +1530,7 @@
if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(true, removals, replacements, removePrivateTags,
MetadataType_AnonymizedFrom, ChangeType_AnonymizedSeries,
ResourceType_Series, call);
}
@@ -1395,7 +1545,7 @@
if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags,
MetadataType_ModifiedFrom, ChangeType_ModifiedStudy,
ResourceType_Study, call);
}
@@ -1410,14 +1560,14 @@
if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(true, removals, replacements, removePrivateTags,
MetadataType_AnonymizedFrom, ChangeType_AnonymizedStudy,
ResourceType_Study, call);
}
}
- static void ModifyPatientInplace(RestApi::PostCall& call)
+ /*static void ModifyPatientInplace(RestApi::PostCall& call)
{
Removals removals;
Replacements replacements;
@@ -1425,11 +1575,11 @@
if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags,
MetadataType_ModifiedFrom, ChangeType_ModifiedPatient,
ResourceType_Patient, call);
}
- }
+ }*/
static void AnonymizePatientInplace(RestApi::PostCall& call)
@@ -1440,13 +1590,182 @@
if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, call))
{
- AnonymizeOrModifyResource(removals, replacements, removePrivateTags,
+ AnonymizeOrModifyResource(true, removals, replacements, removePrivateTags,
MetadataType_AnonymizedFrom, ChangeType_AnonymizedPatient,
ResourceType_Patient, call);
}
}
+ // Handling of metadata -----------------------------------------------------
+
+ static void ListMetadata(RestApi::GetCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string publicId = call.GetUriComponent("id", "");
+ std::list metadata;
+ if (context.GetIndex().ListAvailableMetadata(metadata, publicId))
+ {
+ Json::Value result = Json::arrayValue;
+
+ for (std::list::const_iterator
+ it = metadata.begin(); it != metadata.end(); it++)
+ {
+ result.append(EnumerationToString(*it));
+ }
+
+ call.GetOutput().AnswerJson(result);
+ }
+ }
+
+
+ static void GetMetadata(RestApi::GetCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string publicId = call.GetUriComponent("id", "");
+ std::string name = call.GetUriComponent("name", "");
+ MetadataType metadata = StringToMetadata(name);
+
+ std::string value;
+ if (context.GetIndex().LookupMetadata(value, publicId, metadata))
+ {
+ call.GetOutput().AnswerBuffer(value, "text/plain");
+ }
+ }
+
+
+ static void DeleteMetadata(RestApi::DeleteCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string publicId = call.GetUriComponent("id", "");
+ std::string name = call.GetUriComponent("name", "");
+ MetadataType metadata = StringToMetadata(name);
+
+ if (metadata >= MetadataType_StartUser &&
+ metadata <= MetadataType_EndUser)
+ {
+ // It is forbidden to modify internal metadata
+ context.GetIndex().DeleteMetadata(publicId, metadata);
+ call.GetOutput().AnswerBuffer("", "text/plain");
+ }
+ }
+
+
+ static void SetMetadata(RestApi::PutCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string publicId = call.GetUriComponent("id", "");
+ std::string name = call.GetUriComponent("name", "");
+ MetadataType metadata = StringToMetadata(name);
+ std::string value = call.GetPutBody();
+
+ if (metadata >= MetadataType_StartUser &&
+ metadata <= MetadataType_EndUser)
+ {
+ // It is forbidden to modify internal metadata
+ context.GetIndex().SetMetadata(publicId, metadata, value);
+ call.GetOutput().AnswerBuffer("", "text/plain");
+ }
+ }
+
+
+ static void GetResourceStatistics(RestApi::GetCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+ std::string publicId = call.GetUriComponent("id", "");
+ Json::Value result;
+ context.GetIndex().GetStatistics(result, publicId);
+ call.GetOutput().AnswerJson(result);
+ }
+
+
+
+ // Orthanc Peers ------------------------------------------------------------
+
+ static bool IsExistingPeer(const OrthancRestApi::SetOfStrings& peers,
+ const std::string& id)
+ {
+ return peers.find(id) != peers.end();
+ }
+
+ static void ListPeers(RestApi::GetCall& call)
+ {
+ RETRIEVE_PEERS(call);
+
+ Json::Value result = Json::arrayValue;
+ for (OrthancRestApi::SetOfStrings::const_iterator
+ it = peers.begin(); it != peers.end(); it++)
+ {
+ result.append(*it);
+ }
+
+ call.GetOutput().AnswerJson(result);
+ }
+
+ static void ListPeerOperations(RestApi::GetCall& call)
+ {
+ RETRIEVE_PEERS(call);
+
+ std::string id = call.GetUriComponent("id", "");
+ if (IsExistingPeer(peers, id))
+ {
+ Json::Value result = Json::arrayValue;
+ result.append("store");
+ call.GetOutput().AnswerJson(result);
+ }
+ }
+
+ static void PeerStore(RestApi::PostCall& call)
+ {
+ RETRIEVE_CONTEXT(call);
+
+ std::string remote = call.GetUriComponent("id", "");
+
+ std::list instances;
+ if (!GetInstancesToExport(instances, remote, call))
+ {
+ return;
+ }
+
+ std::string url, username, password;
+ GetOrthancPeer(remote, url, username, password);
+
+ // Configure the HTTP client
+ HttpClient client;
+ if (username.size() != 0 && password.size() != 0)
+ {
+ client.SetCredentials(username.c_str(), password.c_str());
+ }
+
+ client.SetUrl(url + "instances");
+ client.SetMethod(HttpMethod_Post);
+
+ // Loop over the instances that are to be sent
+ for (std::list::const_iterator
+ it = instances.begin(); it != instances.end(); it++)
+ {
+ LOG(INFO) << "Sending resource " << *it << " to peer \"" << remote << "\"";
+
+ context.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom);
+
+ std::string answer;
+ if (!client.Apply(answer))
+ {
+ LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << remote << "\"";
+ return;
+ }
+ }
+
+ call.GetOutput().AnswerBuffer("{}", "application/json");
+ }
+
+
+
+
// Registration of the various REST handlers --------------------------------
@@ -1454,12 +1773,15 @@
context_(context)
{
GetListOfDicomModalities(modalities_);
+ GetListOfOrthancPeers(peers_);
Register("/", ServeRoot);
Register("/system", GetSystemInformation);
Register("/statistics", GetStatistics);
Register("/changes", GetChanges);
+ Register("/changes", DeleteChanges);
Register("/exports", GetExports);
+ Register("/exports", DeleteExports);
Register("/instances", UploadDicomFile);
Register("/instances", ListResources);
@@ -1480,9 +1802,32 @@
Register("/studies/{id}/archive", GetArchive);
Register("/series/{id}/archive", GetArchive);
+ Register("/instances/{id}/statistics", GetResourceStatistics);
+ Register("/patients/{id}/statistics", GetResourceStatistics);
+ Register("/studies/{id}/statistics", GetResourceStatistics);
+ Register("/series/{id}/statistics", GetResourceStatistics);
+
+ Register("/instances/{id}/metadata", ListMetadata);
+ Register("/instances/{id}/metadata/{name}", DeleteMetadata);
+ Register("/instances/{id}/metadata/{name}", GetMetadata);
+ Register("/instances/{id}/metadata/{name}", SetMetadata);
+ Register("/patients/{id}/metadata", ListMetadata);
+ Register("/patients/{id}/metadata/{name}", DeleteMetadata);
+ Register("/patients/{id}/metadata/{name}", GetMetadata);
+ Register("/patients/{id}/metadata/{name}", SetMetadata);
+ Register("/series/{id}/metadata", ListMetadata);
+ Register("/series/{id}/metadata/{name}", DeleteMetadata);
+ Register("/series/{id}/metadata/{name}", GetMetadata);
+ Register("/series/{id}/metadata/{name}", SetMetadata);
+ Register("/studies/{id}/metadata", ListMetadata);
+ Register("/studies/{id}/metadata/{name}", DeleteMetadata);
+ Register("/studies/{id}/metadata/{name}", GetMetadata);
+ Register("/studies/{id}/metadata/{name}", SetMetadata);
+
Register("/patients/{id}/protected", IsProtectedPatient);
Register("/patients/{id}/protected", SetPatientProtection);
Register("/instances/{id}/file", GetInstanceFile);
+ Register("/instances/{id}/export", ExportInstanceFile);
Register("/instances/{id}/tags", GetInstanceTags);
Register("/instances/{id}/simplified-tags", GetInstanceTags);
Register("/instances/{id}/frames", ListFrames);
@@ -1491,22 +1836,29 @@
Register("/instances/{id}/frames/{frame}/preview", GetImage);
Register("/instances/{id}/frames/{frame}/image-uint8", GetImage);
Register("/instances/{id}/frames/{frame}/image-uint16", GetImage);
+ Register("/instances/{id}/frames/{frame}/image-int16", GetImage);
Register("/instances/{id}/preview", GetImage);
Register("/instances/{id}/image-uint8", GetImage);
Register("/instances/{id}/image-uint16", GetImage);
+ Register("/instances/{id}/image-int16", GetImage);
Register("/modalities", ListModalities);
Register("/modalities/{id}", ListModalityOperations);
Register("/modalities/{id}/find-patient", DicomFindPatient);
Register("/modalities/{id}/find-study", DicomFindStudy);
Register("/modalities/{id}/find-series", DicomFindSeries);
+ Register("/modalities/{id}/find-instance", DicomFindInstance);
Register("/modalities/{id}/find", DicomFind);
Register("/modalities/{id}/store", DicomStore);
+ Register("/peers", ListPeers);
+ Register("/peers/{id}", ListPeerOperations);
+ Register("/peers/{id}/store", PeerStore);
+
Register("/instances/{id}/modify", ModifyInstance);
Register("/series/{id}/modify", ModifySeriesInplace);
Register("/studies/{id}/modify", ModifyStudyInplace);
- Register("/patients/{id}/modify", ModifyPatientInplace);
+ //Register("/patients/{id}/modify", ModifyPatientInplace);
Register("/instances/{id}/anonymize", AnonymizeInstance);
Register("/series/{id}/anonymize", AnonymizeSeriesInplace);
@@ -1514,5 +1866,7 @@
Register("/patients/{id}/anonymize", AnonymizePatientInplace);
Register("/tools/generate-uid", GenerateUid);
+ Register("/tools/execute-script", ExecuteScript);
+ Register("/tools/now", GetNowIsoString);
}
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/OrthancRestApi.h
--- a/OrthancServer/OrthancRestApi.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/OrthancRestApi.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
@@ -42,11 +42,12 @@
class OrthancRestApi : public RestApi
{
public:
- typedef std::set Modalities;
+ typedef std::set SetOfStrings;
private:
ServerContext& context_;
- Modalities modalities_;
+ SetOfStrings modalities_;
+ SetOfStrings peers_;
public:
OrthancRestApi(ServerContext& context);
@@ -56,9 +57,14 @@
return context_;
}
- Modalities& GetModalities()
+ SetOfStrings& GetModalities()
{
return modalities_;
}
+
+ SetOfStrings& GetPeers()
+ {
+ return peers_;
+ }
};
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerContext.cpp
--- a/OrthancServer/ServerContext.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerContext.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
@@ -33,11 +33,15 @@
#include "ServerContext.h"
#include "../Core/HttpServer/FilesystemHttpSender.h"
+#include "../Core/Lua/LuaFunctionCall.h"
+#include "ServerToolbox.h"
#include
+#include
#define ENABLE_DICOM_CACHE 1
+static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter";
static const size_t DICOM_CACHE_SIZE = 2;
@@ -60,6 +64,7 @@
provider_(*this),
dicomCache_(provider_, DICOM_CACHE_SIZE)
{
+ lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
}
void ServerContext::SetCompressionEnabled(bool enabled)
@@ -83,6 +88,23 @@
const Json::Value& dicomJson,
const std::string& remoteAet)
{
+ // Test if the instance must be filtered out
+ if (lua_.IsExistingFunction(RECEIVED_INSTANCE_FILTER))
+ {
+ Json::Value simplified;
+ SimplifyTags(simplified, dicomJson);
+
+ LuaFunctionCall call(lua_, RECEIVED_INSTANCE_FILTER);
+ call.PushJSON(simplified);
+ call.PushString(remoteAet);
+
+ if (!call.ExecutePredicate())
+ {
+ LOG(INFO) << "An incoming instance has been discarded by the filter";
+ return StoreStatus_FilteredOut;
+ }
+ }
+
if (compressionEnabled_)
{
accessor_.SetCompressionForNextOperations(CompressionType_Zlib);
@@ -109,17 +131,21 @@
switch (status)
{
- case StoreStatus_Success:
- LOG(INFO) << "New instance stored";
- break;
+ case StoreStatus_Success:
+ LOG(INFO) << "New instance stored";
+ break;
+
+ case StoreStatus_AlreadyStored:
+ LOG(INFO) << "Already stored";
+ break;
- case StoreStatus_AlreadyStored:
- LOG(INFO) << "Already stored";
- break;
+ case StoreStatus_Failure:
+ LOG(ERROR) << "Store failure";
+ break;
- case StoreStatus_Failure:
- LOG(ERROR) << "Store failure";
- break;
+ default:
+ // This should never happen
+ break;
}
return status;
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerContext.h
--- a/OrthancServer/ServerContext.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerContext.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,6 +36,7 @@
#include "../Core/FileStorage/CompressedFileStorageAccessor.h"
#include "../Core/FileStorage/FileStorage.h"
#include "../Core/RestApi/RestApiOutput.h"
+#include "../Core/Lua/LuaContext.h"
#include "ServerIndex.h"
#include "FromDcmtkBridge.h"
@@ -70,6 +71,8 @@
DicomCacheProvider provider_;
MemoryCache dicomCache_;
+ LuaContext lua_;
+
public:
ServerContext(const boost::filesystem::path& storagePath,
const boost::filesystem::path& indexPath);
@@ -129,5 +132,10 @@
// TODO IMPLEMENT MULTITHREADING FOR THIS METHOD
ParsedDicomFile& GetDicomFile(const std::string& instancePublicId);
+
+ LuaContext& GetLuaContext()
+ {
+ return lua_;
+ }
};
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerEnumerations.cpp
--- a/OrthancServer/ServerEnumerations.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerEnumerations.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
@@ -32,10 +32,57 @@
#include "ServerEnumerations.h"
#include "../Core/OrthancException.h"
+#include "../Core/EnumerationDictionary.h"
+
+#include
namespace Orthanc
{
- const char* ToString(ResourceType type)
+ static boost::mutex enumerationsMutex_;
+ static Toolbox::EnumerationDictionary dictMetadataType_;
+
+ void InitializeServerEnumerations()
+ {
+ boost::mutex::scoped_lock lock(enumerationsMutex_);
+
+ dictMetadataType_.Add(MetadataType_Instance_IndexInSeries, "IndexInSeries");
+ dictMetadataType_.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate");
+ dictMetadataType_.Add(MetadataType_Instance_RemoteAet, "RemoteAET");
+ dictMetadataType_.Add(MetadataType_Series_ExpectedNumberOfInstances, "ExpectedNumberOfInstances");
+ dictMetadataType_.Add(MetadataType_ModifiedFrom, "ModifiedFrom");
+ dictMetadataType_.Add(MetadataType_AnonymizedFrom, "AnonymizedFrom");
+ dictMetadataType_.Add(MetadataType_LastUpdate, "LastUpdate");
+ }
+
+ void RegisterUserMetadata(int metadata,
+ const std::string name)
+ {
+ boost::mutex::scoped_lock lock(enumerationsMutex_);
+
+ if (metadata < static_cast(MetadataType_StartUser) ||
+ metadata > static_cast(MetadataType_EndUser))
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ dictMetadataType_.Add(static_cast(metadata), name);
+ }
+
+ std::string EnumerationToString(MetadataType type)
+ {
+ // This function MUST return a "std::string" and not "const
+ // char*", as the result is not a static string
+ boost::mutex::scoped_lock lock(enumerationsMutex_);
+ return dictMetadataType_.Translate(type);
+ }
+
+ MetadataType StringToMetadata(const std::string& str)
+ {
+ boost::mutex::scoped_lock lock(enumerationsMutex_);
+ return dictMetadataType_.Translate(str);
+ }
+
+ const char* EnumerationToString(ResourceType type)
{
switch (type)
{
@@ -78,7 +125,7 @@
}
}
- const char* ToString(SeriesStatus status)
+ const char* EnumerationToString(SeriesStatus status)
{
switch (status)
{
@@ -99,7 +146,7 @@
}
}
- const char* ToString(StoreStatus status)
+ const char* EnumerationToString(StoreStatus status)
{
switch (status)
{
@@ -112,13 +159,16 @@
case StoreStatus_Failure:
return "Failure";
+ case StoreStatus_FilteredOut:
+ return "FilteredOut";
+
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
- const char* ToString(ChangeType type)
+ const char* EnumerationToString(ChangeType type)
{
switch (type)
{
@@ -155,6 +205,15 @@
case ChangeType_ModifiedPatient:
return "ModifiedPatient";
+ case ChangeType_StablePatient:
+ return "StablePatient";
+
+ case ChangeType_StableStudy:
+ return "StableStudy";
+
+ case ChangeType_StableSeries:
+ return "StableSeries";
+
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
@@ -197,4 +256,39 @@
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
+
+
+ const char* EnumerationToString(ModalityManufacturer manufacturer)
+ {
+ switch (manufacturer)
+ {
+ case ModalityManufacturer_Generic:
+ return "Generic";
+
+ case ModalityManufacturer_ClearCanvas:
+ return "ClearCanvas";
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
+ ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer)
+ {
+ if (manufacturer == "Generic")
+ {
+ return ModalityManufacturer_Generic;
+ }
+ else if (manufacturer == "ClearCanvas")
+ {
+ return ModalityManufacturer_ClearCanvas;
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ }
+
+
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerEnumerations.h
--- a/OrthancServer/ServerEnumerations.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerEnumerations.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
@@ -47,7 +47,14 @@
{
StoreStatus_Success,
StoreStatus_AlreadyStored,
- StoreStatus_Failure
+ StoreStatus_Failure,
+ StoreStatus_FilteredOut // Removed by NewInstanceFilter
+ };
+
+ enum ModalityManufacturer
+ {
+ ModalityManufacturer_Generic,
+ ModalityManufacturer_ClearCanvas
};
@@ -79,7 +86,12 @@
MetadataType_Instance_RemoteAet = 3,
MetadataType_Series_ExpectedNumberOfInstances = 4,
MetadataType_ModifiedFrom = 5,
- MetadataType_AnonymizedFrom = 6
+ MetadataType_AnonymizedFrom = 6,
+ MetadataType_LastUpdate = 7,
+
+ // Make sure that the value "65535" can be stored into this enumeration
+ MetadataType_StartUser = 1024,
+ MetadataType_EndUser = 65535
};
enum ChangeType
@@ -94,19 +106,35 @@
ChangeType_ModifiedStudy = 8,
ChangeType_ModifiedSeries = 9,
ChangeType_AnonymizedPatient = 10,
- ChangeType_ModifiedPatient = 11
+ ChangeType_ModifiedPatient = 11,
+ ChangeType_StablePatient = 12,
+ ChangeType_StableStudy = 13,
+ ChangeType_StableSeries = 14
};
+ void InitializeServerEnumerations();
+
+ void RegisterUserMetadata(int metadata,
+ const std::string name);
+
std::string GetBasePath(ResourceType type,
const std::string& publicId);
- const char* ToString(ResourceType type);
+ MetadataType StringToMetadata(const std::string& str);
+
+ const char* EnumerationToString(ResourceType type);
- const char* ToString(SeriesStatus status);
+ std::string EnumerationToString(MetadataType type);
+
+ const char* EnumerationToString(SeriesStatus status);
- const char* ToString(StoreStatus status);
+ const char* EnumerationToString(StoreStatus status);
+
+ const char* EnumerationToString(ChangeType type);
- const char* ToString(ChangeType type);
+ const char* EnumerationToString(ModalityManufacturer manufacturer);
+
+ ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer);
ResourceType GetParentResourceType(ResourceType type);
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerIndex.cpp
--- a/OrthancServer/ServerIndex.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerIndex.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
@@ -37,6 +37,7 @@
#endif
#include "EmbeddedResources.h"
+#include "OrthancInitialization.h"
#include "../Core/Toolbox.h"
#include "../Core/Uuid.h"
#include "../Core/DicomFormat/DicomArray.h"
@@ -48,6 +49,8 @@
#include
#include
+static const uint64_t MEGA_BYTES = 1024 * 1024;
+
namespace Orthanc
{
namespace Internals
@@ -185,6 +188,27 @@
};
+ struct ServerIndex::UnstableResourcePayload
+ {
+ Orthanc::ResourceType type_;
+ boost::posix_time::ptime time_;
+
+ UnstableResourcePayload() : type_(Orthanc::ResourceType_Instance)
+ {
+ }
+
+ UnstableResourcePayload(Orthanc::ResourceType type) : type_(type)
+ {
+ time_ = boost::posix_time::second_clock::local_time();
+ }
+
+ unsigned int GetAge() const
+ {
+ return (boost::posix_time::second_clock::local_time() - time_).total_seconds();
+ }
+ };
+
+
bool ServerIndex::DeleteResource(Json::Value& target,
const std::string& uuid,
ResourceType expectedType)
@@ -211,7 +235,7 @@
target["RemainingAncestor"] = Json::Value(Json::objectValue);
target["RemainingAncestor"]["Path"] = GetBasePath(type, uuid);
- target["RemainingAncestor"]["Type"] = ToString(type);
+ target["RemainingAncestor"]["Type"] = EnumerationToString(type);
target["RemainingAncestor"]["ID"] = uuid;
}
else
@@ -225,23 +249,86 @@
}
- static void FlushThread(DatabaseWrapper* db,
- boost::mutex* mutex,
- unsigned int sleep)
+ void ServerIndex::FlushThread(ServerIndex* that)
{
+ unsigned int sleep;
+
+ try
+ {
+ std::string sleepString = that->db_->GetGlobalProperty(GlobalProperty_FlushSleep);
+ sleep = boost::lexical_cast(sleepString);
+ }
+ catch (boost::bad_lexical_cast&)
+ {
+ // By default, wait for 10 seconds before flushing
+ sleep = 10;
+ }
+
LOG(INFO) << "Starting the database flushing thread (sleep = " << sleep << ")";
- while (1)
+ unsigned int count = 0;
+
+ while (!that->done_)
{
- boost::this_thread::sleep(boost::posix_time::seconds(sleep));
- boost::mutex::scoped_lock lock(*mutex);
- db->FlushToDisk();
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+ count++;
+ if (count < sleep)
+ {
+ continue;
+ }
+
+ boost::mutex::scoped_lock lock(that->mutex_);
+ that->db_->FlushToDisk();
+ count = 0;
+ }
+
+ LOG(INFO) << "Stopping the database flushing thread";
+ }
+
+
+ static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db,
+ int64_t series,
+ const DicomMap& dicomSummary)
+ {
+ const DicomValue* value;
+ const DicomValue* value2;
+
+ try
+ {
+ if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL &&
+ (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL)
+ {
+ // Patch for series with temporal positions thanks to Will Ryder
+ int64_t imagesInAcquisition = boost::lexical_cast(value->AsString());
+ int64_t countTemporalPositions = boost::lexical_cast(value2->AsString());
+ std::string expected = boost::lexical_cast(imagesInAcquisition * countTemporalPositions);
+ db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected);
+ }
+
+ else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL &&
+ (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TIME_SLICES)) != NULL)
+ {
+ // Support of Cardio-PET images
+ int64_t numberOfSlices = boost::lexical_cast(value->AsString());
+ int64_t numberOfTimeSlices = boost::lexical_cast(value2->AsString());
+ std::string expected = boost::lexical_cast(numberOfSlices * numberOfTimeSlices);
+ db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected);
+ }
+
+ else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL)
+ {
+ db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString());
+ }
+ }
+ catch (boost::bad_lexical_cast)
+ {
}
}
ServerIndex::ServerIndex(ServerContext& context,
const std::string& dbPath) :
+ done_(false),
maximumStorageSize_(0),
maximumPatients_(0)
{
@@ -272,27 +359,24 @@
// execution of Orthanc
StandaloneRecycling();
- unsigned int sleep;
- try
- {
- std::string sleepString = db_->GetGlobalProperty(GlobalProperty_FlushSleep);
- sleep = boost::lexical_cast(sleepString);
- }
- catch (boost::bad_lexical_cast&)
- {
- // By default, wait for 10 seconds before flushing
- sleep = 10;
- }
-
- flushThread_ = boost::thread(FlushThread, db_.get(), &mutex_, sleep);
+ flushThread_ = boost::thread(FlushThread, this);
+ unstableResourcesMonitorThread_ = boost::thread(UnstableResourcesMonitorThread, this);
}
ServerIndex::~ServerIndex()
{
- LOG(INFO) << "Stopping the database flushing thread";
- /*flushThread_.terminate();
- flushThread_.join();*/
+ done_ = true;
+
+ if (flushThread_.joinable())
+ {
+ flushThread_.join();
+ }
+
+ if (unstableResourcesMonitorThread_.joinable())
+ {
+ unstableResourcesMonitorThread_.join();
+ }
}
@@ -309,15 +393,15 @@
{
Transaction t(*this);
- int64_t patient, study, series, instance;
- ResourceType type;
- bool isNewSeries = false;
-
// Do nothing if the instance already exists
- if (db_->LookupResource(hasher.HashInstance(), patient, type))
{
- assert(type == ResourceType_Instance);
- return StoreStatus_AlreadyStored;
+ ResourceType type;
+ int64_t tmp;
+ if (db_->LookupResource(hasher.HashInstance(), tmp, type))
+ {
+ assert(type == ResourceType_Instance);
+ return StoreStatus_AlreadyStored;
+ }
}
// Ensure there is enough room in the storage for the new instance
@@ -331,56 +415,101 @@
Recycle(instanceSize, hasher.HashPatient());
// Create the instance
- instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance);
+ int64_t instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance);
DicomMap dicom;
dicomSummary.ExtractInstanceInformation(dicom);
db_->SetMainDicomTags(instance, dicom);
- // Create the patient/study/series/instance hierarchy
- if (!db_->LookupResource(hasher.HashSeries(), series, type))
+ // Detect up to which level the patient/study/series/instance
+ // hierarchy must be created
+ int64_t patient = -1, study = -1, series = -1;
+ bool isNewPatient = false;
+ bool isNewStudy = false;
+ bool isNewSeries = false;
+
{
- // This is a new series
- isNewSeries = true;
+ ResourceType dummy;
+
+ if (db_->LookupResource(hasher.HashSeries(), series, dummy))
+ {
+ assert(dummy == ResourceType_Series);
+ // The patient, the study and the series already exist
+
+ bool ok = (db_->LookupResource(hasher.HashPatient(), patient, dummy) &&
+ db_->LookupResource(hasher.HashStudy(), study, dummy));
+ assert(ok);
+ }
+ else if (db_->LookupResource(hasher.HashStudy(), study, dummy))
+ {
+ assert(dummy == ResourceType_Study);
+
+ // New series: The patient and the study already exist
+ isNewSeries = true;
+
+ bool ok = db_->LookupResource(hasher.HashPatient(), patient, dummy);
+ assert(ok);
+ }
+ else if (db_->LookupResource(hasher.HashPatient(), patient, dummy))
+ {
+ assert(dummy == ResourceType_Patient);
+
+ // New study and series: The patient already exist
+ isNewStudy = true;
+ isNewSeries = true;
+ }
+ else
+ {
+ // New patient, study and series: Nothing exists
+ isNewPatient = true;
+ isNewStudy = true;
+ isNewSeries = true;
+ }
+ }
+
+ // Create the series if needed
+ if (isNewSeries)
+ {
series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series);
dicomSummary.ExtractSeriesInformation(dicom);
db_->SetMainDicomTags(series, dicom);
- db_->AttachChild(series, instance);
+ }
- if (!db_->LookupResource(hasher.HashStudy(), study, type))
- {
- // This is a new study
- study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study);
- dicomSummary.ExtractStudyInformation(dicom);
- db_->SetMainDicomTags(study, dicom);
- db_->AttachChild(study, series);
+ // Create the study if needed
+ if (isNewStudy)
+ {
+ study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study);
+ dicomSummary.ExtractStudyInformation(dicom);
+ db_->SetMainDicomTags(study, dicom);
+ }
+
+ // Create the patient if needed
+ if (isNewPatient)
+ {
+ patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient);
+ dicomSummary.ExtractPatientInformation(dicom);
+ db_->SetMainDicomTags(patient, dicom);
+ }
- if (!db_->LookupResource(hasher.HashPatient(), patient, type))
- {
- // This is a new patient
- patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient);
- dicomSummary.ExtractPatientInformation(dicom);
- db_->SetMainDicomTags(patient, dicom);
- db_->AttachChild(patient, study);
- }
- else
- {
- assert(type == ResourceType_Patient);
- db_->AttachChild(patient, study);
- }
- }
- else
- {
- assert(type == ResourceType_Study);
- db_->AttachChild(study, series);
- }
+ // Create the parent-to-child links
+ db_->AttachChild(series, instance);
+
+ if (isNewSeries)
+ {
+ db_->AttachChild(study, series);
}
- else
+
+ if (isNewStudy)
{
- assert(type == ResourceType_Series);
- db_->AttachChild(series, instance);
+ db_->AttachChild(patient, study);
}
+ // Sanity checks
+ assert(patient != -1);
+ assert(study != -1);
+ assert(series != -1);
+ assert(instance != -1);
+
// Attach the files to the newly created instance
for (Attachments::const_iterator it = attachments.begin();
it != attachments.end(); it++)
@@ -389,7 +518,11 @@
}
// Attach the metadata
- db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString());
+ std::string now = Toolbox::GetNowIsoString();
+ db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now);
+ db_->SetMetadata(series, MetadataType_LastUpdate, now);
+ db_->SetMetadata(study, MetadataType_LastUpdate, now);
+ db_->SetMetadata(patient, MetadataType_LastUpdate, now);
db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet);
const DicomValue* value;
@@ -401,12 +534,7 @@
if (isNewSeries)
{
- if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL ||
- (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL ||
- (value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL)
- {
- db_->SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString());
- }
+ ComputeExpectedNumberOfInstances(*db_, series, dicomSummary);
}
// Check whether the series of this new instance is now completed
@@ -416,6 +544,11 @@
db_->LogChange(ChangeType_CompletedSeries, series, ResourceType_Series);
}
+ // Mark the parent resources of this instance as unstable
+ MarkAsUnstable(patient, ResourceType_Patient);
+ MarkAsUnstable(study, ResourceType_Study);
+ MarkAsUnstable(series, ResourceType_Series);
+
t.Commit(instanceSize);
return StoreStatus_Success;
@@ -432,18 +565,16 @@
void ServerIndex::ComputeStatistics(Json::Value& target)
{
- static const uint64_t MB = 1024 * 1024;
-
boost::mutex::scoped_lock lock(mutex_);
target = Json::objectValue;
uint64_t cs = currentStorageSize_;
assert(cs == db_->GetTotalCompressedSize());
uint64_t us = db_->GetTotalUncompressedSize();
- target["TotalDiskSpace"] = boost::lexical_cast(cs);
+ target["TotalDiskSize"] = boost::lexical_cast(cs);
target["TotalUncompressedSize"] = boost::lexical_cast(us);
- target["TotalDiskSpaceMB"] = boost::lexical_cast(cs / MB);
- target["TotalUncompressedSizeMB"] = boost::lexical_cast(us / MB);
+ target["TotalDiskSizeMB"] = boost::lexical_cast(cs / MEGA_BYTES);
+ target["TotalUncompressedSizeMB"] = boost::lexical_cast(us / MEGA_BYTES);
target["CountPatients"] = static_cast(db_->GetResourceCount(ResourceType_Patient));
target["CountStudies"] = static_cast(db_->GetResourceCount(ResourceType_Study));
@@ -622,7 +753,7 @@
case ResourceType_Series:
{
result["Type"] = "Series";
- result["Status"] = ToString(GetSeriesStatus(id));
+ result["Status"] = EnumerationToString(GetSeriesStatus(id));
int i;
if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances))
@@ -673,6 +804,13 @@
if (tmp.size() != 0)
result["ModifiedFrom"] = tmp;
+ if (type == ResourceType_Patient ||
+ type == ResourceType_Study ||
+ type == ResourceType_Series)
+ {
+ result["IsStable"] = !unstableResources_.Contains(id);
+ }
+
return true;
}
@@ -918,7 +1056,7 @@
}
else
{
- LOG(WARNING) << "At most " << (size / (1024 * 1024)) << "MB will be used for the storage area";
+ LOG(WARNING) << "At most " << (size / MEGA_BYTES) << "MB will be used for the storage area";
}
StandaloneRecycling();
@@ -1040,6 +1178,23 @@
db_->SetMetadata(id, type, value);
}
+
+ void ServerIndex::DeleteMetadata(const std::string& publicId,
+ MetadataType type)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ ResourceType rtype;
+ int64_t id;
+ if (!db_->LookupResource(publicId, id, rtype))
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
+ }
+
+ db_->DeleteMetadata(id, type);
+ }
+
+
bool ServerIndex::LookupMetadata(std::string& target,
const std::string& publicId,
MetadataType type)
@@ -1057,6 +1212,22 @@
}
+ bool ServerIndex::ListAvailableMetadata(std::list& target,
+ const std::string& publicId)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ ResourceType rtype;
+ int64_t id;
+ if (!db_->LookupResource(publicId, id, rtype))
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
+ }
+
+ return db_->ListAvailableMetadata(target, id);
+ }
+
+
bool ServerIndex::LookupParent(std::string& target,
const std::string& publicId)
{
@@ -1115,4 +1286,224 @@
transaction->Commit();
}
+
+
+ void ServerIndex::DeleteChanges()
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ db_->ClearTable("Changes");
+ }
+
+ void ServerIndex::DeleteExportedResources()
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+ db_->ClearTable("ExportedResources");
+ }
+
+
+ void ServerIndex::GetStatistics(Json::Value& target,
+ const std::string& publicId)
+ {
+ boost::mutex::scoped_lock lock(mutex_);
+
+ ResourceType type;
+ int64_t top;
+ if (!db_->LookupResource(publicId, top, type))
+ {
+ throw OrthancException(ErrorCode_UnknownResource);
+ }
+
+ std::stack toExplore;
+ toExplore.push(top);
+
+ int countInstances = 0;
+ int countSeries = 0;
+ int countStudies = 0;
+ uint64_t compressedSize = 0;
+ uint64_t uncompressedSize = 0;
+
+ while (!toExplore.empty())
+ {
+ // Get the internal ID of the current resource
+ int64_t resource = toExplore.top();
+ toExplore.pop();
+
+ ResourceType thisType = db_->GetResourceType(resource);
+
+ if (thisType == ResourceType_Instance)
+ {
+ std::list f;
+ db_->ListAvailableAttachments(f, resource);
+
+ for (std::list::const_iterator
+ it = f.begin(); it != f.end(); it++)
+ {
+ FileInfo attachment;
+ if (db_->LookupAttachment(attachment, resource, *it))
+ {
+ compressedSize += attachment.GetCompressedSize();
+ uncompressedSize += attachment.GetUncompressedSize();
+ }
+ }
+
+ countInstances++;
+ }
+ else
+ {
+ switch (thisType)
+ {
+ case ResourceType_Study:
+ countStudies++;
+ break;
+
+ case ResourceType_Series:
+ countSeries++;
+ break;
+
+ default:
+ break;
+ }
+
+ // Tag all the children of this resource as to be explored
+ std::list tmp;
+ db_->GetChildrenInternalId(tmp, resource);
+ for (std::list::const_iterator
+ it = tmp.begin(); it != tmp.end(); it++)
+ {
+ toExplore.push(*it);
+ }
+ }
+ }
+
+ target = Json::objectValue;
+ target["DiskSize"] = boost::lexical_cast(compressedSize);
+ target["DiskSizeMB"] = boost::lexical_cast(compressedSize / MEGA_BYTES);
+ target["UncompressedSize"] = boost::lexical_cast(uncompressedSize);
+ target["UncompressedSizeMB"] = boost::lexical_cast(uncompressedSize / MEGA_BYTES);
+
+ switch (type)
+ {
+ // Do NOT add "break" below this point!
+ case ResourceType_Patient:
+ target["CountStudies"] = countStudies;
+
+ case ResourceType_Study:
+ target["CountSeries"] = countSeries;
+
+ case ResourceType_Series:
+ target["CountInstances"] = countInstances;
+
+ case ResourceType_Instance:
+ default:
+ break;
+ }
+ }
+
+
+ void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that)
+ {
+ int stableAge = GetGlobalIntegerParameter("StableAge", 60);
+ if (stableAge <= 0)
+ {
+ stableAge = 60;
+ }
+
+ LOG(INFO) << "Starting the monitor for stable resources (stable age = " << stableAge << ")";
+
+ while (!that->done_)
+ {
+ // Check for stable resources each second
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+
+ boost::mutex::scoped_lock lock(that->mutex_);
+
+ while (!that->unstableResources_.IsEmpty() &&
+ that->unstableResources_.GetOldestPayload().GetAge() > static_cast(stableAge))
+ {
+ // This DICOM resource has not received any new instance for
+ // some time. It can be considered as stable.
+
+ UnstableResourcePayload payload;
+ int64_t id = that->unstableResources_.RemoveOldest(payload);
+
+ // Ensure that the resource is still existing before logging the change
+ if (that->db_->IsExistingResource(id))
+ {
+ switch (payload.type_)
+ {
+ case Orthanc::ResourceType_Patient:
+ that->db_->LogChange(ChangeType_StablePatient, id, ResourceType_Patient);
+ break;
+
+ case Orthanc::ResourceType_Study:
+ that->db_->LogChange(ChangeType_StableStudy, id, ResourceType_Study);
+ break;
+
+ case Orthanc::ResourceType_Series:
+ that->db_->LogChange(ChangeType_StableSeries, id, ResourceType_Series);
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_InternalError);
+ }
+
+ //LOG(INFO) << "Stable resource: " << EnumerationToString(payload.type_) << " " << id;
+ }
+ }
+ }
+
+ LOG(INFO) << "Closing the monitor thread for stable resources";
+ }
+
+
+ void ServerIndex::MarkAsUnstable(int64_t id,
+ Orthanc::ResourceType type)
+ {
+ // WARNING: Before calling this method, "mutex_" must be locked.
+
+ assert(type == Orthanc::ResourceType_Patient ||
+ type == Orthanc::ResourceType_Study ||
+ type == Orthanc::ResourceType_Series);
+
+ unstableResources_.AddOrMakeMostRecent(id, type);
+ //LOG(INFO) << "Unstable resource: " << EnumerationToString(type) << " " << id;
+ }
+
+
+
+ void ServerIndex::LookupTagValue(std::list& result,
+ DicomTag tag,
+ const std::string& value)
+ {
+ result.clear();
+
+ boost::mutex::scoped_lock lock(mutex_);
+
+ std::list id;
+ db_->LookupTagValue(id, tag, value);
+
+ for (std::list::const_iterator
+ it = id.begin(); it != id.end(); it++)
+ {
+ result.push_back(db_->GetPublicId(*it));
+ }
+ }
+
+
+ void ServerIndex::LookupTagValue(std::list& result,
+ const std::string& value)
+ {
+ result.clear();
+
+ boost::mutex::scoped_lock lock(mutex_);
+
+ std::list id;
+ db_->LookupTagValue(id, value);
+
+ for (std::list::const_iterator
+ it = id.begin(); it != id.end(); it++)
+ {
+ result.push_back(db_->GetPublicId(*it));
+ }
+ }
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerIndex.h
--- a/OrthancServer/ServerIndex.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerIndex.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
@@ -34,6 +34,7 @@
#include
#include
+#include "../Core/Cache/LeastRecentlyUsedIndex.h"
#include "../Core/SQLite/Connection.h"
#include "../Core/DicomFormat/DicomMap.h"
#include "../Core/DicomFormat/DicomInstanceHasher.h"
@@ -55,17 +56,25 @@
{
private:
class Transaction;
+ struct UnstableResourcePayload;
+ bool done_;
boost::mutex mutex_;
boost::thread flushThread_;
+ boost::thread unstableResourcesMonitorThread_;
std::auto_ptr listener_;
std::auto_ptr db_;
+ LeastRecentlyUsedIndex unstableResources_;
uint64_t currentStorageSize_;
uint64_t maximumStorageSize_;
unsigned int maximumPatients_;
+ static void FlushThread(ServerIndex* that);
+
+ static void UnstableResourcesMonitorThread(ServerIndex* that);
+
void MainDicomTagsToJson(Json::Value& result,
int64_t resourceId);
@@ -78,6 +87,9 @@
void StandaloneRecycling();
+ void MarkAsUnstable(int64_t id,
+ Orthanc::ResourceType type);
+
public:
typedef std::list Attachments;
@@ -150,10 +162,16 @@
MetadataType type,
const std::string& value);
+ void DeleteMetadata(const std::string& publicId,
+ MetadataType type);
+
bool LookupMetadata(std::string& target,
const std::string& publicId,
MetadataType type);
+ bool ListAvailableMetadata(std::list& target,
+ const std::string& publicId);
+
bool LookupParent(std::string& target,
const std::string& publicId);
@@ -161,5 +179,19 @@
void LogChange(ChangeType changeType,
const std::string& publicId);
+
+ void DeleteChanges();
+
+ void DeleteExportedResources();
+
+ void GetStatistics(Json::Value& target,
+ const std::string& publicId);
+
+ void LookupTagValue(std::list& result,
+ DicomTag tag,
+ const std::string& value);
+
+ void LookupTagValue(std::list& result,
+ const std::string& value);
};
}
diff -r f6e61e329bbf -r b26a7c397c34 OrthancServer/ServerToolbox.cpp
--- a/OrthancServer/ServerToolbox.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerToolbox.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 OrthancServer/ServerToolbox.h
--- a/OrthancServer/ServerToolbox.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ServerToolbox.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 OrthancServer/ToDcmtkBridge.cpp
--- a/OrthancServer/ToDcmtkBridge.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ToDcmtkBridge.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 OrthancServer/ToDcmtkBridge.h
--- a/OrthancServer/ToDcmtkBridge.h Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/ToDcmtkBridge.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 OrthancServer/main.cpp
--- a/OrthancServer/main.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/OrthancServer/main.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,7 @@
#include "../Core/HttpServer/EmbeddedResourceHttpHandler.h"
#include "../Core/HttpServer/FilesystemHttpHandler.h"
-#include "../Core/HttpServer/MongooseServer.h"
+#include "../Core/Lua/LuaFunctionCall.h"
#include "../Core/DicomFormat/DicomArray.h"
#include "DicomProtocol/DicomServer.h"
#include "OrthancInitialization.h"
@@ -51,11 +51,11 @@
class MyStoreRequestHandler : public IStoreRequestHandler
{
private:
- ServerContext& context_;
+ ServerContext& server_;
public:
MyStoreRequestHandler(ServerContext& context) :
- context_(context)
+ server_(context)
{
}
@@ -66,7 +66,7 @@
{
if (dicomFile.size() > 0)
{
- context_.Store(&dicomFile[0], dicomFile.size(), dicomSummary, dicomJson, remoteAet);
+ server_.Store(&dicomFile[0], dicomFile.size(), dicomSummary, dicomJson, remoteAet);
}
}
};
@@ -148,6 +148,66 @@
};
+class MyIncomingHttpRequestFilter : public IIncomingHttpRequestFilter
+{
+private:
+ ServerContext& context_;
+
+public:
+ MyIncomingHttpRequestFilter(ServerContext& context) : context_(context)
+ {
+ }
+
+ virtual bool IsAllowed(HttpMethod method,
+ const char* uri,
+ const char* ip,
+ const char* username) const
+ {
+ static const char* HTTP_FILTER = "IncomingHttpRequestFilter";
+
+ // Test if the instance must be filtered out
+ if (context_.GetLuaContext().IsExistingFunction(HTTP_FILTER))
+ {
+ LuaFunctionCall call(context_.GetLuaContext(), HTTP_FILTER);
+
+ switch (method)
+ {
+ case HttpMethod_Get:
+ call.PushString("GET");
+ break;
+
+ case HttpMethod_Put:
+ call.PushString("PUT");
+ break;
+
+ case HttpMethod_Post:
+ call.PushString("POST");
+ break;
+
+ case HttpMethod_Delete:
+ call.PushString("DELETE");
+ break;
+
+ default:
+ return true;
+ }
+
+ call.PushString(uri);
+ call.PushString(ip);
+ call.PushString(username);
+
+ if (!call.ExecutePredicate())
+ {
+ LOG(INFO) << "An incoming HTTP request has been discarded by the filter";
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+
void PrintHelp(char* path)
{
std::cout
@@ -264,8 +324,10 @@
OrthancInitialize();
}
- boost::filesystem::path storageDirectory = GetGlobalStringParameter("StorageDirectory", "OrthancStorage");
- boost::filesystem::path indexDirectory = GetGlobalStringParameter("IndexDirectory", storageDirectory.string());
+ std::string storageDirectoryStr = GetGlobalStringParameter("StorageDirectory", "OrthancStorage");
+ boost::filesystem::path storageDirectory = InterpretStringParameterAsPath(storageDirectoryStr);
+ boost::filesystem::path indexDirectory =
+ InterpretStringParameterAsPath(GetGlobalStringParameter("IndexDirectory", storageDirectoryStr));
ServerContext context(storageDirectory, indexDirectory);
LOG(WARNING) << "Storage directory: " << storageDirectory;
@@ -273,6 +335,19 @@
context.SetCompressionEnabled(GetGlobalBoolParameter("StorageCompression", false));
+ std::list luaScripts;
+ GetGlobalListOfStringsParameter(luaScripts, "LuaScripts");
+ for (std::list::const_iterator
+ it = luaScripts.begin(); it != luaScripts.end(); it++)
+ {
+ std::string path = InterpretStringParameterAsPath(*it);
+ LOG(WARNING) << "Installing the Lua scripts from: " << path;
+ std::string script;
+ Toolbox::ReadFile(script, path);
+ context.GetLuaContext().Execute(script);
+ }
+
+
try
{
context.GetIndex().SetMaximumPatientCount(GetGlobalIntegerParameter("MaximumPatientCount", 0));
@@ -306,16 +381,19 @@
dicomServer.SetApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC"));
// HTTP server
+ MyIncomingHttpRequestFilter httpFilter(context);
MongooseServer httpServer;
httpServer.SetPortNumber(GetGlobalIntegerParameter("HttpPort", 8042));
httpServer.SetRemoteAccessAllowed(GetGlobalBoolParameter("RemoteAccessAllowed", false));
+ httpServer.SetIncomingHttpRequestFilter(httpFilter);
httpServer.SetAuthenticationEnabled(GetGlobalBoolParameter("AuthenticationEnabled", false));
SetupRegisteredUsers(httpServer);
if (GetGlobalBoolParameter("SslEnabled", false))
{
- std::string certificate = GetGlobalStringParameter("SslCertificate", "certificate.pem");
+ std::string certificate =
+ InterpretStringParameterAsPath(GetGlobalStringParameter("SslCertificate", "certificate.pem"));
httpServer.SetSslEnabled(true);
httpServer.SetSslCertificate(certificate.c_str());
}
diff -r f6e61e329bbf -r b26a7c397c34 README
--- a/README Mon Apr 29 12:48:10 2013 +0200
+++ b/README Wed Sep 18 15:27:07 2013 +0200
@@ -41,12 +41,18 @@
exception:
http://people.gnome.org/~markmc/openssl-and-the-gpl.html
-Because Orthanc uses the Software-as-a-Service paradigm, commercial
-products are allowed to access the Orthanc REST services and to bundle
-Orthanc in an commercial aggregate.
+We also kindly require scientific works and clinical studies that make
+use of Orthanc to cite Orthanc in their associated
+publications. Similarly, we require open-source and closed-source
+products that make use of Orthanc to warn us about this use. You can
+cite our work using the following BibTeX entry:
-We also kindly require scientific works and clinical studies that make
-use of Orthanc to cite Orthanc in their associated publications.
+@inproceedings{Jodogne:ISBI2013,
+ author = "Jodogne, S. and Bernard, C. and Devillers, M. and Lenaerts, E. and Coucke, P.",
+ title = "Orthanc -- {A} Lightweight, {REST}ful {DICOM} Server for Healthcare and Medical Research",
+ booktitle = "Proc. of the International Symposium on Biomedical Imaging ({ISBI})",
+ year = "2013",
+}
Licensing of special directories
@@ -54,10 +60,6 @@
The following directories have separate licensing terms:
-* The files of the "OrthancCppClient/" directory are licensed under
- the MIT license, which allows commercial products to statically link
- against the C++ client library.
-
* The file of the "Core/SQLite/" directory are licensed under the
3-clause BSD license, as they are derived from the Chromium project.
@@ -68,7 +70,7 @@
This archive contains the following directories:
* Core/ - The core C++ classes (independent of DCMTK)
-* OrthancCppClient/ - Code of the C++ client (under MIT license)
+* OrthancCppClient/ - Code of the C++ client
* OrthancExplorer/ - Code of the Web application (HTML5/Javascript)
* OrthancServer/ - Code of the Orthanc server (depends on DCMTK)
* Resources/ - Scripts, resources for building third-party code
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/BoostConfiguration.cmake
--- a/Resources/CMake/BoostConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/BoostConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -39,9 +39,17 @@
if (BOOST_STATIC)
- SET(BOOST_NAME boost_1_49_0)
+ # Parameters for Boost 1.54.0
+ SET(BOOST_NAME boost_1_54_0)
+ SET(BOOST_MD5 "cee688c35a9c7775b7305587e782e3f5")
+ SET(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src")
+
SET(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
- DownloadPackage("http://switch.dl.sourceforge.net/project/boost/boost/1.49.0/${BOOST_NAME}.tar.gz" "${BOOST_SOURCES_DIR}" "${BOOST_PRELOADED}" "${BOOST_NAME}/boost ${BOOST_NAME}/libs/thread/src ${BOOST_NAME}/libs/system/src ${BOOST_NAME}/libs/filesystem/v3/src ${BOOST_NAME}/libs/locale/src ${BOOST_NAME}/libs/date_time/src")
+ DownloadPackage(
+ "${BOOST_MD5}"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_bcpdigest.tar.gz"
+ "${BOOST_SOURCES_DIR}"
+ )
set(BOOST_SOURCES)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@@ -62,7 +70,7 @@
${BOOST_SOURCES_DIR}/libs/thread/src/win32/tss_dll.cpp
${BOOST_SOURCES_DIR}/libs/thread/src/win32/thread.cpp
${BOOST_SOURCES_DIR}/libs/thread/src/win32/tss_pe.cpp
- ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/windows_file_codecvt.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/windows_file_codecvt.cpp
)
add_definitions(
-DBOOST_LOCALE_WITH_WCONV=1
@@ -73,10 +81,10 @@
list(APPEND BOOST_SOURCES
${BOOST_SOURCES_DIR}/libs/date_time/src/gregorian/greg_month.cpp
- ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/codecvt_error_category.cpp
- ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/operations.cpp
- ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/path.cpp
- ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/path_traits.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/codecvt_error_category.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/operations.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/path.cpp
+ ${BOOST_FILESYSTEM_SOURCES_DIR}/path_traits.cpp
${BOOST_SOURCES_DIR}/libs/locale/src/encoding/codepage.cpp
${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/BoostConfiguration.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/BoostConfiguration.sh Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -e
+set -u
+
+## Starting with version 0.6.2, Orthanc is shipped with a subset of the
+## Boost libraries that is generated with the BCP tool:
+##
+## http://www.boost.org/doc/libs/1_54_0/tools/bcp/doc/html/index.html
+##
+## This script generates this subset.
+
+rm -rf /tmp/boost_1_54_0
+rm -rf /tmp/bcp/boost_1_54_0
+
+cd /tmp
+echo "Uncompressing the source of Boost 1.54.0..."
+tar xfz boost_1_54_0.tar.gz
+
+echo "Generating the subset..."
+mkdir -p /tmp/bcp/boost_1_54_0
+bcp --boost=/tmp/boost_1_54_0 thread system locale date_time filesystem math/special_functions algorithm /tmp/bcp/boost_1_54_0
+cd /tmp/bcp
+
+echo "Compressing the subset..."
+tar cfz boost_1_54_0_bcpdigest.tar.gz boost_1_54_0
+ls -l boost_1_54_0_bcpdigest.tar.gz
+md5sum boost_1_54_0_bcpdigest.tar.gz
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/Compiler.cmake
--- a/Resources/CMake/Compiler.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/Compiler.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -29,28 +29,6 @@
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
- if (DEBIAN_FORCE_HARDENING)
- execute_process(
- COMMAND dpkg-buildflags --get CPPFLAGS
- OUTPUT_VARIABLE DEBIAN_CPP_FLAGS
- OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(
- COMMAND dpkg-buildflags --get CFLAGS
- OUTPUT_VARIABLE DEBIAN_C_FLAGS
- OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(
- COMMAND dpkg-buildflags --get CXXFLAGS
- OUTPUT_VARIABLE DEBIAN_CXX_FLAGS
- OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(
- COMMAND dpkg-buildflags --get LDFLAGS
- OUTPUT_VARIABLE DEBIAN_LD_FLAGS
- OUTPUT_STRIP_TRAILING_WHITESPACE)
-
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEBIAN_C_FLAGS} ${DEBIAN_CPP_FLAGS}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEBIAN_CXX_FLAGS} ${DEBIAN_CPP_FLAGS}")
- endif()
-
if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward -fpermissive")
endif()
@@ -59,9 +37,9 @@
-D_LARGEFILE64_SOURCE=1
-D_FILE_OFFSET_BITS=64
)
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed ${DEBIAN_LD_FLAGS}")
- set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined ${DEBIAN_LD_FLAGS}")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined ${DEBIAN_LD_FLAGS}")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
# Remove the "-rdynamic" option
# http://www.mail-archive.com/cmake@cmake.org/msg08837.html
@@ -74,6 +52,12 @@
-D_CRT_SECURE_NO_WARNINGS=1
)
link_libraries(rpcrt4 ws2_32)
+
+ if (${CMAKE_COMPILER_IS_GNUCXX})
+ # This is a patch for MinGW64
+ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+ endif()
+
endif()
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/DcmtkConfiguration.cmake
--- a/Resources/CMake/DcmtkConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/DcmtkConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,7 +1,12 @@
+add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR}")
+
if (${STATIC_BUILD})
SET(DCMTK_VERSION_NUMBER 360)
SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
- DownloadPackage("ftp://dicom.offis.de/pub/dicom/offis/software/dcmtk/dcmtk360/dcmtk-3.6.0.zip" "${DCMTK_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "219ad631b82031806147e4abbfba4fa4"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip"
+ "${DCMTK_SOURCES_DIR}")
IF(CMAKE_CROSSCOMPILING)
SET(C_CHAR_UNSIGNED 1 CACHE INTERNAL "Whether char is unsigned.")
@@ -42,6 +47,15 @@
list(REMOVE_ITEM DCMTK_SOURCES
${DCMTK_SOURCES_DIR}/oflog/libsrc/unixsock.cc
)
+
+ if (${CMAKE_COMPILER_IS_GNUCXX})
+ # This is a patch for MinGW64
+ execute_process(
+ COMMAND patch -p0 -i ${CMAKE_SOURCE_DIR}/Resources/Patches/dcmtk-mingw64.patch
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ )
+ endif()
+
endif()
list(REMOVE_ITEM DCMTK_SOURCES
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/DownloadPackage.cmake
--- a/Resources/CMake/DownloadPackage.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/DownloadPackage.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -10,20 +10,16 @@
endmacro()
-macro(DownloadPackage Url TargetDirectory PreloadedVariable UncompressArguments)
+macro(DownloadPackage MD5 Url TargetDirectory)
if (NOT IS_DIRECTORY "${TargetDirectory}")
GetUrlFilename(TMP_FILENAME "${Url}")
- if ("${PreloadedVariable}" STREQUAL "")
- set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}")
- if (NOT EXISTS "${TMP_PATH}")
- message("Downloading ${Url}")
- file(DOWNLOAD "${Url}" "${TMP_PATH}" SHOW_PROGRESS)
- else()
- message("Using local copy of ${Url}")
- endif()
+
+ set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}")
+ if (NOT EXISTS "${TMP_PATH}")
+ message("Downloading ${Url}")
+ file(DOWNLOAD "${Url}" "${TMP_PATH}" SHOW_PROGRESS EXPECTED_MD5 "${MD5}")
else()
- message("Using preloaded archive ${PreloadedVariable} for ${Url}")
- set(TMP_PATH "${PreloadedVariable}")
+ message("Using local copy of ${Url}")
endif()
GetUrlExtension(TMP_EXTENSION "${Url}")
@@ -48,37 +44,18 @@
message(FATAL_ERROR "Error while running the uncompression tool")
endif()
- set(ARGS ${UncompressArguments})
- SEPARATE_ARGUMENTS(ARGS)
- list(LENGTH ARGS TMP_LENGTH)
-
if ("${TMP_EXTENSION}" STREQUAL "tgz")
string(REGEX REPLACE ".tgz$" ".tar" TMP_FILENAME2 "${TMP_FILENAME}")
else()
string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}")
endif()
- if (TMP_LENGTH EQUAL 0)
- execute_process(
- COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_FILENAME2}
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- RESULT_VARIABLE Failure
- OUTPUT_QUIET
- )
- else()
- foreach(SUBDIR ${ARGS})
- execute_process(
- COMMAND ${ZIP_EXECUTABLE} x -y "-i!${SUBDIR}" "${TMP_FILENAME2}"
- WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
- RESULT_VARIABLE Failure
- OUTPUT_QUIET
- )
-
- if (Failure)
- message(FATAL_ERROR "Error while running the uncompression tool")
- endif()
- endforeach()
- endif()
+ execute_process(
+ COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_FILENAME2}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ RESULT_VARIABLE Failure
+ OUTPUT_QUIET
+ )
elseif ("${TMP_EXTENSION}" STREQUAL "zip")
execute_process(
COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_PATH}
@@ -93,20 +70,20 @@
else()
if ("${TMP_EXTENSION}" STREQUAL "zip")
execute_process(
- COMMAND sh -c "unzip -q ${TMP_PATH} ${UncompressArguments}"
+ COMMAND sh -c "unzip -q ${TMP_PATH}"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
elseif (("${TMP_EXTENSION}" STREQUAL "gz") OR ("${TMP_EXTENSION}" STREQUAL "tgz"))
- #message("tar xvfz ${TMP_PATH} ${UncompressArguments}")
+ #message("tar xvfz ${TMP_PATH}")
execute_process(
- COMMAND sh -c "tar xfz ${TMP_PATH} ${UncompressArguments}"
+ COMMAND sh -c "tar xfz ${TMP_PATH}"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
elseif ("${TMP_EXTENSION}" STREQUAL "bz2")
execute_process(
- COMMAND sh -c "tar xfj ${TMP_PATH} ${UncompressArguments}"
+ COMMAND sh -c "tar xfj ${TMP_PATH}"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE Failure
)
@@ -124,4 +101,3 @@
endif()
endif()
endmacro()
-
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/GoogleLogConfiguration.cmake
--- a/Resources/CMake/GoogleLogConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/GoogleLogConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (STATIC_BUILD OR NOT USE_DYNAMIC_GOOGLE_LOG)
SET(GOOGLE_LOG_SOURCES_DIR ${CMAKE_BINARY_DIR}/glog-0.3.2)
- DownloadPackage("http://google-glog.googlecode.com/files/glog-0.3.2.tar.gz" "${GOOGLE_LOG_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "897fbff90d91ea2b6d6e78c8cea641cc"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/glog-0.3.2.tar.gz"
+ "${GOOGLE_LOG_SOURCES_DIR}")
set(GOOGLE_LOG_HEADERS
${GOOGLE_LOG_SOURCES_DIR}/src/glog/logging.h
@@ -94,8 +97,16 @@
-DNO_FRAME_POINTER=1
-DGOOGLE_GLOG_DLL_DECL=
)
+
+ if (${CMAKE_COMPILER_IS_GNUCXX})
+ # This is a patch for MinGW64
+ add_definitions(-D_TIME_H__S=1)
+ endif()
+
endif()
+
+
add_library(GoogleLog STATIC ${GOOGLE_LOG_SOURCES})
link_libraries(GoogleLog)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/GoogleTestConfiguration.cmake
--- a/Resources/CMake/GoogleTestConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/GoogleTestConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -9,7 +9,10 @@
elseif (STATIC_BUILD OR NOT USE_DYNAMIC_GOOGLE_TEST)
SET(GTEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.6.0)
- DownloadPackage("http://googletest.googlecode.com/files/gtest-1.6.0.zip" "${GTEST_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "4577b49f2973c90bf9ba69aa8166b786"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/gtest-1.6.0.zip"
+ "${GTEST_SOURCES_DIR}")
include_directories(
${GTEST_SOURCES_DIR}/include
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/JsonCppConfiguration.cmake
--- a/Resources/CMake/JsonCppConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/JsonCppConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -8,8 +8,11 @@
link_libraries(jsoncpp)
else()
- SET(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-src-0.5.0)
- DownloadPackage("http://downloads.sourceforge.net/project/jsoncpp/jsoncpp/0.5.0/jsoncpp-src-0.5.0.tar.gz" "${JSONCPP_SOURCES_DIR}" "" "")
+ SET(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-src-0.6.0-rc2)
+ DownloadPackage(
+ "363e2f4cbd3aeb63bf4e571f377400fb"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jsoncpp-src-0.6.0-rc2.tar.gz"
+ "${JSONCPP_SOURCES_DIR}")
list(APPEND THIRD_PARTY_SOURCES
${JSONCPP_SOURCES_DIR}/src/lib_json/json_reader.cpp
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/LibCurlConfiguration.cmake
--- a/Resources/CMake/LibCurlConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/LibCurlConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (${STATIC_BUILD})
SET(CURL_SOURCES_DIR ${CMAKE_BINARY_DIR}/curl-7.26.0)
- DownloadPackage("http://curl.haxx.se/download/curl-7.26.0.tar.gz" "${CURL_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "3fa4d5236f2a36ca5c3af6715e837691"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/curl-7.26.0.tar.gz"
+ "${CURL_SOURCES_DIR}")
include_directories(${CURL_SOURCES_DIR}/include)
AUX_SOURCE_DIRECTORY(${CURL_SOURCES_DIR}/lib CURL_SOURCES)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/LibPngConfiguration.cmake
--- a/Resources/CMake/LibPngConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/LibPngConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (${STATIC_BUILD})
SET(LIBPNG_SOURCES_DIR ${CMAKE_BINARY_DIR}/libpng-1.5.12)
- DownloadPackage("http://download.sourceforge.net/libpng/libpng-1.5.12.tar.gz" "${LIBPNG_SOURCES_DIR}" "${LIBPNG_PRELOADED}" "")
+ DownloadPackage(
+ "8ea7f60347a306c5faf70b977fa80e28"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libpng-1.5.12.tar.gz"
+ "${LIBPNG_SOURCES_DIR}")
include_directories(
${LIBPNG_SOURCES_DIR}
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/LuaConfiguration.cmake
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LuaConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,67 @@
+if (STATIC_BUILD OR NOT USE_DYNAMIC_LUA)
+ SET(LUA_SOURCES_DIR ${CMAKE_BINARY_DIR}/lua-5.1.5)
+ DownloadPackage(
+ "2e115fe26e435e33b0d5c022e4490567"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/lua-5.1.5.tar.gz"
+ "${LUA_SOURCES_DIR}")
+
+ add_definitions(
+ #-DLUA_LIB=1
+ #-Dluaall_c=1
+ #-DLUA_COMPAT_ALL=1 # Compile a generic version of Lua
+ )
+
+ include_directories(
+ ${LUA_SOURCES_DIR}/src
+ )
+
+ set(LUA_SOURCES
+ # Core Lua
+ ${LUA_SOURCES_DIR}/src/lapi.c
+ ${LUA_SOURCES_DIR}/src/lcode.c
+ ${LUA_SOURCES_DIR}/src/ldebug.c
+ ${LUA_SOURCES_DIR}/src/ldo.c
+ ${LUA_SOURCES_DIR}/src/ldump.c
+ ${LUA_SOURCES_DIR}/src/lfunc.c
+ ${LUA_SOURCES_DIR}/src/lgc.c
+ ${LUA_SOURCES_DIR}/src/llex.c
+ ${LUA_SOURCES_DIR}/src/lmem.c
+ ${LUA_SOURCES_DIR}/src/lobject.c
+ ${LUA_SOURCES_DIR}/src/lopcodes.c
+ ${LUA_SOURCES_DIR}/src/lparser.c
+ ${LUA_SOURCES_DIR}/src/lstate.c
+ ${LUA_SOURCES_DIR}/src/lstring.c
+ ${LUA_SOURCES_DIR}/src/ltable.c
+ ${LUA_SOURCES_DIR}/src/ltm.c
+ ${LUA_SOURCES_DIR}/src/lundump.c
+ ${LUA_SOURCES_DIR}/src/lvm.c
+ ${LUA_SOURCES_DIR}/src/lzio.c
+
+ # Base Lua modules
+ ${LUA_SOURCES_DIR}/src/lauxlib.c
+ ${LUA_SOURCES_DIR}/src/lbaselib.c
+ ${LUA_SOURCES_DIR}/src/ldblib.c
+ ${LUA_SOURCES_DIR}/src/liolib.c
+ ${LUA_SOURCES_DIR}/src/lmathlib.c
+ ${LUA_SOURCES_DIR}/src/loslib.c
+ ${LUA_SOURCES_DIR}/src/ltablib.c
+ ${LUA_SOURCES_DIR}/src/lstrlib.c
+ ${LUA_SOURCES_DIR}/src/loadlib.c
+ ${LUA_SOURCES_DIR}/src/linit.c
+ )
+
+ add_library(Lua STATIC ${LUA_SOURCES})
+ link_libraries(Lua)
+
+ source_group(ThirdParty\\Lua REGULAR_EXPRESSION ${LUA_SOURCES_DIR}/.*)
+
+else()
+ include(FindLua51)
+
+ if (NOT LUA51_FOUND)
+ message(FATAL_ERROR "Please install the liblua-dev package")
+ endif()
+
+ include_directories(${LUA_INCLUDE_DIR})
+ link_libraries(${LUA_LIBRARIES})
+endif()
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/MongooseConfiguration.cmake
--- a/Resources/CMake/MongooseConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/MongooseConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (STATIC_BUILD OR NOT USE_DYNAMIC_MONGOOSE)
SET(MONGOOSE_SOURCES_DIR ${CMAKE_BINARY_DIR}/mongoose)
- DownloadPackage("http://mongoose.googlecode.com/files/mongoose-3.1.tgz" "${MONGOOSE_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "e718fc287b4eb1bd523be3fa00942bb0"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/mongoose-3.1.tgz"
+ "${MONGOOSE_SOURCES_DIR}")
# Patch mongoose
execute_process(
@@ -31,6 +34,14 @@
)
endif()
+
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ if (${CMAKE_COMPILER_IS_GNUCXX})
+ # This is a patch for MinGW64
+ add_definitions(-D_TIMESPEC_DEFINED=1)
+ endif()
+ endif()
+
source_group(ThirdParty\\Mongoose REGULAR_EXPRESSION ${MONGOOSE_SOURCES_DIR}/.*)
else()
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/OpenSslConfiguration.cmake
--- a/Resources/CMake/OpenSslConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/OpenSslConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (${STATIC_BUILD})
SET(OPENSSL_SOURCES_DIR ${CMAKE_BINARY_DIR}/openssl-1.0.1c)
- DownloadPackage("http://www.openssl.org/source/openssl-1.0.1c.tar.gz" "${OPENSSL_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "ae412727c8c15b67880aef7bd2999b2e"
+ "www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/openssl-1.0.1c.tar.gz"
+ "${OPENSSL_SOURCES_DIR}")
if (NOT EXISTS "${OPENSSL_SOURCES_DIR}/include/PATCHED")
if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/SQLiteConfiguration.cmake
--- a/Resources/CMake/SQLiteConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/SQLiteConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -1,6 +1,9 @@
if (STATIC_BUILD OR NOT USE_DYNAMIC_SQLITE)
SET(SQLITE_SOURCES_DIR ${CMAKE_BINARY_DIR}/sqlite-amalgamation-3071300)
- DownloadPackage("http://www.sqlite.org/sqlite-amalgamation-3071300.zip" "${SQLITE_SOURCES_DIR}" "" "")
+ DownloadPackage(
+ "5fbeff9645ab035a1f580e90b279a16d"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/sqlite-amalgamation-3071300.zip"
+ "${SQLITE_SOURCES_DIR}")
list(APPEND THIRD_PARTY_SOURCES
${SQLITE_SOURCES_DIR}/sqlite3.c
@@ -18,6 +21,7 @@
)
source_group(ThirdParty\\SQLite REGULAR_EXPRESSION ${SQLITE_SOURCES_DIR}/.*)
+
else()
CHECK_INCLUDE_FILE_CXX(sqlite3.h HAVE_SQLITE_H)
if (NOT HAVE_SQLITE_H)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/CMake/ZlibConfiguration.cmake
--- a/Resources/CMake/ZlibConfiguration.cmake Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/CMake/ZlibConfiguration.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -6,7 +6,10 @@
if (${STATIC_BUILD})
SET(ZLIB_SOURCES_DIR ${CMAKE_BINARY_DIR}/zlib-1.2.7)
- DownloadPackage("http://zlib.net/zlib-1.2.7.tar.gz" "${ZLIB_SOURCES_DIR}" "${ZLIB_PRELOADED}" "")
+ DownloadPackage(
+ "60df6a37c56e7c1366cca812414f7b85"
+ "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/zlib-1.2.7.tar.gz"
+ "${ZLIB_SOURCES_DIR}")
include_directories(
${ZLIB_SOURCES_DIR}
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Configuration.json
--- a/Resources/Configuration.json Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/Configuration.json Wed Sep 18 15:27:07 2013 +0200
@@ -1,98 +1,136 @@
{
- /**
- * General configuration of Orthanc
- **/
+ /**
+ * General configuration of Orthanc
+ **/
- // The logical name of this instance of Orthanc. This one is
- // displayed in Orthanc Explorer and at the URI "/system".
- "Name" : "MyOrthanc",
+ // The logical name of this instance of Orthanc. This one is
+ // displayed in Orthanc Explorer and at the URI "/system".
+ "Name" : "MyOrthanc",
- // Path to the directory that holds the heavyweight files
- // (i.e. the raw DICOM instances)
- "StorageDirectory" : "OrthancStorage",
+ // Path to the directory that holds the heavyweight files
+ // (i.e. the raw DICOM instances)
+ "StorageDirectory" : "OrthancStorage",
- // Path to the directory that holds the SQLite index (if unset,
- // the value of StorageDirectory is used). This index could be
- // stored on a RAM-drive or a SSD device for performance reasons.
- "IndexDirectory" : "OrthancStorage",
+ // Path to the directory that holds the SQLite index (if unset,
+ // the value of StorageDirectory is used). This index could be
+ // stored on a RAM-drive or a SSD device for performance reasons.
+ "IndexDirectory" : "OrthancStorage",
- // Enable the transparent compression of the DICOM instances
- "StorageCompression" : false,
+ // Enable the transparent compression of the DICOM instances
+ "StorageCompression" : false,
- // Maximum size of the storage in MB (a value of "0" indicates no
- // limit on the storage size)
- "MaximumStorageSize" : 0,
+ // Maximum size of the storage in MB (a value of "0" indicates no
+ // limit on the storage size)
+ "MaximumStorageSize" : 0,
- // Maximum number of patients that can be stored at a given time
- // in the storage (a value of "0" indicates no limit on the number
- // of patients)
- "MaximumPatientCount" : 0,
+ // Maximum number of patients that can be stored at a given time
+ // in the storage (a value of "0" indicates no limit on the number
+ // of patients)
+ "MaximumPatientCount" : 0,
+
+ // List of paths to the custom Lua scripts to load into this
+ // instance of Orthanc
+ "LuaScripts" : [
+ ],
- /**
- * Configuration of the HTTP server
- **/
+
+ /**
+ * Configuration of the HTTP server
+ **/
+
+ // HTTP port for the REST services and for the GUI
+ "HttpPort" : 8042,
+
+
- // HTTP port for the REST services and for the GUI
- "HttpPort" : 8042,
+ /**
+ * Configuration of the DICOM server
+ **/
+
+ // The DICOM Application Entity Title
+ "DicomAet" : "ORTHANC",
+
+ // Check whether the called AET corresponds during a DICOM request
+ "DicomCheckCalledAet" : false,
+
+ // The DICOM port
+ "DicomPort" : 4242,
- /**
- * Configuration of the DICOM server
- **/
+ /**
+ * Security-related options for the HTTP server
+ **/
+
+ // Whether remote hosts can connect to the HTTP server
+ "RemoteAccessAllowed" : false,
+
+ // Whether or not SSL is enabled
+ "SslEnabled" : false,
- // The DICOM Application Entity Title
- "DicomAet" : "ORTHANC",
+ // Path to the SSL certificate (meaningful only if SSL is enabled)
+ "SslCertificate" : "certificate.pem",
+
+ // Whether or not the password protection is enabled
+ "AuthenticationEnabled" : false,
- // Check whether the called AET corresponds during a DICOM request
- "DicomCheckCalledAet" : false,
-
- // The DICOM port
- "DicomPort" : 4242,
+ // The list of the registered users. Because Orthanc uses HTTP
+ // Basic Authentication, the passwords are stored as plain text.
+ "RegisteredUsers" : {
+ // "alice" : "alicePassword"
+ },
- /**
- * Security-related options for the HTTP server
- **/
+ /**
+ * Network topology
+ **/
- // Whether remote hosts can connect to the HTTP server
- "RemoteAccessAllowed" : false,
-
- // Whether or not SSL is enabled
- "SslEnabled" : false,
+ // The list of the known DICOM modalities
+ "DicomModalities" : {
+ /**
+ * Uncommenting the following line would enable Orthanc to
+ * connect to an instance of the "storescp" open-source DICOM
+ * store (shipped in the DCMTK distribution) started by the
+ * command line "storescp 2000".
+ **/
+ // "sample" : [ "STORESCP", "localhost", 2000 ]
- // Path to the SSL certificate (meaningful only if SSL is enabled)
- "SslCertificate" : "certificate.pem",
-
- // Whether or not the password protection is enabled
- "AuthenticationEnabled" : false,
+ /**
+ * A fourth parameter is available to enable patches for a
+ * specific PACS manufacturer. The allowed values are currently
+ * "Generic" (default value) and "ClearCanvas". This parameter is
+ * case-sensitive.
+ **/
+ // "clearcanvas" : [ "CLEARCANVAS", "192.168.1.1", 104, "ClearCanvas" ]
+ },
- // The list of the registered users. Because Orthanc uses HTTP
- // Basic Authentication, the passwords are stored as plain text.
- "RegisteredUsers" : {
- "alice" : "alicePassword"
- },
+ // The list of the known Orthanc peers
+ "OrthancPeers" : {
+ /**
+ * Each line gives the base URL of an Orthanc peer, possibly
+ * followed by the username/password pair (if the password
+ * protection is enabled on the peer).
+ **/
+ // "peer" : [ "http://localhost:8043/", "alice", "alicePassword" ]
+ // "peer2" : [ "http://localhost:8044/" ]
+ },
- /**
- * Network topology
- **/
+ /**
+ * Advanced options
+ **/
- // The list of the known DICOM modalities
- "DicomModalities" : {
- /**
- * Uncommenting the following line would enable Orthanc to
- * connect to an instance of the "storescp" open-source DICOM
- * store (shipped in the DCMTK distribution) started by the
- * command line "storescp 2000".
- **/
- // "sample" : [ "STORESCP", "localhost", 2000 ]
- },
+ // Dictionary of symbolic names for the user-defined metadata. Each
+ // entry must map a number between 1024 and 65535 to an unique
+ // string.
+ "UserMetadata" : {
+ // "Sample" : 1024
+ },
- // The list of the known Orthanc peers (currently unused)
- "OrthancPeers" : {
- }
+ // Number of seconds without receiving any instance before a
+ // patient, a study or a series is considered as stable.
+ "StableAge" : 60
}
diff -r f6e61e329bbf -r b26a7c397c34 Resources/EclipseCodingStyle.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/EclipseCodingStyle.xml Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r f6e61e329bbf -r b26a7c397c34 Resources/EmbedResources.py
--- a/Resources/EmbedResources.py Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/EmbedResources.py Wed Sep 18 15:27:07 2013 +0200
@@ -1,5 +1,5 @@
# 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 Resources/Patches/dcmtk-mingw64.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/dcmtk-mingw64.patch Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,22 @@
+diff -urEb dcmtk-3.6.0.orig/ofstd/include/dcmtk/ofstd/offile.h dcmtk-3.6.0/ofstd/include/dcmtk/ofstd/offile.h
+--- dcmtk-3.6.0.orig/ofstd/include/dcmtk/ofstd/offile.h 2010-12-17 11:50:30.000000000 +0100
++++ dcmtk-3.6.0/ofstd/include/dcmtk/ofstd/offile.h 2013-07-19 15:56:25.688996134 +0200
+@@ -196,7 +196,7 @@
+ OFBool popen(const char *command, const char *modes)
+ {
+ if (file_) fclose();
+-#ifdef _WIN32
++#if 0
+ file_ = _popen(command, modes);
+ #else
+ file_ = :: popen(command, modes);
+@@ -258,7 +258,7 @@
+ {
+ if (popened_)
+ {
+-#ifdef _WIN32
++#if 0
+ result = _pclose(file_);
+ #else
+ result = :: pclose(file_);
+Only in dcmtk-3.6.0/ofstd/include/dcmtk/ofstd: offile.h~
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Patches/dcmtk-mingw64.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/dcmtk-mingw64.txt Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,1 @@
+diff -urEb dcmtk-3.6.0.orig/ dcmtk-3.6.0 > ../Resources/Patches/dcmtk-mingw64.patch
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/OrthancCppClient/Basic/CMakeLists.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/OrthancCppClient/Basic/CMakeLists.txt Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,16 @@
+# Mini-project to check whether "OrthancCppClient" can compile in a
+# standalone fashion
+
+cmake_minimum_required(VERSION 2.8)
+
+project(OrthancCppClientTest)
+
+set(STATIC_BUILD OFF)
+set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/../../../..)
+
+include(../OrthancCppClient.cmake)
+
+add_executable(Test
+ main.cpp
+ ${ORTHANC_CPP_CLIENT_SOURCES}
+ )
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/OrthancCppClient/Basic/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/OrthancCppClient/Basic/main.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,73 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2013 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
+
+#include "../../../../Core/HttpClient.h"
+#include "../../../../OrthancCppClient/OrthancConnection.h"
+
+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;
+
+ // Display the content of the local Orthanc instance
+ OrthancClient::OrthancConnection orthanc("http://localhost:8042");
+
+ for (unsigned int i = 0; i < orthanc.GetPatientCount(); i++)
+ {
+ OrthancClient::Patient& patient = orthanc.GetPatient(i);
+ std::cout << "Patient: " << patient.GetId() << std::endl;
+
+ for (unsigned int j = 0; j < patient.GetStudyCount(); j++)
+ {
+ OrthancClient::Study& study = patient.GetStudy(j);
+ std::cout << " Study: " << study.GetId() << std::endl;
+
+ for (unsigned int k = 0; k < study.GetSeriesCount(); k++)
+ {
+ OrthancClient::Series& series = study.GetSeries(k);
+ std::cout << " Series: " << series.GetId() << std::endl;
+
+ for (unsigned int l = 0; l < series.GetInstanceCount(); l++)
+ {
+ std::cout << " Instance: " << series.GetInstance(l).GetId() << std::endl;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/OrthancCppClient/OrthancCppClient.cmake
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/OrthancCppClient/OrthancCppClient.cmake Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,34 @@
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/LibCurlConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/LibPngConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.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()
+
+set(ORTHANC_CPP_CLIENT_SOURCES
+ ${THIRD_PARTY_SOURCES}
+ ${ORTHANC_ROOT}/Core/OrthancException.cpp
+ ${ORTHANC_ROOT}/Core/Enumerations.cpp
+ ${ORTHANC_ROOT}/Core/Toolbox.cpp
+ ${ORTHANC_ROOT}/Core/HttpClient.cpp
+ ${ORTHANC_ROOT}/Core/MultiThreading/ArrayFilledByThreads.cpp
+ ${ORTHANC_ROOT}/Core/MultiThreading/ThreadedCommandProcessor.cpp
+ ${ORTHANC_ROOT}/Core/MultiThreading/SharedMessageQueue.cpp
+ ${ORTHANC_ROOT}/Core/FileFormats/PngReader.cpp
+ ${ORTHANC_ROOT}/OrthancCppClient/OrthancConnection.cpp
+ ${ORTHANC_ROOT}/OrthancCppClient/Series.cpp
+ ${ORTHANC_ROOT}/OrthancCppClient/Study.cpp
+ ${ORTHANC_ROOT}/OrthancCppClient/Instance.cpp
+ ${ORTHANC_ROOT}/OrthancCppClient/Patient.cpp
+ ${ORTHANC_ROOT}/Resources/sha1/sha1.cpp
+ ${ORTHANC_ROOT}/Resources/md5/md5.c
+ ${ORTHANC_ROOT}/Resources/base64/base64.cpp
+ )
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/OrthancCppClient/Vtk/CMakeLists.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/OrthancCppClient/Vtk/CMakeLists.txt Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(OrthancCppClientTest)
+
+set(STATIC_BUILD OFF)
+set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/../../../..)
+
+include(../OrthancCppClient.cmake)
+
+find_package(VTK REQUIRED)
+include(${VTK_USE_FILE})
+
+add_executable(Test
+ main.cpp
+ ${ORTHANC_CPP_CLIENT_SOURCES}
+ )
+
+if(VTK_LIBRARIES)
+ target_link_libraries(Test ${VTK_LIBRARIES})
+else()
+ target_link_libraries(Test vtkHybrid vtkVolumeRendering)
+endif()
+
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/OrthancCppClient/Vtk/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/OrthancCppClient/Vtk/main.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,196 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2013 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
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../../../../OrthancCppClient/OrthancConnection.h"
+
+
+class DisplayProgress : public Orthanc::ThreadedCommandProcessor::IListener
+{
+public:
+ virtual void SignalProgress(unsigned int current,
+ unsigned int total)
+ {
+ std::cout << "Slice loaded (" << current << "/" << total << ")" << std::endl;
+ }
+
+ virtual void SignalSuccess(unsigned int total)
+ {
+ std::cout << "Success loading image (" << total << " images)" << std::endl;
+ }
+
+ virtual void SignalCancel()
+ {
+ }
+
+ virtual void SignalFailure()
+ {
+ std::cout << "Error loading image" << std::endl;
+ }
+};
+
+
+
+void Display(OrthancClient::Series& series)
+{
+ /**
+ * Load the 3D image from Orthanc into VTK.
+ **/
+
+ vtkSmartPointer image = vtkSmartPointer::New();
+ image->SetDimensions(series.GetWidth(), series.GetHeight(), series.GetInstanceCount());
+ image->SetScalarType(VTK_SHORT);
+ image->AllocateScalars();
+
+ if (series.GetWidth() != 0 &&
+ series.GetHeight() != 0 &&
+ series.GetInstanceCount() != 0)
+ {
+ DisplayProgress listener;
+ series.Load3DImage(image->GetScalarPointer(0, 0, 0), Orthanc::PixelFormat_SignedGrayscale16,
+ 2 * series.GetWidth(), 2 * series.GetHeight() * series.GetWidth(), listener);
+ }
+
+ float sx, sy, sz;
+ series.GetVoxelSize(sx, sy, sz);
+ image->SetSpacing(sx, sy, sz);
+
+
+ /**
+ * The following code is based on the VTK sample for MIP
+ * http://www.vtk.org/Wiki/VTK/Examples/Cxx/VolumeRendering/MinIntensityRendering
+ **/
+
+ // Create a transfer function mapping scalar value to opacity
+ double range[2];
+ image->GetScalarRange(range);
+
+ vtkSmartPointer opacityTransfer =
+ vtkSmartPointer::New();
+ opacityTransfer->AddSegment(range[0], 0.0, range[1], 1.0);
+
+ vtkSmartPointer colorTransfer =
+ vtkSmartPointer::New();
+ colorTransfer->AddRGBPoint(0, 1.0, 1.0, 1.0);
+ colorTransfer->AddRGBPoint(range[1], 1.0, 1.0, 1.0);
+
+ vtkSmartPointer property =
+ vtkSmartPointer::New();
+ property->SetScalarOpacity(opacityTransfer);
+ property->SetColor(colorTransfer);
+ property->SetInterpolationTypeToLinear();
+
+ // Create a Maximum Intensity Projection rendering
+ vtkSmartPointer mapper =
+ vtkSmartPointer::New();
+ mapper->SetBlendModeToMaximumIntensity();
+ mapper->SetInput(image);
+
+ vtkSmartPointer volume = vtkSmartPointer::New();
+ volume->SetMapper(mapper);
+ volume->SetProperty(property);
+
+ vtkSmartPointer renderer = vtkSmartPointer::New();
+ renderer->AddViewProp(volume);
+ renderer->SetBackground(0.1, 0.2, 0.3); // Background color dark blue
+
+ vtkSmartPointer style =
+ vtkSmartPointer::New();
+
+ vtkSmartPointer window = vtkSmartPointer::New();
+ window->AddRenderer(renderer);
+
+ vtkSmartPointer interactor = vtkSmartPointer::New();
+ interactor->SetRenderWindow(window);
+ interactor->SetInteractorStyle(style);
+ interactor->Start();
+}
+
+
+int main()
+{
+ // Use the commented code below if you know the identifier of a
+ // series that corresponds to a 3D image.
+
+ /*
+ {
+ OrthancClient::OrthancConnection orthanc("http://localhost:8042");
+ OrthancClient::Series series(orthanc, "c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5");
+ Display(series);
+ return 0;
+ }
+ */
+
+
+ // Try and find a 3D image inside the local store
+ OrthancClient::OrthancConnection orthanc("http://localhost:8042");
+
+ for (unsigned int i = 0; i < orthanc.GetPatientCount(); i++)
+ {
+ OrthancClient::Patient& patient = orthanc.GetPatient(i);
+ std::cout << "Patient: " << patient.GetId() << std::endl;
+
+ for (unsigned int j = 0; j < patient.GetStudyCount(); j++)
+ {
+ OrthancClient::Study& study = patient.GetStudy(j);
+ std::cout << " Study: " << study.GetId() << std::endl;
+
+ for (unsigned int k = 0; k < study.GetSeriesCount(); k++)
+ {
+ OrthancClient::Series& series = study.GetSeries(k);
+ std::cout << " Series: " << series.GetId() << std::endl;
+
+ if (series.Is3DImage())
+ {
+ Display(series);
+ return 0;
+ }
+ else
+ {
+ std::cout << " => Not a 3D image..." << std::endl;
+ }
+ }
+ }
+ }
+
+ std::cout << "Unable to find a 3D image in the local Orthanc store" << std::endl;
+
+ return 0;
+}
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/Python/HighPerformanceAutoRouting.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Python/HighPerformanceAutoRouting.py Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+
+URL = 'http://localhost:8042'
+TARGET = 'sample'
+
+
+#
+# This sample code shows how to setup a simple, high-performance DICOM
+# auto-routing. All the DICOM instances that arrive inside Orthanc
+# will be sent to a remote modality. A producer-consumer pattern is
+# used. The target modality is specified by the TARGET variable above:
+# It must match an entry in the Orthanc configuration file inside the
+# "DicomModalities" section.
+#
+# NOTE: This sample only works with Orthanc >= 0.5.2. Make sure that
+# Orthanc was built with "-DCMAKE_BUILD_TYPE=Release" to get the best
+# performance.
+#
+
+import Queue
+import sys
+import time
+import threading
+
+import RestToolbox
+
+
+#
+# Queue that is shared between the producer and the consumer
+# threads. It holds the instances that are still to be sent.
+#
+
+queue = Queue.Queue()
+
+
+#
+# The producer thread. It monitors the arrival of new instances into
+# Orthanc, and pushes their ID into the shared queue. This code is
+# based upon the "ChangesLoop.py" sample code.
+#
+
+def Producer(queue):
+ current = 0
+
+ while True:
+ r = RestToolbox.DoGet(URL + '/changes', {
+ 'since' : current,
+ 'limit' : 4 # Retrieve at most 4 changes at once
+ })
+
+ for change in r['Changes']:
+ # We are only interested interested in the arrival of new instances
+ if change['ChangeType'] == 'NewInstance':
+ queue.put(change['ID'])
+
+ current = r['Last']
+
+ if r['Done']:
+ time.sleep(1)
+
+
+#
+# The consumer thread. It continuously reads the instances from the
+# queue, and send them to the remote modality. Each time a packet of
+# instances is sent, a single DICOM connexion is used, hence improving
+# the performance.
+#
+
+def Consumer(queue):
+ TIMEOUT = 0.1
+
+ while True:
+ instances = []
+
+ while True:
+ try:
+ # Block for a while, waiting for the arrival of a new
+ # instance
+ instance = queue.get(True, TIMEOUT)
+
+ # A new instance has arrived: Record its ID
+ instances.append(instance)
+ queue.task_done()
+
+ except Queue.Empty:
+ # Timeout: No more data was received
+ break
+
+ if len(instances) > 0:
+ print 'Sending a packet of %d instances' % len(instances)
+ start = time.time()
+
+ # Send all the instances with a single DICOM connexion
+ RestToolbox.DoPost('%s/modalities/sample/store' % URL, instances)
+
+ # Remove all the instances from Orthanc
+ for instance in instances:
+ RestToolbox.DoDelete('%s/instances/%s' % (URL, instance))
+
+ # Clear the log of the exported instances (to prevent the
+ # SQLite database from growing indefinitely)
+ RestToolbox.DoDelete('%s/exports' % URL)
+
+ end = time.time()
+ print 'The packet of %d instances has been sent in %d seconds' % (len(instances), end - start)
+
+
+#
+# Thread to display the progress
+#
+
+def PrintProgress(queue):
+ while True:
+ print 'Current queue size: %d' % (queue.qsize())
+ time.sleep(1)
+
+
+#
+# Start the various threads
+#
+
+progress = threading.Thread(None, PrintProgress, None, (queue, ))
+progress.daemon = True
+progress.start()
+
+producer = threading.Thread(None, Producer, None, (queue, ))
+producer.daemon = True
+producer.start()
+
+consumer = threading.Thread(None, Consumer, None, (queue, ))
+consumer.daemon = True
+consumer.start()
+
+
+#
+# Active waiting for Ctrl-C
+#
+
+while True:
+ time.sleep(0.1)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/Python/RestToolbox.py
--- a/Resources/Samples/Python/RestToolbox.py Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/Samples/Python/RestToolbox.py Wed Sep 18 15:27:07 2013 +0200
@@ -13,7 +13,7 @@
if _credentials != None:
h.add_credentials(_credentials[0], _credentials[1])
-def DoGet(uri, data = {}):
+def DoGet(uri, data = {}, interpretAsJson = True):
d = ''
if len(data.keys()) > 0:
d = '?' + urlencode(data)
@@ -23,6 +23,8 @@
resp, content = h.request(uri + d, 'GET')
if not (resp.status in [ 200 ]):
raise Exception(resp.status)
+ elif not interpretAsJson:
+ return content
else:
try:
return json.loads(content)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/RestApi/CMakeLists.txt
--- a/Resources/Samples/RestApi/CMakeLists.txt Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/Samples/RestApi/CMakeLists.txt Wed Sep 18 15:27:07 2013 +0200
@@ -22,8 +22,8 @@
UPDATE_COMMAND ""
SOURCE_DIR ${CMAKE_BINARY_DIR}/Orthanc/src/orthanc/
CMAKE_COMMAND ${CMAKE_COMMAND}
- CMAKE_ARGS -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DUSE_DYNAMIC_GOOGLE_LOG=OFF -DUSE_DYNAMIC_SQLITE=OFF -DONLY_CORE_LIBRARY=ON -DENABLE_SSL=OFF ${TOOLCHAIN}
- BUILD_COMMAND $(MAKE)
+ CMAKE_ARGS -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DUSE_DYNAMIC_GOOGLE_LOG=OFF -DUSE_DYNAMIC_SQLITE=OFF -DENABLE_SSL=OFF ${TOOLCHAIN}
+ BUILD_COMMAND $(MAKE) CoreLibrary
INSTALL_COMMAND ""
BUILD_IN_SOURCE 0
)
diff -r f6e61e329bbf -r b26a7c397c34 Resources/Samples/RestApi/Sample.cpp
--- a/Resources/Samples/RestApi/Sample.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/Samples/RestApi/Sample.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 Resources/Samples/RestApiLinuxDynamic/Sample.cpp
--- a/Resources/Samples/RestApiLinuxDynamic/Sample.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/Resources/Samples/RestApiLinuxDynamic/Sample.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 Resources/Toolbox.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Toolbox.lua Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,21 @@
+--[[ PrintRecursive(struct, [limit], [indent]) Recursively print arbitrary data.
+Set limit (default 100) to stanch infinite loops.
+Indents tables as [KEY] VALUE, nested tables as [KEY] [KEY]...[KEY] VALUE
+Set indent ("") to prefix each line: Mytable [KEY] [KEY]...[KEY] VALUE
+Source: https://gist.github.com/stuby/5445834#file-rprint-lua
+--]]
+
+function PrintRecursive(s, l, i) -- recursive Print (structure, limit, indent)
+ l = (l) or 100; i = i or ""; -- default item limit, indent string
+ if (l<1) then print "ERROR: Item limit reached."; return l-1 end;
+ local ts = type(s);
+ if (ts ~= "table") then print (i,ts,s); return l-1 end
+ print (i,ts); -- print "table"
+ for k,v in pairs(s) do -- print "[KEY] VALUE"
+ l = PrintRecursive(v, l, i.."\t["..tostring(k).."]");
+ if (l < 0) then break end
+ end
+ return l
+end
+
+print('Lua toolbox installed')
diff -r f6e61e329bbf -r b26a7c397c34 THANKS
--- a/THANKS Mon Apr 29 12:48:10 2013 +0200
+++ b/THANKS Wed Sep 18 15:27:07 2013 +0200
@@ -13,6 +13,11 @@
* Cyril Paulus (cyril.paulus@student.ulg.ac.be), for the build process
and suggestions about the REST API.
+* Will Ryder (will.ryder@sydney.edu.au), for improvements with the
+ handling of series with temporal positions (fMRI and dynamic PET).
+* Ryan Walklin (ryanwalklin@gmail.com), for Mac OS X build.
+* Peter Somlo (peter.somlo@gmail.com), for ClearCanvas support.
+* 12maksqwe@gmail.com, for fixing issue #8.
Artwork
@@ -30,4 +35,10 @@
------
* Mathieu Malaterre (mathieu.malaterre@gmail.com), for sponsoring Orthanc.
-* Andreas Tille (andreas@an3as.eu), for help about Debian packaging.
+* Andreas Tille (andreas@an3as.eu), for help about packaging.
+
+
+Fedora and Red Hat
+------------------
+
+* Mario Ceresa (mrceresa@gmail.com), for help about packaging.
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/Lua.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTests/Lua.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,72 @@
+#include "gtest/gtest.h"
+
+#include "../Core/Lua/LuaFunctionCall.h"
+
+
+TEST(Lua, Simple)
+{
+ Orthanc::LuaContext lua;
+ lua.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
+ lua.Execute("a={}");
+ lua.Execute("a['x'] = 10");
+ lua.Execute("a['y'] = {}");
+ lua.Execute("a['y'][1] = 20");
+ lua.Execute("a['y'][2] = 20");
+ lua.Execute("PrintRecursive(a)");
+
+ lua.Execute("function f(a) print(a.bool) return a.bool,20,30,40,50,60 end");
+
+ Json::Value v, vv, o;
+ //v["a"] = "b";
+ v.append("hello");
+ v.append("world");
+ v.append("42");
+ vv.append("sub");
+ vv.append("set");
+ v.append(vv);
+ o = Json::objectValue;
+ o["x"] = 10;
+ o["y"] = 20;
+ o["z"] = 20.5f;
+ v.append(o);
+
+ {
+ Orthanc::LuaFunctionCall f(lua, "PrintRecursive");
+ f.PushJSON(v);
+ f.Execute();
+ }
+
+ {
+ Orthanc::LuaFunctionCall f(lua, "f");
+ f.PushJSON(o);
+ ASSERT_THROW(f.ExecutePredicate(), Orthanc::LuaException);
+ }
+
+ o["bool"] = false;
+
+ {
+ Orthanc::LuaFunctionCall f(lua, "f");
+ f.PushJSON(o);
+ ASSERT_FALSE(f.ExecutePredicate());
+ }
+
+ o["bool"] = true;
+
+ {
+ Orthanc::LuaFunctionCall f(lua, "f");
+ f.PushJSON(o);
+ ASSERT_TRUE(f.ExecutePredicate());
+ }
+}
+
+
+TEST(Lua, Existing)
+{
+ Orthanc::LuaContext lua;
+ lua.Execute("a={}");
+ lua.Execute("function f() end");
+
+ ASSERT_TRUE(lua.IsExistingFunction("f"));
+ ASSERT_FALSE(lua.IsExistingFunction("a"));
+ ASSERT_FALSE(lua.IsExistingFunction("Dummy"));
+}
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/MemoryCache.cpp
--- a/UnitTests/MemoryCache.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/UnitTests/MemoryCache.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -8,46 +8,53 @@
#include "../Core/Cache/MemoryCache.h"
-TEST(CacheIndex, Basic)
+TEST(LRU, Basic)
{
- Orthanc::CacheIndex r;
+ Orthanc::LeastRecentlyUsedIndex r;
r.Add("d");
r.Add("a");
r.Add("c");
r.Add("b");
- r.TagAsMostRecent("a");
- r.TagAsMostRecent("d");
- r.TagAsMostRecent("b");
- r.TagAsMostRecent("c");
- r.TagAsMostRecent("d");
- r.TagAsMostRecent("c");
+ r.MakeMostRecent("a");
+ r.MakeMostRecent("d");
+ r.MakeMostRecent("b");
+ r.MakeMostRecent("c");
+ r.MakeMostRecent("d");
+ r.MakeMostRecent("c");
+ ASSERT_EQ("a", r.GetOldest());
ASSERT_EQ("a", r.RemoveOldest());
+ ASSERT_EQ("b", r.GetOldest());
ASSERT_EQ("b", r.RemoveOldest());
+ ASSERT_EQ("d", r.GetOldest());
ASSERT_EQ("d", r.RemoveOldest());
+ ASSERT_EQ("c", r.GetOldest());
ASSERT_EQ("c", r.RemoveOldest());
ASSERT_TRUE(r.IsEmpty());
+
+ ASSERT_THROW(r.GetOldest(), Orthanc::OrthancException);
+ ASSERT_THROW(r.RemoveOldest(), Orthanc::OrthancException);
}
-TEST(CacheIndex, Payload)
+TEST(LRU, Payload)
{
- Orthanc::CacheIndex r;
+ Orthanc::LeastRecentlyUsedIndex r;
r.Add("a", 420);
r.Add("b", 421);
r.Add("c", 422);
r.Add("d", 423);
- r.TagAsMostRecent("a");
- r.TagAsMostRecent("d");
- r.TagAsMostRecent("b");
- r.TagAsMostRecent("c");
- r.TagAsMostRecent("d");
- r.TagAsMostRecent("c");
+ r.MakeMostRecent("a");
+ r.MakeMostRecent("d");
+ r.MakeMostRecent("b");
+ r.MakeMostRecent("c");
+ r.MakeMostRecent("d");
+ r.MakeMostRecent("c");
ASSERT_TRUE(r.Contains("b"));
ASSERT_EQ(421, r.Invalidate("b"));
@@ -58,14 +65,76 @@
ASSERT_TRUE(r.Contains("c", p)); ASSERT_EQ(422, p);
ASSERT_TRUE(r.Contains("d", p)); ASSERT_EQ(423, p);
+ ASSERT_EQ("a", r.GetOldest());
+ ASSERT_EQ(420, r.GetOldestPayload());
ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p);
+
+ ASSERT_EQ("d", r.GetOldest());
+ ASSERT_EQ(423, r.GetOldestPayload());
ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p);
+
+ ASSERT_EQ("c", r.GetOldest());
+ ASSERT_EQ(422, r.GetOldestPayload());
ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p);
ASSERT_TRUE(r.IsEmpty());
}
+TEST(LRU, PayloadUpdate)
+{
+ Orthanc::LeastRecentlyUsedIndex r;
+
+ r.Add("a", 420);
+ r.Add("b", 421);
+ r.Add("d", 423);
+
+ r.MakeMostRecent("a", 424);
+ r.MakeMostRecent("d", 421);
+
+ ASSERT_EQ("b", r.GetOldest());
+ ASSERT_EQ(421, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_EQ("a", r.GetOldest());
+ ASSERT_EQ(424, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_EQ("d", r.GetOldest());
+ ASSERT_EQ(421, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_TRUE(r.IsEmpty());
+}
+
+
+
+TEST(LRU, PayloadUpdateBis)
+{
+ Orthanc::LeastRecentlyUsedIndex r;
+
+ r.AddOrMakeMostRecent("a", 420);
+ r.AddOrMakeMostRecent("b", 421);
+ r.AddOrMakeMostRecent("d", 423);
+ r.AddOrMakeMostRecent("a", 424);
+ r.AddOrMakeMostRecent("d", 421);
+
+ ASSERT_EQ("b", r.GetOldest());
+ ASSERT_EQ(421, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_EQ("a", r.GetOldest());
+ ASSERT_EQ(424, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_EQ("d", r.GetOldest());
+ ASSERT_EQ(421, r.GetOldestPayload());
+ r.RemoveOldest();
+
+ ASSERT_TRUE(r.IsEmpty());
+}
+
+
namespace
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/Png.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTests/Png.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -0,0 +1,109 @@
+#include "gtest/gtest.h"
+
+#include
+#include "../Core/FileFormats/PngReader.h"
+#include "../Core/FileFormats/PngWriter.h"
+
+TEST(PngWriter, ColorPattern)
+{
+ Orthanc::PngWriter w;
+ int width = 17;
+ int height = 61;
+ int pitch = width * 3;
+
+ std::vector image(height * pitch);
+ for (int y = 0; y < height; y++)
+ {
+ uint8_t *p = &image[0] + y * pitch;
+ for (int x = 0; x < width; x++, p += 3)
+ {
+ p[0] = (y % 3 == 0) ? 255 : 0;
+ p[1] = (y % 3 == 1) ? 255 : 0;
+ p[2] = (y % 3 == 2) ? 255 : 0;
+ }
+ }
+
+ w.WriteToFile("ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]);
+}
+
+TEST(PngWriter, Gray8Pattern)
+{
+ Orthanc::PngWriter w;
+ int width = 17;
+ int height = 256;
+ int pitch = width;
+
+ std::vector image(height * pitch);
+ for (int y = 0; y < height; y++)
+ {
+ uint8_t *p = &image[0] + y * pitch;
+ for (int x = 0; x < width; x++, p++)
+ {
+ *p = y;
+ }
+ }
+
+ w.WriteToFile("Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]);
+}
+
+TEST(PngWriter, Gray16Pattern)
+{
+ Orthanc::PngWriter w;
+ int width = 256;
+ int height = 256;
+ int pitch = width * 2 + 16;
+
+ std::vector image(height * pitch);
+
+ int v = 0;
+ for (int y = 0; y < height; y++)
+ {
+ uint16_t *p = reinterpret_cast(&image[0] + y * pitch);
+ for (int x = 0; x < width; x++, p++, v++)
+ {
+ *p = v;
+ }
+ }
+
+ w.WriteToFile("Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+}
+
+TEST(PngWriter, EndToEnd)
+{
+ Orthanc::PngWriter w;
+ int width = 256;
+ int height = 256;
+ int pitch = width * 2 + 16;
+
+ std::vector image(height * pitch);
+
+ int v = 0;
+ for (int y = 0; y < height; y++)
+ {
+ uint16_t *p = reinterpret_cast(&image[0] + y * pitch);
+ for (int x = 0; x < width; x++, p++, v++)
+ {
+ *p = v;
+ }
+ }
+
+ std::string s;
+ w.WriteToMemory(s, width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
+
+ Orthanc::PngReader r;
+ r.ReadFromMemory(s);
+
+ ASSERT_EQ(r.GetWidth(), width);
+ ASSERT_EQ(r.GetHeight(), height);
+
+ v = 0;
+ for (int y = 0; y < height; y++)
+ {
+ uint16_t *p = reinterpret_cast((uint8_t*) r.GetBuffer() + y * r.GetPitch());
+ for (int x = 0; x < width; x++, p++, v++)
+ {
+ ASSERT_EQ(*p, v);
+ }
+ }
+
+}
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/PngWriter.cpp
--- a/UnitTests/PngWriter.cpp Mon Apr 29 12:48:10 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-#include "gtest/gtest.h"
-
-#include
-#include "../Core/PngWriter.h"
-
-TEST(PngWriter, ColorPattern)
-{
- Orthanc::PngWriter w;
- int width = 17;
- int height = 61;
- int pitch = width * 3;
-
- std::vector image(height * pitch);
- for (int y = 0; y < height; y++)
- {
- uint8_t *p = &image[0] + y * pitch;
- for (int x = 0; x < width; x++, p += 3)
- {
- p[0] = (y % 3 == 0) ? 255 : 0;
- p[1] = (y % 3 == 1) ? 255 : 0;
- p[2] = (y % 3 == 2) ? 255 : 0;
- }
- }
-
- w.WriteToFile("ColorPattern.png", width, height, pitch, Orthanc::PixelFormat_RGB24, &image[0]);
-}
-
-TEST(PngWriter, Gray8Pattern)
-{
- Orthanc::PngWriter w;
- int width = 17;
- int height = 256;
- int pitch = width;
-
- std::vector image(height * pitch);
- for (int y = 0; y < height; y++)
- {
- uint8_t *p = &image[0] + y * pitch;
- for (int x = 0; x < width; x++, p++)
- {
- *p = y;
- }
- }
-
- w.WriteToFile("Gray8Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale8, &image[0]);
-}
-
-TEST(PngWriter, Gray16Pattern)
-{
- Orthanc::PngWriter w;
- int width = 256;
- int height = 256;
- int pitch = width * 2 + 16;
-
- std::vector image(height * pitch);
-
- int v = 0;
- for (int y = 0; y < height; y++)
- {
- uint16_t *p = reinterpret_cast(&image[0] + y * pitch);
- for (int x = 0; x < width; x++, p++, v++)
- {
- *p = v;
- }
- }
-
- w.WriteToFile("Gray16Pattern.png", width, height, pitch, Orthanc::PixelFormat_Grayscale16, &image[0]);
-}
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/ServerIndex.cpp
--- a/UnitTests/ServerIndex.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/UnitTests/ServerIndex.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -5,6 +5,7 @@
#include
#include
+#include
using namespace Orthanc;
@@ -135,10 +136,25 @@
ASSERT_EQ("e", l.front());
}
+ std::list md;
+ index.ListAvailableMetadata(md, a[4]);
+ ASSERT_EQ(0u, md.size());
+
index.AddAttachment(a[4], FileInfo("my json file", FileContentType_Json, 42, CompressionType_Zlib, 21));
index.AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42));
index.AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44));
index.SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE");
+
+ index.ListAvailableMetadata(md, a[4]);
+ ASSERT_EQ(1u, md.size());
+ ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front());
+ index.SetMetadata(a[4], MetadataType_ModifiedFrom, "TUTU");
+ index.ListAvailableMetadata(md, a[4]);
+ ASSERT_EQ(2u, md.size());
+ index.DeleteMetadata(a[4], MetadataType_ModifiedFrom);
+ index.ListAvailableMetadata(md, a[4]);
+ ASSERT_EQ(1u, md.size());
+ ASSERT_EQ(MetadataType_Instance_RemoteAet, md.front());
ASSERT_EQ(21u + 42u + 44u, index.GetTotalCompressedSize());
ASSERT_EQ(42u + 42u + 44u, index.GetTotalUncompressedSize());
@@ -417,3 +433,57 @@
ASSERT_EQ(3u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
ASSERT_EQ(4u, index.IncrementGlobalSequence(GlobalProperty_AnonymizationSequence));
}
+
+
+
+TEST(DatabaseWrapper, LookupTagValue)
+{
+ ServerIndexListener listener;
+ DatabaseWrapper index(listener);
+
+ int64_t a[] = {
+ index.CreateResource("a", ResourceType_Study), // 0
+ index.CreateResource("b", ResourceType_Study), // 1
+ index.CreateResource("c", ResourceType_Study), // 2
+ index.CreateResource("d", ResourceType_Series) // 3
+ };
+
+ DicomMap m;
+ m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[0], m);
+ m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "1"); index.SetMainDicomTags(a[1], m);
+ m.Clear(); m.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "0"); index.SetMainDicomTags(a[2], m);
+ m.Clear(); m.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "0"); index.SetMainDicomTags(a[3], m);
+
+ std::list s;
+
+ index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "0");
+ ASSERT_EQ(2u, s.size());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end());
+
+ index.LookupTagValue(s, "0");
+ ASSERT_EQ(3u, s.size());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[3]) != s.end());
+
+ index.LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1");
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end());
+
+ index.LookupTagValue(s, "1");
+ ASSERT_EQ(1u, s.size());
+ ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end());
+
+
+ /*{
+ std::list s;
+ context.GetIndex().LookupTagValue(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059");
+ for (std::list::iterator i = s.begin(); i != s.end(); i++)
+ {
+ std::cout << "*** " << *i << std::endl;;
+ }
+ }*/
+
+
+}
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/Versions.cpp
--- a/UnitTests/Versions.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/UnitTests/Versions.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
TEST(Versions, Zlib)
@@ -40,6 +41,14 @@
}
+TEST(Versions, Lua)
+{
+ // Ensure that the Lua version is above 5.1.0. This version has
+ // introduced some API changes.
+ ASSERT_GE(LUA_VERSION_NUM, 501);
+}
+
+
#if ORTHANC_STATIC == 1
TEST(Versions, ZlibStatic)
{
@@ -48,7 +57,7 @@
TEST(Versions, BoostStatic)
{
- ASSERT_STREQ("1_49", BOOST_LIB_VERSION);
+ ASSERT_STREQ("1_54", BOOST_LIB_VERSION);
}
TEST(Versions, CurlStatic)
@@ -63,7 +72,7 @@
ASSERT_STREQ("1.5.12", PNG_LIBPNG_VER_STRING);
}
-TEST(Versions, CurlSsl)
+TEST(Versions, CurlSslStatic)
{
curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW);
@@ -76,4 +85,10 @@
ASSERT_TRUE(curlSupportsSsl);
#endif
}
+
+TEST(Version, LuaStatic)
+{
+ ASSERT_STREQ("Lua 5.1.5", LUA_RELEASE);
+}
#endif
+
diff -r f6e61e329bbf -r b26a7c397c34 UnitTests/main.cpp
--- a/UnitTests/main.cpp Mon Apr 29 12:48:10 2013 +0200
+++ b/UnitTests/main.cpp Wed Sep 18 15:27:07 2013 +0200
@@ -1,16 +1,18 @@
+#include "../Core/EnumerationDictionary.h"
+
#include "gtest/gtest.h"
#include
#include "../Core/Compression/ZlibCompressor.h"
#include "../Core/DicomFormat/DicomTag.h"
-#include "../OrthancCppClient/HttpClient.h"
#include "../Core/HttpServer/HttpHandler.h"
#include "../Core/OrthancException.h"
#include "../Core/Toolbox.h"
#include "../Core/Uuid.h"
#include "../OrthancServer/FromDcmtkBridge.h"
#include "../OrthancServer/OrthancInitialization.h"
+#include "../Core/MultiThreading/SharedMessageQueue.h"
using namespace Orthanc;
@@ -29,6 +31,23 @@
ASSERT_FALSE(Toolbox::IsUuid(""));
ASSERT_FALSE(Toolbox::IsUuid("012345678901234567890123456789012345"));
ASSERT_TRUE(Toolbox::IsUuid("550e8400-e29b-41d4-a716-446655440000"));
+ ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-44665544000"));
+ ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000"));
+ ASSERT_TRUE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000 ok"));
+ ASSERT_FALSE(Toolbox::StartsWithUuid("550e8400-e29b-41d4-a716-446655440000ok"));
+}
+
+TEST(Toolbox, IsSHA1)
+{
+ ASSERT_FALSE(Toolbox::IsSHA1(""));
+ ASSERT_FALSE(Toolbox::IsSHA1("01234567890123456789012345678901234567890123"));
+ ASSERT_FALSE(Toolbox::IsSHA1("012345678901234567890123456789012345678901234"));
+ ASSERT_TRUE(Toolbox::IsSHA1("b5ed549f-956400ce-69a8c063-bf5b78be-2732a4b9"));
+
+ std::string s;
+ Toolbox::ComputeSHA1(s, "The quick brown fox jumps over the lazy dog");
+ ASSERT_TRUE(Toolbox::IsSHA1(s));
+ ASSERT_EQ("2fd4e1c6-7a2d28fc-ed849ee1-bb76e739-1b93eb12", s);
}
TEST(Zlib, Basic)
@@ -318,6 +337,149 @@
}
+#if defined(__linux)
+TEST(OrthancInitialization, AbsoluteDirectory)
+{
+ ASSERT_EQ("/tmp/hello", InterpretRelativePath("/tmp", "hello"));
+ ASSERT_EQ("/tmp", InterpretRelativePath("/tmp", "/tmp"));
+}
+#endif
+
+
+
+#include "../OrthancServer/ServerEnumerations.h"
+
+TEST(EnumerationDictionary, Simple)
+{
+ Toolbox::EnumerationDictionary d;
+
+ ASSERT_THROW(d.Translate("ReceptionDate"), OrthancException);
+ ASSERT_EQ(MetadataType_ModifiedFrom, d.Translate("5"));
+ ASSERT_EQ(256, d.Translate("256"));
+
+ d.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate");
+
+ ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("ReceptionDate"));
+ ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("2"));
+ ASSERT_EQ("ReceptionDate", d.Translate(MetadataType_Instance_ReceptionDate));
+
+ ASSERT_THROW(d.Add(MetadataType_Instance_ReceptionDate, "Hello"), OrthancException);
+ ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "ReceptionDate"), OrthancException); // already used
+ ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "1024"), OrthancException); // cannot register numbers
+ d.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); // ok
+}
+
+
+TEST(EnumerationDictionary, ServerEnumerations)
+{
+ ASSERT_STREQ("Patient", EnumerationToString(ResourceType_Patient));
+ ASSERT_STREQ("Study", EnumerationToString(ResourceType_Study));
+ ASSERT_STREQ("Series", EnumerationToString(ResourceType_Series));
+ ASSERT_STREQ("Instance", EnumerationToString(ResourceType_Instance));
+
+ ASSERT_STREQ("ModifiedSeries", EnumerationToString(ChangeType_ModifiedSeries));
+
+ ASSERT_STREQ("Failure", EnumerationToString(StoreStatus_Failure));
+ ASSERT_STREQ("Success", EnumerationToString(StoreStatus_Success));
+
+ ASSERT_STREQ("CompletedSeries", EnumerationToString(ChangeType_CompletedSeries));
+
+ ASSERT_EQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries));
+ ASSERT_EQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate));
+
+ ASSERT_EQ(2047, StringToMetadata("2047"));
+ ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException);
+ ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024)
+ ASSERT_THROW(RegisterUserMetadata(128000, ""), OrthancException); // too high (> 65535)
+ RegisterUserMetadata(2047, "Ceci est un test");
+ ASSERT_EQ(2047, StringToMetadata("2047"));
+ ASSERT_EQ(2047, StringToMetadata("Ceci est un test"));
+}
+
+
+
+class DynamicInteger : public IDynamicObject
+{
+private:
+ int value_;
+
+public:
+ DynamicInteger(int value) : value_(value)
+ {
+ }
+
+ int GetValue() const
+ {
+ return value_;
+ }
+};
+
+
+TEST(SharedMessageQueue, Basic)
+{
+ SharedMessageQueue q;
+ ASSERT_TRUE(q.WaitEmpty(0));
+ q.Enqueue(new DynamicInteger(10));
+ ASSERT_FALSE(q.WaitEmpty(1));
+ q.Enqueue(new DynamicInteger(20));
+ q.Enqueue(new DynamicInteger(30));
+ q.Enqueue(new DynamicInteger(40));
+
+ std::auto_ptr i;
+ i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(10, i->GetValue());
+ i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(20, i->GetValue());
+ i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(30, i->GetValue());
+ ASSERT_FALSE(q.WaitEmpty(1));
+ i.reset(dynamic_cast(q.Dequeue(1))); ASSERT_EQ(40, i->GetValue());
+ ASSERT_TRUE(q.WaitEmpty(0));
+ ASSERT_EQ(NULL, q.Dequeue(1));
+}
+
+
+TEST(SharedMessageQueue, Clean)
+{
+ try
+ {
+ SharedMessageQueue q;
+ q.Enqueue(new DynamicInteger(10));
+ q.Enqueue(new DynamicInteger(20));
+ throw OrthancException("Nope");
+ }
+ catch (OrthancException&)
+ {
+ }
+}
+
+
+TEST(Toolbox, WriteFile)
+{
+ std::string path;
+
+ {
+ Toolbox::TemporaryFile tmp;
+ path = tmp.GetPath();
+
+ std::string s;
+ s.append("Hello");
+ s.push_back('\0');
+ s.append("World");
+ ASSERT_EQ(11u, s.size());
+
+ Toolbox::WriteFile(s, path.c_str());
+
+ std::string t;
+ Toolbox::ReadFile(t, path.c_str());
+
+ ASSERT_EQ(11u, t.size());
+ ASSERT_EQ(0, t[5]);
+ ASSERT_EQ(0, memcmp(s.c_str(), t.c_str(), s.size()));
+ }
+
+ std::string u;
+ ASSERT_THROW(Toolbox::ReadFile(u, path.c_str()), OrthancException);
+}
+
+
int main(int argc, char **argv)
{
// Initialize Google's logging library.
@@ -327,6 +489,8 @@
// Go to trace-level verbosity
//FLAGS_v = 1;
+ Toolbox::DetectEndianness();
+
google::InitGoogleLogging("Orthanc");
OrthancInitialize();