[med-svn] [orthanc-dicomweb] 01/06: New upstream version 0.4+dfsg
Sebastien Jodogne
jodogne-guest at moszumanska.debian.org
Thu Jul 20 12:08:36 UTC 2017
This is an automated email from the git hooks/post-receive script.
jodogne-guest pushed a commit to branch master
in repository orthanc-dicomweb.
commit 0e0f2ec615381c0ee278768731e4ac38c9e2b369
Author: jodogne-guest <s.jodogne at gmail.com>
Date: Thu Jul 20 13:48:58 2017 +0200
New upstream version 0.4+dfsg
---
.hg_archival.txt | 7 +-
AUTHORS | 11 +-
CMakeLists.txt | 6 +-
NEWS | 20 +-
Orthanc/Core/ChunkedBuffer.cpp | 1 +
Orthanc/Core/ChunkedBuffer.h | 1 +
Orthanc/Core/Enumerations.cpp | 113 +++-
Orthanc/Core/Enumerations.h | 11 +-
Orthanc/Core/Logging.h | 69 ++-
Orthanc/Core/OrthancException.h | 3 +-
Orthanc/Core/PrecompiledHeaders.h | 4 +-
Orthanc/Core/SystemToolbox.cpp | 552 +++++++++++++++++
.../Core/{OrthancException.h => SystemToolbox.h} | 101 ++--
Orthanc/Core/Toolbox.cpp | 650 ++++++---------------
Orthanc/Core/Toolbox.h | 106 ++--
Orthanc/Core/WebServiceParameters.cpp | 14 +-
Orthanc/Core/WebServiceParameters.h | 7 +
.../Samples/Common/OrthancPluginCppWrapper.cpp | 595 +++++++++++++++----
.../Samples/Common/OrthancPluginCppWrapper.h | 280 ++++++---
.../Samples/Common/OrthancPluginException.h | 101 ++++
Orthanc/Resources/CMake/BoostConfiguration.cmake | 60 +-
Orthanc/Resources/CMake/Compiler.cmake | 29 +-
Orthanc/Resources/CMake/DownloadPackage.cmake | 22 +-
.../Resources/CMake/GoogleTestConfiguration.cmake | 29 +-
Orthanc/Resources/CMake/JsonCppConfiguration.cmake | 2 +-
Orthanc/Resources/CMake/PugixmlConfiguration.cmake | 8 +-
Orthanc/Resources/CMake/ZlibConfiguration.cmake | 6 +-
Orthanc/Resources/ThirdParty/VisualStudio/stdint.h | 506 ++++++++--------
Orthanc/Resources/WindowsResources.py | 1 +
Plugin/Configuration.cpp | 223 ++++---
Plugin/Configuration.h | 1 +
Plugin/Dicom.cpp | 11 +-
Plugin/Dicom.h | 2 +
Plugin/DicomResults.cpp | 17 +-
Plugin/DicomResults.h | 1 +
Plugin/DicomWebClient.cpp | 119 ++--
Plugin/DicomWebClient.h | 1 +
Plugin/DicomWebServers.cpp | 13 +-
Plugin/DicomWebServers.h | 1 +
Plugin/Plugin.cpp | 18 +-
Plugin/Plugin.h | 1 +
Plugin/QidoRs.cpp | 37 +-
Plugin/QidoRs.h | 1 +
Plugin/StowRs.cpp | 7 +
Plugin/StowRs.h | 1 +
Plugin/WadoRs.cpp | 21 +-
Plugin/WadoRs.h | 1 +
Plugin/WadoRsRetrieveFrames.cpp | 133 +++--
Plugin/WadoUri.cpp | 15 +-
Plugin/WadoUri.h | 1 +
README | 5 +-
Resources/BuildInstructions.txt | 11 +-
Resources/CMake/GdcmConfiguration.cmake | 4 +-
Resources/Samples/JavaScript/qido-rs.js | 1 +
Resources/Samples/JavaScript/stow-rs.js | 1 +
Resources/Samples/Python/SendStow.py | 8 +-
Resources/Samples/Python/WadoRetrieveStudy.py | 1 +
Resources/SyncOrthancFolder.py | 3 +
Status.txt | 16 +
UnitTestsSources/UnitTestsMain.cpp | 10 +
Usage.txt | 284 ---------
61 files changed, 2724 insertions(+), 1560 deletions(-)
diff --git a/.hg_archival.txt b/.hg_archival.txt
index 447ae03..7e15e2f 100644
--- a/.hg_archival.txt
+++ b/.hg_archival.txt
@@ -1,5 +1,6 @@
repo: d5f45924411123cfd02d035fd50b8e37536eadef
-node: b6f0743a917faa06e53e0b34d663a74b2ef9d4ab
-branch: OrthancDicomWeb-0.3
+node: f58f8d749b95d85a792e786a0256d0cc2278cc4d
+branch: OrthancDicomWeb-0.4
latesttag: null
-latesttagdistance: 141
+latesttagdistance: 179
+changessincelatesttag: 189
diff --git a/AUTHORS b/AUTHORS
index 2d804cf..bdc3e42 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,8 +6,15 @@ Authors
-------
* Sebastien Jodogne <s.jodogne at gmail.com>
- Department of Medical Physics
+
+ Overall design and lead developer.
+
+* Department of Medical Physics
University Hospital of Liege
+ 4000 Liege
Belgium
- Overall design and lead developer.
+* Osimis <info at osimis.io>
+ Rue des Chasseurs Ardennais 3
+ 4031 Liege
+ Belgium
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d41d2a6..dd4c806 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017 Osimis, Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
@@ -20,7 +21,7 @@ cmake_minimum_required(VERSION 2.8)
project(OrthancDicomWeb)
-set(ORTHANC_DICOM_WEB_VERSION "0.3")
+set(ORTHANC_DICOM_WEB_VERSION "0.4")
# Parameters of the build
@@ -42,6 +43,7 @@ mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE)
set(USE_PUGIXML ON)
set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/Orthanc)
+set(ORTHANC_DISABLE_PATCH ON) # No need for the "patch" command-line tool
include(CheckIncludeFiles)
include(CheckIncludeFileCXX)
include(CheckLibraryExists)
@@ -102,6 +104,7 @@ add_definitions(
-DORTHANC_ENABLE_MD5=0
-DORTHANC_ENABLE_BASE64=0
-DORTHANC_ENABLE_LOGGING=0
+ -DORTHANC_SANDBOXED=0
-DHAS_ORTHANC_EXCEPTION=1
)
@@ -116,6 +119,7 @@ set(CORE_SOURCES
${ORTHANC_ROOT}/Core/ChunkedBuffer.cpp
${ORTHANC_ROOT}/Core/Enumerations.cpp
${ORTHANC_ROOT}/Core/Toolbox.cpp
+ ${ORTHANC_ROOT}/Core/SystemToolbox.cpp
${ORTHANC_ROOT}/Core/WebServiceParameters.cpp
${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
diff --git a/NEWS b/NEWS
index ccf9faf..295a72a 100644
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,19 @@ Pending changes in the mainline
===============================
-Version 0.3 (2016/06/28)
+Version 0.4 (2017-07-19)
+========================
+
+* Improved robustness in the STOW-RS server (occurrences of "\r\n\r\n" in DICOM are supported)
+* Performance warning if runtime debug assertions are turned on
+* WADO-RS client supports quoted Content-Type header in HTTP answers
+* Added "Arguments" to WADO-RS and STOW-RS client to handle query arguments in uri
+* Using MIME types of DICOM version 2017c in WADO RetrieveFrames
+* Fix issue #53 (DICOMWeb plugin support for "limit" and "offset" parameters in QIDO-RS)
+* Fix issue #28 (Non-compliant enumerations for "accept" header for WADO RetrieveFrames)
+
+
+Version 0.3 (2016-06-28)
========================
=> Minimum SDK version: 1.1.0 <=
@@ -15,7 +27,7 @@ Version 0.3 (2016/06/28)
* Fix issue #14 (Aggregate fields empty for QIDO-RS study/series-level queries)
-Version 0.2 (2015/12/10)
+Version 0.2 (2015-12-10)
========================
=> Minimum SDK version: 0.9.5 <=
@@ -25,7 +37,7 @@ Version 0.2 (2015/12/10)
* Upgrade to GDCM 2.6.0 for static and Windows builds
-Version 0.1 (2015/08/03)
+Version 0.1 (2015-08-03)
========================
=> Minimum SDK version: 0.9.1 <=
@@ -45,7 +57,7 @@ Production
* Upgrade to Boost 1.58.0 for static and Windows builds
-2015/03/13
+2015-03-13
==========
* Initial commit
diff --git a/Orthanc/Core/ChunkedBuffer.cpp b/Orthanc/Core/ChunkedBuffer.cpp
index 5d2c2c8..22a3c0e 100644
--- a/Orthanc/Core/ChunkedBuffer.cpp
+++ b/Orthanc/Core/ChunkedBuffer.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
diff --git a/Orthanc/Core/ChunkedBuffer.h b/Orthanc/Core/ChunkedBuffer.h
index 552c1ec..05c724e 100644
--- a/Orthanc/Core/ChunkedBuffer.h
+++ b/Orthanc/Core/ChunkedBuffer.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
diff --git a/Orthanc/Core/Enumerations.cpp b/Orthanc/Core/Enumerations.cpp
index e600828..8309e1a 100644
--- a/Orthanc/Core/Enumerations.cpp
+++ b/Orthanc/Core/Enumerations.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -64,7 +65,7 @@ namespace Orthanc
return "Parameter out of range";
case ErrorCode_NotEnoughMemory:
- return "Not enough memory";
+ return "The server hosting Orthanc is running out of memory";
case ErrorCode_BadParameterType:
return "Bad type for a parameter";
@@ -156,6 +157,9 @@ namespace Orthanc
case ErrorCode_NotAcceptable:
return "Cannot send a response which is acceptable according to the Accept HTTP header";
+ case ErrorCode_NullPointer:
+ return "Cannot handle a NULL pointer";
+
case ErrorCode_SQLiteNotOpened:
return "SQLite: The database is not opened";
@@ -632,10 +636,10 @@ namespace Orthanc
return "RGB";
case PhotometricInterpretation_Monochrome1:
- return "Monochrome1";
+ return "MONOCHROME1";
case PhotometricInterpretation_Monochrome2:
- return "Monochrome2";
+ return "MONOCHROME2";
case PhotometricInterpretation_ARGB:
return "ARGB";
@@ -647,25 +651,25 @@ namespace Orthanc
return "HSV";
case PhotometricInterpretation_Palette:
- return "Palette color";
+ return "PALETTE COLOR";
case PhotometricInterpretation_YBRFull:
- return "YBR full";
+ return "YBR_FULL";
case PhotometricInterpretation_YBRFull422:
- return "YBR full 422";
+ return "YBR_FULL_422";
case PhotometricInterpretation_YBRPartial420:
- return "YBR partial 420";
+ return "YBR_PARTIAL_420";
case PhotometricInterpretation_YBRPartial422:
- return "YBR partial 422";
+ return "YBR_PARTIAL_422";
case PhotometricInterpretation_YBR_ICT:
- return "YBR ICT";
+ return "YBR_ICT";
case PhotometricInterpretation_YBR_RCT:
- return "YBR RCT";
+ return "YBR_RCT";
case PhotometricInterpretation_Unknown:
return "Unknown";
@@ -733,6 +737,9 @@ namespace Orthanc
case PixelFormat_RGBA32:
return "RGBA32";
+ case PixelFormat_BGRA32:
+ return "BGRA32";
+
case PixelFormat_Grayscale8:
return "Grayscale (unsigned 8bpp)";
@@ -1046,6 +1053,80 @@ namespace Orthanc
}
+ PhotometricInterpretation StringToPhotometricInterpretation(const char* value)
+ {
+ // http://dicom.nema.org/medical/dicom/2017a/output/chtml/part03/sect_C.7.6.3.html#sect_C.7.6.3.1.2
+ std::string s(value);
+
+ if (s == "MONOCHROME1")
+ {
+ return PhotometricInterpretation_Monochrome1;
+ }
+
+ if (s == "MONOCHROME2")
+ {
+ return PhotometricInterpretation_Monochrome2;
+ }
+
+ if (s == "PALETTE COLOR")
+ {
+ return PhotometricInterpretation_Palette;
+ }
+
+ if (s == "RGB")
+ {
+ return PhotometricInterpretation_RGB;
+ }
+
+ if (s == "HSV")
+ {
+ return PhotometricInterpretation_HSV;
+ }
+
+ if (s == "ARGB")
+ {
+ return PhotometricInterpretation_ARGB;
+ }
+
+ if (s == "CMYK")
+ {
+ return PhotometricInterpretation_CMYK;
+ }
+
+ if (s == "YBR_FULL")
+ {
+ return PhotometricInterpretation_YBRFull;
+ }
+
+ if (s == "YBR_FULL_422")
+ {
+ return PhotometricInterpretation_YBRFull422;
+ }
+
+ if (s == "YBR_PARTIAL_422")
+ {
+ return PhotometricInterpretation_YBRPartial422;
+ }
+
+ if (s == "YBR_PARTIAL_420")
+ {
+ return PhotometricInterpretation_YBRPartial420;
+ }
+
+ if (s == "YBR_ICT")
+ {
+ return PhotometricInterpretation_YBR_ICT;
+ }
+
+ if (s == "YBR_RCT")
+ {
+ return PhotometricInterpretation_YBR_RCT;
+ }
+
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+
unsigned int GetBytesPerPixel(PixelFormat format)
{
switch (format)
@@ -1061,6 +1142,7 @@ namespace Orthanc
return 3;
case PixelFormat_RGBA32:
+ case PixelFormat_BGRA32:
return 4;
case PixelFormat_Float32:
@@ -1076,15 +1158,18 @@ namespace Orthanc
bool GetDicomEncoding(Encoding& encoding,
const char* specificCharacterSet)
{
- std::string s = specificCharacterSet;
+ std::string s = Toolbox::StripSpaces(specificCharacterSet);
Toolbox::ToUpperCase(s);
// http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
// https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java
if (s == "ISO_IR 6" ||
- s == "ISO_IR 192" ||
s == "ISO 2022 IR 6")
{
+ encoding = Encoding_Ascii;
+ }
+ else if (s == "ISO_IR 192")
+ {
encoding = Encoding_Utf8;
}
else if (s == "ISO_IR 100" ||
@@ -1231,8 +1316,10 @@ namespace Orthanc
// http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
switch (encoding)
{
- case Encoding_Utf8:
case Encoding_Ascii:
+ return "ISO_IR 6";
+
+ case Encoding_Utf8:
return "ISO_IR 192";
case Encoding_Latin1:
diff --git a/Orthanc/Core/Enumerations.h b/Orthanc/Core/Enumerations.h
index 7eef119..aae6627 100644
--- a/Orthanc/Core/Enumerations.h
+++ b/Orthanc/Core/Enumerations.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -52,7 +53,7 @@ namespace Orthanc
ErrorCode_Plugin = 1 /*!< Error encountered within the plugin engine */,
ErrorCode_NotImplemented = 2 /*!< Not implemented yet */,
ErrorCode_ParameterOutOfRange = 3 /*!< Parameter out of range */,
- ErrorCode_NotEnoughMemory = 4 /*!< Not enough memory */,
+ ErrorCode_NotEnoughMemory = 4 /*!< The server hosting Orthanc is running out of memory */,
ErrorCode_BadParameterType = 5 /*!< Bad type for a parameter */,
ErrorCode_BadSequenceOfCalls = 6 /*!< Bad sequence of calls */,
ErrorCode_InexistentItem = 7 /*!< Accessing an inexistent item */,
@@ -83,6 +84,7 @@ namespace Orthanc
ErrorCode_StorageAreaPlugin = 32 /*!< Error in the plugin implementing a custom storage area */,
ErrorCode_EmptyRequest = 33 /*!< The request is empty */,
ErrorCode_NotAcceptable = 34 /*!< Cannot send a response which is acceptable according to the Accept HTTP header */,
+ ErrorCode_NullPointer = 35 /*!< Cannot handle a NULL pointer */,
ErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */,
ErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */,
ErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */,
@@ -195,7 +197,10 @@ namespace Orthanc
* {summary}{Graylevel, floating-point image.}
* {description}{The image is graylevel. Each pixel is floating-point and stored in 4 bytes.}
**/
- PixelFormat_Float32 = 6
+ PixelFormat_Float32 = 6,
+
+ // This is the memory layout for Cairo
+ PixelFormat_BGRA32 = 7
};
@@ -519,6 +524,8 @@ namespace Orthanc
ValueRepresentation StringToValueRepresentation(const std::string& vr,
bool throwIfUnsupported);
+ PhotometricInterpretation StringToPhotometricInterpretation(const char* value);
+
unsigned int GetBytesPerPixel(PixelFormat format);
bool GetDicomEncoding(Encoding& encoding,
diff --git a/Orthanc/Core/Logging.h b/Orthanc/Core/Logging.h
index 7318246..8c419e2 100644
--- a/Orthanc/Core/Logging.h
+++ b/Orthanc/Core/Logging.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -34,11 +35,31 @@
#include <iostream>
+#if !defined(ORTHANC_ENABLE_LOGGING)
+# error The macro ORTHANC_ENABLE_LOGGING must be defined
+#endif
+
+#if !defined(ORTHANC_ENABLE_LOGGING_PLUGIN)
+# if ORTHANC_ENABLE_LOGGING == 1
+# error The macro ORTHANC_ENABLE_LOGGING_PLUGIN must be defined
+# else
+# define ORTHANC_ENABLE_LOGGING_PLUGIN 0
+# endif
+#endif
+
+#if ORTHANC_ENABLE_LOGGING_PLUGIN == 1
+# include <orthanc/OrthancCPlugin.h>
+#endif
+
namespace Orthanc
{
namespace Logging
{
+#if ORTHANC_ENABLE_LOGGING_PLUGIN == 1
+ void Initialize(OrthancPluginContext* context);
+#else
void Initialize();
+#endif
void Finalize();
@@ -66,6 +87,12 @@ namespace Orthanc
{
return *this;
}
+
+ // This overload fixes build problems with Visual Studio 2015
+ std::ostream& operator<< (const char* message)
+ {
+ return *this;
+ }
};
}
}
@@ -76,7 +103,41 @@ namespace Orthanc
# define LOG(level) ::Orthanc::Logging::NullStream()
# define VLOG(level) ::Orthanc::Logging::NullStream()
-#else /* ORTHANC_ENABLE_LOGGING == 1 */
+
+#elif ORTHANC_ENABLE_LOGGING_PLUGIN == 1
+
+# include <boost/noncopyable.hpp>
+# define LOG(level) ::Orthanc::Logging::InternalLogger(#level, __FILE__, __LINE__)
+# define VLOG(level) ::Orthanc::Logging::InternalLogger("TRACE", __FILE__, __LINE__)
+
+namespace Orthanc
+{
+ namespace Logging
+ {
+ class InternalLogger : public boost::noncopyable
+ {
+ private:
+ std::string level_;
+ std::string message_;
+
+ public:
+ InternalLogger(const char* level,
+ const char* file,
+ int line);
+
+ ~InternalLogger();
+
+ InternalLogger& operator<< (const std::string& message);
+
+ InternalLogger& operator<< (const char* message);
+
+ InternalLogger& operator<< (int message);
+ };
+ }
+}
+
+
+#else /* ORTHANC_ENABLE_LOGGING_PLUGIN == 0 && ORTHANC_ENABLE_LOGGING == 1 */
# include <boost/thread/mutex.hpp>
# define LOG(level) ::Orthanc::Logging::InternalLogger(#level, __FILE__, __LINE__)
@@ -104,6 +165,12 @@ namespace Orthanc
{
return (*stream_) << message;
}
+
+ // This overload fixes build problems with Visual Studio 2015
+ std::ostream& operator<< (const char* message)
+ {
+ return (*stream_) << message;
+ }
};
}
}
diff --git a/Orthanc/Core/OrthancException.h b/Orthanc/Core/OrthancException.h
index 5afa41f..ee9b603 100644
--- a/Orthanc/Core/OrthancException.h
+++ b/Orthanc/Core/OrthancException.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -45,7 +46,7 @@ namespace Orthanc
HttpStatus httpStatus_;
public:
- OrthancException(ErrorCode errorCode) :
+ explicit OrthancException(ErrorCode errorCode) :
errorCode_(errorCode),
httpStatus_(ConvertErrorCodeToHttpStatus(errorCode))
{
diff --git a/Orthanc/Core/PrecompiledHeaders.h b/Orthanc/Core/PrecompiledHeaders.h
index e22f012..4fc6435 100644
--- a/Orthanc/Core/PrecompiledHeaders.h
+++ b/Orthanc/Core/PrecompiledHeaders.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -48,7 +49,7 @@
#include <json/value.h>
-#if ORTHANC_PUGIXML_ENABLED == 1
+#if ORTHANC_ENABLE_PUGIXML == 1
#include <pugixml.hpp>
#endif
@@ -56,6 +57,5 @@
#include "Logging.h"
#include "OrthancException.h"
#include "Toolbox.h"
-#include "Uuid.h"
#endif
diff --git a/Orthanc/Core/SystemToolbox.cpp b/Orthanc/Core/SystemToolbox.cpp
new file mode 100644
index 0000000..3e4e36d
--- /dev/null
+++ b/Orthanc/Core/SystemToolbox.cpp
@@ -0,0 +1,552 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeaders.h"
+#include "SystemToolbox.h"
+
+
+#if BOOST_HAS_DATE_TIME == 1
+# include <boost/date_time/posix_time/posix_time.hpp>
+#endif
+
+
+#if defined(_WIN32)
+# include <windows.h>
+# include <process.h> // For "_spawnvp()" and "_getpid()"
+#else
+# include <unistd.h> // For "execvp()"
+# include <sys/wait.h> // For "waitpid()"
+#endif
+
+
+#if defined(__APPLE__) && defined(__MACH__)
+# include <mach-o/dyld.h> /* _NSGetExecutablePath */
+# include <limits.h> /* PATH_MAX */
+#endif
+
+
+#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+# include <limits.h> /* PATH_MAX */
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+
+// Inclusions for UUID
+// http://stackoverflow.com/a/1626302
+
+extern "C"
+{
+#ifdef WIN32
+# include <rpc.h>
+#else
+# include <uuid/uuid.h>
+#endif
+}
+
+
+#include "Logging.h"
+#include "OrthancException.h"
+#include "Toolbox.h"
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+
+namespace Orthanc
+{
+ static bool finish_;
+ static ServerBarrierEvent barrierEvent_;
+
+#if defined(_WIN32)
+ static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType)
+ {
+ // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
+ finish_ = true;
+ return true;
+ }
+#else
+ static void SignalHandler(int signal)
+ {
+ if (signal == SIGHUP)
+ {
+ barrierEvent_ = ServerBarrierEvent_Reload;
+ }
+
+ finish_ = true;
+ }
+#endif
+
+
+ static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag)
+ {
+#if defined(_WIN32)
+ SetConsoleCtrlHandler(ConsoleControlHandler, true);
+#else
+ signal(SIGINT, SignalHandler);
+ signal(SIGQUIT, SignalHandler);
+ signal(SIGTERM, SignalHandler);
+ signal(SIGHUP, SignalHandler);
+#endif
+
+ // Active loop that awakens every 100ms
+ finish_ = false;
+ barrierEvent_ = ServerBarrierEvent_Stop;
+ while (!(*stopFlag || finish_))
+ {
+ SystemToolbox::USleep(100 * 1000);
+ }
+
+#if defined(_WIN32)
+ SetConsoleCtrlHandler(ConsoleControlHandler, false);
+#else
+ signal(SIGINT, NULL);
+ signal(SIGQUIT, NULL);
+ signal(SIGTERM, NULL);
+ signal(SIGHUP, NULL);
+#endif
+
+ return barrierEvent_;
+ }
+
+
+ ServerBarrierEvent SystemToolbox::ServerBarrier(const bool& stopFlag)
+ {
+ return ServerBarrierInternal(&stopFlag);
+ }
+
+
+ ServerBarrierEvent SystemToolbox::ServerBarrier()
+ {
+ const bool stopFlag = false;
+ return ServerBarrierInternal(&stopFlag);
+ }
+
+
+ void SystemToolbox::USleep(uint64_t microSeconds)
+ {
+#if defined(_WIN32)
+ ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__native_client__)
+ usleep(microSeconds);
+#else
+#error Support your platform here
+#endif
+ }
+
+
+ static std::streamsize GetStreamSize(std::istream& f)
+ {
+ // http://www.cplusplus.com/reference/iostream/istream/tellg/
+ f.seekg(0, std::ios::end);
+ std::streamsize size = f.tellg();
+ f.seekg(0, std::ios::beg);
+
+ return size;
+ }
+
+
+ void SystemToolbox::ReadFile(std::string& content,
+ const std::string& path)
+ {
+ if (!IsRegularFile(path))
+ {
+ LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
+ throw OrthancException(ErrorCode_RegularFileExpected);
+ }
+
+ boost::filesystem::ifstream f;
+ f.open(path, std::ifstream::in | std::ifstream::binary);
+ if (!f.good())
+ {
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ std::streamsize size = GetStreamSize(f);
+ content.resize(size);
+ if (size != 0)
+ {
+ f.read(reinterpret_cast<char*>(&content[0]), size);
+ }
+
+ f.close();
+ }
+
+
+ bool SystemToolbox::ReadHeader(std::string& header,
+ const std::string& path,
+ size_t headerSize)
+ {
+ if (!IsRegularFile(path))
+ {
+ LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
+ throw OrthancException(ErrorCode_RegularFileExpected);
+ }
+
+ boost::filesystem::ifstream f;
+ f.open(path, std::ifstream::in | std::ifstream::binary);
+ if (!f.good())
+ {
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+
+ bool full = true;
+
+ {
+ std::streamsize size = GetStreamSize(f);
+ if (size <= 0)
+ {
+ headerSize = 0;
+ full = false;
+ }
+ else if (static_cast<size_t>(size) < headerSize)
+ {
+ headerSize = size; // Truncate to the size of the file
+ full = false;
+ }
+ }
+
+ header.resize(headerSize);
+ if (headerSize != 0)
+ {
+ f.read(reinterpret_cast<char*>(&header[0]), headerSize);
+ }
+
+ f.close();
+
+ return full;
+ }
+
+
+ void SystemToolbox::WriteFile(const void* content,
+ size_t size,
+ const std::string& path)
+ {
+ boost::filesystem::ofstream f;
+ f.open(path, std::ofstream::out | std::ofstream::binary);
+ if (!f.good())
+ {
+ throw OrthancException(ErrorCode_CannotWriteFile);
+ }
+
+ if (size != 0)
+ {
+ f.write(reinterpret_cast<const char*>(content), size);
+
+ if (!f.good())
+ {
+ f.close();
+ throw OrthancException(ErrorCode_FileStorageCannotWrite);
+ }
+ }
+
+ f.close();
+ }
+
+
+ void SystemToolbox::WriteFile(const std::string& content,
+ const std::string& path)
+ {
+ WriteFile(content.size() > 0 ? content.c_str() : NULL,
+ content.size(), path);
+ }
+
+
+ void SystemToolbox::RemoveFile(const std::string& path)
+ {
+ if (boost::filesystem::exists(path))
+ {
+ if (IsRegularFile(path))
+ {
+ boost::filesystem::remove(path);
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_RegularFileExpected);
+ }
+ }
+ }
+
+
+ uint64_t SystemToolbox::GetFileSize(const std::string& path)
+ {
+ try
+ {
+ return static_cast<uint64_t>(boost::filesystem::file_size(path));
+ }
+ catch (boost::filesystem::filesystem_error&)
+ {
+ throw OrthancException(ErrorCode_InexistentFile);
+ }
+ }
+
+
+ void SystemToolbox::MakeDirectory(const std::string& path)
+ {
+ if (boost::filesystem::exists(path))
+ {
+ if (!boost::filesystem::is_directory(path))
+ {
+ throw OrthancException(ErrorCode_DirectoryOverFile);
+ }
+ }
+ else
+ {
+ if (!boost::filesystem::create_directories(path))
+ {
+ throw OrthancException(ErrorCode_MakeDirectory);
+ }
+ }
+ }
+
+
+ bool SystemToolbox::IsExistingFile(const std::string& path)
+ {
+ return boost::filesystem::exists(path);
+ }
+
+
+#if defined(_WIN32)
+ static std::string GetPathToExecutableInternal()
+ {
+ // Yes, this is ugly, but there is no simple way to get the
+ // required buffer size, so we use a big constant
+ std::vector<char> buffer(32768);
+ /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast<DWORD>(buffer.size() - 1));
+ return std::string(&buffer[0]);
+ }
+
+#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
+ static std::string GetPathToExecutableInternal()
+ {
+ std::vector<char> buffer(PATH_MAX + 1);
+ ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1);
+ if (bytes == 0)
+ {
+ throw OrthancException(ErrorCode_PathToExecutable);
+ }
+
+ return std::string(&buffer[0]);
+ }
+
+#elif defined(__APPLE__) && defined(__MACH__)
+ static std::string GetPathToExecutableInternal()
+ {
+ char pathbuf[PATH_MAX + 1];
+ unsigned int bufsize = static_cast<int>(sizeof(pathbuf));
+
+ _NSGetExecutablePath( pathbuf, &bufsize);
+
+ return std::string(pathbuf);
+ }
+
+#else
+#error Support your platform here
+#endif
+
+
+ std::string SystemToolbox::GetPathToExecutable()
+ {
+ boost::filesystem::path p(GetPathToExecutableInternal());
+ return boost::filesystem::absolute(p).string();
+ }
+
+
+ std::string SystemToolbox::GetDirectoryOfExecutable()
+ {
+ boost::filesystem::path p(GetPathToExecutableInternal());
+ return boost::filesystem::absolute(p.parent_path()).string();
+ }
+
+
+ void SystemToolbox::ExecuteSystemCommand(const std::string& command,
+ const std::vector<std::string>& arguments)
+ {
+ // Convert the arguments as a C array
+ std::vector<char*> args(arguments.size() + 2);
+
+ args.front() = const_cast<char*>(command.c_str());
+
+ for (size_t i = 0; i < arguments.size(); i++)
+ {
+ args[i + 1] = const_cast<char*>(arguments[i].c_str());
+ }
+
+ args.back() = NULL;
+
+ int status;
+
+#if defined(_WIN32)
+ // http://msdn.microsoft.com/en-us/library/275khfab.aspx
+ status = static_cast<int>(_spawnvp(_P_OVERLAY, command.c_str(), &args[0]));
+
+#else
+ int pid = fork();
+
+ if (pid == -1)
+ {
+ // Error in fork()
+#if ORTHANC_ENABLE_LOGGING == 1
+ LOG(ERROR) << "Cannot fork a child process";
+#endif
+
+ throw OrthancException(ErrorCode_SystemCommand);
+ }
+ else if (pid == 0)
+ {
+ // Execute the system command in the child process
+ execvp(command.c_str(), &args[0]);
+
+ // We should never get here
+ _exit(1);
+ }
+ else
+ {
+ // Wait for the system command to exit
+ waitpid(pid, &status, 0);
+ }
+#endif
+
+ if (status != 0)
+ {
+#if ORTHANC_ENABLE_LOGGING == 1
+ LOG(ERROR) << "System command failed with status code " << status;
+#endif
+
+ throw OrthancException(ErrorCode_SystemCommand);
+ }
+ }
+
+
+ int SystemToolbox::GetProcessId()
+ {
+#if defined(_WIN32)
+ return static_cast<int>(_getpid());
+#else
+ return static_cast<int>(getpid());
+#endif
+ }
+
+
+ bool SystemToolbox::IsRegularFile(const std::string& path)
+ {
+ namespace fs = boost::filesystem;
+
+ try
+ {
+ if (fs::exists(path))
+ {
+ fs::file_status status = fs::status(path);
+ return (status.type() == boost::filesystem::regular_file ||
+ status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11
+ }
+ }
+ catch (fs::filesystem_error&)
+ {
+ }
+
+ return false;
+ }
+
+
+ FILE* SystemToolbox::OpenFile(const std::string& path,
+ FileMode mode)
+ {
+#if defined(_WIN32)
+ // TODO Deal with special characters by converting to the current locale
+#endif
+
+ const char* m;
+ switch (mode)
+ {
+ case FileMode_ReadBinary:
+ m = "rb";
+ break;
+
+ case FileMode_WriteBinary:
+ m = "wb";
+ break;
+
+ default:
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+
+ return fopen(path.c_str(), m);
+ }
+
+
+ std::string SystemToolbox::GenerateUuid()
+ {
+#ifdef WIN32
+ UUID uuid;
+ UuidCreate ( &uuid );
+
+ unsigned char * str;
+ UuidToStringA ( &uuid, &str );
+
+ std::string s( ( char* ) str );
+
+ RpcStringFreeA ( &str );
+#else
+ uuid_t uuid;
+ uuid_generate_random ( uuid );
+ char s[37];
+ uuid_unparse ( uuid, s );
+#endif
+ return s;
+ }
+
+
+#if BOOST_HAS_DATE_TIME == 1
+ std::string SystemToolbox::GetNowIsoString()
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ return boost::posix_time::to_iso_string(now);
+ }
+
+ void SystemToolbox::GetNowDicom(std::string& date,
+ std::string& time)
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ tm tm = boost::posix_time::to_tm(now);
+
+ char s[32];
+ sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ date.assign(s);
+
+ // TODO milliseconds
+ sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
+ time.assign(s);
+ }
+#endif
+}
diff --git a/Orthanc/Core/OrthancException.h b/Orthanc/Core/SystemToolbox.h
similarity index 51%
copy from Orthanc/Core/OrthancException.h
copy to Orthanc/Core/SystemToolbox.h
index 5afa41f..0b3fe3f 100644
--- a/Orthanc/Core/OrthancException.h
+++ b/Orthanc/Core/SystemToolbox.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -32,45 +33,73 @@
#pragma once
-#include <stdint.h>
-#include <string>
+#if !defined(ORTHANC_SANDBOXED)
+# error The macro ORTHANC_SANDBOXED must be defined
+#endif
+
+#if ORTHANC_SANDBOXED == 1
+# error The namespace SystemToolbox cannot be used in sandboxed environments
+#endif
+
#include "Enumerations.h"
+#include <vector>
+#include <string>
+#include <stdint.h>
+
namespace Orthanc
{
- class OrthancException
+ namespace SystemToolbox
{
- protected:
- ErrorCode errorCode_;
- HttpStatus httpStatus_;
-
- public:
- OrthancException(ErrorCode errorCode) :
- errorCode_(errorCode),
- httpStatus_(ConvertErrorCodeToHttpStatus(errorCode))
- {
- }
-
- OrthancException(ErrorCode errorCode,
- HttpStatus httpStatus) :
- errorCode_(errorCode),
- httpStatus_(httpStatus)
- {
- }
-
- ErrorCode GetErrorCode() const
- {
- return errorCode_;
- }
-
- HttpStatus GetHttpStatus() const
- {
- return httpStatus_;
- }
-
- const char* What() const
- {
- return EnumerationToString(errorCode_);
- }
- };
+ void USleep(uint64_t microSeconds);
+
+ ServerBarrierEvent ServerBarrier(const bool& stopFlag);
+
+ ServerBarrierEvent ServerBarrier();
+
+ void ReadFile(std::string& content,
+ const std::string& path);
+
+ bool ReadHeader(std::string& header,
+ const std::string& path,
+ size_t headerSize);
+
+ void WriteFile(const void* content,
+ size_t size,
+ const std::string& path);
+
+ void WriteFile(const std::string& content,
+ const std::string& path);
+
+ void RemoveFile(const std::string& path);
+
+ uint64_t GetFileSize(const std::string& path);
+
+ void MakeDirectory(const std::string& path);
+
+ bool IsExistingFile(const std::string& path);
+
+ std::string GetPathToExecutable();
+
+ std::string GetDirectoryOfExecutable();
+
+ void ExecuteSystemCommand(const std::string& command,
+ const std::vector<std::string>& arguments);
+
+ int GetProcessId();
+
+ bool IsRegularFile(const std::string& path);
+
+ FILE* OpenFile(const std::string& path,
+ FileMode mode);
+
+ std::string GenerateUuid();
+
+#if BOOST_HAS_DATE_TIME == 1
+ std::string GetNowIsoString();
+
+ void GetNowDicom(std::string& date,
+ std::string& time);
+#endif
+ }
}
diff --git a/Orthanc/Core/Toolbox.cpp b/Orthanc/Core/Toolbox.cpp
index 4990204..4a67878 100644
--- a/Orthanc/Core/Toolbox.cpp
+++ b/Orthanc/Core/Toolbox.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -36,57 +37,31 @@
#include "OrthancException.h"
#include "Logging.h"
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/locale.hpp>
+#include <boost/uuid/sha1.hpp>
+
#include <string>
#include <stdint.h>
#include <string.h>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/uuid/sha1.hpp>
-#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <ctype.h>
-#if BOOST_HAS_DATE_TIME == 1
-#include <boost/date_time/posix_time/posix_time.hpp>
-#endif
-
#if BOOST_HAS_REGEX == 1
-#include <boost/regex.hpp>
-#endif
-
-#if defined(_WIN32)
-#include <windows.h>
-#include <process.h> // For "_spawnvp()" and "_getpid()"
-#else
-#include <unistd.h> // For "execvp()"
-#include <sys/wait.h> // For "waitpid()"
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#include <mach-o/dyld.h> /* _NSGetExecutablePath */
-#include <limits.h> /* PATH_MAX */
-#endif
-
-#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
-#include <limits.h> /* PATH_MAX */
-#include <signal.h>
-#include <unistd.h>
+# include <boost/regex.hpp>
#endif
#if BOOST_HAS_LOCALE != 1
-#error Since version 0.7.6, Orthanc entirely relies on boost::locale
+# error Since version 0.7.6, Orthanc entirely relies on boost::locale
#endif
-#include <boost/locale.hpp>
-
-
-#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
-#include "../Resources/ThirdParty/md5/md5.h"
+#if ORTHANC_ENABLE_MD5 == 1
+# include "../Resources/ThirdParty/md5/md5.h"
#endif
-
-#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
-#include "../Resources/ThirdParty/base64/base64.h"
+#if ORTHANC_ENABLE_BASE64 == 1
+# include "../Resources/ThirdParty/base64/base64.h"
#endif
@@ -103,93 +78,19 @@ extern "C"
#endif
-#if ORTHANC_PUGIXML_ENABLED == 1
-#include "ChunkedBuffer.h"
-#include <pugixml.hpp>
-#endif
-
-
-namespace Orthanc
-{
- static bool finish_;
- static ServerBarrierEvent barrierEvent_;
-
-#if defined(_WIN32)
- static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType)
- {
- // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
- finish_ = true;
- return true;
- }
-#else
- static void SignalHandler(int signal)
- {
- if (signal == SIGHUP)
- {
- barrierEvent_ = ServerBarrierEvent_Reload;
- }
-
- finish_ = true;
- }
-#endif
-
-
- void Toolbox::USleep(uint64_t microSeconds)
- {
#if defined(_WIN32)
- ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
- usleep(microSeconds);
-#else
-#error Support your platform here
+# include <windows.h> // For ::Sleep
#endif
- }
- static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag)
- {
-#if defined(_WIN32)
- SetConsoleCtrlHandler(ConsoleControlHandler, true);
-#else
- signal(SIGINT, SignalHandler);
- signal(SIGQUIT, SignalHandler);
- signal(SIGTERM, SignalHandler);
- signal(SIGHUP, SignalHandler);
-#endif
-
- // Active loop that awakens every 100ms
- finish_ = false;
- barrierEvent_ = ServerBarrierEvent_Stop;
- while (!(*stopFlag || finish_))
- {
- Toolbox::USleep(100 * 1000);
- }
-
-#if defined(_WIN32)
- SetConsoleCtrlHandler(ConsoleControlHandler, false);
-#else
- signal(SIGINT, NULL);
- signal(SIGQUIT, NULL);
- signal(SIGTERM, NULL);
- signal(SIGHUP, NULL);
+#if ORTHANC_ENABLE_PUGIXML == 1
+# include "ChunkedBuffer.h"
+# include <pugixml.hpp>
#endif
- return barrierEvent_;
- }
-
-
- ServerBarrierEvent Toolbox::ServerBarrier(const bool& stopFlag)
- {
- return ServerBarrierInternal(&stopFlag);
- }
-
- ServerBarrierEvent Toolbox::ServerBarrier()
- {
- const bool stopFlag = false;
- return ServerBarrierInternal(&stopFlag);
- }
-
+namespace Orthanc
+{
void Toolbox::ToUpperCase(std::string& s)
{
std::transform(s.begin(), s.end(), s.begin(), toupper);
@@ -217,134 +118,6 @@ namespace Orthanc
}
- static std::streamsize GetStreamSize(std::istream& f)
- {
- // http://www.cplusplus.com/reference/iostream/istream/tellg/
- f.seekg(0, std::ios::end);
- std::streamsize size = f.tellg();
- f.seekg(0, std::ios::beg);
-
- return size;
- }
-
-
- void Toolbox::ReadFile(std::string& content,
- const std::string& path)
- {
- if (!IsRegularFile(path))
- {
- LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
- throw OrthancException(ErrorCode_RegularFileExpected);
- }
-
- boost::filesystem::ifstream f;
- f.open(path, std::ifstream::in | std::ifstream::binary);
- if (!f.good())
- {
- throw OrthancException(ErrorCode_InexistentFile);
- }
-
- std::streamsize size = GetStreamSize(f);
- content.resize(size);
- if (size != 0)
- {
- f.read(reinterpret_cast<char*>(&content[0]), size);
- }
-
- f.close();
- }
-
-
- bool Toolbox::ReadHeader(std::string& header,
- const std::string& path,
- size_t headerSize)
- {
- if (!IsRegularFile(path))
- {
- LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
- throw OrthancException(ErrorCode_RegularFileExpected);
- }
-
- boost::filesystem::ifstream f;
- f.open(path, std::ifstream::in | std::ifstream::binary);
- if (!f.good())
- {
- throw OrthancException(ErrorCode_InexistentFile);
- }
-
- bool full = true;
-
- {
- std::streamsize size = GetStreamSize(f);
- if (size <= 0)
- {
- headerSize = 0;
- full = false;
- }
- else if (static_cast<size_t>(size) < headerSize)
- {
- headerSize = size; // Truncate to the size of the file
- full = false;
- }
- }
-
- header.resize(headerSize);
- if (headerSize != 0)
- {
- f.read(reinterpret_cast<char*>(&header[0]), headerSize);
- }
-
- f.close();
-
- return full;
- }
-
-
- void Toolbox::WriteFile(const void* content,
- size_t size,
- const std::string& path)
- {
- boost::filesystem::ofstream f;
- f.open(path, std::ofstream::binary);
- if (!f.good())
- {
- throw OrthancException(ErrorCode_CannotWriteFile);
- }
-
- if (size != 0)
- {
- f.write(reinterpret_cast<const char*>(content), size);
- }
-
- f.close();
- }
-
-
- void Toolbox::WriteFile(const std::string& content,
- const std::string& path)
- {
- WriteFile(content.size() > 0 ? content.c_str() : NULL,
- content.size(), path);
- }
-
-
- void Toolbox::RemoveFile(const std::string& path)
- {
- if (boost::filesystem::exists(path))
- {
- if (IsRegularFile(path))
- {
- boost::filesystem::remove(path);
- }
- else
- {
- throw OrthancException(ErrorCode_RegularFileExpected);
- }
- }
- }
-
-
-
void Toolbox::SplitUriComponents(UriComponents& components,
const std::string& uri)
{
@@ -512,21 +285,7 @@ namespace Orthanc
}
-
- uint64_t Toolbox::GetFileSize(const std::string& path)
- {
- try
- {
- return static_cast<uint64_t>(boost::filesystem::file_size(path));
- }
- catch (boost::filesystem::filesystem_error&)
- {
- throw OrthancException(ErrorCode_InexistentFile);
- }
- }
-
-
-#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
+#if ORTHANC_ENABLE_MD5 == 1
static char GetHexadecimalCharacter(uint8_t value)
{
assert(value < 16);
@@ -583,7 +342,7 @@ namespace Orthanc
#endif
-#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
+#if ORTHANC_ENABLE_BASE64 == 1
void Toolbox::EncodeBase64(std::string& result,
const std::string& data)
{
@@ -642,60 +401,6 @@ namespace Orthanc
#endif
-
-#if defined(_WIN32)
- static std::string GetPathToExecutableInternal()
- {
- // Yes, this is ugly, but there is no simple way to get the
- // required buffer size, so we use a big constant
- std::vector<char> buffer(32768);
- /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast<DWORD>(buffer.size() - 1));
- return std::string(&buffer[0]);
- }
-
-#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
- static std::string GetPathToExecutableInternal()
- {
- std::vector<char> buffer(PATH_MAX + 1);
- ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1);
- if (bytes == 0)
- {
- throw OrthancException(ErrorCode_PathToExecutable);
- }
-
- return std::string(&buffer[0]);
- }
-
-#elif defined(__APPLE__) && defined(__MACH__)
- static std::string GetPathToExecutableInternal()
- {
- char pathbuf[PATH_MAX + 1];
- unsigned int bufsize = static_cast<int>(sizeof(pathbuf));
-
- _NSGetExecutablePath( pathbuf, &bufsize);
-
- return std::string(pathbuf);
- }
-
-#else
-#error Support your platform here
-#endif
-
-
- std::string Toolbox::GetPathToExecutable()
- {
- boost::filesystem::path p(GetPathToExecutableInternal());
- return boost::filesystem::absolute(p).string();
- }
-
-
- std::string Toolbox::GetDirectoryOfExecutable()
- {
- boost::filesystem::path p(GetPathToExecutableInternal());
- return boost::filesystem::absolute(p.parent_path()).string();
- }
-
-
static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding)
{
switch (sourceEncoding)
@@ -820,6 +525,23 @@ namespace Orthanc
}
+ bool Toolbox::IsAsciiString(const void* data,
+ size_t size)
+ {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+
+ for (size_t i = 0; i < size; i++, p++)
+ {
+ if (*p > 127 || (*p != 0 && iscntrl(*p)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
std::string Toolbox::ConvertToAscii(const std::string& source)
{
std::string result;
@@ -956,30 +678,6 @@ namespace Orthanc
}
-#if BOOST_HAS_DATE_TIME == 1
- std::string Toolbox::GetNowIsoString()
- {
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- return boost::posix_time::to_iso_string(now);
- }
-
- void Toolbox::GetNowDicom(std::string& date,
- std::string& time)
- {
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- tm tm = boost::posix_time::to_tm(now);
-
- char s[32];
- sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- date.assign(s);
-
- // TODO milliseconds
- sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
- time.assign(s);
- }
-#endif
-
-
std::string Toolbox::StripSpaces(const std::string& source)
{
size_t first = 0;
@@ -1137,32 +835,7 @@ namespace Orthanc
}
- void Toolbox::MakeDirectory(const std::string& path)
- {
- if (boost::filesystem::exists(path))
- {
- if (!boost::filesystem::is_directory(path))
- {
- throw OrthancException(ErrorCode_DirectoryOverFile);
- }
- }
- else
- {
- if (!boost::filesystem::create_directories(path))
- {
- throw OrthancException(ErrorCode_MakeDirectory);
- }
- }
- }
-
-
- bool Toolbox::IsExistingFile(const std::string& path)
- {
- return boost::filesystem::exists(path);
- }
-
-
-#if ORTHANC_PUGIXML_ENABLED == 1
+#if ORTHANC_ENABLE_PUGIXML == 1
class ChunkedBufferWriter : public pugi::xml_writer
{
private:
@@ -1284,64 +957,6 @@ namespace Orthanc
#endif
- void Toolbox::ExecuteSystemCommand(const std::string& command,
- const std::vector<std::string>& arguments)
- {
- // Convert the arguments as a C array
- std::vector<char*> args(arguments.size() + 2);
-
- args.front() = const_cast<char*>(command.c_str());
-
- for (size_t i = 0; i < arguments.size(); i++)
- {
- args[i + 1] = const_cast<char*>(arguments[i].c_str());
- }
-
- args.back() = NULL;
-
- int status;
-
-#if defined(_WIN32)
- // http://msdn.microsoft.com/en-us/library/275khfab.aspx
- status = static_cast<int>(_spawnvp(_P_OVERLAY, command.c_str(), &args[0]));
-
-#else
- int pid = fork();
-
- if (pid == -1)
- {
- // Error in fork()
-#if ORTHANC_ENABLE_LOGGING == 1
- LOG(ERROR) << "Cannot fork a child process";
-#endif
-
- throw OrthancException(ErrorCode_SystemCommand);
- }
- else if (pid == 0)
- {
- // Execute the system command in the child process
- execvp(command.c_str(), &args[0]);
-
- // We should never get here
- _exit(1);
- }
- else
- {
- // Wait for the system command to exit
- waitpid(pid, &status, 0);
- }
-#endif
-
- if (status != 0)
- {
-#if ORTHANC_ENABLE_LOGGING == 1
- LOG(ERROR) << "System command failed with status code " << status;
-#endif
-
- throw OrthancException(ErrorCode_SystemCommand);
- }
- }
-
bool Toolbox::IsInteger(const std::string& str)
{
@@ -1449,64 +1064,6 @@ namespace Orthanc
return str.compare(0, prefix.size(), prefix) == 0;
}
}
-
-
- int Toolbox::GetProcessId()
- {
-#if defined(_WIN32)
- return static_cast<int>(_getpid());
-#else
- return static_cast<int>(getpid());
-#endif
- }
-
-
- bool Toolbox::IsRegularFile(const std::string& path)
- {
- namespace fs = boost::filesystem;
-
- try
- {
- if (fs::exists(path))
- {
- fs::file_status status = fs::status(path);
- return (status.type() == boost::filesystem::regular_file ||
- status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11
- }
- }
- catch (fs::filesystem_error&)
- {
- }
-
- return false;
- }
-
-
- FILE* Toolbox::OpenFile(const std::string& path,
- FileMode mode)
- {
-#if defined(_WIN32)
- // TODO Deal with special characters by converting to the current locale
-#endif
-
- const char* m;
- switch (mode)
- {
- case FileMode_ReadBinary:
- m = "rb";
- break;
-
- case FileMode_WriteBinary:
- m = "wb";
- break;
-
- default:
- throw OrthancException(ErrorCode_ParameterOutOfRange);
- }
-
- return fopen(path.c_str(), m);
- }
-
static bool IsUnreservedCharacter(char c)
@@ -1564,5 +1121,134 @@ namespace Orthanc
target.push_back(b < 10 ? b + '0' : b - 10 + 'A');
}
}
- }
+ }
+
+
+ static bool HasField(const Json::Value& json,
+ const std::string& key,
+ Json::ValueType expectedType)
+ {
+ if (json.type() != Json::objectValue ||
+ !json.isMember(key))
+ {
+ return false;
+ }
+ else if (json[key].type() == expectedType)
+ {
+ return true;
+ }
+ else
+ {
+ throw OrthancException(ErrorCode_BadParameterType);
+ }
+ }
+
+
+ std::string Toolbox::GetJsonStringField(const Json::Value& json,
+ const std::string& key,
+ const std::string& defaultValue)
+ {
+ if (HasField(json, key, Json::stringValue))
+ {
+ return json[key].asString();
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ bool Toolbox::GetJsonBooleanField(const ::Json::Value& json,
+ const std::string& key,
+ bool defaultValue)
+ {
+ if (HasField(json, key, Json::booleanValue))
+ {
+ return json[key].asBool();
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ int Toolbox::GetJsonIntegerField(const ::Json::Value& json,
+ const std::string& key,
+ int defaultValue)
+ {
+ if (HasField(json, key, Json::intValue))
+ {
+ return json[key].asInt();
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+
+
+ unsigned int Toolbox::GetJsonUnsignedIntegerField(const ::Json::Value& json,
+ const std::string& key,
+ unsigned int defaultValue)
+ {
+ int v = GetJsonIntegerField(json, key, defaultValue);
+
+ if (v < 0)
+ {
+ throw OrthancException(ErrorCode_ParameterOutOfRange);
+ }
+ else
+ {
+ return static_cast<unsigned int>(v);
+ }
+ }
+
+
+ bool Toolbox::IsUuid(const std::string& str)
+ {
+ if (str.size() != 36)
+ {
+ return false;
+ }
+
+ for (size_t i = 0; i < str.length(); i++)
+ {
+ if (i == 8 || i == 13 || i == 18 || i == 23)
+ {
+ if (str[i] != '-')
+ return false;
+ }
+ else
+ {
+ if (!isalnum(str[i]))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ bool Toolbox::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));
+ }
}
diff --git a/Orthanc/Core/Toolbox.h b/Orthanc/Core/Toolbox.h
index cb67473..d79907b 100644
--- a/Orthanc/Core/Toolbox.h
+++ b/Orthanc/Core/Toolbox.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -39,6 +40,35 @@
#include <string>
#include <json/json.h>
+
+#if !defined(ORTHANC_ENABLE_BASE64)
+# error The macro ORTHANC_ENABLE_BASE64 must be defined
+#endif
+
+#if !defined(ORTHANC_ENABLE_MD5)
+# error The macro ORTHANC_ENABLE_MD5 must be defined
+#endif
+
+#if !defined(ORTHANC_ENABLE_PUGIXML)
+# error The macro ORTHANC_ENABLE_PUGIXML must be defined
+#endif
+
+#if !defined(BOOST_HAS_REGEX)
+# error The macro BOOST_HAS_REGEX must be defined
+#endif
+
+
+/**
+ * NOTE: GUID vs. UUID
+ * The simple answer is: no difference, they are the same thing. Treat
+ * them as a 16 byte (128 bits) value that is used as a unique
+ * value. In Microsoft-speak they are called GUIDs, but call them
+ * UUIDs when not using Microsoft-speak.
+ * http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid
+ **/
+
+
+
namespace Orthanc
{
typedef std::vector<std::string> UriComponents;
@@ -49,10 +79,6 @@ namespace Orthanc
namespace Toolbox
{
- ServerBarrierEvent ServerBarrier(const bool& stopFlag);
-
- ServerBarrierEvent ServerBarrier();
-
void ToUpperCase(std::string& s); // Inplace version
void ToLowerCase(std::string& s); // Inplace version
@@ -63,24 +89,6 @@ namespace Orthanc
void ToLowerCase(std::string& result,
const std::string& source);
- void ReadFile(std::string& content,
- const std::string& path);
-
- bool ReadHeader(std::string& header,
- const std::string& path,
- size_t headerSize);
-
- void WriteFile(const std::string& content,
- const std::string& path);
-
- void WriteFile(const void* content,
- size_t size,
- const std::string& path);
-
- void USleep(uint64_t microSeconds);
-
- void RemoveFile(const std::string& path);
-
void SplitUriComponents(UriComponents& components,
const std::string& uri);
@@ -96,9 +104,7 @@ namespace Orthanc
std::string FlattenUri(const UriComponents& components,
size_t fromLevel = 0);
- uint64_t GetFileSize(const std::string& path);
-
-#if !defined(ORTHANC_ENABLE_MD5) || ORTHANC_ENABLE_MD5 == 1
+#if ORTHANC_ENABLE_MD5 == 1
void ComputeMD5(std::string& result,
const std::string& data);
@@ -119,7 +125,7 @@ namespace Orthanc
bool IsSHA1(const std::string& s);
-#if !defined(ORTHANC_ENABLE_BASE64) || ORTHANC_ENABLE_BASE64 == 1
+#if ORTHANC_ENABLE_BASE64 == 1
void DecodeBase64(std::string& result,
const std::string& data);
@@ -137,27 +143,19 @@ namespace Orthanc
const std::string& content);
#endif
- std::string GetPathToExecutable();
-
- std::string GetDirectoryOfExecutable();
-
std::string ConvertToUtf8(const std::string& source,
Encoding sourceEncoding);
std::string ConvertFromUtf8(const std::string& source,
Encoding targetEncoding);
+ bool IsAsciiString(const void* data,
+ size_t size);
+
std::string ConvertToAscii(const std::string& source);
std::string StripSpaces(const std::string& source);
-#if BOOST_HAS_DATE_TIME == 1
- std::string GetNowIsoString();
-
- void GetNowDicom(std::string& date,
- std::string& time);
-#endif
-
// In-place percent-decoding for URL
void UrlDecode(std::string& s);
@@ -171,20 +169,13 @@ namespace Orthanc
const std::string& source,
char separator);
- void MakeDirectory(const std::string& path);
-
- bool IsExistingFile(const std::string& path);
-
-#if ORTHANC_PUGIXML_ENABLED == 1
+#if ORTHANC_ENABLE_PUGIXML == 1
void JsonToXml(std::string& target,
const Json::Value& source,
const std::string& rootElement = "root",
const std::string& arrayElement = "item");
#endif
- void ExecuteSystemCommand(const std::string& command,
- const std::vector<std::string>& arguments);
-
bool IsInteger(const std::string& str);
void CopyJsonWithoutComments(Json::Value& target,
@@ -193,14 +184,27 @@ namespace Orthanc
bool StartsWith(const std::string& str,
const std::string& prefix);
- int GetProcessId();
+ void UriEncode(std::string& target,
+ const std::string& source);
+
+ std::string GetJsonStringField(const ::Json::Value& json,
+ const std::string& key,
+ const std::string& defaultValue);
- bool IsRegularFile(const std::string& path);
+ bool GetJsonBooleanField(const ::Json::Value& json,
+ const std::string& key,
+ bool defaultValue);
- FILE* OpenFile(const std::string& path,
- FileMode mode);
+ int GetJsonIntegerField(const ::Json::Value& json,
+ const std::string& key,
+ int defaultValue);
- void UriEncode(std::string& target,
- const std::string& source);
+ unsigned int GetJsonUnsignedIntegerField(const ::Json::Value& json,
+ const std::string& key,
+ unsigned int defaultValue);
+
+ bool IsUuid(const std::string& str);
+
+ bool StartsWithUuid(const std::string& str);
}
}
diff --git a/Orthanc/Core/WebServiceParameters.cpp b/Orthanc/Core/WebServiceParameters.cpp
index 52e0ea6..2d44a85 100644
--- a/Orthanc/Core/WebServiceParameters.cpp
+++ b/Orthanc/Core/WebServiceParameters.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -34,9 +35,12 @@
#include "WebServiceParameters.h"
#include "../Core/Logging.h"
-#include "../Core/Toolbox.h"
#include "../Core/OrthancException.h"
+#if ORTHANC_SANDBOXED == 0
+# include "../Core/SystemToolbox.h"
+#endif
+
#include <cassert>
namespace Orthanc
@@ -57,6 +61,7 @@ namespace Orthanc
}
+#if ORTHANC_SANDBOXED == 0
void WebServiceParameters::SetClientCertificate(const std::string& certificateFile,
const std::string& certificateKeyFile,
const std::string& certificateKeyPassword)
@@ -66,14 +71,14 @@ namespace Orthanc
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
- if (!Toolbox::IsRegularFile(certificateFile))
+ if (!SystemToolbox::IsRegularFile(certificateFile))
{
LOG(ERROR) << "Cannot open certificate file: " << certificateFile;
throw OrthancException(ErrorCode_InexistentFile);
}
if (!certificateKeyFile.empty() &&
- !Toolbox::IsRegularFile(certificateKeyFile))
+ !SystemToolbox::IsRegularFile(certificateKeyFile))
{
LOG(ERROR) << "Cannot open key file: " << certificateKeyFile;
throw OrthancException(ErrorCode_InexistentFile);
@@ -84,6 +89,7 @@ namespace Orthanc
certificateKeyFile_ = certificateKeyFile;
certificateKeyPassword_ = certificateKeyPassword;
}
+#endif
static void AddTrailingSlash(std::string& url)
@@ -171,12 +177,14 @@ namespace Orthanc
SetUsername(GetStringMember(peer, "Username", ""));
SetPassword(GetStringMember(peer, "Password", ""));
+#if ORTHANC_SANDBOXED == 0
if (peer.isMember("CertificateFile"))
{
SetClientCertificate(GetStringMember(peer, "CertificateFile", ""),
GetStringMember(peer, "CertificateKeyFile", ""),
GetStringMember(peer, "CertificateKeyPassword", ""));
}
+#endif
if (peer.isMember("Pkcs11"))
{
diff --git a/Orthanc/Core/WebServiceParameters.h b/Orthanc/Core/WebServiceParameters.h
index 40d6cf9..5249f85 100644
--- a/Orthanc/Core/WebServiceParameters.h
+++ b/Orthanc/Core/WebServiceParameters.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -32,6 +33,10 @@
#pragma once
+#if !defined(ORTHANC_SANDBOXED)
+# error The macro ORTHANC_SANDBOXED must be defined
+#endif
+
#include <string>
#include <json/json.h>
@@ -88,9 +93,11 @@ namespace Orthanc
void ClearClientCertificate();
+#if ORTHANC_SANDBOXED == 0
void SetClientCertificate(const std::string& certificateFile,
const std::string& certificateKeyFile,
const std::string& certificateKeyPassword);
+#endif
const std::string& GetCertificateFile() const
{
diff --git a/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
index 6a7dbc0..23bd4d4 100644
--- a/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
+++ b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -33,20 +34,44 @@
#include "OrthancPluginCppWrapper.h"
#include <json/reader.h>
+#include <json/writer.h>
namespace OrthancPlugins
{
- const char* PluginException::GetErrorDescription(OrthancPluginContext* context) const
+ void MemoryBuffer::Check(OrthancPluginErrorCode code)
{
- const char* description = OrthancPluginGetErrorDescription(context, code_);
- if (description)
+ if (code != OrthancPluginErrorCode_Success)
{
- return description;
+ // Prevent using garbage information
+ buffer_.data = NULL;
+ buffer_.size = 0;
+ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+ }
+ }
+
+
+ bool MemoryBuffer::CheckHttp(OrthancPluginErrorCode code)
+ {
+ if (code != OrthancPluginErrorCode_Success)
+ {
+ // Prevent using garbage information
+ buffer_.data = NULL;
+ buffer_.size = 0;
+ }
+
+ if (code == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (code == OrthancPluginErrorCode_UnknownResource ||
+ code == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
}
else
{
- return "No description available";
+ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
@@ -100,7 +125,7 @@ namespace OrthancPlugins
if (buffer_.data == NULL ||
buffer_.size == 0)
{
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
const char* tmp = reinterpret_cast<const char*>(buffer_.data);
@@ -109,7 +134,7 @@ namespace OrthancPlugins
if (!reader.parse(tmp, tmp + buffer_.size, target))
{
OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
@@ -119,29 +144,13 @@ namespace OrthancPlugins
{
Clear();
- OrthancPluginErrorCode error;
-
if (applyPlugins)
{
- error = OrthancPluginRestApiGetAfterPlugins(context_, &buffer_, uri.c_str());
+ return CheckHttp(OrthancPluginRestApiGetAfterPlugins(context_, &buffer_, uri.c_str()));
}
else
{
- error = OrthancPluginRestApiGet(context_, &buffer_, uri.c_str());
- }
-
- if (error == OrthancPluginErrorCode_Success)
- {
- return true;
- }
- else if (error == OrthancPluginErrorCode_UnknownResource ||
- error == OrthancPluginErrorCode_InexistentItem)
- {
- return false;
- }
- else
- {
- throw PluginException(error);
+ return CheckHttp(OrthancPluginRestApiGet(context_, &buffer_, uri.c_str()));
}
}
@@ -153,29 +162,13 @@ namespace OrthancPlugins
{
Clear();
- OrthancPluginErrorCode error;
-
if (applyPlugins)
{
- error = OrthancPluginRestApiPostAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize);
+ return CheckHttp(OrthancPluginRestApiPostAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize));
}
else
{
- error = OrthancPluginRestApiPost(context_, &buffer_, uri.c_str(), body, bodySize);
- }
-
- if (error == OrthancPluginErrorCode_Success)
- {
- return true;
- }
- else if (error == OrthancPluginErrorCode_UnknownResource ||
- error == OrthancPluginErrorCode_InexistentItem)
- {
- return false;
- }
- else
- {
- throw PluginException(error);
+ return CheckHttp(OrthancPluginRestApiPost(context_, &buffer_, uri.c_str(), body, bodySize));
}
}
@@ -187,41 +180,75 @@ namespace OrthancPlugins
{
Clear();
- OrthancPluginErrorCode error;
-
if (applyPlugins)
{
- error = OrthancPluginRestApiPutAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize);
+ return CheckHttp(OrthancPluginRestApiPutAfterPlugins(context_, &buffer_, uri.c_str(), body, bodySize));
}
else
{
- error = OrthancPluginRestApiPut(context_, &buffer_, uri.c_str(), body, bodySize);
+ return CheckHttp(OrthancPluginRestApiPut(context_, &buffer_, uri.c_str(), body, bodySize));
}
+ }
- if (error == OrthancPluginErrorCode_Success)
- {
- return true;
- }
- else if (error == OrthancPluginErrorCode_UnknownResource ||
- error == OrthancPluginErrorCode_InexistentItem)
+
+ bool MemoryBuffer::RestApiPost(const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins)
+ {
+ Json::FastWriter writer;
+ return RestApiPost(uri, writer.write(body), applyPlugins);
+ }
+
+
+ bool MemoryBuffer::RestApiPut(const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins)
+ {
+ Json::FastWriter writer;
+ return RestApiPut(uri, writer.write(body), applyPlugins);
+ }
+
+
+ void MemoryBuffer::CreateDicom(const Json::Value& tags,
+ OrthancPluginCreateDicomFlags flags)
+ {
+ Clear();
+
+ Json::FastWriter writer;
+ std::string s = writer.write(tags);
+
+ Check(OrthancPluginCreateDicom(context_, &buffer_, s.c_str(), NULL, flags));
+ }
+
+
+ void MemoryBuffer::ReadFile(const std::string& path)
+ {
+ Clear();
+ Check(OrthancPluginReadFile(context_, &buffer_, path.c_str()));
+ }
+
+
+ void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
+ {
+ Clear();
+ Check(OrthancPluginWorklistGetDicomQuery(context_, &buffer_, query));
+ }
+
+
+ void OrthancString::Assign(char* str)
+ {
+ if (str == NULL)
{
- return false;
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
else
{
- throw PluginException(error);
+ Clear();
+ str_ = str;
}
}
- OrthancString::OrthancString(OrthancPluginContext* context,
- char* str) :
- context_(context),
- str_(str)
- {
- }
-
-
void OrthancString::Clear()
{
if (str_ != NULL)
@@ -250,14 +277,89 @@ namespace OrthancPlugins
if (str_ == NULL)
{
OrthancPluginLogError(context_, "Cannot convert an empty memory buffer to JSON");
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
Json::Reader reader;
if (!reader.parse(str_, target))
{
OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+ }
+ }
+
+
+ void MemoryBuffer::DicomToJson(Json::Value& target,
+ OrthancPluginDicomToJsonFormat format,
+ OrthancPluginDicomToJsonFlags flags,
+ uint32_t maxStringLength)
+ {
+ OrthancString str(context_);
+ str.Assign(OrthancPluginDicomBufferToJson(context_, GetData(), GetSize(), format, flags, maxStringLength));
+ str.ToJson(target);
+ }
+
+
+ bool MemoryBuffer::HttpGet(const std::string& url,
+ const std::string& username,
+ const std::string& password)
+ {
+ Clear();
+ return CheckHttp(OrthancPluginHttpGet(context_, &buffer_, url.c_str(),
+ username.empty() ? NULL : username.c_str(),
+ password.empty() ? NULL : password.c_str()));
+ }
+
+
+ bool MemoryBuffer::HttpPost(const std::string& url,
+ const std::string& body,
+ const std::string& username,
+ const std::string& password)
+ {
+ Clear();
+ return CheckHttp(OrthancPluginHttpPost(context_, &buffer_, url.c_str(),
+ body.c_str(), body.size(),
+ username.empty() ? NULL : username.c_str(),
+ password.empty() ? NULL : password.c_str()));
+ }
+
+
+ bool MemoryBuffer::HttpPut(const std::string& url,
+ const std::string& body,
+ const std::string& username,
+ const std::string& password)
+ {
+ Clear();
+ return CheckHttp(OrthancPluginHttpPut(context_, &buffer_, url.c_str(),
+ body.empty() ? NULL : body.c_str(),
+ body.size(),
+ username.empty() ? NULL : username.c_str(),
+ password.empty() ? NULL : password.c_str()));
+ }
+
+
+ bool HttpDelete(OrthancPluginContext* context_,
+ const std::string& url,
+ const std::string& username,
+ const std::string& password)
+ {
+ OrthancPluginErrorCode error = OrthancPluginHttpDelete
+ (context_, url.c_str(),
+ username.empty() ? NULL : username.c_str(),
+ password.empty() ? NULL : password.c_str());
+
+ if (error == OrthancPluginErrorCode_Success)
+ {
+ return true;
+ }
+ else if (error == OrthancPluginErrorCode_UnknownResource ||
+ error == OrthancPluginErrorCode_InexistentItem)
+ {
+ return false;
+ }
+ else
+ {
+ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
}
}
@@ -265,12 +367,13 @@ namespace OrthancPlugins
OrthancConfiguration::OrthancConfiguration(OrthancPluginContext* context) :
context_(context)
{
- OrthancString str(context, OrthancPluginGetConfiguration(context));
+ OrthancString str(context);
+ str.Assign(OrthancPluginGetConfiguration(context));
if (str.GetContent() == NULL)
{
OrthancPluginLogError(context, "Cannot access the Orthanc configuration");
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
str.ToJson(configuration_);
@@ -278,7 +381,7 @@ namespace OrthancPlugins
if (configuration_.type() != Json::objectValue)
{
OrthancPluginLogError(context, "Unable to read the Orthanc configuration");
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
@@ -287,7 +390,7 @@ namespace OrthancPlugins
{
if (context_ == NULL)
{
- throw PluginException(OrthancPluginErrorCode_Plugin);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
@@ -309,6 +412,15 @@ namespace OrthancPlugins
}
+ bool OrthancConfiguration::IsSection(const std::string& key) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ return (configuration_.isMember(key) &&
+ configuration_[key].type() == Json::objectValue);
+ }
+
+
void OrthancConfiguration::GetSection(OrthancConfiguration& target,
const std::string& key) const
{
@@ -331,7 +443,7 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target.configuration_ = configuration_[key];
@@ -357,7 +469,7 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target = configuration_[key].asString();
@@ -392,7 +504,7 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
@@ -414,7 +526,7 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
else
{
@@ -442,7 +554,7 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target = configuration_[key].asBool();
@@ -467,11 +579,11 @@ namespace OrthancPlugins
return true;
case Json::intValue:
- target = configuration_[key].asInt();
+ target = static_cast<float>(configuration_[key].asInt());
return true;
case Json::uintValue:
- target = configuration_[key].asUInt();
+ target = static_cast<float>(configuration_[key].asUInt());
return true;
default:
@@ -481,7 +593,95 @@ namespace OrthancPlugins
OrthancPluginLogError(context_, s.c_str());
}
- throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+ }
+ }
+
+
+ bool OrthancConfiguration::LookupListOfStrings(std::list<std::string>& target,
+ const std::string& key,
+ bool allowSingleString) const
+ {
+ assert(configuration_.type() == Json::objectValue);
+
+ target.clear();
+
+ if (!configuration_.isMember(key))
+ {
+ return false;
+ }
+
+ switch (configuration_[key].type())
+ {
+ case Json::arrayValue:
+ {
+ bool ok = true;
+
+ for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++)
+ {
+ if (configuration_[key][i].type() == Json::stringValue)
+ {
+ target.push_back(configuration_[key][i].asString());
+ }
+ else
+ {
+ ok = false;
+ }
+ }
+
+ if (ok)
+ {
+ return true;
+ }
+
+ break;
+ }
+
+ case Json::stringValue:
+ if (allowSingleString)
+ {
+ target.push_back(configuration_[key].asString());
+ return true;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if (context_ != NULL)
+ {
+ std::string s = ("The configuration option \"" + GetPath(key) +
+ "\" is not a list of strings as expected");
+ OrthancPluginLogError(context_, s.c_str());
+ }
+
+ ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
+ }
+
+
+ bool OrthancConfiguration::LookupSetOfStrings(std::set<std::string>& target,
+ const std::string& key,
+ bool allowSingleString) const
+ {
+ std::list<std::string> lst;
+
+ if (LookupListOfStrings(lst, key, allowSingleString))
+ {
+ target.clear();
+
+ for (std::list<std::string>::const_iterator
+ it = lst.begin(); it != lst.end(); ++it)
+ {
+ target.insert(*it);
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
}
}
@@ -576,7 +776,7 @@ namespace OrthancPlugins
if (image_ == NULL)
{
OrthancPluginLogError(context_, "Trying to access a NULL image");
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -587,7 +787,7 @@ namespace OrthancPlugins
{
if (context == NULL)
{
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -599,7 +799,7 @@ namespace OrthancPlugins
{
if (context == NULL)
{
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -612,7 +812,7 @@ namespace OrthancPlugins
{
if (context == NULL)
{
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
else
{
@@ -629,7 +829,7 @@ namespace OrthancPlugins
if (image_ == NULL)
{
OrthancPluginLogError(context_, "Cannot uncompress a PNG image");
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -642,7 +842,7 @@ namespace OrthancPlugins
if (image_ == NULL)
{
OrthancPluginLogError(context_, "Cannot uncompress a JPEG image");
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -656,7 +856,7 @@ namespace OrthancPlugins
if (image_ == NULL)
{
OrthancPluginLogError(context_, "Cannot uncompress a DICOM image");
- throw PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
@@ -738,10 +938,87 @@ namespace OrthancPlugins
}
- bool RestApiGetJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- bool applyPlugins)
+
+#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+ FindMatcher::FindMatcher(OrthancPluginContext* context,
+ const OrthancPluginWorklistQuery* worklist) :
+ context_(context),
+ matcher_(NULL),
+ worklist_(worklist)
+ {
+ if (worklist_ == NULL)
+ {
+ ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
+ }
+ }
+
+
+ void FindMatcher::SetupDicom(OrthancPluginContext* context,
+ const void* query,
+ uint32_t size)
+ {
+ context_ = context;
+ worklist_ = NULL;
+
+ matcher_ = OrthancPluginCreateFindMatcher(context_, query, size);
+ if (matcher_ == NULL)
+ {
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+ }
+ }
+
+
+ FindMatcher::~FindMatcher()
+ {
+ // The "worklist_" field
+
+ if (matcher_ != NULL)
+ {
+ OrthancPluginFreeFindMatcher(context_, matcher_);
+ }
+ }
+
+
+
+ bool FindMatcher::IsMatch(const void* dicom,
+ uint32_t size) const
+ {
+ int32_t result;
+
+ if (matcher_ != NULL)
+ {
+ result = OrthancPluginFindMatcherIsMatch(context_, matcher_, dicom, size);
+ }
+ else if (worklist_ != NULL)
+ {
+ result = OrthancPluginWorklistIsMatch(context_, worklist_, dicom, size);
+ }
+ else
+ {
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+ }
+
+ if (result == 0)
+ {
+ return false;
+ }
+ else if (result == 1)
+ {
+ return true;
+ }
+ else
+ {
+ ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+ }
+ }
+
+#endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
+
+
+ bool RestApiGet(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins)
{
MemoryBuffer answer(context);
if (!answer.RestApiGet(uri, applyPlugins))
@@ -756,12 +1033,12 @@ namespace OrthancPlugins
}
- bool RestApiPostJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const char* body,
- size_t bodySize,
- bool applyPlugins)
+ bool RestApiPost(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
{
MemoryBuffer answer(context);
if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
@@ -776,12 +1053,23 @@ namespace OrthancPlugins
}
- bool RestApiPutJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const char* body,
- size_t bodySize,
- bool applyPlugins)
+ bool RestApiPost(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins)
+ {
+ Json::FastWriter writer;
+ return RestApiPost(result, context, uri, writer.write(body), applyPlugins);
+ }
+
+
+ bool RestApiPut(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins)
{
MemoryBuffer answer(context);
if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
@@ -796,6 +1084,17 @@ namespace OrthancPlugins
}
+ bool RestApiPut(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins)
+ {
+ Json::FastWriter writer;
+ return RestApiPut(result, context, uri, writer.write(body), applyPlugins);
+ }
+
+
bool RestApiDelete(OrthancPluginContext* context,
const std::string& uri,
bool applyPlugins)
@@ -822,7 +1121,101 @@ namespace OrthancPlugins
}
else
{
- throw PluginException(error);
+ ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+ }
+ }
+
+
+ void ReportMinimalOrthancVersion(OrthancPluginContext* context,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int revision)
+ {
+ std::string s = ("Your version of the Orthanc core (" +
+ std::string(context->orthancVersion) +
+ ") is too old to run this plugin (version " +
+ boost::lexical_cast<std::string>(major) + "." +
+ boost::lexical_cast<std::string>(minor) + "." +
+ boost::lexical_cast<std::string>(revision) +
+ " is required)");
+
+ OrthancPluginLogError(context, s.c_str());
+ }
+
+
+ bool CheckMinimalOrthancVersion(OrthancPluginContext* context,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int revision)
+ {
+ if (context == NULL)
+ {
+ OrthancPluginLogError(context, "Bad Orthanc context in the plugin");
+ return false;
+ }
+
+ if (!strcmp(context->orthancVersion, "mainline"))
+ {
+ // Assume compatibility with the mainline
+ return true;
+ }
+
+ // Parse the version of the Orthanc core
+ int aa, bb, cc;
+ if (
+#ifdef _MSC_VER
+ sscanf_s
+#else
+ sscanf
+#endif
+ (context->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 ||
+ aa < 0 ||
+ bb < 0 ||
+ cc < 0)
+ {
+ throw false;
+ }
+
+ unsigned int a = static_cast<unsigned int>(aa);
+ unsigned int b = static_cast<unsigned int>(bb);
+ unsigned int c = static_cast<unsigned int>(cc);
+
+ // Check the major version number
+
+ if (a > major)
+ {
+ return true;
+ }
+
+ if (a < major)
+ {
+ return false;
+ }
+
+
+ // Check the minor version number
+ assert(a == major);
+
+ if (b > minor)
+ {
+ return true;
+ }
+
+ if (b < minor)
+ {
+ return false;
+ }
+
+ // Check the patch level version number
+ assert(a == major && b == minor);
+
+ if (c >= revision)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
}
}
}
diff --git a/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h
index 33b550e..4f8c1df 100644
--- a/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h
+++ b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -32,48 +33,50 @@
#pragma once
+#include "OrthancPluginException.h"
+
#include <orthanc/OrthancCPlugin.h>
#include <boost/noncopyable.hpp>
#include <boost/lexical_cast.hpp>
#include <json/value.h>
+#include <list>
+#include <set>
+
+
+
+#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \
+ (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major || \
+ (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major && \
+ (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor || \
+ (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor && \
+ ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
+
-#if HAS_ORTHANC_EXCEPTION == 1
-# include <OrthancException.h>
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
+// The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0
+# define HAS_ORTHANC_PLUGIN_FIND_MATCHER 1
+#else
+# define HAS_ORTHANC_PLUGIN_FIND_MATCHER 0
#endif
+
namespace OrthancPlugins
{
typedef void (*RestCallback) (OrthancPluginRestOutput* output,
const char* url,
const OrthancPluginHttpRequest* request);
-
- class PluginException
- {
- private:
- OrthancPluginErrorCode code_;
-
- public:
- PluginException(OrthancPluginErrorCode code) : code_(code)
- {
- }
-
- OrthancPluginErrorCode GetErrorCode() const
- {
- return code_;
- }
-
- const char* GetErrorDescription(OrthancPluginContext* context) const;
- };
-
-
class MemoryBuffer : public boost::noncopyable
{
private:
OrthancPluginContext* context_;
OrthancPluginMemoryBuffer buffer_;
+ void Check(OrthancPluginErrorCode code);
+
+ bool CheckHttp(OrthancPluginErrorCode code);
+
public:
MemoryBuffer(OrthancPluginContext* context);
@@ -127,6 +130,14 @@ namespace OrthancPlugins
bool applyPlugins);
bool RestApiPost(const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins);
+
+ bool RestApiPut(const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins);
+
+ bool RestApiPost(const std::string& uri,
const std::string& body,
bool applyPlugins)
{
@@ -139,6 +150,32 @@ namespace OrthancPlugins
{
return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
}
+
+ void CreateDicom(const Json::Value& tags,
+ OrthancPluginCreateDicomFlags flags);
+
+ void ReadFile(const std::string& path);
+
+ void GetDicomQuery(const OrthancPluginWorklistQuery* query);
+
+ void DicomToJson(Json::Value& target,
+ OrthancPluginDicomToJsonFormat format,
+ OrthancPluginDicomToJsonFlags flags,
+ uint32_t maxStringLength);
+
+ bool HttpGet(const std::string& url,
+ const std::string& username,
+ const std::string& password);
+
+ bool HttpPost(const std::string& url,
+ const std::string& body,
+ const std::string& username,
+ const std::string& password);
+
+ bool HttpPut(const std::string& url,
+ const std::string& body,
+ const std::string& username,
+ const std::string& password);
};
@@ -148,16 +185,23 @@ namespace OrthancPlugins
OrthancPluginContext* context_;
char* str_;
+ void Clear();
+
public:
- OrthancString(OrthancPluginContext* context,
- char* str);
+ OrthancString(OrthancPluginContext* context) :
+ context_(context),
+ str_(NULL)
+ {
+ }
~OrthancString()
{
Clear();
}
- void Clear();
+ // This transfers ownership, warning: The string must have been
+ // allocated by the Orthanc core
+ void Assign(char* str);
const char* GetContent() const
{
@@ -174,7 +218,7 @@ namespace OrthancPlugins
{
private:
OrthancPluginContext* context_;
- Json::Value configuration_;
+ Json::Value configuration_; // Necessarily a Json::objectValue
std::string path_;
std::string GetPath(const std::string& key) const;
@@ -193,6 +237,8 @@ namespace OrthancPlugins
return configuration_;
}
+ bool IsSection(const std::string& key) const;
+
void GetSection(OrthancConfiguration& target,
const std::string& key) const;
@@ -211,6 +257,14 @@ namespace OrthancPlugins
bool LookupFloatValue(float& target,
const std::string& key) const;
+ bool LookupListOfStrings(std::list<std::string>& target,
+ const std::string& key,
+ bool allowSingleString) const;
+
+ bool LookupSetOfStrings(std::set<std::string>& target,
+ const std::string& key,
+ bool allowSingleString) const;
+
std::string GetStringValue(const std::string& key,
const std::string& defaultValue) const;
@@ -227,7 +281,7 @@ namespace OrthancPlugins
float defaultValue) const;
};
- class OrthancImage
+ class OrthancImage : public boost::noncopyable
{
private:
OrthancPluginContext* context_;
@@ -285,52 +339,144 @@ namespace OrthancPlugins
};
- bool RestApiGetJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- bool applyPlugins);
-
- bool RestApiPostJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const char* body,
- size_t bodySize,
- bool applyPlugins);
-
- bool RestApiPutJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const char* body,
- size_t bodySize,
- bool applyPlugins);
-
- inline bool RestApiPostJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const std::string& body,
- bool applyPlugins)
+#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+ class FindMatcher : public boost::noncopyable
+ {
+ private:
+ OrthancPluginContext* context_;
+ OrthancPluginFindMatcher* matcher_;
+ const OrthancPluginWorklistQuery* worklist_;
+
+ void SetupDicom(OrthancPluginContext* context,
+ const void* query,
+ uint32_t size);
+
+ public:
+ FindMatcher(OrthancPluginContext* context,
+ const OrthancPluginWorklistQuery* worklist);
+
+ FindMatcher(OrthancPluginContext* context,
+ const void* query,
+ uint32_t size)
+ {
+ SetupDicom(context, query, size);
+ }
+
+ FindMatcher(OrthancPluginContext* context,
+ const MemoryBuffer& dicom)
+ {
+ SetupDicom(context, dicom.GetData(), dicom.GetSize());
+ }
+
+ ~FindMatcher();
+
+ bool IsMatch(const void* dicom,
+ uint32_t size) const;
+
+ bool IsMatch(const MemoryBuffer& dicom) const
+ {
+ return IsMatch(dicom.GetData(), dicom.GetSize());
+ }
+ };
+#endif
+
+
+ bool RestApiGet(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ bool applyPlugins);
+
+ bool RestApiPost(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ bool RestApiPost(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins);
+
+ inline bool RestApiPost(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
+ {
+ return RestApiPost(result, context, uri, body.empty() ? NULL : body.c_str(),
+ body.size(), applyPlugins);
+ }
+
+ bool RestApiPut(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const char* body,
+ size_t bodySize,
+ bool applyPlugins);
+
+ bool RestApiPut(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const Json::Value& body,
+ bool applyPlugins);
+
+ inline bool RestApiPut(Json::Value& result,
+ OrthancPluginContext* context,
+ const std::string& uri,
+ const std::string& body,
+ bool applyPlugins)
{
- return RestApiPostJson(result, context, uri, body.empty() ? NULL : body.c_str(),
- body.size(), applyPlugins);
+ return RestApiPut(result, context, uri, body.empty() ? NULL : body.c_str(),
+ body.size(), applyPlugins);
}
bool RestApiDelete(OrthancPluginContext* context,
const std::string& uri,
bool applyPlugins);
- inline bool RestApiPutJson(Json::Value& result,
- OrthancPluginContext* context,
- const std::string& uri,
- const std::string& body,
- bool applyPlugins)
+ bool HttpDelete(OrthancPluginContext* context,
+ const std::string& url,
+ const std::string& username,
+ const std::string& password);
+
+ inline void LogError(OrthancPluginContext* context,
+ const std::string& message)
{
- return RestApiPutJson(result, context, uri, body.empty() ? NULL : body.c_str(),
- body.size(), applyPlugins);
+ if (context != NULL)
+ {
+ OrthancPluginLogError(context, message.c_str());
+ }
}
- bool RestApiDelete(OrthancPluginContext* context,
- const std::string& uri,
- bool applyPlugins);
+ inline void LogWarning(OrthancPluginContext* context,
+ const std::string& message)
+ {
+ if (context != NULL)
+ {
+ OrthancPluginLogWarning(context, message.c_str());
+ }
+ }
+
+ inline void LogInfo(OrthancPluginContext* context,
+ const std::string& message)
+ {
+ if (context != NULL)
+ {
+ OrthancPluginLogInfo(context, message.c_str());
+ }
+ }
+
+ void ReportMinimalOrthancVersion(OrthancPluginContext* context,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int revision);
+
+ bool CheckMinimalOrthancVersion(OrthancPluginContext* context,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int revision);
namespace Internals
@@ -345,16 +491,10 @@ namespace OrthancPlugins
Callback(output, url, request);
return OrthancPluginErrorCode_Success;
}
- catch (OrthancPlugins::PluginException& e)
- {
- return e.GetErrorCode();
- }
-#if HAS_ORTHANC_EXCEPTION == 1
- catch (Orthanc::OrthancException& e)
+ catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
-#endif
catch (boost::bad_lexical_cast&)
{
return OrthancPluginErrorCode_BadFileFormat;
diff --git a/Orthanc/Plugins/Samples/Common/OrthancPluginException.h b/Orthanc/Plugins/Samples/Common/OrthancPluginException.h
new file mode 100644
index 0000000..e549a26
--- /dev/null
+++ b/Orthanc/Plugins/Samples/Common/OrthancPluginException.h
@@ -0,0 +1,101 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if !defined(HAS_ORTHANC_EXCEPTION)
+# error The macro HAS_ORTHANC_EXCEPTION must be defined
+#endif
+
+
+#if HAS_ORTHANC_EXCEPTION == 1
+# include "../../../Core/OrthancException.h"
+# define ORTHANC_PLUGINS_ERROR_ENUMERATION ::Orthanc::ErrorCode
+# define ORTHANC_PLUGINS_EXCEPTION_CLASS ::Orthanc::OrthancException
+# define ORTHANC_PLUGINS_GET_ERROR_CODE(code) ::Orthanc::ErrorCode_ ## code
+#else
+# include <orthanc/OrthancCPlugin.h>
+# define ORTHANC_PLUGINS_ERROR_ENUMERATION ::OrthancPluginErrorCode
+# define ORTHANC_PLUGINS_EXCEPTION_CLASS ::OrthancPlugins::PluginException
+# define ORTHANC_PLUGINS_GET_ERROR_CODE(code) ::OrthancPluginErrorCode_ ## code
+#endif
+
+
+#define ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code) \
+ throw ORTHANC_PLUGINS_EXCEPTION_CLASS(static_cast<ORTHANC_PLUGINS_ERROR_ENUMERATION>(code));
+
+
+#define ORTHANC_PLUGINS_THROW_EXCEPTION(code) \
+ throw ORTHANC_PLUGINS_EXCEPTION_CLASS(ORTHANC_PLUGINS_GET_ERROR_CODE(code));
+
+
+#define ORTHANC_PLUGINS_CHECK_ERROR(code) \
+ if (code != ORTHANC_PLUGINS_GET_ERROR_CODE(Success)) \
+ { \
+ ORTHANC_PLUGINS_THROW_EXCEPTION(code); \
+ }
+
+
+namespace OrthancPlugins
+{
+#if HAS_ORTHANC_EXCEPTION == 0
+ class PluginException
+ {
+ private:
+ OrthancPluginErrorCode code_;
+
+ public:
+ explicit PluginException(OrthancPluginErrorCode code) : code_(code)
+ {
+ }
+
+ OrthancPluginErrorCode GetErrorCode() const
+ {
+ return code_;
+ }
+
+ const char* What(OrthancPluginContext* context) const
+ {
+ const char* description = OrthancPluginGetErrorDescription(context, code_);
+ if (description)
+ {
+ return description;
+ }
+ else
+ {
+ return "No description available";
+ }
+ }
+ };
+#endif
+}
diff --git a/Orthanc/Resources/CMake/BoostConfiguration.cmake b/Orthanc/Resources/CMake/BoostConfiguration.cmake
index 20c50df..f7c9287 100644
--- a/Orthanc/Resources/CMake/BoostConfiguration.cmake
+++ b/Orthanc/Resources/CMake/BoostConfiguration.cmake
@@ -43,7 +43,7 @@ if (BOOST_STATIC)
set(BOOST_NAME boost_1_60_0)
set(BOOST_BCP_SUFFIX bcpdigest-1.0.1)
set(BOOST_MD5 "a789f8ec2056ad1c2d5f0cb64687cc7b")
- set(BOOST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
+ set(BOOST_URL "http://www.orthanc-server.com/downloads/third-party/${BOOST_NAME}_${BOOST_BCP_SUFFIX}.tar.gz")
set(BOOST_FILESYSTEM_SOURCES_DIR "${BOOST_NAME}/libs/filesystem/src")
set(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
@@ -54,8 +54,12 @@ if (BOOST_STATIC)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
+ ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "PNaCl" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl32" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl64")
list(APPEND BOOST_SOURCES
+ ${BOOST_SOURCES_DIR}/libs/atomic/src/lockpool.cpp
${BOOST_SOURCES_DIR}/libs/thread/src/pthread/once.cpp
${BOOST_SOURCES_DIR}/libs/thread/src/pthread/thread.cpp
)
@@ -65,7 +69,10 @@ if (BOOST_STATIC)
-DBOOST_LOCALE_NO_STD_BACKEND=1
)
- if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+ if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "PNaCl" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl32" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl64")
add_definitions(-DBOOST_HAS_SCHED_YIELD=1)
endif()
@@ -92,6 +99,13 @@ if (BOOST_STATIC)
-DBOOST_LOCALE_NO_POSIX_BACKEND=1
-DBOOST_LOCALE_NO_STD_BACKEND=1
)
+
+ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
+ add_definitions(
+ -DBOOST_LOCALE_NO_POSIX_BACKEND=1
+ -DBOOST_LOCALE_NO_STD_BACKEND=1
+ )
+
else()
message(FATAL_ERROR "Support your platform here")
endif()
@@ -107,19 +121,43 @@ if (BOOST_STATIC)
list(APPEND BOOST_SOURCES
${BOOST_REGEX_SOURCES}
${BOOST_SOURCES_DIR}/libs/date_time/src/gregorian/greg_month.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
)
+ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
+ list(APPEND BOOST_SOURCES
+ ${BOOST_SOURCES_DIR}/libs/locale/src/encoding/codepage.cpp
+ )
+ endif()
+
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "PNaCl" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl32" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl64")
+ # boost::filesystem is not available on PNaCl
+ add_definitions(
+ -DBOOST_HAS_FILESYSTEM_V3=0
+ -D__INTEGRITY=1
+ )
+ else()
+ add_definitions(
+ -DBOOST_HAS_FILESYSTEM_V3=1
+ )
+ list(APPEND BOOST_SOURCES
+ ${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
+ )
+ endif()
+
if (USE_BOOST_LOCALE_BACKENDS)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
- ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
+ ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "PNaCl" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl32" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl64")
list(APPEND BOOST_SOURCES
${BOOST_SOURCES_DIR}/libs/locale/src/posix/codecvt.cpp
${BOOST_SOURCES_DIR}/libs/locale/src/posix/collate.cpp
@@ -175,7 +213,6 @@ if (BOOST_STATIC)
-DBOOST_SYSTEM_NO_LIB
-DBOOST_LOCALE_NO_LIB
-DBOOST_HAS_LOCALE=1
- -DBOOST_HAS_FILESYSTEM_V3=1
)
if (CMAKE_COMPILER_IS_GNUCXX)
@@ -186,7 +223,8 @@ if (BOOST_STATIC)
${BOOST_SOURCES_DIR}
)
- source_group(ThirdParty\\Boost REGULAR_EXPRESSION ${BOOST_SOURCES_DIR}/.*)
+ source_group(ThirdParty\\boost REGULAR_EXPRESSION ${BOOST_SOURCES_DIR}/.*)
+
else()
add_definitions(
-DBOOST_HAS_LOCALE=1
diff --git a/Orthanc/Resources/CMake/Compiler.cmake b/Orthanc/Resources/CMake/Compiler.cmake
index 52f4fab..fa7577d 100644
--- a/Orthanc/Resources/CMake/Compiler.cmake
+++ b/Orthanc/Resources/CMake/Compiler.cmake
@@ -41,7 +41,17 @@ elseif (MSVC)
-D_CRT_SECURE_NO_WARNINGS=1
-D_CRT_SECURE_NO_DEPRECATE=1
)
- include_directories(${ORTHANC_ROOT}/Resources/ThirdParty/VisualStudio)
+
+ if (MSVC_VERSION LESS 1600)
+ # Starting with Visual Studio >= 2010 (i.e. macro _MSC_VER >=
+ # 1600), Microsoft ships a standard-compliant <stdint.h>
+ # header. For earlier versions of Visual Studio, give access to a
+ # compatibility header.
+ # http://stackoverflow.com/a/70630/881731
+ # https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h#External_links
+ include_directories(${ORTHANC_ROOT}/Resources/ThirdParty/VisualStudio)
+ endif()
+
link_libraries(netapi32)
endif()
@@ -154,6 +164,23 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
endif()
+if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING)
+ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(WARNING "Enabling profiling on a non-debug build will not produce full information")
+ endif()
+
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pg")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg")
+ else()
+ message(FATAL_ERROR "Don't know how to enable profiling on your configuration")
+ endif()
+endif()
+
+
if (STATIC_BUILD)
add_definitions(-DORTHANC_STATIC=1)
else()
diff --git a/Orthanc/Resources/CMake/DownloadPackage.cmake b/Orthanc/Resources/CMake/DownloadPackage.cmake
index 492a352..2a73cda 100644
--- a/Orthanc/Resources/CMake/DownloadPackage.cmake
+++ b/Orthanc/Resources/CMake/DownloadPackage.cmake
@@ -15,12 +15,18 @@ endmacro()
## Setup the patch command-line tool
##
-if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
- set(PATCH_EXECUTABLE ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/patch/patch.exe)
-else ()
- find_program(PATCH_EXECUTABLE patch)
- if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND")
- message(FATAL_ERROR "Please install the 'patch' standard command-line tool")
+if (NOT ORTHANC_DISABLE_PATCH)
+ if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+ set(PATCH_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/../ThirdParty/patch/patch.exe)
+ if (NOT EXISTS ${PATCH_EXECUTABLE})
+ message(FATAL_ERROR "Unable to find the patch.exe tool that is shipped with Orthanc")
+ endif()
+
+ else ()
+ find_program(PATCH_EXECUTABLE patch)
+ if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND")
+ message(FATAL_ERROR "Please install the 'patch' standard command-line tool")
+ endif()
endif()
endif()
@@ -70,7 +76,9 @@ macro(DownloadPackage MD5 Url TargetDirectory)
message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON")
endif()
- file(DOWNLOAD "${Url}" "${TMP_PATH}" SHOW_PROGRESS EXPECTED_MD5 "${MD5}")
+ file(DOWNLOAD "${Url}" "${TMP_PATH}"
+ SHOW_PROGRESS EXPECTED_MD5 "${MD5}"
+ TIMEOUT 60 INACTIVITY_TIMEOUT 60)
else()
message("Using local copy of ${Url}")
endif()
diff --git a/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake b/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake
index af6e67b..b9074b4 100644
--- a/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake
+++ b/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake
@@ -1,15 +1,32 @@
if (USE_GTEST_DEBIAN_SOURCE_PACKAGE)
- set(GTEST_SOURCES /usr/src/gtest/src/gtest-all.cc)
- include_directories(/usr/src/gtest)
+ find_path(GTEST_DEBIAN_SOURCES_DIR
+ NAMES src/gtest-all.cc
+ PATHS
+ /usr/src/gtest
+ /usr/src/googletest/googletest
+ PATH_SUFFIXES src
+ )
+
+ find_path(GTEST_DEBIAN_INCLUDE_DIR
+ NAMES gtest.h
+ PATHS
+ /usr/include/gtest
+ )
- if (NOT EXISTS /usr/include/gtest/gtest.h OR
- NOT EXISTS ${GTEST_SOURCES})
+ message("Path to the Debian Google Test sources: ${GTEST_DEBIAN_SOURCES_DIR}")
+ message("Path to the Debian Google Test includes: ${GTEST_DEBIAN_INCLUDE_DIR}")
+
+ set(GTEST_SOURCES ${GTEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc)
+ include_directories(${GTEST_DEBIAN_SOURCES_DIR})
+
+ if (NOT EXISTS ${GTEST_SOURCES} OR
+ NOT EXISTS ${GTEST_DEBIAN_INCLUDE_DIR}/gtest.h)
message(FATAL_ERROR "Please install the libgtest-dev package")
endif()
elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST)
set(GTEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0)
- set(GTEST_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/gtest-1.7.0.zip")
+ set(GTEST_URL "http://www.orthanc-server.com/downloads/third-party/gtest-1.7.0.zip")
set(GTEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7")
DownloadPackage(${GTEST_MD5} ${GTEST_URL} "${GTEST_SOURCES_DIR}")
@@ -28,6 +45,8 @@ elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST)
add_definitions(/D _VARIADIC_MAX=10)
endif()
+ source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GTEST_SOURCES_DIR}/.*)
+
else()
include(FindGTest)
if (NOT GTEST_FOUND)
diff --git a/Orthanc/Resources/CMake/JsonCppConfiguration.cmake b/Orthanc/Resources/CMake/JsonCppConfiguration.cmake
index bad61b8..1532a81 100644
--- a/Orthanc/Resources/CMake/JsonCppConfiguration.cmake
+++ b/Orthanc/Resources/CMake/JsonCppConfiguration.cmake
@@ -1,6 +1,6 @@
if (STATIC_BUILD OR NOT USE_SYSTEM_JSONCPP)
set(JSONCPP_SOURCES_DIR ${CMAKE_BINARY_DIR}/jsoncpp-0.10.5)
- set(JSONCPP_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jsoncpp-0.10.5.tar.gz")
+ set(JSONCPP_URL "http://www.orthanc-server.com/downloads/third-party/jsoncpp-0.10.5.tar.gz")
set(JSONCPP_MD5 "db146bac5a126ded9bd728ab7b61ed6b")
DownloadPackage(${JSONCPP_MD5} ${JSONCPP_URL} "${JSONCPP_SOURCES_DIR}")
diff --git a/Orthanc/Resources/CMake/PugixmlConfiguration.cmake b/Orthanc/Resources/CMake/PugixmlConfiguration.cmake
index 4094fa3..4d57b12 100644
--- a/Orthanc/Resources/CMake/PugixmlConfiguration.cmake
+++ b/Orthanc/Resources/CMake/PugixmlConfiguration.cmake
@@ -1,10 +1,10 @@
if (USE_PUGIXML)
- add_definitions(-DORTHANC_PUGIXML_ENABLED=1)
+ add_definitions(-DORTHANC_ENABLE_PUGIXML=1)
if (STATIC_BUILD OR NOT USE_SYSTEM_PUGIXML)
set(PUGIXML_SOURCES_DIR ${CMAKE_BINARY_DIR}/pugixml-1.4)
set(PUGIXML_MD5 "7c56c91cfe3ecdee248a8e4892ef5781")
- set(PUGIXML_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/pugixml-1.4.tar.gz")
+ set(PUGIXML_URL "http://www.orthanc-server.com/downloads/third-party/pugixml-1.4.tar.gz")
DownloadPackage(${PUGIXML_MD5} ${PUGIXML_URL} "${PUGIXML_SOURCES_DIR}")
@@ -26,6 +26,8 @@ if (USE_PUGIXML)
link_libraries(pugixml)
endif()
+ source_group(ThirdParty\\pugixml REGULAR_EXPRESSION ${PUGIXML_SOURCES_DIR}/.*)
+
else()
- add_definitions(-DORTHANC_PUGIXML_ENABLED=0)
+ add_definitions(-DORTHANC_ENABLE_PUGIXML=0)
endif()
diff --git a/Orthanc/Resources/CMake/ZlibConfiguration.cmake b/Orthanc/Resources/CMake/ZlibConfiguration.cmake
index e1940c7..4487c1e 100644
--- a/Orthanc/Resources/CMake/ZlibConfiguration.cmake
+++ b/Orthanc/Resources/CMake/ZlibConfiguration.cmake
@@ -1,6 +1,6 @@
if (STATIC_BUILD OR NOT USE_SYSTEM_ZLIB)
SET(ZLIB_SOURCES_DIR ${CMAKE_BINARY_DIR}/zlib-1.2.7)
- SET(ZLIB_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/zlib-1.2.7.tar.gz")
+ SET(ZLIB_URL "http://www.orthanc-server.com/downloads/third-party/zlib-1.2.7.tar.gz")
SET(ZLIB_MD5 "60df6a37c56e7c1366cca812414f7b85")
DownloadPackage(${ZLIB_MD5} ${ZLIB_URL} "${ZLIB_SOURCES_DIR}")
@@ -27,10 +27,10 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_ZLIB)
${ZLIB_SOURCES_DIR}/zutil.c
)
+ source_group(ThirdParty\\zlib REGULAR_EXPRESSION ${ZLIB_SOURCES_DIR}/.*)
+
else()
include(FindZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
link_libraries(${ZLIB_LIBRARIES})
endif()
-
-source_group(ThirdParty\\ZLib REGULAR_EXPRESSION ${ZLIB_SOURCES_DIR}/.*)
diff --git a/Orthanc/Resources/ThirdParty/VisualStudio/stdint.h b/Orthanc/Resources/ThirdParty/VisualStudio/stdint.h
index 59d0673..4fe0ef9 100644
--- a/Orthanc/Resources/ThirdParty/VisualStudio/stdint.h
+++ b/Orthanc/Resources/ThirdParty/VisualStudio/stdint.h
@@ -1,247 +1,259 @@
-// ISO C9x compliant stdint.h for Microsoft Visual Studio
-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
-//
-// Copyright (c) 2006-2008 Alexander Chemeris
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. 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.
-//
-// 3. The name of the author may be used to endorse or promote products
-// derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
-
-#ifndef _MSC_STDINT_H_ // [
-#define _MSC_STDINT_H_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif
-
-#include <limits.h>
-
-// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
-// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
-// or compiler give many errors like this:
-// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
-#ifdef __cplusplus
-extern "C" {
-#endif
-# include <wchar.h>
-#ifdef __cplusplus
-}
-#endif
-
-// Define _W64 macros to mark types changing their size, like intptr_t.
-#ifndef _W64
-# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
-# define _W64 __w64
-# else
-# define _W64
-# endif
-#endif
-
-
-// 7.18.1 Integer types
-
-// 7.18.1.1 Exact-width integer types
-
-// Visual Studio 6 and Embedded Visual C++ 4 doesn't
-// realize that, e.g. char has the same size as __int8
-// so we give up on __intX for them.
-#if (_MSC_VER < 1300)
- typedef signed char int8_t;
- typedef signed short int16_t;
- typedef signed int int32_t;
- typedef unsigned char uint8_t;
- typedef unsigned short uint16_t;
- typedef unsigned int uint32_t;
-#else
- typedef signed __int8 int8_t;
- typedef signed __int16 int16_t;
- typedef signed __int32 int32_t;
- typedef unsigned __int8 uint8_t;
- typedef unsigned __int16 uint16_t;
- typedef unsigned __int32 uint32_t;
-#endif
-typedef signed __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-
-
-// 7.18.1.2 Minimum-width integer types
-typedef int8_t int_least8_t;
-typedef int16_t int_least16_t;
-typedef int32_t int_least32_t;
-typedef int64_t int_least64_t;
-typedef uint8_t uint_least8_t;
-typedef uint16_t uint_least16_t;
-typedef uint32_t uint_least32_t;
-typedef uint64_t uint_least64_t;
-
-// 7.18.1.3 Fastest minimum-width integer types
-typedef int8_t int_fast8_t;
-typedef int16_t int_fast16_t;
-typedef int32_t int_fast32_t;
-typedef int64_t int_fast64_t;
-typedef uint8_t uint_fast8_t;
-typedef uint16_t uint_fast16_t;
-typedef uint32_t uint_fast32_t;
-typedef uint64_t uint_fast64_t;
-
-// 7.18.1.4 Integer types capable of holding object pointers
-#ifdef _WIN64 // [
- typedef signed __int64 intptr_t;
- typedef unsigned __int64 uintptr_t;
-#else // _WIN64 ][
- typedef _W64 signed int intptr_t;
- typedef _W64 unsigned int uintptr_t;
-#endif // _WIN64 ]
-
-// 7.18.1.5 Greatest-width integer types
-typedef int64_t intmax_t;
-typedef uint64_t uintmax_t;
-
-
-// 7.18.2 Limits of specified-width integer types
-
-#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
-
-// 7.18.2.1 Limits of exact-width integer types
-#define INT8_MIN ((int8_t)_I8_MIN)
-#define INT8_MAX _I8_MAX
-#define INT16_MIN ((int16_t)_I16_MIN)
-#define INT16_MAX _I16_MAX
-#define INT32_MIN ((int32_t)_I32_MIN)
-#define INT32_MAX _I32_MAX
-#define INT64_MIN ((int64_t)_I64_MIN)
-#define INT64_MAX _I64_MAX
-#define UINT8_MAX _UI8_MAX
-#define UINT16_MAX _UI16_MAX
-#define UINT32_MAX _UI32_MAX
-#define UINT64_MAX _UI64_MAX
-
-// 7.18.2.2 Limits of minimum-width integer types
-#define INT_LEAST8_MIN INT8_MIN
-#define INT_LEAST8_MAX INT8_MAX
-#define INT_LEAST16_MIN INT16_MIN
-#define INT_LEAST16_MAX INT16_MAX
-#define INT_LEAST32_MIN INT32_MIN
-#define INT_LEAST32_MAX INT32_MAX
-#define INT_LEAST64_MIN INT64_MIN
-#define INT_LEAST64_MAX INT64_MAX
-#define UINT_LEAST8_MAX UINT8_MAX
-#define UINT_LEAST16_MAX UINT16_MAX
-#define UINT_LEAST32_MAX UINT32_MAX
-#define UINT_LEAST64_MAX UINT64_MAX
-
-// 7.18.2.3 Limits of fastest minimum-width integer types
-#define INT_FAST8_MIN INT8_MIN
-#define INT_FAST8_MAX INT8_MAX
-#define INT_FAST16_MIN INT16_MIN
-#define INT_FAST16_MAX INT16_MAX
-#define INT_FAST32_MIN INT32_MIN
-#define INT_FAST32_MAX INT32_MAX
-#define INT_FAST64_MIN INT64_MIN
-#define INT_FAST64_MAX INT64_MAX
-#define UINT_FAST8_MAX UINT8_MAX
-#define UINT_FAST16_MAX UINT16_MAX
-#define UINT_FAST32_MAX UINT32_MAX
-#define UINT_FAST64_MAX UINT64_MAX
-
-// 7.18.2.4 Limits of integer types capable of holding object pointers
-#ifdef _WIN64 // [
-# define INTPTR_MIN INT64_MIN
-# define INTPTR_MAX INT64_MAX
-# define UINTPTR_MAX UINT64_MAX
-#else // _WIN64 ][
-# define INTPTR_MIN INT32_MIN
-# define INTPTR_MAX INT32_MAX
-# define UINTPTR_MAX UINT32_MAX
-#endif // _WIN64 ]
-
-// 7.18.2.5 Limits of greatest-width integer types
-#define INTMAX_MIN INT64_MIN
-#define INTMAX_MAX INT64_MAX
-#define UINTMAX_MAX UINT64_MAX
-
-// 7.18.3 Limits of other integer types
-
-#ifdef _WIN64 // [
-# define PTRDIFF_MIN _I64_MIN
-# define PTRDIFF_MAX _I64_MAX
-#else // _WIN64 ][
-# define PTRDIFF_MIN _I32_MIN
-# define PTRDIFF_MAX _I32_MAX
-#endif // _WIN64 ]
-
-#define SIG_ATOMIC_MIN INT_MIN
-#define SIG_ATOMIC_MAX INT_MAX
-
-#ifndef SIZE_MAX // [
-# ifdef _WIN64 // [
-# define SIZE_MAX _UI64_MAX
-# else // _WIN64 ][
-# define SIZE_MAX _UI32_MAX
-# endif // _WIN64 ]
-#endif // SIZE_MAX ]
-
-// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
-#ifndef WCHAR_MIN // [
-# define WCHAR_MIN 0
-#endif // WCHAR_MIN ]
-#ifndef WCHAR_MAX // [
-# define WCHAR_MAX _UI16_MAX
-#endif // WCHAR_MAX ]
-
-#define WINT_MIN 0
-#define WINT_MAX _UI16_MAX
-
-#endif // __STDC_LIMIT_MACROS ]
-
-
-// 7.18.4 Limits of other integer types
-
-#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
-
-// 7.18.4.1 Macros for minimum-width integer constants
-
-#define INT8_C(val) val##i8
-#define INT16_C(val) val##i16
-#define INT32_C(val) val##i32
-#define INT64_C(val) val##i64
-
-#define UINT8_C(val) val##ui8
-#define UINT16_C(val) val##ui16
-#define UINT32_C(val) val##ui32
-#define UINT64_C(val) val##ui64
-
-// 7.18.4.2 Macros for greatest-width integer constants
-#define INTMAX_C INT64_C
-#define UINTMAX_C UINT64_C
-
-#endif // __STDC_CONSTANT_MACROS ]
-
-
-#endif // _MSC_STDINT_H_ ]
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2013 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. 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.
+//
+// 3. Neither the name of the product nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#if _MSC_VER >= 1600 // [
+#include <stdint.h>
+#else // ] _MSC_VER >= 1600 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+# include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C // [
+# define INTMAX_C INT64_C
+#endif // INTMAX_C ]
+#ifndef UINTMAX_C // [
+# define UINTMAX_C UINT64_C
+#endif // UINTMAX_C ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/Orthanc/Resources/WindowsResources.py b/Orthanc/Resources/WindowsResources.py
index c56733b..b59e77a 100755
--- a/Orthanc/Resources/WindowsResources.py
+++ b/Orthanc/Resources/WindowsResources.py
@@ -3,6 +3,7 @@
# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017 Osimis, Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
diff --git a/Plugin/Configuration.cpp b/Plugin/Configuration.cpp
index 950db7f..d9c2e6f 100644
--- a/Plugin/Configuration.cpp
+++ b/Plugin/Configuration.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -67,15 +68,15 @@ namespace OrthancPlugins
Orthanc::Toolbox::StripSpaces(application);
Orthanc::Toolbox::ToLowerCase(application);
- boost::regex pattern("\\s*([^=]+)\\s*=\\s*([^=]+)\\s*");
-
+ boost::regex pattern("\\s*([^=]+)\\s*=\\s*(([^=\"]+)|\"([^=\"]+)\")\\s*");
+
for (size_t i = 1; i < tokens.size(); i++)
{
boost::cmatch what;
if (boost::regex_match(tokens[i].c_str(), what, pattern))
{
std::string key(what[1]);
- std::string value(what[2]);
+ std::string value(what.length(3) != 0 ? what[3] : what[4]);
Orthanc::Toolbox::ToLowerCase(key);
attributes[key] = value;
}
@@ -83,107 +84,163 @@ namespace OrthancPlugins
}
- void ParseMultipartBody(std::vector<MultipartItem>& result,
- OrthancPluginContext* context,
- const char* body,
- const uint64_t bodySize,
- const std::string& boundary)
- {
- // Reference:
- // https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
+ static const boost::regex MULTIPART_HEADERS_ENDING("(.*?\r\n)\r\n(.*)");
+ static const boost::regex MULTIPART_HEADERS_LINE(".*?\r\n");
- result.clear();
+ static void ParseMultipartHeaders(bool& hasLength /* out */,
+ size_t& length /* out */,
+ std::string& contentType /* out */,
+ OrthancPluginContext* context,
+ const char* startHeaders,
+ const char* endHeaders)
+ {
+ hasLength = false;
+ contentType = "application/octet-stream";
- const boost::regex separator("(^|\r\n)--" + boundary + "(--|\r\n)");
- const boost::regex encapsulation("(.*)\r\n\r\n(.*)");
-
- std::vector< std::pair<const char*, const char*> > parts;
-
- const char* start = body;
- const char* end = body + bodySize;
+ // Loop over the HTTP headers of this multipart item
+ boost::cregex_token_iterator it(startHeaders, endHeaders, MULTIPART_HEADERS_LINE, 0);
+ boost::cregex_token_iterator iteratorEnd;
- boost::cmatch what;
- boost::match_flag_type flags = boost::match_perl | boost::match_single_line;
- while (boost::regex_search(start, end, what, separator, flags))
+ for (; it != iteratorEnd; ++it)
{
- if (start != body) // Ignore the first separator
+ const std::string line(*it);
+ size_t colon = line.find(':');
+ size_t eol = line.find('\r');
+
+ if (colon != std::string::npos &&
+ eol != std::string::npos &&
+ colon < eol &&
+ eol + 2 == line.length())
{
- parts.push_back(std::make_pair(start, what[0].first));
- }
+ std::string key = Orthanc::Toolbox::StripSpaces(line.substr(0, colon));
+ Orthanc::Toolbox::ToLowerCase(key);
- if (*what[2].first == '-')
- {
- // This is the last separator (there is a trailing "--")
- break;
+ const std::string value = Orthanc::Toolbox::StripSpaces(line.substr(colon + 1, eol - colon - 1));
+
+ if (key == "content-length")
+ {
+ try
+ {
+ int tmp = boost::lexical_cast<int>(value);
+ if (tmp >= 0)
+ {
+ hasLength = true;
+ length = tmp;
+ }
+ }
+ catch (boost::bad_lexical_cast&)
+ {
+ OrthancPluginLogWarning(context, "Unable to parse the Content-Length of a multipart item");
+ }
+ }
+ else if (key == "content-type")
+ {
+ contentType = value;
+ }
}
+ }
+ }
- start = what[0].second;
- flags |= boost::match_prev_avail;
+
+ static const char* ParseMultipartItem(std::vector<MultipartItem>& result,
+ OrthancPluginContext* context,
+ const char* start,
+ const char* end,
+ const boost::regex& nextSeparator)
+ {
+ // Just before "start", it is guaranteed that "--[BOUNDARY]\r\n" is present
+
+ boost::cmatch what;
+ if (!boost::regex_match(start, end, what, MULTIPART_HEADERS_ENDING, boost::match_perl))
+ {
+ // Cannot find the HTTP headers of this multipart item
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
- for (size_t i = 0; i < parts.size(); i++)
+ // Some aliases for more clarity
+ assert(what[1].first == start);
+ const char* startHeaders = what[1].first;
+ const char* endHeaders = what[1].second;
+ const char* startBody = what[2].first;
+
+ bool hasLength;
+ size_t length;
+ std::string contentType;
+ ParseMultipartHeaders(hasLength, length, contentType, context, startHeaders, endHeaders);
+
+ boost::cmatch separator;
+
+ if (hasLength)
{
- if (boost::regex_match(parts[i].first, parts[i].second, what, encapsulation, boost::match_perl))
+ if (!boost::regex_match(startBody + length, end, separator, nextSeparator, boost::match_perl) ||
+ startBody + length != separator[1].first)
{
- size_t dicomSize = what[2].second - what[2].first;
+ // Cannot find the separator after skipping the "Content-Length" bytes
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+ }
+ }
+ else
+ {
+ if (!boost::regex_match(startBody, end, separator, nextSeparator, boost::match_perl))
+ {
+ // No more occurrence of the boundary separator
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+ }
+ }
- std::string contentType = "application/octet-stream";
- std::vector<std::string> headers;
+ MultipartItem item;
+ item.data_ = startBody;
+ item.size_ = separator[1].first - startBody;
+ item.contentType_ = contentType;
+ result.push_back(item);
- {
- std::string tmp;
- tmp.assign(what[1].first, what[1].second);
- Orthanc::Toolbox::TokenizeString(headers, tmp, '\n');
- }
+ return separator[1].second; // Return the end of the separator
+ }
- bool valid = true;
- for (size_t j = 0; j < headers.size(); j++)
- {
- std::vector<std::string> tokens;
- Orthanc::Toolbox::TokenizeString(tokens, headers[j], ':');
+ void ParseMultipartBody(std::vector<MultipartItem>& result,
+ OrthancPluginContext* context,
+ const char* body,
+ const uint64_t bodySize,
+ const std::string& boundary)
+ {
+ // Reference:
+ // https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
- if (tokens.size() == 2)
- {
- std::string key = Orthanc::Toolbox::StripSpaces(tokens[0]);
- std::string value = Orthanc::Toolbox::StripSpaces(tokens[1]);
- Orthanc::Toolbox::ToLowerCase(key);
+ result.clear();
- if (key == "content-type")
- {
- contentType = value;
- }
- else if (key == "content-length")
- {
- try
- {
- size_t s = boost::lexical_cast<size_t>(value);
- if (s != dicomSize)
- {
- valid = false;
- }
- }
- catch (boost::bad_lexical_cast&)
- {
- valid = false;
- }
- }
- }
- }
+ // Look for the first boundary separator in the body (note the "?"
+ // to request non-greedy search)
+ const boost::regex firstSeparator1("--" + boundary + "(--|\r\n).*");
+ const boost::regex firstSeparator2(".*?\r\n--" + boundary + "(--|\r\n).*");
+
+ // Look for the next boundary separator in the body (note the "?"
+ // to request non-greedy search)
+ const boost::regex nextSeparator(".*?(\r\n--" + boundary + ").*");
- if (valid)
+ const char* end = body + bodySize;
+
+ boost::cmatch what;
+ if (boost::regex_match(body, end, what, firstSeparator1, boost::match_perl | boost::match_single_line) ||
+ boost::regex_match(body, end, what, firstSeparator2, boost::match_perl | boost::match_single_line))
+ {
+ const char* current = what[1].first;
+
+ while (current != NULL &&
+ current + 2 < end)
+ {
+ if (current[0] != '\r' ||
+ current[1] != '\n')
{
- MultipartItem item;
- item.data_ = what[2].first;
- item.size_ = dicomSize;
- item.contentType_ = contentType;
- result.push_back(item);
+ // We reached a separator with a trailing "--", which
+ // means that reading the multipart body is done
+ break;
}
else
{
- OrthancPluginLogWarning(context, "Ignoring a badly-formatted item in a multipart body");
+ current = ParseMultipartItem(result, context, current + 2, end, nextSeparator);
}
- }
+ }
}
}
@@ -195,7 +252,7 @@ namespace OrthancPlugins
if (value.type() != Json::objectValue)
{
OrthancPlugins::Configuration::LogError("This is not a JSON object");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
if (!value.isMember(key))
@@ -209,7 +266,7 @@ namespace OrthancPlugins
{
OrthancPlugins::Configuration::LogError("The field \"" + key + "\" of a JSON object is "
"not a JSON associative array as expected");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
Json::Value::Members names = tmp.getMemberNames();
@@ -220,7 +277,7 @@ namespace OrthancPlugins
{
OrthancPlugins::Configuration::LogError("Some value in the associative array \"" + key +
"\" is not a string as expected");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
else
{
diff --git a/Plugin/Configuration.h b/Plugin/Configuration.h
index 7d2de3a..50d7e25 100644
--- a/Plugin/Configuration.h
+++ b/Plugin/Configuration.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/Dicom.cpp b/Plugin/Dicom.cpp
index 901de8e..ce78917 100644
--- a/Plugin/Dicom.cpp
+++ b/Plugin/Dicom.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -176,7 +177,7 @@ namespace OrthancPlugins
{
OrthancPlugins::Configuration::LogError("GDCM cannot decode this DICOM instance of length " +
boost::lexical_cast<std::string>(dicom.size()));
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
}
@@ -253,7 +254,7 @@ namespace OrthancPlugins
{
if (!GetDataSet().FindDataElement(tag))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InexistentTag);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentTag);
}
const gdcm::DataElement& element = GetDataSet().GetDataElement(tag);
@@ -700,7 +701,7 @@ namespace OrthancPlugins
if (key.find('.') != std::string::npos)
{
OrthancPlugins::Configuration::LogError("This DICOMweb plugin does not support hierarchical queries: " + key);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotImplemented);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
}
if (key.size() == 8 && // This is the DICOMweb convention
@@ -740,12 +741,12 @@ namespace OrthancPlugins
if (key.find('.') != std::string::npos)
{
OrthancPlugins::Configuration::LogError("This QIDO-RS implementation does not support search over sequences: " + key);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotImplemented);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
}
else
{
OrthancPlugins::Configuration::LogError("Illegal tag name in QIDO-RS: " + key);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownDicomTag);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownDicomTag);
}
}
diff --git a/Plugin/Dicom.h b/Plugin/Dicom.h
index c9af9fb..dbfc53e 100644
--- a/Plugin/Dicom.h
+++ b/Plugin/Dicom.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -49,6 +50,7 @@ namespace OrthancPlugins
static const gdcm::Tag DICOM_TAG_ACCESSION_NUMBER(0x0008, 0x0050);
static const gdcm::Tag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005);
static const gdcm::Tag DICOM_TAG_PIXEL_DATA(0x7fe0, 0x0010);
+ static const gdcm::Tag DICOM_TAG_SAMPLES_PER_PIXEL(0x0028, 0x0002);
static const gdcm::Tag DICOM_TAG_COLUMNS(0x0028, 0x0011);
static const gdcm::Tag DICOM_TAG_ROWS(0x0028, 0x0010);
static const gdcm::Tag DICOM_TAG_BITS_ALLOCATED(0x0028, 0x0100);
diff --git a/Plugin/DicomResults.cpp b/Plugin/DicomResults.cpp
index a4ef1d7..80003cb 100644
--- a/Plugin/DicomResults.cpp
+++ b/Plugin/DicomResults.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -47,7 +48,7 @@ namespace OrthancPlugins
OrthancPluginStartMultipartAnswer(context_, output_, "related", "application/dicom+xml") != 0)
{
OrthancPlugins::Configuration::LogError("Unable to create a multipart stream of DICOM+XML answers");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
jsonWriter_.AddChunk("[\n");
@@ -61,7 +62,7 @@ namespace OrthancPlugins
if (OrthancPluginSendMultipartItem(context_, output_, item.c_str(), item.size()) != 0)
{
OrthancPlugins::Configuration::LogError("Unable to create a multipart stream of DICOM+XML answers");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
}
else
@@ -120,7 +121,7 @@ namespace OrthancPlugins
{
if (source.type() != Json::objectValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
Json::Value::Members members = source.getMemberNames();
@@ -133,7 +134,7 @@ namespace OrthancPlugins
!source[members[i]].isMember("Type") ||
source[members[i]]["Type"].type() != Json::stringValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
const Json::Value& value = source[members[i]]["Value"];
@@ -213,7 +214,7 @@ namespace OrthancPlugins
if (type != "Sequence" ||
value.type() != Json::arrayValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
node["Value"] = Json::arrayValue;
@@ -222,7 +223,7 @@ namespace OrthancPlugins
{
if (value[i].type() != Json::objectValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
Json::Value child;
@@ -307,14 +308,14 @@ namespace OrthancPlugins
if (type != "Sequence" ||
value.type() != Json::arrayValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
for (Json::Value::ArrayIndex i = 0; i < value.size(); i++)
{
if (value[i].type() != Json::objectValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
pugi::xml_node child = node.append_child("Item");
diff --git a/Plugin/DicomResults.h b/Plugin/DicomResults.h
index bc7e09a..a348c0c 100644
--- a/Plugin/DicomResults.h
+++ b/Plugin/DicomResults.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/DicomWebClient.cpp b/Plugin/DicomWebClient.cpp
index 4259c02..8b8fcda 100644
--- a/Plugin/DicomWebClient.cpp
+++ b/Plugin/DicomWebClient.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -39,7 +40,7 @@ static void AddInstance(std::list<std::string>& target,
!instance.isMember("ID") ||
instance["ID"].type() != Json::stringValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
else
{
@@ -72,7 +73,7 @@ static bool GetSequenceSize(size_t& result,
{
OrthancPlugins::Configuration::LogError("The STOW-RS JSON response from DICOMweb server " + server +
" does not contain the mandatory tag " + upper);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
else
{
@@ -84,7 +85,7 @@ static bool GetSequenceSize(size_t& result,
(*value) ["Value"].type() != Json::arrayValue)
{
OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
result = (*value) ["Value"].size();
@@ -95,10 +96,12 @@ static bool GetSequenceSize(size_t& result,
static void ParseStowRequest(std::list<std::string>& instances /* out */,
std::map<std::string, std::string>& httpHeaders /* out */,
+ std::map<std::string, std::string>& queryArguments /* out */,
const OrthancPluginHttpRequest* request /* in */)
{
static const char* RESOURCES = "Resources";
static const char* HTTP_HEADERS = "HttpHeaders";
+ static const char* QUERY_ARGUMENTS = "Arguments";
OrthancPluginContext* context = OrthancPlugins::Configuration::GetContext();
@@ -112,9 +115,10 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
OrthancPlugins::Configuration::LogError("A request to the DICOMweb STOW-RS client must provide a JSON object "
"with the field \"" + std::string(RESOURCES) +
"\" containing an array of resources to be sent");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
+ OrthancPlugins::ParseAssociativeArray(queryArguments, body, QUERY_ARGUMENTS);
OrthancPlugins::ParseAssociativeArray(httpHeaders, body, HTTP_HEADERS);
Json::Value& resources = body[RESOURCES];
@@ -124,32 +128,32 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
{
if (resources[i].type() != Json::stringValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
std::string resource = resources[i].asString();
if (resource.empty())
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
}
// Test whether this resource is an instance
Json::Value tmp;
- if (OrthancPlugins::RestApiGetJson(tmp, context, "/instances/" + resource, false))
+ if (OrthancPlugins::RestApiGet(tmp, context, "/instances/" + resource, false))
{
AddInstance(instances, tmp);
}
// This was not an instance, successively try with series/studies/patients
- else if ((OrthancPlugins::RestApiGetJson(tmp, context, "/series/" + resource, false) &&
- OrthancPlugins::RestApiGetJson(tmp, context, "/series/" + resource + "/instances", false)) ||
- (OrthancPlugins::RestApiGetJson(tmp, context, "/studies/" + resource, false) &&
- OrthancPlugins::RestApiGetJson(tmp, context, "/studies/" + resource + "/instances", false)) ||
- (OrthancPlugins::RestApiGetJson(tmp, context, "/patients/" + resource, false) &&
- OrthancPlugins::RestApiGetJson(tmp, context, "/patients/" + resource + "/instances", false)))
+ else if ((OrthancPlugins::RestApiGet(tmp, context, "/series/" + resource, false) &&
+ OrthancPlugins::RestApiGet(tmp, context, "/series/" + resource + "/instances", false)) ||
+ (OrthancPlugins::RestApiGet(tmp, context, "/studies/" + resource, false) &&
+ OrthancPlugins::RestApiGet(tmp, context, "/studies/" + resource + "/instances", false)) ||
+ (OrthancPlugins::RestApiGet(tmp, context, "/patients/" + resource, false) &&
+ OrthancPlugins::RestApiGet(tmp, context, "/patients/" + resource + "/instances", false)))
{
if (tmp.type() != Json::arrayValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
for (Json::Value::ArrayIndex j = 0; j < tmp.size(); j++)
@@ -159,7 +163,7 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
}
else
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
}
}
}
@@ -167,6 +171,7 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
static void SendStowChunks(const Orthanc::WebServiceParameters& server,
const std::map<std::string, std::string>& httpHeaders,
+ const std::map<std::string, std::string>& queryArguments,
const std::string& boundary,
Orthanc::ChunkedBuffer& chunks,
size_t& countInstances,
@@ -186,8 +191,12 @@ static void SendStowChunks(const Orthanc::WebServiceParameters& server,
OrthancPlugins::MemoryBuffer answerBody(OrthancPlugins::Configuration::GetContext());
std::map<std::string, std::string> answerHeaders;
+
+ std::string uri;
+ OrthancPlugins::UriEncode(uri, "studies", queryArguments);
+
OrthancPlugins::CallServer(answerBody, answerHeaders, server, OrthancPluginHttpMethod_Post,
- httpHeaders, "studies", body);
+ httpHeaders, uri, body);
Json::Value response;
Json::Reader reader;
@@ -200,7 +209,7 @@ static void SendStowChunks(const Orthanc::WebServiceParameters& server,
!response.isMember("00081199"))
{
OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server.GetUrl());
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
size_t size;
@@ -210,7 +219,7 @@ static void SendStowChunks(const Orthanc::WebServiceParameters& server,
OrthancPlugins::Configuration::LogError("The STOW-RS server was only able to receive " +
boost::lexical_cast<std::string>(size) + " instances out of " +
boost::lexical_cast<std::string>(countInstances));
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
if (GetSequenceSize(size, response, "00081198", false, server.GetUrl()) &&
@@ -219,7 +228,7 @@ static void SendStowChunks(const Orthanc::WebServiceParameters& server,
OrthancPlugins::Configuration::LogError("The response from the STOW-RS server contains " +
boost::lexical_cast<std::string>(size) +
" items in its Failed SOP Sequence (0008,1198) tag");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
if (GetSequenceSize(size, response, "0008119A", false, server.GetUrl()) &&
@@ -228,7 +237,7 @@ static void SendStowChunks(const Orthanc::WebServiceParameters& server,
OrthancPlugins::Configuration::LogError("The response from the STOW-RS server contains " +
boost::lexical_cast<std::string>(size) +
" items in its Other Failures Sequence (0008,119A) tag");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
countInstances = 0;
@@ -244,7 +253,7 @@ void StowClient(OrthancPluginRestOutput* output,
if (request->groupsCount != 1)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
if (request->method != OrthancPluginHttpMethod_Post)
@@ -266,7 +275,7 @@ void StowClient(OrthancPluginRestOutput* output,
catch (...)
{
OrthancPluginFreeString(context, uuid);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotEnoughMemory);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
}
OrthancPluginFreeString(context, uuid);
@@ -274,15 +283,16 @@ void StowClient(OrthancPluginRestOutput* output,
std::string mime = "multipart/related; type=application/dicom; boundary=" + boundary;
+ std::map<std::string, std::string> queryArguments;
std::map<std::string, std::string> httpHeaders;
httpHeaders["Accept"] = "application/json";
httpHeaders["Expect"] = "";
httpHeaders["Content-Type"] = mime;
std::list<std::string> instances;
- ParseStowRequest(instances, httpHeaders, request);
+ ParseStowRequest(instances, httpHeaders, queryArguments, request);
- OrthancPlugins::Configuration::LogInfo("Sending " + boost::lexical_cast<std::string>(instances.size()) +
+ OrthancPlugins::Configuration::LogInfo("Sending " + boost::lexical_cast<std::string>(instances.size()) +
" instances using STOW-RS to DICOMweb server: " + server.GetUrl());
Orthanc::ChunkedBuffer chunks;
@@ -300,11 +310,11 @@ void StowClient(OrthancPluginRestOutput* output,
chunks.AddChunk(dicom.GetData(), dicom.GetSize());
countInstances ++;
- SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, false);
+ SendStowChunks(server, httpHeaders, queryArguments, boundary, chunks, countInstances, false);
}
}
- SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, true);
+ SendStowChunks(server, httpHeaders, queryArguments, boundary, chunks, countInstances, true);
std::string answer = "{}\n";
OrthancPluginAnswerBuffer(context, output, answer.c_str(), answer.size(), "application/json");
@@ -317,7 +327,7 @@ static bool GetStringValue(std::string& target,
{
if (json.type() != Json::objectValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
else if (!json.isMember(key))
{
@@ -327,7 +337,7 @@ static bool GetStringValue(std::string& target,
else if (json[key].type() != Json::stringValue)
{
OrthancPlugins::Configuration::LogError("The field \"" + key + "\" in a JSON object should be a string");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
else
{
@@ -362,9 +372,9 @@ void GetFromServer(OrthancPluginRestOutput* output,
body.type() != Json::objectValue ||
!GetStringValue(tmp, body, URI))
{
- OrthancPlugins::Configuration::LogError("A request to the DICOMweb STOW-RS client must provide a JSON object "
+ OrthancPlugins::Configuration::LogError("A request to the DICOMweb client must provide a JSON object "
"with the field \"Uri\" containing the URI of interest");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
std::map<std::string, std::string> getArguments;
@@ -412,6 +422,7 @@ void GetFromServer(OrthancPluginRestOutput* output,
static void RetrieveFromServerInternal(std::set<std::string>& instances,
const Orthanc::WebServiceParameters& server,
const std::map<std::string, std::string>& httpHeaders,
+ const std::map<std::string, std::string>& getArguments,
const Json::Value& resource)
{
static const std::string STUDY = "Study";
@@ -426,7 +437,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
{
OrthancPlugins::Configuration::LogError("Resources of interest for the DICOMweb WADO-RS Retrieve client "
"must be provided as a JSON object");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
std::string study, series, instance;
@@ -435,7 +446,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
{
OrthancPlugins::Configuration::LogError("A non-empty \"" + STUDY + "\" field is mandatory for the "
"DICOMweb WADO-RS Retrieve client");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
GetStringValue(series, resource, SERIES);
@@ -446,19 +457,22 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
{
OrthancPlugins::Configuration::LogError("When specifying a \"" + INSTANCE + "\" field in a call to DICOMweb "
"WADO-RS Retrieve client, the \"" + SERIES + "\" field is mandatory");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
- std::string uri = "studies/" + study;
+ std::string tmpUri = "studies/" + study;
if (!series.empty())
{
- uri += "/series/" + series;
+ tmpUri += "/series/" + series;
if (!instance.empty())
{
- uri += "/instances/" + instance;
+ tmpUri += "/instances/" + instance;
}
}
+ std::string uri;
+ OrthancPlugins::UriEncode(uri, tmpUri, getArguments);
+
OrthancPlugins::MemoryBuffer answerBody(context);
std::map<std::string, std::string> answerHeaders;
OrthancPlugins::CallServer(answerBody, answerHeaders, server, OrthancPluginHttpMethod_Get, httpHeaders, uri, "");
@@ -479,7 +493,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
if (contentType.empty())
{
OrthancPlugins::Configuration::LogError("No Content-Type provided by the remote WADO-RS server");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
Orthanc::Toolbox::ToLowerCase(contentType[0]);
@@ -487,7 +501,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
{
OrthancPlugins::Configuration::LogError("The remote WADO-RS server answers with a \"" + contentType[0] +
"\" Content-Type, but \"" + MULTIPART_RELATED + "\" is expected");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
std::string type, boundary;
@@ -504,6 +518,18 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
if (s == "type")
{
type = Orthanc::Toolbox::StripSpaces(tokens[1]);
+
+ // This covers the case where the content-type is quoted,
+ // which COULD be the case
+ // cf. https://tools.ietf.org/html/rfc7231#section-3.1.1.1
+ size_t len = type.length();
+ if (len >= 2 &&
+ type[0] == '"' &&
+ type[len - 1] == '"')
+ {
+ type = type.substr(1, len - 2);
+ }
+
Orthanc::Toolbox::ToLowerCase(type);
}
else if (s == "boundary")
@@ -517,13 +543,13 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
{
OrthancPlugins::Configuration::LogError("The remote WADO-RS server answers with a \"" + type +
"\" multipart Content-Type, but \"" + APPLICATION_DICOM + "\" is expected");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
if (boundary.empty())
{
OrthancPlugins::Configuration::LogError("The remote WADO-RS server does not provide a boundary for its multipart answer");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
std::vector<OrthancPlugins::MultipartItem> parts;
@@ -540,7 +566,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
if (parts[i].contentType_ != APPLICATION_DICOM)
{
OrthancPlugins::Configuration::LogError("The remote WADO-RS server has provided a non-DICOM file in its multipart answer");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
OrthancPlugins::MemoryBuffer tmp(context);
@@ -553,7 +579,7 @@ static void RetrieveFromServerInternal(std::set<std::string>& instances,
!result.isMember("ID") ||
result["ID"].type() != Json::stringValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
else
{
@@ -570,6 +596,7 @@ void RetrieveFromServer(OrthancPluginRestOutput* output,
{
static const std::string RESOURCES("Resources");
static const char* HTTP_HEADERS = "HttpHeaders";
+ static const std::string GET_ARGUMENTS = "Arguments";
OrthancPluginContext* context = OrthancPlugins::Configuration::GetContext();
@@ -590,16 +617,20 @@ void RetrieveFromServer(OrthancPluginRestOutput* output,
{
OrthancPlugins::Configuration::LogError("A request to the DICOMweb WADO-RS Retrieve client must provide a JSON object "
"with the field \"" + RESOURCES + "\" containing an array of resources");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
std::map<std::string, std::string> httpHeaders;
OrthancPlugins::ParseAssociativeArray(httpHeaders, body, HTTP_HEADERS);
+ std::map<std::string, std::string> getArguments;
+ OrthancPlugins::ParseAssociativeArray(getArguments, body, GET_ARGUMENTS);
+
+
std::set<std::string> instances;
for (Json::Value::ArrayIndex i = 0; i < body[RESOURCES].size(); i++)
{
- RetrieveFromServerInternal(instances, server, httpHeaders, body[RESOURCES][i]);
+ RetrieveFromServerInternal(instances, server, httpHeaders, getArguments, body[RESOURCES][i]);
}
Json::Value status = Json::objectValue;
diff --git a/Plugin/DicomWebClient.h b/Plugin/DicomWebClient.h
index 79922db..83b7f4d 100644
--- a/Plugin/DicomWebClient.h
+++ b/Plugin/DicomWebClient.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/DicomWebServers.cpp b/Plugin/DicomWebServers.cpp
index bbc8112..4df24ee 100644
--- a/Plugin/DicomWebServers.cpp
+++ b/Plugin/DicomWebServers.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -71,7 +72,7 @@ namespace OrthancPlugins
if (!ok)
{
OrthancPlugins::Configuration::LogError("Cannot parse the \"DicomWeb.Servers\" section of the configuration file");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
}
@@ -92,7 +93,7 @@ namespace OrthancPlugins
server->second == NULL)
{
OrthancPlugins::Configuration::LogError("Inexistent server: " + name);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InexistentItem);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem);
}
else
{
@@ -206,7 +207,7 @@ namespace OrthancPlugins
{
OrthancPlugins::Configuration::LogError("Cannot issue an HTTP query to " + url +
" (HTTP status: " + boost::lexical_cast<std::string>(status) + ")");
- throw PluginException(code);
+ throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(code));
}
Json::Value json;
@@ -215,7 +216,7 @@ namespace OrthancPlugins
if (json.type() != Json::objectValue)
{
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
Json::Value::Members members = json.getMemberNames();
@@ -225,7 +226,7 @@ namespace OrthancPlugins
if (json[key].type() != Json::stringValue)
{
- throw PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
else
{
@@ -243,7 +244,7 @@ namespace OrthancPlugins
{
OrthancPlugins::Configuration::LogError("The GET arguments must be provided in a separate field "
"(explicit \"?\" is disallowed): " + resource);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
uri = resource;
diff --git a/Plugin/DicomWebServers.h b/Plugin/DicomWebServers.h
index df3aded..744e40f 100644
--- a/Plugin/DicomWebServers.h
+++ b/Plugin/DicomWebServers.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/Plugin.cpp b/Plugin/Plugin.cpp
index 0bf076e..e1832f8 100644
--- a/Plugin/Plugin.cpp
+++ b/Plugin/Plugin.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -140,10 +141,21 @@ void ListServerOperations(OrthancPluginRestOutput* output,
}
+static bool DisplayPerformanceWarning(OrthancPluginContext* context)
+{
+ (void) DisplayPerformanceWarning; // Disable warning about unused function
+ OrthancPluginLogWarning(context, "Performance warning in DICOMweb: "
+ "Non-release build, runtime debug assertions are turned on");
+ return true;
+}
+
+
extern "C"
{
ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
{
+ assert(DisplayPerformanceWarning(context));
+
/* Check the version of the Orthanc core */
if (OrthancPluginCheckVersion(context) == 0)
{
@@ -215,12 +227,6 @@ extern "C"
OrthancPlugins::Configuration::LogWarning("WADO-URI support is disabled");
}
}
- catch (OrthancPlugins::PluginException& e)
- {
- OrthancPlugins::Configuration::LogError("Exception while initializing the DICOMweb plugin: " +
- std::string(e.GetErrorDescription(context)));
- return -1;
- }
catch (Orthanc::OrthancException& e)
{
OrthancPlugins::Configuration::LogError("Exception while initializing the DICOMweb plugin: " +
diff --git a/Plugin/Plugin.h b/Plugin/Plugin.h
index e3a7721..b678331 100644
--- a/Plugin/Plugin.h
+++ b/Plugin/Plugin.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/QidoRs.cpp b/Plugin/QidoRs.cpp
index 7a69482..312aa9e 100644
--- a/Plugin/QidoRs.cpp
+++ b/Plugin/QidoRs.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -150,7 +151,7 @@ namespace
break;
default:
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
}
@@ -162,10 +163,13 @@ namespace
limit_(0),
includeAllFields_(false)
{
+ std::string args;
+
for (uint32_t i = 0; i < request->getCount; i++)
{
std::string key(request->getKeys[i]);
std::string value(request->getValues[i]);
+ args += " [" + key + "=" + value + "]";
if (key == "limit")
{
@@ -188,7 +192,7 @@ namespace
else
{
OrthancPlugins::Configuration::LogError("Not a proper value for fuzzy matching (true or false): " + value);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
}
else if (key == "includefield")
@@ -213,6 +217,8 @@ namespace
filters_[OrthancPlugins::ParseTag(*dictionary_, key)] = value;
}
}
+
+ OrthancPlugins::Configuration::LogInfo("Arguments of QIDO-RS request:" + args);
}
unsigned int GetLimit() const
@@ -260,13 +266,24 @@ namespace
break;
default:
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
result["Expand"] = false;
result["CaseSensitive"] = true;
result["Query"] = Json::objectValue;
+ result["Limit"] = limit_;
+ result["Since"] = offset_;
+ if (offset_ != 0 &&
+ !OrthancPlugins::CheckMinimalOrthancVersion(
+ OrthancPlugins::Configuration::GetContext(), 1, 2, 1))
+ {
+ OrthancPlugins::Configuration::LogError(
+ "QIDO-RS request with \"offset\" argument: "
+ "Only available if the Orthanc core version is >= 1.2.1");
+ }
+
for (Filters::const_iterator it = filters_.begin();
it != filters_.end(); ++it)
{
@@ -288,8 +305,8 @@ namespace
case QueryLevel_Study:
{
Json::Value series, instances;
- if (OrthancPlugins::RestApiGetJson(series, context, "/studies/" + resource + "/series?expand", false) &&
- OrthancPlugins::RestApiGetJson(instances, context, "/studies/" + resource + "/instances", false))
+ if (OrthancPlugins::RestApiGet(series, context, "/studies/" + resource + "/series?expand", false) &&
+ OrthancPlugins::RestApiGet(instances, context, "/studies/" + resource + "/instances", false))
{
// Number of Study Related Series
target[gdcm::Tag(0x0020, 0x1206)] = boost::lexical_cast<std::string>(series.size());
@@ -335,7 +352,7 @@ namespace
case QueryLevel_Series:
{
Json::Value instances;
- if (OrthancPlugins::RestApiGetJson(instances, context, "/series/" + resource + "/instances", false))
+ if (OrthancPlugins::RestApiGet(instances, context, "/series/" + resource + "/instances", false))
{
// Number of Series Related Instances
target[gdcm::Tag(0x0020, 0x1209)] = boost::lexical_cast<std::string>(instances.size());
@@ -507,10 +524,10 @@ static void ApplyMatcher(OrthancPluginRestOutput* output,
std::string body = writer.write(find);
Json::Value resources;
- if (!OrthancPlugins::RestApiPostJson(resources, context, "/tools/find", body, false) ||
+ if (!OrthancPlugins::RestApiPost(resources, context, "/tools/find", body, false) ||
resources.type() != Json::arrayValue)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
typedef std::list< std::pair<std::string, std::string> > ResourcesAndInstances;
@@ -527,7 +544,7 @@ static void ApplyMatcher(OrthancPluginRestOutput* output,
{
// Find one child instance of this resource
Json::Value tmp;
- if (OrthancPlugins::RestApiGetJson(tmp, context, root + resource + "/instances", false) &&
+ if (OrthancPlugins::RestApiGet(tmp, context, root + resource + "/instances", false) &&
tmp.type() == Json::arrayValue &&
tmp.size() > 0)
{
@@ -585,7 +602,7 @@ static void ApplyMatcher(OrthancPluginRestOutput* output,
it = resourcesAndInstances.begin(); it != resourcesAndInstances.end(); ++it)
{
Json::Value tags;
- if (OrthancPlugins::RestApiGetJson(tags, context, "/instances/" + it->second + "/tags", false))
+ if (OrthancPlugins::RestApiGet(tags, context, "/instances/" + it->second + "/tags", false))
{
std::string wadoUrl = OrthancPlugins::Configuration::GetWadoUrl(
wadoBase,
diff --git a/Plugin/QidoRs.h b/Plugin/QidoRs.h
index 77a5c80..490d356 100644
--- a/Plugin/QidoRs.h
+++ b/Plugin/QidoRs.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/StowRs.cpp b/Plugin/StowRs.cpp
index a8b1c55..a4a5e66 100644
--- a/Plugin/StowRs.cpp
+++ b/Plugin/StowRs.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -153,6 +154,12 @@ void StowCallback(OrthancPluginRestOutput* output,
std::vector<OrthancPlugins::MultipartItem> items;
OrthancPlugins::ParseMultipartBody(items, context, request->body, request->bodySize, boundary);
+ for (size_t i = 0; i < items.size(); i++)
+ {
+ OrthancPlugins::Configuration::LogInfo("Detected multipart item with content type \"" +
+ items[i].contentType_ + "\" of size " +
+ boost::lexical_cast<std::string>(items[i].size_));
+ }
for (size_t i = 0; i < items.size(); i++)
{
diff --git a/Plugin/StowRs.h b/Plugin/StowRs.h
index 9e57321..7899c6c 100644
--- a/Plugin/StowRs.h
+++ b/Plugin/StowRs.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/WadoRs.cpp b/Plugin/WadoRs.cpp
index 173071a..2bd619f 100644
--- a/Plugin/WadoRs.cpp
+++ b/Plugin/WadoRs.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -174,7 +175,7 @@ static void AnswerListOfDicomInstances(OrthancPluginRestOutput* output,
OrthancPluginContext* context = OrthancPlugins::Configuration::GetContext();
Json::Value instances;
- if (!OrthancPlugins::RestApiGetJson(instances, context, resource + "/instances", false))
+ if (!OrthancPlugins::RestApiGet(instances, context, resource + "/instances", false))
{
// Internal error
OrthancPluginSendHttpStatusCode(context, output, 400);
@@ -183,7 +184,7 @@ static void AnswerListOfDicomInstances(OrthancPluginRestOutput* output,
if (OrthancPluginStartMultipartAnswer(context, output, "related", "application/dicom"))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++)
@@ -194,7 +195,7 @@ static void AnswerListOfDicomInstances(OrthancPluginRestOutput* output,
if (dicom.RestApiGet(uri, false) &&
OrthancPluginSendMultipartItem(context, output, dicom.GetData(), dicom.GetSize()) != 0)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
}
}
@@ -217,7 +218,7 @@ static void AnswerMetadata(OrthancPluginRestOutput* output,
else
{
Json::Value instances;
- if (!OrthancPlugins::RestApiGetJson(instances, context, resource + "/instances", false))
+ if (!OrthancPlugins::RestApiGet(instances, context, resource + "/instances", false))
{
// Internal error
OrthancPluginSendHttpStatusCode(context, output, 400);
@@ -310,7 +311,7 @@ static bool LocateSeries(OrthancPluginRestOutput* output,
}
Json::Value study;
- if (!OrthancPlugins::RestApiGetJson(study, context, "/series/" + id + "/study", false))
+ if (!OrthancPlugins::RestApiGet(study, context, "/series/" + id + "/study", false))
{
OrthancPluginSendHttpStatusCode(context, output, 404);
return false;
@@ -358,8 +359,8 @@ bool LocateInstance(OrthancPluginRestOutput* output,
}
Json::Value study, series;
- if (!OrthancPlugins::RestApiGetJson(series, context, "/instances/" + id + "/series", false) ||
- !OrthancPlugins::RestApiGetJson(study, context, "/instances/" + id + "/study", false))
+ if (!OrthancPlugins::RestApiGet(series, context, "/instances/" + id + "/series", false) ||
+ !OrthancPlugins::RestApiGet(study, context, "/instances/" + id + "/study", false))
{
OrthancPluginSendHttpStatusCode(context, output, 404);
return false;
@@ -436,14 +437,14 @@ void RetrieveDicomInstance(OrthancPluginRestOutput* output,
{
if (OrthancPluginStartMultipartAnswer(context, output, "related", "application/dicom"))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
OrthancPlugins::MemoryBuffer dicom(context);
if (dicom.RestApiGet(uri + "/file", false) &&
OrthancPluginSendMultipartItem(context, output, dicom.GetData(), dicom.GetSize()) != 0)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
}
}
@@ -612,7 +613,7 @@ void RetrieveBulkData(OrthancPluginRestOutput* output,
if (OrthancPluginStartMultipartAnswer(context, output, "related", "application/octet-stream") != 0 ||
OrthancPluginSendMultipartItem(context, output, result.c_str(), result.size()) != 0)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_Plugin);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
}
}
else
diff --git a/Plugin/WadoRs.h b/Plugin/WadoRs.h
index bbbd302..e48a9fd 100644
--- a/Plugin/WadoRs.h
+++ b/Plugin/WadoRs.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Plugin/WadoRsRetrieveFrames.cpp b/Plugin/WadoRsRetrieveFrames.cpp
index 7ad13fa..4eeeb70 100644
--- a/Plugin/WadoRsRetrieveFrames.cpp
+++ b/Plugin/WadoRsRetrieveFrames.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -68,7 +69,7 @@ static gdcm::TransferSyntax ParseTransferSyntax(const OrthancPluginHttpRequest*
if (tokens[0] != "multipart/related")
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
}
std::string type("application/octet-stream");
@@ -81,7 +82,7 @@ static gdcm::TransferSyntax ParseTransferSyntax(const OrthancPluginHttpRequest*
if (parsed.size() != 2)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
if (parsed[0] == "type")
@@ -105,12 +106,70 @@ static gdcm::TransferSyntax ParseTransferSyntax(const OrthancPluginHttpRequest*
{
OrthancPlugins::Configuration::LogError("DICOMweb RetrieveFrames: Cannot specify a transfer syntax (" +
transferSyntax + ") for default Little Endian uncompressed pixel data");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
}
else
{
- // http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.5-1
+ /**
+ * DICOM 2017c
+ * http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.1.1.8-3b
+ **/
+ if (type == "image/jpeg" && (transferSyntax.empty() || // Default
+ transferSyntax == "1.2.840.10008.1.2.4.70"))
+ {
+ return gdcm::TransferSyntax::JPEGLosslessProcess14_1;
+ }
+ else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.50")
+ {
+ return gdcm::TransferSyntax::JPEGBaselineProcess1;
+ }
+ else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.51")
+ {
+ return gdcm::TransferSyntax::JPEGExtendedProcess2_4;
+ }
+ else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.57")
+ {
+ return gdcm::TransferSyntax::JPEGLosslessProcess14;
+ }
+ else if (type == "image/x-dicom-rle" && (transferSyntax.empty() || // Default
+ transferSyntax == "1.2.840.10008.1.2.5"))
+ {
+ return gdcm::TransferSyntax::RLELossless;
+ }
+ else if (type == "image/x-jls" && (transferSyntax.empty() || // Default
+ transferSyntax == "1.2.840.10008.1.2.4.80"))
+ {
+ return gdcm::TransferSyntax::JPEGLSLossless;
+ }
+ else if (type == "image/x-jls" && transferSyntax == "1.2.840.10008.1.2.4.81")
+ {
+ return gdcm::TransferSyntax::JPEGLSNearLossless;
+ }
+ else if (type == "image/jp2" && (transferSyntax.empty() || // Default
+ transferSyntax == "1.2.840.10008.1.2.4.90"))
+ {
+ return gdcm::TransferSyntax::JPEG2000Lossless;
+ }
+ else if (type == "image/jp2" && transferSyntax == "1.2.840.10008.1.2.4.91")
+ {
+ return gdcm::TransferSyntax::JPEG2000;
+ }
+ else if (type == "image/jpx" && (transferSyntax.empty() || // Default
+ transferSyntax == "1.2.840.10008.1.2.4.92"))
+ {
+ return gdcm::TransferSyntax::JPEG2000Part2Lossless;
+ }
+ else if (type == "image/jpx" && transferSyntax == "1.2.840.10008.1.2.4.93")
+ {
+ return gdcm::TransferSyntax::JPEG2000Part2;
+ }
+
+
+ /**
+ * Backward compatibility with DICOM 2014a
+ * http://dicom.nema.org/medical/dicom/2014a/output/html/part18.html#table_6.5-1
+ **/
if (type == "image/dicom+jpeg" && transferSyntax == "1.2.840.10008.1.2.4.50")
{
return gdcm::TransferSyntax::JPEGBaselineProcess1;
@@ -160,12 +219,11 @@ static gdcm::TransferSyntax ParseTransferSyntax(const OrthancPluginHttpRequest*
{
return gdcm::TransferSyntax::JPEG2000Part2;
}
- else
- {
- OrthancPlugins::Configuration::LogError("DICOMweb RetrieveFrames: Transfer syntax \"" +
+
+
+ OrthancPlugins::Configuration::LogError("DICOMweb RetrieveFrames: Transfer syntax \"" +
transferSyntax + "\" is incompatible with media type \"" + type + "\"");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
- }
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
}
}
@@ -199,7 +257,7 @@ static void ParseFrameList(std::list<unsigned int>& frames,
if (frame <= 0)
{
OrthancPlugins::Configuration::LogError("Invalid frame number (must be > 0): " + tokens[i]);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
}
frames.push_back(static_cast<unsigned int>(frame - 1));
@@ -210,46 +268,48 @@ static void ParseFrameList(std::list<unsigned int>& frames,
static const char* GetMimeType(const gdcm::TransferSyntax& syntax)
{
+ // http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.1.1.8-3b
+
switch (syntax)
{
case gdcm::TransferSyntax::ImplicitVRLittleEndian:
return "application/octet-stream";
case gdcm::TransferSyntax::JPEGBaselineProcess1:
- return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.50";
+ return "image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.50";
case gdcm::TransferSyntax::JPEGExtendedProcess2_4:
- return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.51";
+ return "image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.51";
case gdcm::TransferSyntax::JPEGLosslessProcess14:
- return "image/dicom+jpeg; transfer-syntax=1.2.840.10008.1.2.4.57";
+ return "image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.57";
case gdcm::TransferSyntax::JPEGLosslessProcess14_1:
- return "image/dicom+jpeg; transferSyntax=1.2.840.10008.1.2.4.70";
+ return "image/jpeg; transferSyntax=1.2.840.10008.1.2.4.70";
case gdcm::TransferSyntax::RLELossless:
- return "image/dicom+rle; transferSyntax=1.2.840.10008.1.2.5";
+ return "image/x-dicom-rle; transferSyntax=1.2.840.10008.1.2.5";
case gdcm::TransferSyntax::JPEGLSLossless:
- return "image/dicom+jpeg-ls; transferSyntax=1.2.840.10008.1.2.4.80";
+ return "image/x-jls; transferSyntax=1.2.840.10008.1.2.4.80";
case gdcm::TransferSyntax::JPEGLSNearLossless:
- return "image/dicom+jpeg-ls; transfer-syntax=1.2.840.10008.1.2.4.81";
+ return "image/x-jls; transfer-syntax=1.2.840.10008.1.2.4.81";
case gdcm::TransferSyntax::JPEG2000Lossless:
- return "image/dicom+jp2; transferSyntax=1.2.840.10008.1.2.4.90";
+ return "image/jp2; transferSyntax=1.2.840.10008.1.2.4.90";
case gdcm::TransferSyntax::JPEG2000:
- return "image/dicom+jp2; transfer-syntax=1.2.840.10008.1.2.4.91";
+ return "image/jp2; transfer-syntax=1.2.840.10008.1.2.4.91";
case gdcm::TransferSyntax::JPEG2000Part2Lossless:
- return "image/dicom+jpx; transferSyntax=1.2.840.10008.1.2.4.92";
+ return "image/jpx; transferSyntax=1.2.840.10008.1.2.4.92";
case gdcm::TransferSyntax::JPEG2000Part2:
- return "image/dicom+jpx; transfer-syntax=1.2.840.10008.1.2.4.93";
+ return "image/jpx; transfer-syntax=1.2.840.10008.1.2.4.93";
default:
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
}
@@ -275,7 +335,7 @@ static void AnswerSingleFrame(OrthancPluginRestOutput* output,
if (error != OrthancPluginErrorCode_Success)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
}
}
@@ -289,7 +349,7 @@ static bool AnswerFrames(OrthancPluginRestOutput* output,
{
if (!dicom.GetDataSet().FindDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_IncompatibleImageFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
}
const gdcm::DataElement& pixelData = dicom.GetDataSet().GetDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA);
@@ -308,23 +368,24 @@ static bool AnswerFrames(OrthancPluginRestOutput* output,
if (pixelData.GetByteValue() == NULL)
{
OrthancPlugins::Configuration::LogError("Image was not properly decoded");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
- int width, height, bits;
+ int width, height, bits, samplesPerPixel;
if (!dicom.GetIntegerTag(height, *dictionary_, OrthancPlugins::DICOM_TAG_ROWS) ||
!dicom.GetIntegerTag(width, *dictionary_, OrthancPlugins::DICOM_TAG_COLUMNS) ||
- !dicom.GetIntegerTag(bits, *dictionary_, OrthancPlugins::DICOM_TAG_BITS_ALLOCATED))
+ !dicom.GetIntegerTag(bits, *dictionary_, OrthancPlugins::DICOM_TAG_BITS_ALLOCATED) ||
+ !dicom.GetIntegerTag(samplesPerPixel, *dictionary_, OrthancPlugins::DICOM_TAG_SAMPLES_PER_PIXEL))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
- size_t frameSize = height * width * bits / 8;
+ size_t frameSize = height * width * bits * samplesPerPixel / 8;
if (pixelData.GetByteValue()->GetLength() % frameSize != 0)
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
size_t framesCount = pixelData.GetByteValue()->GetLength() / frameSize;
@@ -348,7 +409,7 @@ static bool AnswerFrames(OrthancPluginRestOutput* output,
{
OrthancPlugins::Configuration::LogError("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) +
" of an image with " + boost::lexical_cast<std::string>(framesCount) + " frames");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
}
else
{
@@ -381,7 +442,7 @@ static bool AnswerFrames(OrthancPluginRestOutput* output,
" of an image with " +
boost::lexical_cast<std::string>(fragments->GetNumberOfFragments()) +
" frames");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
}
else
{
@@ -413,7 +474,7 @@ void RetrieveFrames(OrthancPluginRestOutput* output,
OrthancPlugins::MemoryBuffer content(context);
if (LocateInstance(output, uri, request) &&
content.RestApiGet(uri + "/file", false) &&
- OrthancPlugins::RestApiGetJson(header, context, uri + "/header?simplify", false))
+ OrthancPlugins::RestApiGet(header, context, uri + "/header?simplify", false))
{
{
std::string s = "DICOMweb RetrieveFrames on " + uri + ", frames: ";
@@ -476,14 +537,14 @@ void RetrieveFrames(OrthancPluginRestOutput* output,
if (!reader.Read())
{
OrthancPlugins::Configuration::LogError("Cannot decode the image");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
}
change.SetInput(reader.GetImage());
if (!change.Change())
{
OrthancPlugins::Configuration::LogError("Cannot change the transfer syntax of the image");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
gdcm::ImageWriter writer;
@@ -494,7 +555,7 @@ void RetrieveFrames(OrthancPluginRestOutput* output,
writer.SetStream(ss);
if (!writer.Write())
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotEnoughMemory);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
}
OrthancPlugins::ParsedDicomFile transcoded(ss.str());
diff --git a/Plugin/WadoUri.cpp b/Plugin/WadoUri.cpp
index 3eb13d4..af3f1b6 100644
--- a/Plugin/WadoUri.cpp
+++ b/Plugin/WadoUri.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -116,7 +117,7 @@ static bool LocateInstance(std::string& instance,
else
{
Json::Value info;
- if (!OrthancPlugins::RestApiGetJson(info, context, "/instances/" + instance + "/series", false) ||
+ if (!OrthancPlugins::RestApiGet(info, context, "/instances/" + instance + "/series", false) ||
info["MainDicomTags"]["SeriesInstanceUID"] != seriesUid)
{
OrthancPlugins::Configuration::LogError("WADO-URI: Instance " + objectUid + " does not belong to series " + seriesUid);
@@ -136,7 +137,7 @@ static bool LocateInstance(std::string& instance,
else
{
Json::Value info;
- if (!OrthancPlugins::RestApiGetJson(info, context, "/instances/" + instance + "/study", false) ||
+ if (!OrthancPlugins::RestApiGet(info, context, "/instances/" + instance + "/study", false) ||
info["MainDicomTags"]["StudyInstanceUID"] != studyUid)
{
OrthancPlugins::Configuration::LogError("WADO-URI: Instance " + objectUid + " does not belong to study " + studyUid);
@@ -165,7 +166,7 @@ static void AnswerDicom(OrthancPluginRestOutput* output,
else
{
OrthancPlugins::Configuration::LogError("WADO-URI: Unable to retrieve DICOM file from " + uri);
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_Plugin);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
}
}
@@ -200,7 +201,7 @@ static void AnswerPngPreview(OrthancPluginRestOutput* output,
}
else
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_Plugin);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
}
}
@@ -214,7 +215,7 @@ static void AnswerJpegPreview(OrthancPluginRestOutput* output,
OrthancPlugins::MemoryBuffer png(context);
if (!RetrievePngPreview(png, instance))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_Plugin);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
}
OrthancPlugins::OrthancImage image(context);
@@ -237,7 +238,7 @@ void WadoUriCallback(OrthancPluginRestOutput* output,
std::string contentType = "image/jpg"; // By default, JPEG image will be returned
if (!LocateInstance(instance, contentType, request))
{
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
}
if (contentType == "application/dicom")
@@ -256,6 +257,6 @@ void WadoUriCallback(OrthancPluginRestOutput* output,
else
{
OrthancPlugins::Configuration::LogError("WADO-URI: Unsupported content type: \"" + contentType + "\"");
- throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
}
}
diff --git a/Plugin/WadoUri.h b/Plugin/WadoUri.h
index 1784723..53b3d05 100644
--- a/Plugin/WadoUri.h
+++ b/Plugin/WadoUri.h
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/README b/README
index 347fb6d..fea03a7 100644
--- a/README
+++ b/README
@@ -35,9 +35,8 @@ the "./Status.txt" file.
Installation and usage
----------------------
-Build instructions can be found in "./Resources/BuildInstructions.txt".
-
-Usage instructions can be found in "./Usage.txt".
+Build and usage instructions are available in the Orthanc Book:
+https://orthanc.chu.ulg.ac.be/book/plugins/dicomweb.html
Licensing: AGPL
diff --git a/Resources/BuildInstructions.txt b/Resources/BuildInstructions.txt
index b02a77f..b63195c 100644
--- a/Resources/BuildInstructions.txt
+++ b/Resources/BuildInstructions.txt
@@ -1,5 +1,5 @@
-Generic Linux (static linking)
-==============================
+Generic GNU/Linux (static linking)
+==================================
# mkdir Build
# cd Build
@@ -29,8 +29,8 @@ Dynamic linking for Ubuntu 12.10
# make
-Cross-compiling for Windows from Linux using MinGW
-==================================================
+Cross-compiling for Windows from GNU/Linux using MinGW
+======================================================
# mkdir Build
# cd Build
@@ -41,6 +41,7 @@ Cross-compiling for Windows from Linux using MinGW
Notes
=====
-List the public symbols exported by the shared library under Linux:
+List the public symbols exported by the shared library under
+GNU/Linux:
# nm -C -D --defined-only ./libOrthancDicomWeb.so
diff --git a/Resources/CMake/GdcmConfiguration.cmake b/Resources/CMake/GdcmConfiguration.cmake
index 402e470..7f37199 100644
--- a/Resources/CMake/GdcmConfiguration.cmake
+++ b/Resources/CMake/GdcmConfiguration.cmake
@@ -1,6 +1,7 @@
# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017 Osimis, Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
@@ -44,8 +45,9 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_GDCM)
include(ExternalProject)
externalproject_add(GDCM
- URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/gdcm-2.6.0.tar.gz"
+ URL "http://www.orthanc-server.com/downloads/third-party/gdcm-2.6.0.tar.gz"
URL_MD5 "978afe57af448b1c97c9f116790aca9c"
+ TIMEOUT 60
CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ${Flags}
#-DLIBRARY_OUTPUT_PATH=${CMAKE_CURRENT_BINARY_DIR}
INSTALL_COMMAND "" # Skip the install step
diff --git a/Resources/Samples/JavaScript/qido-rs.js b/Resources/Samples/JavaScript/qido-rs.js
index ab7e387..b954d23 100644
--- a/Resources/Samples/JavaScript/qido-rs.js
+++ b/Resources/Samples/JavaScript/qido-rs.js
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Resources/Samples/JavaScript/stow-rs.js b/Resources/Samples/JavaScript/stow-rs.js
index cc37f84..9a3fe3b 100644
--- a/Resources/Samples/JavaScript/stow-rs.js
+++ b/Resources/Samples/JavaScript/stow-rs.js
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
diff --git a/Resources/Samples/Python/SendStow.py b/Resources/Samples/Python/SendStow.py
index 98c5b18..2547fe0 100755
--- a/Resources/Samples/Python/SendStow.py
+++ b/Resources/Samples/Python/SendStow.py
@@ -3,6 +3,7 @@
# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017 Osimis, Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
@@ -30,7 +31,8 @@ import sys
import json
import uuid
-if len(sys.argv) < 2:
+#if len(sys.argv) < 2:
+if len(sys.argv) < 1:
print('Usage: %s <StowUri> <file>...' % sys.argv[0])
print('')
print('Example: %s http://localhost:8042/dicom-web/studies hello.dcm world.dcm' % sys.argv[0])
@@ -45,9 +47,11 @@ body = bytearray()
for i in range(2, len(sys.argv)):
try:
with open(sys.argv[i], 'rb') as f:
+ content = f.read()
body += bytearray('--%s\r\n' % boundary, 'ascii')
+ body += bytearray('Content-Length: %d\r\n' % len(content), 'ascii')
body += bytearray('Content-Type: application/dicom\r\n\r\n', 'ascii')
- body += f.read()
+ body += content
body += bytearray('\r\n', 'ascii')
except:
print('Ignoring directory %s' % sys.argv[i])
diff --git a/Resources/Samples/Python/WadoRetrieveStudy.py b/Resources/Samples/Python/WadoRetrieveStudy.py
index 25ed702..3e280d6 100755
--- a/Resources/Samples/Python/WadoRetrieveStudy.py
+++ b/Resources/Samples/Python/WadoRetrieveStudy.py
@@ -3,6 +3,7 @@
# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017 Osimis, Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
diff --git a/Resources/SyncOrthancFolder.py b/Resources/SyncOrthancFolder.py
index 5ed3021..9756cef 100755
--- a/Resources/SyncOrthancFolder.py
+++ b/Resources/SyncOrthancFolder.py
@@ -25,9 +25,12 @@ FILES = [
'Core/PrecompiledHeaders.h',
'Core/Toolbox.cpp',
'Core/Toolbox.h',
+ 'Core/SystemToolbox.h',
+ 'Core/SystemToolbox.cpp',
'Core/WebServiceParameters.cpp',
'Core/WebServiceParameters.h',
'Plugins/Samples/Common/ExportedSymbols.list',
+ 'Plugins/Samples/Common/OrthancPluginException.h',
'Plugins/Samples/Common/OrthancPluginCppWrapper.h',
'Plugins/Samples/Common/OrthancPluginCppWrapper.cpp',
'Plugins/Samples/Common/VersionScript.map',
diff --git a/Status.txt b/Status.txt
index 3d4fdae..946bea7 100644
--- a/Status.txt
+++ b/Status.txt
@@ -120,3 +120,19 @@ Ignored
* Flag "fuzzymatching"
* Header "Cache-control"
+
+
+
+==========================================================
+CP 1509 - Refactor media type description for web services
+==========================================================
+
+Not supported.
+
+"There are some significant changes described in CP 1509 to various
+parts of the PS3.18 standard that defines DICOMweb services. [...] The
+most important changes are cleaning up the bulk data media types,
+adding a rendered component to the URL for rendered resources,
+clarifying that compressed bulk data never contains the encapsulation
+item tags, and making JSON support required on the server side and the
+default for query responses." [David Clunie]
diff --git a/UnitTestsSources/UnitTestsMain.cpp b/UnitTestsSources/UnitTestsMain.cpp
index bd26c3e..10434ca 100644
--- a/UnitTestsSources/UnitTestsMain.cpp
+++ b/UnitTestsSources/UnitTestsMain.cpp
@@ -2,6 +2,7 @@
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
@@ -40,6 +41,15 @@ TEST(ContentType, Parse)
ASSERT_EQ(a["type"], "Application/Dicom");
ASSERT_EQ(a["boundary"], "heLLO");
+ // The WADO-RS client must support the case where the WADO-RS server
+ // escapes the "type" subfield in the Content-Type header
+ // cf. https://tools.ietf.org/html/rfc7231#section-3.1.1.1
+ ParseContentType(c, a, "Multipart/Related; TYPE=\"Application/Dicom\" ; Boundary=heLLO");
+ ASSERT_EQ(c, "multipart/related");
+ ASSERT_EQ(2u, a.size());
+ ASSERT_EQ(a["type"], "Application/Dicom");
+ ASSERT_EQ(a["boundary"], "heLLO");
+
ParseContentType(c, a, "");
ASSERT_TRUE(c.empty());
ASSERT_EQ(0u, a.size());
diff --git a/Usage.txt b/Usage.txt
deleted file mode 100644
index 0f78fe8..0000000
--- a/Usage.txt
+++ /dev/null
@@ -1,284 +0,0 @@
-=============
-Configuration
-=============
-
-(1) You must change the Orthanc configuration file to tell Orthanc
- where it can find the DICOMweb plugin. This is done by properly
- modifying the "Plugins" configuration option of Orthanc. For
- instance, in Linux:
-
-{
- ...
- "Plugins" : [
- "/home/user/OrthancDicomWeb/Build/libOrthancDicomWeb.so"
- ]
- ...
-}
-
- Or in Windows:
-
-{
- ...
- "Plugins" : [
- "c:/Temp/OrthancDicomWeb.dll"
- ]
- ...
-}
-
- Note that the DICOMweb server will share all the parameters of the
- Orthanc HTTP server, notably wrt. authentication and HTTPS
- encryption. For this reason, you will most probably have to enable
- the remote access to the Orthanc HTTP server:
-
-{
- ...
- "RemoteAccessEnabled" : true
- ...
-}
-
-
-(2) There are several configuration options that can be set to
- fine-tune the Orthanc DICOMweb server. Here is the full list of
- the available options, all of them must be grouped inside the
- "DicomWeb" section of the Orthanc configuration file:
-
-{
- ...
- "DicomWeb" : {
- "Enable" : true, // Whether DICOMweb support is enabled
- "Root" : "/dicom-web/", // Root URI of the DICOMweb API (for QIDO-RS, STOW-RS and WADO-RS)
- "EnableWado" : true, // Whether WADO-URI (previously known as WADO) support is enabled
- "WadoRoot" : "/wado", // Root URI of the WADO-URI (aka. WADO) API
- "Host" : "localhost", // Hard-codes the name of the host for subsequent WADO-RS requests
- "Ssl" : false, // Whether HTTPS should be used for subsequent WADO-RS requests
- "StowMaxInstances" : 10, // For STOW-RS client, the maximum number of instances in one single HTTP query (0 = no limit)
- "StowMaxSize" : 10 // For STOW-RS client, the maximum size of the body in one single HTTP query (in MB, 0 = no limit)
- }
- ...
-}
-
-
-(3) If you want to connect Orthanc as a client to remote DICOMweb
- servers (cf. below), you need to modify the configuration file so
- as to define each of them in the option "DicomWeb.Servers". The
- syntax is identical to the "OrthancPeers" parameters.
-
- In the most simple case, here is how to instruct Orthanc about the
- existence of a password-less DICOMweb server that will be refered
- to as "sample" in Orthanc:
-
-{
- ...
- "DicomWeb" : {
- "Servers" : {
- "sample" : [ "http://192.168.1.1/dicom-web/" ]
- }
- }
- ...
-}
-
- You are of course free to add as many DICOMweb servers as you
- need. If the DICOMweb server is protected by a password (with HTTP
- Basic access authentication):
-
-{
- ...
- "DicomWeb" : {
- "Servers" : {
- "sample" : [ "http://192.168.1.1/dicom-web/", "username", "password" ]
- }
- }
- ...
-}
-
- If the DICOMweb server is protected with HTTPS client
- authentication, you must provide your client certificate (in the
- PEM format), your client private key (in the PEM format), together
- with the password protecting the private key:
-
-{
- ...
- "DicomWeb" : {
- "Servers" : {
- "sample" : {
- "Url" : "http://192.168.1.1/dicom-web/",
- "CertificateFile" : "client.crt",
- "CertificateKeyFile" : "client.key",
- "CertificateKeyPassword" : "password"
- }
- }
- }
- ...
-}
-
- Finally, it is also possible to use client authentication with
- hardware security modules and smart cards through PKCS#11 (this
- feature is only available is the core of Orthanc was compiled with
- the "-DENABLE_PKCS11=ON" option in CMake, and if the Orthanc
- configuration file has a proper "Pkcs11" section):
-
-{
- ...
- "DicomWeb" : {
- "Servers" : {
- "sample" : {
- "Url" : "http://192.168.1.1/dicom-web/",
- "Pkcs11" : true
- }
- }
- }
- ...
-}
-
- Important remark: When querying a DICOMweb server, Orthanc will
- automatically use the global configuration options "HttpProxy",
- "HttpTimeout", "HttpsVerifyPeers", "HttpsCACertificates", and
- "Pkcs11". Make sure to adapt them if need be.
-
-
-
-=================================
-Querying a remote DICOMweb server
-=================================
-
-Listing the available servers
------------------------------
-
-The list of the remote DICOMweb servers that are known to the DICOMweb
-plugin can be obtained as follows:
-
-# curl http://localhost:8042/dicom-web/servers/
-[ "sample" ]
-
-Here, a single server called "sample" is configured.
-
-
-Making a call to QIDO-RS or WADO-RS
------------------------------------
-
-In Orthanc, the URI "/{dicom-web}/servers/{name}/get" allows to make a
-HTTP GET call against a DICOMweb server. This can be used to issue a
-QIDO-RS or WADO-RS command. Orthanc will take care of properly
-encoding the URL and authenticating the client.
-
-For instance, here is a sample QIDO-RS search to query all the
-studies (using a bash command-line):
-
-# curl http://localhost:8042/dicom-web/servers/sample/get -d @- << EOF
-{
- "Uri" : "/studies"
-}
-EOF
-
-You do not have to specify the base URL of the remote DICOMweb server,
-as it is encoded in the configuration file.
-
-The result of the command above is a multipart "application/dicom+xml"
-document. It is possible to request a more human-friendly JSON answer
-by adding the "Accept" HTTP header. Here is how to search for a given
-patient name, while requesting a JSON answer and pretty-printing
-through the "json_pp" command-line tool:
-
-# curl http://localhost:8042/dicom-web/servers/sample/get -d @- << EOF | json_pp
-{
- "Uri" : "/studies",
- "HttpHeaders" : {
- "Accept" : "application/json"
- },
- "Arguments" : {
- "00100010" : "*JODOGNE*"
- }
-}
-EOF
-
-Note how all the GET arguments must be specified in the "Arguments"
-field. Orthanc will take care of properly encoding it to a URL.
-
-An user-friendly reference of the features available in QIDO-RS and
-WADO-RS can be found at http://dicomweb.hcintegrations.ca/#/home
-
-
-Sending DICOM resources to a STOW-RS server
--------------------------------------------
-
-STOW-RS allows to send local DICOM resources to a remote DICOMweb
-server. In Orthanc, the STOW-RS client primitive is available at URI
-"/{dicom-web}/servers/{name}/stow". Here is a sample call:
-
-# curl http://localhost:8042/dicom-web/servers/sample/stow -X POST -d @- << EOF
-{
- "Resources" : [
- "6ca4c9f3-5e895cb3-4d82c6da-09e060fe-9c59f228"
- ]
-}
-EOF
-
-Note that this primitive takes as its input a list of Orthanc
-identifiers corresponding to the resources (patients, studies, series
-and/or instances) to be exported:
-https://orthanc.chu.ulg.ac.be/book/faq/orthanc-ids.html
-
-Remark 1: Additional HTTP headers can be added with an optional
-"HttpHeaders" argument, as for QIDO-RS and WADO-RS. This might be
-useful e.g. for cookie-based session management.
-
-Remark 2: One call to this "/stow" primitive will possibly result in
-several HTTP requests to the DICOMweb server, in order to limit the
-size of the HTTP messages. The configuration options
-"DicomWeb.StowMaxInstances" and "DicomWeb.StowMaxSize" can be used to
-tune this behavior (set both options to 0 to send one single request).
-
-
-Retrieving DICOM resources from a WADO-RS server
-------------------------------------------------
-
-Once DICOM resources of interest have been identified through a
-QIDO-RS call to a remote DICOMweb server (cf. above), it is
-interesting to download them locally with a WADO-RS call. You could do
-it manually with a second call to the
-"/{dicom-web}/servers/{name}/get" URI, but Orthanc provides another
-primitive "/retrieve" to automate this process.
-
-Here is how you would download one study, one series and one instance
-whose StudyInstanceUID (0020,000d), SeriesInstanceUID (0020,000e) are
-SOPInstanceUID (0008,0018) have been identified through a former
-QIDO-RS call:
-
-# curl http://localhost:8042/dicom-web/servers/sample/retrieve -X POST -d @- << EOF
-{
- "Resources" : [
- {
- "Study" : "1.3.51.0.1.1.192.168.29.133.1688840.1688819"
- },
- {
- "Study" : "1.3.51.0.1.1.192.168.29.133.1681753.1681732",
- "Series" : "1.3.12.2.1107.5.2.33.37097.2012041613040617636372171.0.0.0"
- },
- {
- "Study" : "1.3.51.0.1.1.192.168.29.133.1681753.1681732",
- "Series" : "1.3.12.2.1107.5.2.33.37097.2012041612474981424569674.0.0.0",
- "Instance" : "1.3.12.2.1107.5.2.33.37097.2012041612485540185869716"
- }
- ]
-}
-EOF
-
-Orthanc will reply with the list of the Orthanc identifiers of all the
-DICOM instances that were downloaded from the remote server.
-
-Remark 1: Contrarily to the "/stow" URI that uses Orthanc identifiers,
-the "/retrieve" URI uses DICOM identifiers.
-
-Remark 2: The "HttpArguments" is also available.
-
-
-
-=======
-Samples
-=======
-
-Samples of how to call DICOMweb services from standalone applications
-can be found in the following folders:
-
-- In Python: see ./Resources/Samples/Python/
-- In JavaScript: see ./Resources/Samples/Python/
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/orthanc-dicomweb.git
More information about the debian-med-commit
mailing list